View Javadoc
1   /*
2     (C) Copyright IBM Corp. 2007, 2009
3    
4     THIS FILE IS PROVIDED UNDER THE TERMS OF THE ECLIPSE PUBLIC LICENSE 
5     ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THIS FILE 
6     CONSTITUTES RECIPIENTS ACCEPTANCE OF THE AGREEMENT.
7    
8     You can obtain a current copy of the Eclipse Public License from
9     http://www.opensource.org/licenses/eclipse-1.0.php
10   
11    @author : Alexander Wolf-Reber, IBM, a.wolf-reber@de.ibm.com
12   * 
13   * Change History
14   * Flag       Date        Prog         Description
15   *------------------------------------------------------------------------------- 
16   * 1678915    2007-03-12  lupusalex    Integrated WBEM service discovery via SLP
17   * 1729361    2007-06-04  lupusalex    Multicast discovery is broken in DiscovererSLP
18   * 2003590    2008-06-30  blaschke-oss Change licensing from CPL to EPL
19   * 2524131    2009-01-21  raman_arora  Upgrade client to JDK 1.5 (Phase 1)
20   * 2531371    2009-02-10  raman_arora  Upgrade client to JDK 1.5 (Phase 2)
21   * 2763216    2009-04-14  blaschke-oss Code cleanup: visible spelling/grammar errors
22   */
23  
24  package org.metricshub.wbem.sblim.cimclient.discovery;
25  
26  import java.util.ArrayList;
27  import java.util.Collection;
28  import java.util.HashMap;
29  import java.util.Iterator;
30  import java.util.LinkedList;
31  import java.util.List;
32  import java.util.Locale;
33  import java.util.Map;
34  import java.util.Set;
35  import java.util.logging.Level;
36  /*-
37   * ╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲
38   * WBEM Java Client
39   * ჻჻჻჻჻჻
40   * Copyright 2023 - 2025 MetricsHub
41   * ჻჻჻჻჻჻
42   * Licensed under the Apache License, Version 2.0 (the "License");
43   * you may not use this file except in compliance with the License.
44   * You may obtain a copy of the License at
45   *
46   *      http://www.apache.org/licenses/LICENSE-2.0
47   *
48   * Unless required by applicable law or agreed to in writing, software
49   * distributed under the License is distributed on an "AS IS" BASIS,
50   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
51   * See the License for the specific language governing permissions and
52   * limitations under the License.
53   * ╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱
54   */
55  
56  import javax.security.auth.Subject;
57  import org.metricshub.wbem.javax.wbem.client.WBEMClient;
58  import org.metricshub.wbem.sblim.cimclient.internal.logging.LogAndTraceBroker;
59  
60  /**
61   * Class AdvertisementCatalog implements a catalog for WBEM service
62   * advertisements. In practice we usually have multiple advertisements for a
63   * single service, because the DMTF mandates an advertisement per remote service
64   * access point (e.g. http, https, rmi). This class tries to ease the management
65   * of this by indexing services by the unique service id and therefore surfacing
66   * which advertisements belong to the same service. The application might then
67   * choose it's preferred communication mechanism.
68   *
69   * No thread synchronization provided, this is the responsibility of
70   *            the caller.
71   * @since 2.0.2
72   */
73  public class AdvertisementCatalog {
74  
75  	/**
76  	 * Interface EventListener specifies listener that are called when an
77  	 * advertisement is added to or removed from the catalog, expires or is
78  	 * renewed.
79  	 *
80  	 */
81  	public static interface EventListener {
82  		/**
83  		 * Called when an advertisement is added to the catalog that has not
84  		 * been a member of the catalog before.
85  		 *
86  		 * @param pEvent
87  		 *            The type of the event. One of the constants
88  		 *            <code>EVENT_ADD, EVENT_REMOVE, EVENT_EXPIRE, EVENT_RENEW</code>
89  		 *            in <code>AdvertisementCatalog</code>.
90  		 *
91  		 * @param pAdvertisment
92  		 *            The added advertisement
93  		 */
94  		public void advertisementEvent(int pEvent, WBEMServiceAdvertisement pAdvertisment);
95  	}
96  
97  	/**
98  	 * Class AdvertisementDecorator decorates a WBEMAdvertisement with state
99  	 * information required by the AdvertisementCatalog class
100 	 *
101 	 * @pattern Decorator
102 	 * <code>iAvertisement!=null</code>
103 	 */
104 	private static class AdvertisementDecorator implements WBEMServiceAdvertisement {
105 		private WBEMServiceAdvertisement iAdvertisement;
106 
107 		private boolean iRefresh = false;
108 
109 		/**
110 		 * Ctor.
111 		 *
112 		 * @param pAdvertisement
113 		 *            The advertisement to decorate
114 		 */
115 		protected AdvertisementDecorator(WBEMServiceAdvertisement pAdvertisement) {
116 			if (pAdvertisement == null) throw new IllegalArgumentException("Advertisement is null");
117 			this.iAdvertisement = pAdvertisement;
118 		}
119 
120 		/**
121 		 * Returns advertisement
122 		 *
123 		 * @return The value of advertisement.
124 		 */
125 		protected WBEMServiceAdvertisement getAdvertisementXXX() {
126 			return this.iAdvertisement;
127 		}
128 
129 		/**
130 		 * Returns refresh
131 		 *
132 		 * @return The value of refresh.
133 		 */
134 		protected boolean isRefresh() {
135 			return this.iRefresh;
136 		}
137 
138 		/**
139 		 * Sets advertisement
140 		 *
141 		 * @param pAdvertisement
142 		 *            The new value of advertisement.
143 		 */
144 		protected void setAdvertisement(WBEMServiceAdvertisement pAdvertisement) {
145 			this.iAdvertisement = pAdvertisement;
146 		}
147 
148 		/**
149 		 * Sets refresh
150 		 *
151 		 * @param pRefresh
152 		 *            The new value of refresh.
153 		 */
154 		protected void setRefresh(boolean pRefresh) {
155 			this.iRefresh = pRefresh;
156 		}
157 
158 		/*
159 		 * (non-Javadoc)
160 		 *
161 		 * @see java.lang.Object#equals(java.lang.Object)
162 		 */
163 		@Override
164 		public boolean equals(Object pObj) {
165 			return this.iAdvertisement.equals(pObj);
166 		}
167 
168 		/*
169 		 * (non-Javadoc)
170 		 *
171 		 * @see java.lang.Object#hashCode()
172 		 */
173 		@Override
174 		public int hashCode() {
175 			return this.iAdvertisement.hashCode();
176 		}
177 
178 		/**
179 		 * @see WBEMServiceAdvertisement#createClient(javax.security.auth.Subject, java.util.Locale[])
180 		 */
181 		public WBEMClient createClient(Subject pSubject, Locale[] pLocales) throws Exception {
182 			return this.iAdvertisement.createClient(pSubject, pLocales);
183 		}
184 
185 		/**
186 		 * @see WBEMServiceAdvertisement#getAttribute(java.lang.String)
187 		 */
188 		public String getAttribute(String pAttributeName) {
189 			return this.iAdvertisement.getAttribute(pAttributeName);
190 		}
191 
192 		/**
193 		 * @see WBEMServiceAdvertisement#getAttributes()
194 		 */
195 		public Set<Map.Entry<String, String>> getAttributes() {
196 			return this.iAdvertisement.getAttributes();
197 		}
198 
199 		/**
200 		 * @see WBEMServiceAdvertisement#getConcreteServiceType()
201 		 */
202 		public String getConcreteServiceType() {
203 			return this.iAdvertisement.getConcreteServiceType();
204 		}
205 
206 		/**
207 		 * @see WBEMServiceAdvertisement#getDirectory()
208 		 */
209 		public String getDirectory() {
210 			return this.iAdvertisement.getDirectory();
211 		}
212 
213 		/**
214 		 * @see WBEMServiceAdvertisement#getInteropNamespaces()
215 		 */
216 		public String[] getInteropNamespaces() {
217 			return this.iAdvertisement.getInteropNamespaces();
218 		}
219 
220 		/**
221 		 * @see WBEMServiceAdvertisement#getServiceId()
222 		 */
223 		public String getServiceId() {
224 			return this.iAdvertisement.getServiceId();
225 		}
226 
227 		/**
228 		 * @see WBEMServiceAdvertisement#getServiceUrl()
229 		 */
230 		public String getServiceUrl() {
231 			return this.iAdvertisement.getServiceUrl();
232 		}
233 
234 		/**
235 		 * @see WBEMServiceAdvertisement#isExpired()
236 		 */
237 		public boolean isExpired() {
238 			return this.iAdvertisement.isExpired();
239 		}
240 
241 		/**
242 		 * @see WBEMServiceAdvertisement#setExpired(boolean)
243 		 */
244 		public void setExpired(boolean pExpired) {
245 			this.iAdvertisement.setExpired(pExpired);
246 		}
247 	}
248 
249 	/**
250 	 * Event code when advertisement is added
251 	 */
252 	public static int EVENT_ADD = 1;
253 
254 	/**
255 	 * Event code when advertisement is removed
256 	 */
257 	public static int EVENT_REMOVE = 2;
258 
259 	/**
260 	 * Event code when advertisement expires
261 	 */
262 	public static int EVENT_EXPIRE = 4;
263 
264 	/**
265 	 * Event code when advertisement is renewed
266 	 */
267 	public static int EVENT_RENEW = 8;
268 
269 	private List<EventListener> iListeners = new LinkedList<EventListener>();
270 
271 	private HashMap<String, List<AdvertisementDecorator>> iCatalogByDirectory = new HashMap<String, List<AdvertisementDecorator>>();
272 
273 	private Map<String, Map<WBEMProtocol, WBEMServiceAdvertisement>> iCatalogById = new HashMap<String, Map<WBEMProtocol, WBEMServiceAdvertisement>>();
274 
275 	/**
276 	 * Ctor.
277 	 */
278 	public AdvertisementCatalog() {
279 		/**/
280 	}
281 
282 	/**
283 	 * Adds a listener for "add" events. The listener will be called whenever a
284 	 * advertisement is added to the catalog.
285 	 *
286 	 * @param pListener
287 	 *            The listener
288 	 */
289 	public void addEventListener(EventListener pListener) {
290 		this.iListeners.add(pListener);
291 	}
292 
293 	/**
294 	 * Adds new advertisements to the catalog. Existing advertisements sharing
295 	 * concrete type, url and directory are replaced.
296 	 *
297 	 * @param pAdvertisements
298 	 *            The new advertisements
299 	 */
300 	public void addAdvertisements(WBEMServiceAdvertisement[] pAdvertisements) {
301 		for (int i = 0; i < pAdvertisements.length; ++i) {
302 			String url = "";
303 			try {
304 				WBEMServiceAdvertisement advertisement = pAdvertisements[i];
305 				url = advertisement.getServiceUrl();
306 				WBEMProtocol protocol = makeProtocol(advertisement);
307 				String serviceId = advertisement.getAttribute(WBEMServiceAdvertisement.SERVICE_ID);
308 				{
309 					List<AdvertisementDecorator> innerList = this.iCatalogByDirectory.get(advertisement.getDirectory());
310 					if (innerList == null) {
311 						innerList = new ArrayList<AdvertisementDecorator>();
312 						this.iCatalogByDirectory.put(advertisement.getDirectory(), innerList);
313 					}
314 					AdvertisementDecorator entry = findAdvertisement(innerList, advertisement);
315 					if (entry != null) {
316 						boolean wasExpired = entry.isExpired();
317 						entry.setAdvertisement(advertisement);
318 						entry.setRefresh(false);
319 						entry.setExpired(false);
320 						if (wasExpired) {
321 							notifyEventListeners(EVENT_RENEW, advertisement);
322 						}
323 					} else {
324 						innerList.add(new AdvertisementDecorator(advertisement));
325 						notifyEventListeners(EVENT_ADD, advertisement);
326 					}
327 				}
328 				{
329 					Map<WBEMProtocol, WBEMServiceAdvertisement> innerMap = this.iCatalogById.get(serviceId);
330 					if (innerMap == null) {
331 						innerMap = new HashMap<WBEMProtocol, WBEMServiceAdvertisement>();
332 						this.iCatalogById.put(serviceId, innerMap);
333 					}
334 					innerMap.put(protocol, advertisement);
335 				}
336 			} catch (Exception e) {
337 				LogAndTraceBroker.getBroker().trace(Level.FINE, "Incomplete advertisement for" + url, e);
338 			}
339 		}
340 	}
341 
342 	/**
343 	 * Returns the advertisement from the catalog corresponding to a given id
344 	 * and with the protocol preferred most.
345 	 *
346 	 * @param pId
347 	 *            The service id
348 	 * @param pProtocols
349 	 *            An array containing the desired protocols in order of
350 	 *            preference. If a service doesn't advertise any of the given
351 	 *            protocols this service returns null.
352 	 * @return The corresponding advertisement
353 	 */
354 	public WBEMServiceAdvertisement getAdvertisement(final String pId, final WBEMProtocol[] pProtocols) {
355 		Map<WBEMProtocol, WBEMServiceAdvertisement> innerMap = this.iCatalogById.get(pId);
356 		if (innerMap == null) {
357 			return null;
358 		}
359 		for (int i = 0; i < pProtocols.length; ++i) {
360 			WBEMServiceAdvertisement advertisement = innerMap.get(pProtocols[i]);
361 			if (advertisement != null) {
362 				return advertisement;
363 			}
364 		}
365 		return null;
366 	}
367 
368 	/**
369 	 * Returns the advertisements from the catalog corresponding to a given
370 	 * directory
371 	 *
372 	 * @param pDirectory
373 	 *            The directory service
374 	 * @return The corresponding advertisements
375 	 */
376 	public WBEMServiceAdvertisement[] getAdvertisementsByDirectory(String pDirectory) {
377 		List<AdvertisementDecorator> result = this.iCatalogByDirectory.get(pDirectory);
378 		return (
379 			result != null
380 				? (WBEMServiceAdvertisement[]) result.toArray(new WBEMServiceAdvertisement[result.size()])
381 				: new WBEMServiceAdvertisement[] {}
382 		);
383 	}
384 
385 	/**
386 	 * Returns the advertisements from the catalog corresponding to a given id
387 	 *
388 	 * @param pId
389 	 *            The service id
390 	 * @return The corresponding advertisements
391 	 */
392 	public WBEMServiceAdvertisement[] getAdvertisementsById(String pId) {
393 		Map<WBEMProtocol, WBEMServiceAdvertisement> innerMap = this.iCatalogById.get(pId);
394 		if (innerMap == null) {
395 			return null;
396 		}
397 		Collection<WBEMServiceAdvertisement> advertisements = innerMap.values();
398 		return advertisements.toArray(new WBEMServiceAdvertisement[advertisements.size()]);
399 	}
400 
401 	/**
402 	 * Returns an array of service ids known by this catalog
403 	 *
404 	 * @return The service ids
405 	 */
406 	public String[] getKnownIds() {
407 		Set<String> ids = this.iCatalogById.keySet();
408 		return ids.toArray(new String[ids.size()]);
409 	}
410 
411 	/**
412 	 * Refreshes the advertisements from a given directory. All existing
413 	 * advertisements from this directory are deleted first before the new ones
414 	 * are added.
415 	 *
416 	 * @param pDirectory
417 	 *            The directory services we got the advertisements from
418 	 * @param pAdvertisements
419 	 *            The advertisements
420 	 */
421 	public void refreshAdvertisements(final String[] pDirectory, WBEMServiceAdvertisement[] pAdvertisements) {
422 		for (int i = 0; i < pDirectory.length; ++i) {
423 			markRefresh(pDirectory[i]);
424 		}
425 		addAdvertisements(pAdvertisements);
426 		for (int i = 0; i < pDirectory.length; ++i) {
427 			expire(pDirectory[i]);
428 		}
429 	}
430 
431 	/**
432 	 * Removes a listener
433 	 *
434 	 * @param pListener
435 	 *            The listener to remove
436 	 */
437 	public void removeEventListener(EventListener pListener) {
438 		this.iListeners.remove(pListener);
439 	}
440 
441 	/**
442 	 * Removes the expired advertisements from the catalog.
443 	 *
444 	 * @param pDirectory
445 	 *            When <code>not null</code> only the expired advertisements of
446 	 *            the given directory are removed. Otherwise all expired
447 	 *            advertisements are removed.
448 	 */
449 	public void removeExpired(String pDirectory) {
450 		if (pDirectory == null) {
451 			Iterator<String> iter = this.iCatalogByDirectory.keySet().iterator();
452 			while (iter.hasNext()) {
453 				removeExpired(iter.next());
454 			}
455 			return;
456 		}
457 
458 		List<AdvertisementDecorator> advertisementList = this.iCatalogByDirectory.get(pDirectory);
459 		Iterator<AdvertisementDecorator> iter = advertisementList.iterator();
460 		while (iter.hasNext()) {
461 			AdvertisementDecorator decorator = iter.next();
462 			if (decorator.isExpired()) {
463 				iter.remove();
464 				notifyEventListeners(EVENT_REMOVE, decorator);
465 				Map<WBEMProtocol, WBEMServiceAdvertisement> innerMap = this.iCatalogById.get(decorator.getServiceId());
466 				innerMap.remove(makeProtocol(decorator));
467 			}
468 		}
469 	}
470 
471 	/*
472 	 * (non-Javadoc)
473 	 *
474 	 * @see java.lang.Object#toString()
475 	 */
476 	@Override
477 	public String toString() {
478 		StringBuffer result = new StringBuffer();
479 		result.append("AdvertisementCatalog:");
480 		Iterator<Map.Entry<String, Map<WBEMProtocol, WBEMServiceAdvertisement>>> outer =
481 			this.iCatalogById.entrySet().iterator();
482 		while (outer.hasNext()) {
483 			Map.Entry<String, Map<WBEMProtocol, WBEMServiceAdvertisement>> entry = outer.next();
484 			if (entry.getValue() == null) {
485 				continue;
486 			}
487 			result.append("[service-id:\"");
488 			result.append(entry.getKey());
489 			result.append("\"");
490 			Map<WBEMProtocol, WBEMServiceAdvertisement> innerMap = entry.getValue();
491 			Iterator<Map.Entry<WBEMProtocol, WBEMServiceAdvertisement>> inner = innerMap.entrySet().iterator();
492 			while (inner.hasNext()) {
493 				Map.Entry<WBEMProtocol, WBEMServiceAdvertisement> innerEntry = inner.next();
494 				result.append("[");
495 				result.append(innerEntry.getKey().toString());
496 				result.append("]");
497 			}
498 			result.append("]");
499 		}
500 		return result.toString();
501 	}
502 
503 	private void expire(String pDirectory) {
504 		List<AdvertisementDecorator> advertisementList = this.iCatalogByDirectory.get(pDirectory);
505 		if (advertisementList == null) {
506 			return;
507 		}
508 		Iterator<AdvertisementDecorator> iter = advertisementList.iterator();
509 		while (iter.hasNext()) {
510 			AdvertisementDecorator advertisement = iter.next();
511 			if (advertisement.isRefresh()) {
512 				advertisement.setRefresh(false);
513 				advertisement.setExpired(true);
514 				notifyEventListeners(EVENT_EXPIRE, advertisement);
515 			}
516 		}
517 	}
518 
519 	private AdvertisementDecorator findAdvertisement(
520 		List<AdvertisementDecorator> pList,
521 		WBEMServiceAdvertisement pAdvertisement
522 	) {
523 		Iterator<AdvertisementDecorator> iter = pList.iterator();
524 		while (iter.hasNext()) {
525 			AdvertisementDecorator entry = iter.next();
526 			if (entry.getServiceUrl().equals(pAdvertisement.getServiceUrl())) {
527 				return entry;
528 			}
529 		}
530 		return null;
531 	}
532 
533 	private WBEMProtocol makeProtocol(WBEMServiceAdvertisement advertisement) {
534 		String presentation = advertisement.getAttribute(WBEMServiceAdvertisement.COMM_MECHANISM);
535 		if ("OTHER".equalsIgnoreCase(presentation)) {
536 			presentation = advertisement.getAttribute(WBEMServiceAdvertisement.OTHER_COMM_MECHN_DESC);
537 		}
538 		String transport = advertisement.getServiceUrl().split(":", 2)[0];
539 		WBEMProtocol protocol = new WBEMProtocol(transport, presentation);
540 		return protocol;
541 	}
542 
543 	private void markRefresh(String pDirectory) {
544 		List<AdvertisementDecorator> advertisementList = this.iCatalogByDirectory.get(pDirectory);
545 		if (advertisementList == null) {
546 			return;
547 		}
548 		Iterator<AdvertisementDecorator> iter = advertisementList.iterator();
549 		while (iter.hasNext()) {
550 			AdvertisementDecorator advertisement = iter.next();
551 			advertisement.setRefresh(true);
552 		}
553 	}
554 
555 	private void notifyEventListeners(int pEvent, WBEMServiceAdvertisement pAdvertisement) {
556 		Iterator<EventListener> iter = this.iListeners.iterator();
557 		while (iter.hasNext()) {
558 			iter.next().advertisementEvent(pEvent, pAdvertisement);
559 		}
560 	}
561 }