View Javadoc
1   /*
2     ServiceURL.java
3   
4     (C) Copyright IBM Corp. 2005, 2009
5   
6     THIS FILE IS PROVIDED UNDER THE TERMS OF THE ECLIPSE PUBLIC LICENSE
7     ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THIS FILE
8     CONSTITUTES RECIPIENTS ACCEPTANCE OF THE AGREEMENT.
9   
10    You can obtain a current copy of the Eclipse Public License from
11    http://www.opensource.org/licenses/eclipse-1.0.php
12  
13    @author : Roberto Pineiro, IBM, roberto.pineiro@us.ibm.com
14   * @author : Chung-hao Tan, IBM, chungtan@us.ibm.com
15   * 
16   * Change History
17   * Flag       Date        Prog         Description
18   *------------------------------------------------------------------------------- 
19   * 1516246    2006-07-22  lupusalex    Integrate SLP client code
20   * 1804402    2007-09-28  ebak         IPv6 ready SLP
21   * 2003590    2008-06-30  blaschke-oss Change licensing from CPL to EPL
22   * 2204488 	  2008-10-28  raman_arora  Fix code to remove compiler warnings
23   * 2524131    2009-01-21  raman_arora  Upgrade client to JDK 1.5 (Phase 1)
24   */
25  
26  package org.sentrysoftware.wbem.sblim.slp;
27  
28  /*-
29   * ╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲
30   * WBEM Java Client
31   * ჻჻჻჻჻჻
32   * Copyright (C) 2023 Sentry Software
33   * ჻჻჻჻჻჻
34   * This program is free software: you can redistribute it and/or modify
35   * it under the terms of the GNU Lesser General Public License as
36   * published by the Free Software Foundation, either version 3 of the
37   * License, or (at your option) any later version.
38   *
39   * This program is distributed in the hope that it will be useful,
40   * but WITHOUT ANY WARRANTY; without even the implied warranty of
41   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
42   * GNU General Lesser Public License for more details.
43   *
44   * You should have received a copy of the GNU General Lesser Public
45   * License along with this program.  If not, see
46   * <http://www.gnu.org/licenses/lgpl-3.0.html>.
47   * ╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱
48   */
49  
50  import java.io.Serializable;
51  
52  /**
53   * The ServiceURL object models the advertised SLP service URL. It can be either
54   * a service: URL or a regular URL. These objects are returned from service
55   * lookup requests, and describe the registered services. This class should be a
56   * subclass of java.net.URL but can't since that class is final.
57   */
58  public class ServiceURL implements Serializable {
59  
60  	private static final long serialVersionUID = 8998115518853094365L;
61  
62  	/**
63  	 * Indicates that no port information is required or was returned for this
64  	 * URL.
65  	 */
66  	public static final int NO_PORT = 0;
67  
68  	/**
69  	 * Indicates that the URL has a zero lifetime. This value is never returned
70  	 * from the API, but can be used to create a ServiceURL object to
71  	 * deregister, delete attributes, or find attributes.
72  	 */
73  	public static final int LIFETIME_NONE = 0;
74  
75  	/**
76  	 * The default URL lifetime (3 hours) in seconds.
77  	 */
78  	public static final int LIFETIME_DEFAULT = 10800;
79  
80  	/**
81  	 * The maximum URL lifetime (about 18 hours) in seconds.
82  	 */
83  	public static final int LIFETIME_MAXIMUM = 65535;
84  
85  	/**
86  	 * Indicates that the API implementation should continuously re-register the
87  	 * URL until the application exits.
88  	 */
89  	public static final int LIFETIME_PERMANENT = -1;
90  
91  	static final int PORT_MAXIMUM = 65535;
92  
93  	private ServiceType iServiceType = null;
94  
95  	private String iTransport = null;
96  
97  	private String iHost = null;
98  
99  	private int iPort = 0;
100 
101 	private String iURLPath = null;
102 
103 	private int iLifetime = LIFETIME_DEFAULT;
104 
105 	/**
106 	 * Construct a service URL object having the specified lifetime.
107 	 * 
108 	 * @param pServiceURL
109 	 *            The URL as a string. Must be either a service: URL or a valid
110 	 *            generic URL according to RFC 2396 [2].
111 	 * @param pLifetime
112 	 *            The service advertisement lifetime in seconds. This value may
113 	 *            be either between LIFETIME_NONE and LIFETIME_MAXIMUM or
114 	 *            LIFETIME_PERMANENT.
115 	 */
116 	public ServiceURL(String pServiceURL, int pLifetime) {
117 
118 		if (pLifetime > LIFETIME_MAXIMUM || pLifetime < LIFETIME_PERMANENT) throw new IllegalArgumentException(
119 				"lifetime:" + pLifetime);
120 
121 		for (int i = 0; i < pServiceURL.length(); i++) {
122 			char c = pServiceURL.charAt(i);
123 			if ("/:-.%_\'*()$!,+\\;@?&=[]".indexOf(c) == -1 && !Character.isLetterOrDigit(c)) { throw new IllegalArgumentException(
124 					"invalid character: '" + c + "' on string \"" + pServiceURL + "\""); }
125 		}
126 
127 		parseURL(pServiceURL);
128 
129 		this.iLifetime = (pLifetime == LIFETIME_PERMANENT) ? LIFETIME_MAXIMUM : pLifetime;
130 	}
131 
132 	/**
133 	 * Returns the service type object representing the service type name of the
134 	 * URL.
135 	 * 
136 	 * @return The service type
137 	 */
138 	public ServiceType getServiceType() {
139 		return this.iServiceType;
140 	}
141 
142 	/**
143 	 * Set the service type name to the object. Ignored if the URL is a service:
144 	 * URL.
145 	 * 
146 	 * @param pServicetype
147 	 *            The service type object.
148 	 */
149 	public void setServiceType(ServiceType pServicetype) {
150 		if (!this.iServiceType.isServiceURL()) this.iServiceType = pServicetype;
151 	}
152 
153 	/**
154 	 * Get the network layer transport identifier. If the transport is IP, an
155 	 * empty string, "", is returned.
156 	 * 
157 	 * @return The NLT identifier
158 	 */
159 	public String getTransport() {
160 		// FIXME What the hell is it?
161 		return "";
162 	}
163 
164 	/**
165 	 * Returns the host identifier. For IP, this will be the machine name or IP
166 	 * address.
167 	 * 
168 	 * @return The host
169 	 */
170 	public String getHost() {
171 		return this.iHost;
172 	}
173 
174 	/**
175 	 * Returns the port number, if any. For non-IP transports, always returns
176 	 * NO_PORT.
177 	 * 
178 	 * @return The port
179 	 */
180 	public int getPort() {
181 		return this.iPort;
182 	}
183 
184 	/**
185 	 * Returns the URL path description, if any.
186 	 * 
187 	 * @return The URL path
188 	 */
189 	public String getURLPath() {
190 		return this.iURLPath;
191 	}
192 
193 	/**
194 	 * Returns the service advertisement lifetime. This will be a positive int
195 	 * between LIFETIME_NONE and LIFETIME_MAXIMUM.
196 	 * 
197 	 * @return The lifetime
198 	 */
199 	public int getLifetime() {
200 		return this.iLifetime;
201 	}
202 
203 	/*
204 	 * (non-Javadoc)
205 	 * 
206 	 * @see java.lang.Object#equals(java.lang.Object)
207 	 * 
208 	 * Compares the object to the ServiceURL and returns true if the two are the
209 	 * same. Two ServiceURL objects are equal if their current service types
210 	 * match and they have the same host, port, transport, and URL path.
211 	 */
212 	@Override
213 	public boolean equals(Object obj) {
214 		if (obj == this) return true;
215 		if (!(obj instanceof ServiceURL)) return false;
216 
217 		ServiceURL that = (ServiceURL) obj;
218 
219 		return equalObjs(this.iServiceType, that.iServiceType)
220 				&& equalStrs(this.iTransport, that.iTransport) && equalStrs(this.iHost, that.iHost)
221 				&& this.iPort == that.iPort;
222 	}
223 
224 	/*
225 	 * (non-Javadoc)
226 	 * 
227 	 * @see java.lang.Object#toString()
228 	 * 
229 	 * Returns a formatted string with the URL. Overrides Object.toString(). The
230 	 * returned URL has the original service type or URL scheme, not the current
231 	 * service type.
232 	 */
233 	@Override
234 	public String toString() {
235 		StringBuffer buf = new StringBuffer();
236 		if (this.iServiceType != null) buf.append(this.iServiceType);
237 		if (this.iURLPath != null) {
238 			if (buf.length() > 0) buf.append("://");
239 			buf.append(this.iURLPath);
240 		}
241 		return buf.toString();
242 	}
243 
244 	private int iHashCode = 0;
245 
246 	/*
247 	 * (non-Javadoc)
248 	 * 
249 	 * @see java.lang.Object#hashCode()
250 	 * 
251 	 * Overrides Object.hashCode(). Hashes on the current service type,
252 	 * transport, host, port, and URL part. !! in this case toString() must not
253 	 * contain the lifeTime
254 	 */
255 	@Override
256 	public int hashCode() {
257 		if (this.iHashCode == 0) {
258 			this.iHashCode = toString().hashCode();
259 		}
260 		return this.iHashCode;
261 	}
262 
263 	private static final String DELIM = "://";
264 
265 	/**
266 	 * <pre>
267 	 * service: URL or URL
268 	 * 
269 	 * &quot;service:&quot; srvtype &quot;://&quot; addrspec
270 	 * &quot;service:&quot; abstract-type &quot;:&quot; concrete-type&gt; &quot;://&quot; addrspecc
271 	 * 
272 	 * addrspesc  = ( hostName / IPv4Address / IPv6Address ) [ &quot;:&quot; port ]
273 	 * </pre>
274 	 * 
275 	 * @param pUrlString
276 	 * @throws IllegalArgumentException
277 	 */
278 	private void parseURL(String pUrlStr) throws IllegalArgumentException {
279 		int srvTypeEndIdx = pUrlStr.indexOf(DELIM);
280 		String addrStr;
281 		if (srvTypeEndIdx >= 0) {
282 			this.iServiceType = new ServiceType(pUrlStr.substring(0, srvTypeEndIdx));
283 			addrStr = pUrlStr.substring(srvTypeEndIdx + DELIM.length());
284 		} else {
285 			if (pUrlStr.startsWith("service:")) {
286 				this.iServiceType = new ServiceType(pUrlStr);
287 				addrStr = null;
288 			} else {
289 				addrStr = pUrlStr;
290 			}
291 		}
292 		if (addrStr == null) return;
293 		this.iURLPath = addrStr;
294 		if (addrStr.charAt(0) == '[') {
295 			parseIPv6Address(addrStr);
296 		} else {
297 			parseIPv4Address(addrStr);
298 		}
299 	}
300 
301 	private void parseIPv6Address(String pAddrStr) throws IllegalArgumentException {
302 		int hostEndIdx = pAddrStr.indexOf(']');
303 		if (hostEndIdx < 0) throw new IllegalArgumentException("']' is not found for IPv6 address");
304 		int colonIdx = hostEndIdx + 1;
305 		this.iHost = pAddrStr.substring(0, colonIdx);
306 		if (colonIdx < pAddrStr.length()) {
307 			if (pAddrStr.charAt(colonIdx) != ':') throw new IllegalArgumentException(
308 					"':' expected in \"" + pAddrStr + "\" at position " + colonIdx + " !");
309 			parsePort(pAddrStr.substring(colonIdx + 1), pAddrStr);
310 		}
311 	}
312 
313 	private void parseIPv4Address(String pAddrStr) {
314 		int colonIdx = pAddrStr.indexOf(':');
315 		if (colonIdx > 0) {
316 			this.iHost = pAddrStr.substring(0, colonIdx);
317 			parsePort(pAddrStr.substring(colonIdx + 1), pAddrStr);
318 		} else {
319 			this.iHost = pAddrStr;
320 		}
321 	}
322 
323 	private void parsePort(String pPortStr, String pAddrStr) throws IllegalArgumentException {
324 		try {
325 			this.iPort = Integer.parseInt(pPortStr);
326 		} catch (NumberFormatException e) {
327 			throw new IllegalArgumentException("Port field : " + pPortStr + " in " + pAddrStr
328 					+ " is invalid!");
329 		}
330 	}
331 
332 	private static boolean equalObjs(Object pThis, Object pThat) {
333 		return pThis == null ? pThat == null : pThis.equals(pThat);
334 	}
335 
336 	private static boolean equalStrs(String pThis, String pThat) {
337 		return (pThis == null || pThis.length() == 0) ? (pThat == null || pThat.length() == 0)
338 				: pThis.equals(pThat);
339 	}
340 
341 }