blob: 25bd78e9322d947fa60e1b382fb19182ba64c978 [file] [log] [blame]
Francesco Furfari677c4592006-10-10 12:02:17 +00001/*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
Humberto Cervantes Macedabd7de442006-04-04 16:05:02 +00009 *
Francesco Furfari677c4592006-10-10 12:02:17 +000010 * http://www.apache.org/licenses/LICENSE-2.0
Humberto Cervantes Macedabd7de442006-04-04 16:05:02 +000011 *
Francesco Furfari677c4592006-10-10 12:02:17 +000012 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
Humberto Cervantes Macedabd7de442006-04-04 16:05:02 +000018 */
Francesco Furfari677c4592006-10-10 12:02:17 +000019
Humberto Cervantes Macedabd7de442006-04-04 16:05:02 +000020package org.apache.felix.wireadmin;
21
22import java.util.Dictionary;
23import java.util.Enumeration;
24import java.util.Vector;
25import java.util.Date;
26
27import org.osgi.framework.Filter;
28import org.osgi.framework.BundleContext;
29import org.osgi.framework.ServiceReference;
30import org.osgi.framework.Constants;
31import org.osgi.framework.InvalidSyntaxException;
32
33import org.osgi.service.wireadmin.Consumer;
34import org.osgi.service.wireadmin.Producer;
35import org.osgi.service.wireadmin.Wire;
36import org.osgi.service.wireadmin.WireConstants;
37import org.osgi.service.wireadmin.WireAdminEvent;
38
39/**
40 * A connection between a Producer service and a Consumer service.
41 *
42 * <p>A <tt>Wire</tt> object connects a Producer service
43 * to a Consumer service.
44 * Both the Producer and Consumer services are identified
45 * by their unique <tt>service.pid</tt> values.
46 * The Producer and Consumer services may communicate with
47 * each other via <tt>Wire</tt> objects that connect them.
48 * The Producer service may send updated values to the
49 * Consumer service by calling the {@link #update} method.
50 * The Consumer service may request an updated value from the
51 * Producer service by calling the {@link #poll} method.
52 *
53 * <p>A Producer service and a Consumer service may be
54 * connected through multiple <tt>Wire</tt> objects.
55 *
56 * <p>Security Considerations. <tt>Wire</tt> objects are available to
57 * Producer and Consumer services connected to a given
58 * <tt>Wire</tt> object and to bundles which can access the <tt>WireAdmin</tt> service.
59 * A bundle must have <tt>ServicePermission[GET,WireAdmin]</tt> to get the <tt>WireAdmin</tt> service to
60 * access all <tt>Wire</tt> objects.
61 * A bundle registering a Producer service or a Consumer service
62 * must have the appropriate <tt>ServicePermission[REGISTER,Consumer|Producer]</tt> to register the service and
63 * will be passed <tt>Wire</tt> objects when the service object's
64 * <tt>consumersConnected</tt> or <tt>producersConnected</tt> method is called.
65 *
66 * <p>Scope. Each Wire object can have a scope set with the <tt>setScope</tt> method. This
67 * method should be called by a Consumer service when it assumes a Producer service that is
68 * composite (supports multiple information items). The names in the scope must be
69 * verified by the <tt>Wire</tt> object before it is used in communication. The semantics of the
70 * names depend on the Producer service and must not be interpreted by the Wire Admin service.
71 *
Karl Paulsd312acc2007-06-18 20:38:33 +000072 * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a> *
Humberto Cervantes Macedabd7de442006-04-04 16:05:02 +000073 */
74public class WireImpl implements Wire, java.io.Serializable
75{
76 static final long serialVersionUID = -3637966367104019136L;
77
78 // Persistent attributes
79
80 // p. 327 "The Wire admin object contains a Persistend Identity (PID) for
81 // a consumer service and a PID for a producer service" These properties
82 // are also contained in the dictionary, but they are stored to optimize
83 // access.
84 private String m_producerPID;
85 private String m_consumerPID;
86
87 private Dictionary m_properties;
88
89 // Transient attributes
90
91 transient private boolean m_isValid = true;
92 transient private boolean m_isConnected = false;
93 transient private Filter m_filter = null;
94
95 transient private ServiceReference m_producerServiceRef;
96 transient private Producer m_producer;
97 transient private boolean m_producerIsComposite = false;
98 transient private String [] m_producerScope = null;
99
100 transient private ServiceReference m_consumerServiceRef;
101 transient private Consumer m_consumer;
102 transient private boolean m_consumerIsComposite = false;
103 transient private String [] m_consumerScope = null;
104
105 transient private BundleContext m_bundleContext;
106 transient private EventManager m_eventManager;
107 transient private Object m_lastValue;
108 transient private String[] m_scope;
109
110 transient private long m_lastUpdate;
111 transient private boolean m_isFirstUpdate;
112 transient FilterDictionary m_dictionary;
113
114 /**
115 * Constructor with package visibility
116 *
117 * @param producerPID
118 * @param consumerPID
119 * @param properties
120 */
121 WireImpl(String producerPID, String consumerPID, Dictionary properties)
122 {
123 m_producerPID = producerPID;
124 m_consumerPID = consumerPID;
125 m_properties = properties;
126
127 // set the scope which is the intersection of strings in the WIREADMIN_PRODUCER_SCOPE properties and the strings in the WIREADMIN_CONSUMER_SCOPE
128 m_scope = null;
129 }
130
131 /**
132 * Method called after construction and after deserialization
133 *
134 * @param ctxt
135 * @param eventManager
136 */
137 void initialize(BundleContext ctxt, EventManager eventManager)
138 {
139 m_isValid = true;
140 m_isConnected = false;
141
142 m_bundleContext = ctxt;
143 m_eventManager = eventManager;
144
145 m_lastValue = null;
146
147 m_lastUpdate = 0;
148
149 m_isFirstUpdate = true;
150
151 /*
152 if(m_date == null)
153 {
154 m_date = new Date();
155 }
156 */
157 m_dictionary = new FilterDictionary();
158 }
159
160 /**
161 * Return the state of this <tt>Wire</tt> object.
162 *
163 * <p>A connected <tt>Wire</tt> must always be disconnected before
164 * becoming invalid.
165 *
166 * @return <tt>false</tt> if this <tt>Wire</tt> object is invalid because it
167 * has been deleted via {@link WireAdmin#deleteWire};
168 * <tt>true</tt> otherwise.
169 */
170 public boolean isValid()
171 {
172 return m_isValid;
173 }
174
175 /**
176 * Return the connection state of this <tt>Wire</tt> object.
177 *
178 * <p>A <tt>Wire</tt> is connected after the Wire Admin service receives
179 * notification that the Producer service and
180 * the Consumer service for this <tt>Wire</tt> object are both registered.
181 * This method will return <tt>true</tt> prior to notifying the Producer
182 * and Consumer services via calls
183 * to their respective <tt>consumersConnected</tt> and <tt>producersConnected</tt>
184 * methods.
185 * <p>A <tt>WireAdminEvent</tt> of type {@link WireAdminEvent#WIRE_CONNECTED}
186 * must be broadcast by the Wire Admin service when
187 * the <tt>Wire</tt> becomes connected.
188 *
189 * <p>A <tt>Wire</tt> object
190 * is disconnected when either the Consumer or Producer
191 * service is unregistered or the <tt>Wire</tt> object is deleted.
192 * <p>A <tt>WireAdminEvent</tt> of type {@link WireAdminEvent#WIRE_DISCONNECTED}
193 * must be broadcast by the Wire Admin service when
194 * the <tt>Wire</tt> becomes disconnected.
195 *
196 * @return <tt>true</tt> if both the Producer and Consumer
197 * for this <tt>Wire</tt> object are connected to the <tt>Wire</tt> object;
198 * <tt>false</tt> otherwise.
199 */
200 public boolean isConnected()
201 {
202 return m_isConnected;
203 }
204
205 /**
206 * Return the list of data types understood by the
207 * Consumer service connected to this <tt>Wire</tt> object. Note that
208 * subclasses of the classes in this list are acceptable data types as well.
209 *
210 * <p>The list is the value of the {@link WireConstants#WIREADMIN_CONSUMER_FLAVORS}
211 * service property of the
212 * Consumer service object connected to this object. If no such
213 * property was registered or the type of the property value is not
214 * <tt>Class[]</tt>, this method must return <tt>null</tt>.
215 *
216 * @return An array containing the list of classes understood by the
217 * Consumer service or <tt>null</tt> if
218 * the <tt>Wire</tt> is not connected,
219 * or the consumer did not register a {@link WireConstants#WIREADMIN_CONSUMER_FLAVORS} property
220 * or the value of the property is not of type <tt>Class[]</tt>.
221 */
222
223 public Class[] getFlavors()
224 {
225 if(isConnected())
226 {
227 try
228 {
229 return (Class [])m_consumerServiceRef.getProperty(WireConstants.WIREADMIN_CONSUMER_FLAVORS);
230 }
231 catch(ClassCastException ex)
232 {
233 return null;
234 }
235 }
236 else
237 {
238 return null;
239
240 }
241 }
242
243 /**
244 * Update the value.
245 *
246 * <p>This methods is called by the Producer service to
247 * notify the Consumer service connected to this <tt>Wire</tt> object
248 * of an updated value.
249 * <p>If the properties of this <tt>Wire</tt> object contain a
250 * {@link WireConstants#WIREADMIN_FILTER} property,
251 * then filtering is performed.
252 * If the Producer service connected to this <tt>Wire</tt>
253 * object was registered with the service
254 * property {@link WireConstants#WIREADMIN_PRODUCER_FILTERS}, the
255 * Producer service will perform the filtering according to the rules specified
256 * for the filter. Otherwise, this <tt>Wire</tt> object
257 * will perform the filtering of the value.
258 * <p>If no filtering is done, or the filter indicates the updated value should
259 * be delivered to the Consumer service, then
260 * this <tt>Wire</tt> object must call
261 * the {@link Consumer#updated} method with the updated value.
262 * If this <tt>Wire</tt> object is not connected, then the Consumer
263 * service must not be called and the value is ignored.<p>
264 * If the value is an <tt>Envelope</tt> object, and the scope name is not permitted, then the
265 * <tt>Wire</tt> object must ignore this call and not transfer the object to the Consumer
266 * service.
267 *
268 * <p>A <tt>WireAdminEvent</tt> of type {@link WireAdminEvent#WIRE_TRACE}
269 * must be broadcast by the Wire Admin service after
270 * the Consumer service has been successfully called.
271 *
272 * @param value The updated value. The value should be an instance of
273 * one of the types returned by {@link #getFlavors}.
274 * @see WireConstants#WIREADMIN_FILTER
275 */
276 public void update(Object value)
277 {
278 // TODO Implement Access Control (p. 338)
279 if (isConnected())
280 {
281 if(m_producerIsComposite == true)
282 {
283 // TODO Implement update for composite producers
284 WireAdminImpl.traceln("WireImpl.update: update for composite producers not yet implemented");
285 return;
286 }
287 else // not a composite (Note: p. 341 "Filtering for composite producer services is not supported")
288 {
289 //long time = m_date.getTime();
290 long time = new Date().getTime();
291
292 // We ignore filtering the first time...
293 if(m_isFirstUpdate == false && m_filter != null)
294 {
295 // If the Producer service was registered with the WIREADMIN_PRODUCER_FILTERS
296 // service property indicating that the Producer service will perform the data
297 // filtering then the Wire object will not perform data filtering. Otherwise,
298 // the Wire object must perform basic filtering.
299 try
300 {
301 m_dictionary.reset(value,time);
302 if(!m_filter.match(m_dictionary))
303 {
304 WireAdminImpl.traceln("### Update rejected ("+m_properties.get(WireConstants.WIREADMIN_PID)+") filter evaluated to false:"+m_filter);
305 WireAdminImpl.traceln(" WIREVALUE_CURRENT.class"+m_dictionary.get(WireConstants.WIREVALUE_CURRENT).getClass().getName());
306 WireAdminImpl.traceln(" WIREVALUE_CURRENT="+m_dictionary.get(WireConstants.WIREVALUE_CURRENT));
307 WireAdminImpl.traceln(" WIREVALUE_PREVIOUS="+m_dictionary.get(WireConstants.WIREVALUE_PREVIOUS));
308 WireAdminImpl.traceln(" WIREVALUE_DELTA_ABSOLUTE="+m_dictionary.get(WireConstants.WIREVALUE_DELTA_ABSOLUTE));
309 WireAdminImpl.traceln(" WIREVALUE_DELTA_RELATIVE="+m_dictionary.get(WireConstants.WIREVALUE_DELTA_RELATIVE));
310 WireAdminImpl.traceln(" WIREVALUE_ELAPSED="+m_dictionary.get(WireConstants.WIREVALUE_ELAPSED));
311 return;
312 }
313 }
314 catch(Exception ex)
315 {
316 // Could happen...
317 WireAdminImpl.trace(ex);
318 }
319
320 }
321 try
322 {
323 m_consumer.updated(this, value);
324 if(m_isFirstUpdate == true)
325 {
326 m_isFirstUpdate = false;
327 }
328 m_lastUpdate = time;
329 m_lastValue = value;
330 // Fire event
331 m_eventManager.fireEvent(WireAdminEvent.WIRE_TRACE,this);
332 }
333 catch(Exception ex)
334 {
335 m_eventManager.fireEvent(WireAdminEvent.CONSUMER_EXCEPTION,this,ex);
336 }
337 }
338 }
339 }
340
341 /**
342 * Poll for an updated value.
343 *
344 * <p>This methods is normally called by the Consumer service to
345 * request an updated value from the Producer service
346 * connected to this <tt>Wire</tt> object.
347 * This <tt>Wire</tt> object will call
348 * the {@link Producer#polled} method to obtain an updated value.
349 * If this <tt>Wire</tt> object is not connected, then the Producer
350 * service must not be called.<p>
351 *
352 * If this <tt>Wire</tt> object has a scope, then this method
353 * must return an array of <tt>Envelope</tt> objects. The objects returned must
354 * match the scope of this object. The <tt>Wire</tt> object must remove
355 * all <tt>Envelope</tt> objects with a scope name that is not in the <tt>Wire</tt> object's scope.
356 * Thus, the list of objects returned
357 * must only contain <tt>Envelope</tt> objects with a permitted scope name. If the
358 * array becomes empty, <tt>null</tt> must be returned.
359 *
360 * <p>A <tt>WireAdminEvent</tt> of type {@link WireAdminEvent#WIRE_TRACE}
361 * must be broadcast by the Wire Admin service after
362 * the Producer service has been successfully called.
363 *
364 * @return A value whose type should be one of the types
365 * returned by {@link #getFlavors}, <tt>Envelope[]</tt>, or <tt>null</tt> if
366 * the <tt>Wire</tt> object is not connected,
367 * the Producer service threw an exception, or
368 * the Producer service returned a value which is not an instance of
369 * one of the types returned by {@link #getFlavors}.
370 */
371 public Object poll() {
372 // p.330 "Update filtering must not apply to polling"
373 if (isConnected())
374 {
375 try
376 {
377 Object value = m_producer.polled(this);
378 Class []flavors = getFlavors();
379
380 boolean valueOk = false;
381
382 // Test if the value is ok with respect to the flavors understood by
383 // the consumer
384 for(int i=0; i<flavors.length; i++)
385 {
386 Class currentClass = flavors[i];
387 if(currentClass.isInstance(value))
388 {
389 valueOk = true;
390 }
391 }
392 if(valueOk)
393 {
394 m_eventManager.fireEvent(WireAdminEvent.WIRE_TRACE,this);
395 m_lastValue = value;
396 return m_lastValue;
397 }
398 else
399 {
400 WireAdminImpl.traceln("WireImpl.poll: value returned by producer is not undestood by consumer");
401 return null;
402 }
403 }
404 catch(Exception ex)
405 {
406 m_eventManager.fireEvent(WireAdminEvent.PRODUCER_EXCEPTION,this,ex);
407 return null;
408 }
409 }
410 else
411 {
412 // p. 333 "If the poll() method on the wire object is called and the
413 // producer is unregistered it must return a null value"
414 return null;
415 }
416 }
417
418 /**
419 * Return the last value sent through this <tt>Wire</tt> object.
420 *
421 * <p>The returned value is the most recent, valid value passed to the
422 * {@link #update} method or returned by the {@link #poll} method
423 * of this object. If filtering is performed by this <tt>Wire</tt> object,
424 * this methods returns the last value provided by the Producer service. This
425 * value may be an <tt>Envelope[]</tt> when the Producer service
426 * uses scoping. If the return value is an Envelope object (or array), it
427 * must be verified that the Consumer service has the proper WirePermission to see it.
428 *
429 * @return The last value passed though this <tt>Wire</tt> object
430 * or <tt>null</tt> if no valid values have been passed or the Consumer service has no permission.
431 */
432 public Object getLastValue()
433 {
434 return m_lastValue;
435 }
436
437 /**
438 * Return the wire properties for this <tt>Wire</tt> object.
439 *
440 * @return The properties for this <tt>Wire</tt> object.
441 * The returned <tt>Dictionary</tt> must be read only.
442 */
443 public Dictionary getProperties()
444 {
445 return m_properties;
446 }
447
448 /**
449 * Return the calculated scope of this <tt>Wire</tt> object.
450 *
451 * The purpose of the <tt>Wire</tt> object's scope is to allow a Producer
452 * and/or Consumer service to produce/consume different types
453 * over a single <tt>Wire</tt> object (this was deemed necessary for efficiency
454 * reasons). Both the Consumer service and the
455 * Producer service must set an array of scope names (their scope) with
456 * the service registration property <tt>WIREADMIN_PRODUCER_SCOPE</tt>, or <tt>WIREADMIN_CONSUMER_SCOPE</tt> when they can
457 * produce multiple types. If a Producer service can produce different types, it should set this property
458 * to the array of scope names it can produce, the Consumer service
459 * must set the array of scope names it can consume. The scope of a <tt>Wire</tt>
460 * object is defined as the intersection of permitted scope names of the
461 * Producer service and Consumer service.
462 * <p>If neither the Consumer, or the Producer service registers scope names with its
463 * service registration, then the <tt>Wire</tt> object's scope must be <tt>null</tt>.
464 * <p>The <tt>Wire</tt> object's scope must not change when a Producer or Consumer services
465 * modifies its scope.
466 * <p>A scope name is permitted for a Producer service when the registering bundle has
467 * <tt>WirePermission[PRODUCE]</tt>, and for a Consumer service when
468 * the registering bundle has <tt>WirePermission[CONSUME]</tt>.<p>
469 * If either Consumer service or Producer service has not set a <tt>WIREADMIN_*_SCOPE</tt> property, then
470 * the returned value must be <tt>null</tt>.<p>
471 * If the scope is set, the <tt>Wire</tt> object must enforce the scope names when <tt>Envelope</tt> objects are
472 * used as a parameter to update or returned from the <tt>poll</tt> method. The <tt>Wire</tt> object must then
473 * remove all <tt>Envelope</tt> objects with a scope name that is not permitted.
474 *
475 * @return A list of permitted scope names or null if the Produce or Consumer service has set no scope names.
476 */
477 public String[] getScope()
478 {
479 return m_scope;
480 }
481
482 /**
483 * Return true if the given name is in this <tt>Wire</tt> object's scope.
484 *
485 * @param name The scope name
486 * @return true if the name is listed in the permitted scope names
487 */
488 public boolean hasScope(String name)
489 {
490 if (m_scope != null)
491 {
492 for (int i = 0; i < m_scope.length; i++)
493 {
494 if (name.equals(m_scope[i]))
495 {
496 return true;
497 }
498 }
499 }
500 return false;
501 }
502
503 /**
504 * @see java.lang.Object#toString()
505 */
506 public String toString()
507 {
508 return super.toString()
509 + ":["
510 + getProperties().get(WireConstants.WIREADMIN_PID)
511 + ","
512 + getProperties().get(WireConstants.WIREADMIN_PRODUCER_PID)
513 + ","
514 + getProperties().get(WireConstants.WIREADMIN_CONSUMER_PID)
515 + "]";
516 }
517
518 /**
519 * Bind the consumer.
520 *
521 * @param consumer A <tt>ServiceReference</tt> corresponding to the consumer service
522 */
523 void bindConsumer(ServiceReference consumerRef)
524 {
525 if(m_consumerServiceRef != null)
526 {
527 WireAdminImpl.traceln("WireImpl: consumer already bound!");
528 return;
529 }
530
531 // Pid must be present
532 if(consumerRef.getProperty(Constants.SERVICE_PID) == null)
533 {
534 WireAdminImpl.traceln("WireImpl: Consumer service has no PID "+consumerRef);
535 return;
536 }
537
538 // Flavors must be present
539 if(consumerRef.getProperty(WireConstants.WIREADMIN_CONSUMER_FLAVORS) == null)
540 {
541 WireAdminImpl.traceln("WireImpl: Consumer service has no WIREADMIN_CONSUMER_FLAVORS "+consumerRef);
542 return;
543 }
544
545 // Is this a composite?
546 if(consumerRef.getProperty(WireConstants.WIREADMIN_CONSUMER_COMPOSITE) != null)
547 {
548 m_consumerIsComposite = true;
549 m_consumerScope = (String []) consumerRef.getProperty(WireConstants.WIREADMIN_CONSUMER_SCOPE);
550
551 }
552
553 m_consumerServiceRef = consumerRef;
554 m_consumer = (Consumer) m_bundleContext.getService(consumerRef);
555
556 if(m_producer != null)
557 {
558 if(m_producerIsComposite)
559 {
560 if(matchComposites() == false)
561 {
562 WireAdminImpl.traceln("WireImpl.bindConsumer : warning composite identities do not match");
563 }
564 }
565 m_isConnected = true;
566 m_eventManager.fireEvent(WireAdminEvent.WIRE_CONNECTED,this);
567 }
568 }
569
570 /**
571 * Unbind the consumer
572 *
573 */
574 void unbindConsumer()
575 {
576 // This test is made because knopflerfish doesn't support ungetService(null);
577 if(m_consumerServiceRef != null)
578 {
579 m_bundleContext.ungetService(m_consumerServiceRef);
580 if(m_isConnected)
581 {
582 m_isConnected = false;
583 m_eventManager.fireEvent(WireAdminEvent.WIRE_DISCONNECTED,this);
584 }
585 m_consumer = null;
586 m_consumerServiceRef = null;
587 }
588 }
589
590 /**
591 * Bind the producer
592 *
593 * @param producer A <tt>ServiceReference</tt> corresponding to the producer service
594 */
595 void bindProducer(ServiceReference producerRef)
596 {
597 if(m_producerServiceRef != null)
598 {
599 WireAdminImpl.traceln("WireImpl: producer already bound!");
600 return;
601 }
602
603 // Pid must be present
604 if(producerRef.getProperty(Constants.SERVICE_PID) == null)
605 {
606 WireAdminImpl.traceln("WireImpl.bindProducer: Producer service has no PID "+producerRef);
607 return;
608 }
609
610 // Flavors must be present
611 if(producerRef.getProperty(WireConstants.WIREADMIN_PRODUCER_FLAVORS) == null)
612 {
613 WireAdminImpl.traceln("WireImpl: Consumer service has no WIREADMIN_PRODUCER_FLAVORS "+producerRef);
614 return;
615 }
616
617 // Is this a composite?
618 if(producerRef.getProperty(WireConstants.WIREADMIN_PRODUCER_COMPOSITE) != null)
619 {
620 m_producerIsComposite = true;
621 m_producerScope = (String []) producerRef.getProperty(WireConstants.WIREADMIN_PRODUCER_SCOPE);
622 }
623
624 m_producerServiceRef = producerRef;
625 m_producer = (Producer) m_bundleContext.getService(producerRef);
626
627 // p. 329 " If this property (wireadmin.producer.filters) is not set,
628 // the Wire object must filter according to the description in CompositeObjects"
629 if(producerRef.getProperty(WireConstants.WIREADMIN_PRODUCER_FILTERS) == null)
630 {
631 String filter = (String) m_properties.get(WireConstants.WIREADMIN_FILTER);
632 if(filter != null)
633 {
634 try
635 {
636 m_filter = m_bundleContext.createFilter(filter);
637 }
638 catch(InvalidSyntaxException ex)
639 {
640 WireAdminImpl.traceln("WireImpl.bindProducer: Ignoring filter with invalid syntax "+filter);
641 }
642 }
643 }
644
645 if(m_consumer != null)
646 {
647 if(m_consumerIsComposite)
648 {
649 if(matchComposites() == false)
650 {
651 WireAdminImpl.traceln("WireImpl.bindProducer : warning composite identities do not match");
652 }
653 }
654 m_isConnected = true;
655 // fire the corresponding event
656 m_eventManager.fireEvent(WireAdminEvent.WIRE_CONNECTED,this);
657 }
658 }
659
660 /**
661 * Unbind the producer
662 *
663 */
664 void unbindProducer()
665 {
666 if(m_producerServiceRef != null)
667 {
668 m_bundleContext.ungetService(m_producerServiceRef);
669 if(m_isConnected)
670 {
671 m_isConnected = false;
672 // fire the corresponding event
673 m_eventManager.fireEvent(WireAdminEvent.WIRE_DISCONNECTED,this);
674 }
675 m_producer = null;
676 m_producerServiceRef = null;
677 }
678 }
679
680 /**
681 * Check if composites match. Match occurs when "at least one equal composite identity is
682 * listed on both the Producer and Consmer composite identity service property" (p. 336)
683 *
684 * @return <tt>true</tt> if they match, <tt>false</tt>otherwise
685 */
686 private boolean matchComposites()
687 {
688 String [] producerIdentities = (String []) m_producerServiceRef.getProperty(WireConstants.WIREADMIN_PRODUCER_COMPOSITE);
689 String [] consumerIdentities = (String []) m_producerServiceRef.getProperty(WireConstants.WIREADMIN_PRODUCER_COMPOSITE);
690
691 boolean match = false;
692
693 // confirm matching
694
695 for(int i = 0; i< producerIdentities.length; i++)
696 {
697 String currentProducerIdentity = producerIdentities [i];
698 for(int j = 0; j < consumerIdentities.length; j++)
699 {
700 String currentConsumerIdentity = consumerIdentities [j];
701 if (currentProducerIdentity.equals(currentConsumerIdentity))
702 {
703 match = true;
704 break;
705 }
706 }
707 }
708
709 // setup wire scope
710
711 if (m_consumerScope != null && m_producerScope != null)
712 {
713 // p. 337 "It is allowed to register with a wildcard, indicating that all scopenames
714 // are supported. In that case, the WIREADMIN_SCOPE_ALL ({"*"}) should be registered as the
715 // scope of the serivce. The WireObject's scope is then fully defined by the
716 // other service connected to the wire object
717 if(m_consumerScope.length == 1 && m_consumerScope[0].equals("*"))
718 {
719 m_scope = m_producerScope;
720 }
721 else if(m_producerScope.length == 1 && m_producerScope[0].equals("*"))
722 {
723 m_scope = m_consumerScope;
724 }
725 else
726 {
727 /*
728 // TODO Implement wire scope creation based on producer, consumer and wire permissions (p. 338)
729
730 ServiceReference ref = m_bundleContext.getServiceReference(PermissionAdmin.class.getName());
731 if(ref != null)
732 {
733 PermissionAdmin permAdmin = (PermissionAdmin) m_bundleContext.getService(ref);
734 PermissionInfo [] producerPermissions = permAdmin.getPermissions(m_producerServiceRef.getBundle().getLocation());
735 PermissionInfo [] consumerPermissions = permAdmin.getPermissions(m_consumerServiceRef.getBundle().getLocation());
736 }
737 */
738 }
739 }
740 else
741 {
742 // p. 337 "Not registering this property (WIREADMIN_CONSUMER_SCOPE or WIREADMIN_PRODUCER_SCOPE)
743 // by the Consumer or the Producer service indicates to the WireAdmin service that any
744 // Wire object connected to that service must return null for the Wire.getScope() method"
745 m_scope = null;
746 }
747
748 return match;
749 }
750
751 /**
752 * Called to invalidate the wire
753 *
754 */
755 void invalidate()
756 {
757 if(m_isValid)
758 {
759 unbindProducer();
760 unbindConsumer();
761 m_isValid=false;
762 }
763 }
764
765 /**
766 * Update the properties
767 *
768 * @param properties new properties
769 */
770 void updateProperties(Dictionary properties)
771 {
772 m_properties = properties;
773 }
774
775 /**
776 * Return the service reference corresponding to the producer
777 *
778 * @return a <tt>ServiceReference</tt> corresponding to the producer
779 */
780 ServiceReference getProducerServiceRef()
781 {
782 return m_producerServiceRef;
783 }
784
785 /**
786 * Return the producer service object
787 *
788 * @return An <tt>Object</tt> corresponding to the producer
789 */
790 Producer getProducer()
791 {
792 return m_producer;
793 }
794
795 /**
796 * return the producer PID
797 *
798 * @return
799 */
800 String getProducerPID()
801 {
802 return m_producerPID;
803 }
804
805 /**
806 * Return the service reference corresponding to the consumer
807 *
808 * @return a <tt>ServiceReference</tt> corresponding to the consumer
809 */
810 ServiceReference getConsumerServiceRef()
811 {
812 return m_consumerServiceRef;
813 }
814
815 /**
816 * Returns the consumer service object
817 *
818 * @return An <tt>Object</tt> corresponding to the consumer
819 */
820 Consumer getConsumer()
821 {
822 return m_consumer;
823 }
824
825 /**
826 * return the consumer PID
827 *
828 * @return
829 */
830 String getConsumerPID()
831 {
832 return m_consumerPID;
833 }
834
835 /**
836 * This inner class implements a dictionary that is used to filter out values
837 * during calls to update. This design choice was favored to avoid constructing
838 * a new dictionary for every call to update and to avoid doing unnecessary
839 * calculations.
840 */
841 class FilterDictionary extends Dictionary
842 {
843 private Object m_value;
844 private long m_time;
845
846 /**
847 * Must be called prior to evaluating the filter
848 *
849 * @param value
850 * @param time
851 */
852 void reset(Object value, long time)
853 {
854 m_value = value;
855 m_time = time;
856 }
857
858 /**
859 *
860 */
861 public Object get(Object key)
862 {
863 if(key.equals(WireConstants.WIREVALUE_CURRENT))
864 {
865 return m_value;
866 }
867 else if(key.equals(WireConstants.WIREVALUE_PREVIOUS))
868 {
869 return m_lastValue;
870 }
871 else if(m_value instanceof Number && key.equals(WireConstants.WIREVALUE_DELTA_ABSOLUTE))
872 {
873 return null;
874 }
875 else if(m_value instanceof Number && key.equals(WireConstants.WIREVALUE_DELTA_RELATIVE))
876 {
877 return null;
878 }
879 else if(key.equals(WireConstants.WIREVALUE_ELAPSED))
880 {
881 if(m_lastUpdate == 0)
882 {
883 return new Long(0);
884 }
885 else
886 {
887 long delay = m_time - m_lastUpdate;
888 //System.out.println("### delay = "+(delay));
889 return new Long(delay);
890 }
891 }
892 else
893 {
894 WireAdminImpl.traceln("### key not found:"+key);
895 return null;
896 }
897 }
898
899 /**
900 * Never empty
901 */
902 public boolean isEmpty()
903 {
904 return false;
905 }
906
907 /**
908 * Remove not supported
909 */
910 public Object remove(Object obj)
911 {
912 return null;
913 }
914
915 /**
916 * Size is static
917 */
918 public int size()
919 {
920 return 5;
921 }
922
923 /**
924 * Put not supported
925 */
926 public Object put(Object key, Object value)
927 {
928 return null;
929 }
930
931 /**
932 *
933 */
934 public Enumeration keys()
935 {
936 Vector keys=new Vector();
937
938 keys.addElement(WireConstants.WIREVALUE_ELAPSED);
939 keys.addElement(WireConstants.WIREVALUE_CURRENT);
940 keys.addElement(WireConstants.WIREVALUE_PREVIOUS);
941 keys.addElement(WireConstants.WIREVALUE_DELTA_ABSOLUTE);
942 keys.addElement(WireConstants.WIREVALUE_DELTA_RELATIVE);
943
944 return keys.elements();
945 }
946
947 /**
948 * Not supported
949 */
950 public Enumeration elements()
951 {
952 return null;
953 }
954 }
Karl Paulsd312acc2007-06-18 20:38:33 +0000955}