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.metricshub.wbem.sblim.slp;
27  
28  /*-
29   * ╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲
30   * WBEM Java Client
31   * ჻჻჻჻჻჻
32   * Copyright 2023 - 2025 MetricsHub
33   * ჻჻჻჻჻჻
34   * Licensed under the Apache License, Version 2.0 (the "License");
35   * you may not use this file except in compliance with the License.
36   * You may obtain a copy of the License at
37   *
38   *      http://www.apache.org/licenses/LICENSE-2.0
39   *
40   * Unless required by applicable law or agreed to in writing, software
41   * distributed under the License is distributed on an "AS IS" BASIS,
42   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
43   * See the License for the specific language governing permissions and
44   * limitations under the License.
45   * ╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱
46   */
47  
48  import java.io.Serializable;
49  
50  /**
51   * The ServiceURL object models the advertised SLP service URL. It can be either
52   * a service: URL or a regular URL. These objects are returned from service
53   * lookup requests, and describe the registered services. This class should be a
54   * subclass of java.net.URL but can't since that class is final.
55   */
56  public class ServiceURL implements Serializable {
57  	private static final long serialVersionUID = 8998115518853094365L;
58  
59  	/**
60  	 * Indicates that no port information is required or was returned for this
61  	 * URL.
62  	 */
63  	public static final int NO_PORT = 0;
64  
65  	/**
66  	 * Indicates that the URL has a zero lifetime. This value is never returned
67  	 * from the API, but can be used to create a ServiceURL object to
68  	 * deregister, delete attributes, or find attributes.
69  	 */
70  	public static final int LIFETIME_NONE = 0;
71  
72  	/**
73  	 * The default URL lifetime (3 hours) in seconds.
74  	 */
75  	public static final int LIFETIME_DEFAULT = 10800;
76  
77  	/**
78  	 * The maximum URL lifetime (about 18 hours) in seconds.
79  	 */
80  	public static final int LIFETIME_MAXIMUM = 65535;
81  
82  	/**
83  	 * Indicates that the API implementation should continuously re-register the
84  	 * URL until the application exits.
85  	 */
86  	public static final int LIFETIME_PERMANENT = -1;
87  
88  	static final int PORT_MAXIMUM = 65535;
89  
90  	private ServiceType iServiceType = null;
91  
92  	private String iTransport = null;
93  
94  	private String iHost = null;
95  
96  	private int iPort = 0;
97  
98  	private String iURLPath = null;
99  
100 	private int iLifetime = LIFETIME_DEFAULT;
101 
102 	/**
103 	 * Construct a service URL object having the specified lifetime.
104 	 *
105 	 * @param pServiceURL
106 	 *            The URL as a string. Must be either a service: URL or a valid
107 	 *            generic URL according to RFC 2396 [2].
108 	 * @param pLifetime
109 	 *            The service advertisement lifetime in seconds. This value may
110 	 *            be either between LIFETIME_NONE and LIFETIME_MAXIMUM or
111 	 *            LIFETIME_PERMANENT.
112 	 */
113 	public ServiceURL(String pServiceURL, int pLifetime) {
114 		if (pLifetime > LIFETIME_MAXIMUM || pLifetime < LIFETIME_PERMANENT) throw new IllegalArgumentException(
115 			"lifetime:" + pLifetime
116 		);
117 
118 		for (int i = 0; i < pServiceURL.length(); i++) {
119 			char c = pServiceURL.charAt(i);
120 			if ("/:-.%_\'*()$!,+\\;@?&=[]".indexOf(c) == -1 && !Character.isLetterOrDigit(c)) {
121 				throw new IllegalArgumentException("invalid character: '" + c + "' on string \"" + pServiceURL + "\"");
122 			}
123 		}
124 
125 		parseURL(pServiceURL);
126 
127 		this.iLifetime = (pLifetime == LIFETIME_PERMANENT) ? LIFETIME_MAXIMUM : pLifetime;
128 	}
129 
130 	/**
131 	 * Returns the service type object representing the service type name of the
132 	 * URL.
133 	 *
134 	 * @return The service type
135 	 */
136 	public ServiceType getServiceType() {
137 		return this.iServiceType;
138 	}
139 
140 	/**
141 	 * Set the service type name to the object. Ignored if the URL is a service:
142 	 * URL.
143 	 *
144 	 * @param pServicetype
145 	 *            The service type object.
146 	 */
147 	public void setServiceType(ServiceType pServicetype) {
148 		if (!this.iServiceType.isServiceURL()) this.iServiceType = pServicetype;
149 	}
150 
151 	/**
152 	 * Get the network layer transport identifier. If the transport is IP, an
153 	 * empty string, "", is returned.
154 	 *
155 	 * @return The NLT identifier
156 	 */
157 	public String getTransport() {
158 		// FIXME What the hell is it?
159 		return "";
160 	}
161 
162 	/**
163 	 * Returns the host identifier. For IP, this will be the machine name or IP
164 	 * address.
165 	 *
166 	 * @return The host
167 	 */
168 	public String getHost() {
169 		return this.iHost;
170 	}
171 
172 	/**
173 	 * Returns the port number, if any. For non-IP transports, always returns
174 	 * NO_PORT.
175 	 *
176 	 * @return The port
177 	 */
178 	public int getPort() {
179 		return this.iPort;
180 	}
181 
182 	/**
183 	 * Returns the URL path description, if any.
184 	 *
185 	 * @return The URL path
186 	 */
187 	public String getURLPath() {
188 		return this.iURLPath;
189 	}
190 
191 	/**
192 	 * Returns the service advertisement lifetime. This will be a positive int
193 	 * between LIFETIME_NONE and LIFETIME_MAXIMUM.
194 	 *
195 	 * @return The lifetime
196 	 */
197 	public int getLifetime() {
198 		return this.iLifetime;
199 	}
200 
201 	/*
202 	 * (non-Javadoc)
203 	 *
204 	 * @see java.lang.Object#equals(java.lang.Object)
205 	 *
206 	 * Compares the object to the ServiceURL and returns true if the two are the
207 	 * same. Two ServiceURL objects are equal if their current service types
208 	 * match and they have the same host, port, transport, and URL path.
209 	 */
210 	@Override
211 	public boolean equals(Object obj) {
212 		if (obj == this) return true;
213 		if (!(obj instanceof ServiceURL)) return false;
214 
215 		ServiceURL that = (ServiceURL) obj;
216 
217 		return (
218 			equalObjs(this.iServiceType, that.iServiceType) &&
219 			equalStrs(this.iTransport, that.iTransport) &&
220 			equalStrs(this.iHost, that.iHost) &&
221 			this.iPort == that.iPort
222 		);
223 	}
224 
225 	/*
226 	 * (non-Javadoc)
227 	 *
228 	 * @see java.lang.Object#toString()
229 	 *
230 	 * Returns a formatted string with the URL. Overrides Object.toString(). The
231 	 * returned URL has the original service type or URL scheme, not the current
232 	 * service type.
233 	 */
234 	@Override
235 	public String toString() {
236 		StringBuffer buf = new StringBuffer();
237 		if (this.iServiceType != null) buf.append(this.iServiceType);
238 		if (this.iURLPath != null) {
239 			if (buf.length() > 0) buf.append("://");
240 			buf.append(this.iURLPath);
241 		}
242 		return buf.toString();
243 	}
244 
245 	private int iHashCode = 0;
246 
247 	/*
248 	 * (non-Javadoc)
249 	 *
250 	 * @see java.lang.Object#hashCode()
251 	 *
252 	 * Overrides Object.hashCode(). Hashes on the current service type,
253 	 * transport, host, port, and URL part. !! in this case toString() must not
254 	 * contain the lifeTime
255 	 */
256 	@Override
257 	public int hashCode() {
258 		if (this.iHashCode == 0) {
259 			this.iHashCode = toString().hashCode();
260 		}
261 		return this.iHashCode;
262 	}
263 
264 	private static final String DELIM = "://";
265 
266 	/**
267 	 * <pre>
268 	 * service: URL or URL
269 	 *
270 	 * &quot;service:&quot; srvtype &quot;://&quot; addrspec
271 	 * &quot;service:&quot; abstract-type &quot;:&quot; concrete-type&gt; &quot;://&quot; addrspecc
272 	 *
273 	 * addrspesc  = ( hostName / IPv4Address / IPv6Address ) [ &quot;:&quot; port ]
274 	 * </pre>
275 	 *
276 	 * @param pUrlString
277 	 * @throws IllegalArgumentException
278 	 */
279 	private void parseURL(String pUrlStr) throws IllegalArgumentException {
280 		int srvTypeEndIdx = pUrlStr.indexOf(DELIM);
281 		String addrStr;
282 		if (srvTypeEndIdx >= 0) {
283 			this.iServiceType = new ServiceType(pUrlStr.substring(0, srvTypeEndIdx));
284 			addrStr = pUrlStr.substring(srvTypeEndIdx + DELIM.length());
285 		} else {
286 			if (pUrlStr.startsWith("service:")) {
287 				this.iServiceType = new ServiceType(pUrlStr);
288 				addrStr = null;
289 			} else {
290 				addrStr = pUrlStr;
291 			}
292 		}
293 		if (addrStr == null) return;
294 		this.iURLPath = addrStr;
295 		if (addrStr.charAt(0) == '[') {
296 			parseIPv6Address(addrStr);
297 		} else {
298 			parseIPv4Address(addrStr);
299 		}
300 	}
301 
302 	private void parseIPv6Address(String pAddrStr) throws IllegalArgumentException {
303 		int hostEndIdx = pAddrStr.indexOf(']');
304 		if (hostEndIdx < 0) throw new IllegalArgumentException("']' is not found for IPv6 address");
305 		int colonIdx = hostEndIdx + 1;
306 		this.iHost = pAddrStr.substring(0, colonIdx);
307 		if (colonIdx < pAddrStr.length()) {
308 			if (pAddrStr.charAt(colonIdx) != ':') throw new IllegalArgumentException(
309 				"':' expected in \"" + pAddrStr + "\" at position " + colonIdx + " !"
310 			);
311 			parsePort(pAddrStr.substring(colonIdx + 1), pAddrStr);
312 		}
313 	}
314 
315 	private void parseIPv4Address(String pAddrStr) {
316 		int colonIdx = pAddrStr.indexOf(':');
317 		if (colonIdx > 0) {
318 			this.iHost = pAddrStr.substring(0, colonIdx);
319 			parsePort(pAddrStr.substring(colonIdx + 1), pAddrStr);
320 		} else {
321 			this.iHost = pAddrStr;
322 		}
323 	}
324 
325 	private void parsePort(String pPortStr, String pAddrStr) throws IllegalArgumentException {
326 		try {
327 			this.iPort = Integer.parseInt(pPortStr);
328 		} catch (NumberFormatException e) {
329 			throw new IllegalArgumentException("Port field : " + pPortStr + " in " + pAddrStr + " is invalid!");
330 		}
331 	}
332 
333 	private static boolean equalObjs(Object pThis, Object pThat) {
334 		return pThis == null ? pThat == null : pThis.equals(pThat);
335 	}
336 
337 	private static boolean equalStrs(String pThis, String pThat) {
338 		return (pThis == null || pThis.length() == 0) ? (pThat == null || pThat.length() == 0) : pThis.equals(pThat);
339 	}
340 }