/******************************************************************************* * Copyright (c) 1997-2009 by ProSyst Software GmbH * http://www.prosyst.com * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * ProSyst Software GmbH - initial API and implementation *******************************************************************************/ package org.eclipse.equinox.internal.wireadmin; import java.security.AccessController; import java.security.PrivilegedAction; import java.util.Dictionary; import java.util.Hashtable; import org.eclipse.equinox.internal.util.ref.Log; import org.osgi.framework.*; import org.osgi.service.event.*; import org.osgi.service.wireadmin.*; import org.osgi.util.tracker.ServiceTracker; /** * This is an implementation of log events redispatching. * * /** * * @author Pavlin Dobrev * @version 1.0 */ public class WireReDispatcher implements WireAdminListener { static final String BUNDLE = "bundle"; static final String BUNDLE_ID = "bundle.id"; static final String BUNDLE_SYMBOLICNAME = "bundle.symbolicName"; static final String EVENT = "event"; static final String EXCEPTION = "exception"; static final String EXCEPTION_CLASS = "exception.class"; static final String EXCEPTION_MESSAGE = "exception.message"; static final String SERVICE = "service"; static final String SERVICE_ID = "service.id"; static final String SERVICE_OBJECTCLASS = "service.objectClass"; static final String SERVICE_PID = "service.pid"; static final char TOPIC_SEPARATOR = '/'; /* ///////WIRE ADMIN EVENTS////////// */ static final String WIRE_HEADER = "org/osgi/service/wireadmin/WireAdminEvent"; static final String WIRE_CREATED = "WIRE_CREATED"; static final String WIRE_CONNECTED = "WIRE_CONNECTED"; static final String WIRE_UPDATED = "WIRE_UPDATED"; static final String WIRE_TRACE = "WIRE_TRACE"; static final String WIRE_DISCONNECTED = "WIRE_DISCONNECTED"; static final String WIRE_DELETED = "WIRE_DELETED"; static final String PRODUCER_EXCEPTION = "PRODUCER_EXCEPTION"; static final String CONSUMER_EXCEPTION = "CONSUMER_EXCEPTION"; static final String WIRE_ENTRY = "wire.entry"; static final String WA_WIRE = "wire"; static final String WA_WIRE_FLAVORS = "wire.flavors"; static final String WA_WIRE_SCOPE = "wire.scope"; static final String WA_WIRE_CONNECTED = "wire.connected"; static final String WA_WIRE_VALID = "wire.valid"; BundleContext bc; ServiceRegistration waReg; Log log; ServiceTracker eventAdminTracker; /* * (non-Javadoc) * * @see org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext) */ public void start(BundleContext bc) throws Exception { this.bc = bc; log = new Log(bc, false); log.setDebug(Activator.getBoolean("equinox.wireadmin.redispatcher.debug")); log.setPrintOnConsole(Activator.getBoolean("equinox.wireadmin.redispatcher.console")); Hashtable props = new Hashtable(3); props.put(WireConstants.WIREADMIN_EVENTS, new Integer(Integer.MAX_VALUE)); waReg = bc.registerService(WireAdminListener.class.getName(), this, props); eventAdminTracker = new ServiceTracker(bc, EventAdmin.class.getName(), null); eventAdminTracker.open(); } /* * (non-Javadoc) * * @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext) */ public void stop() throws Exception { if (eventAdminTracker != null) { eventAdminTracker.close(); eventAdminTracker = null; } if (waReg != null) { waReg.unregister(); waReg = null; } log.close(); this.bc = null; } /* ////////UTILITY METHODS//////// */ /* * Add exception properties needed in event by EventAdmin specification. */ void addExceptionProps(Hashtable props, Throwable t) { props.put(EXCEPTION, t); props.put(EXCEPTION_CLASS, t.getClass().getName()); String message = t.getMessage(); if (message != null) { props.put(EXCEPTION_MESSAGE, t.getMessage()); } } /* * Add service properties needed in event by EventAdmin specification. */ void addServiceProps(Hashtable props, ServiceReference ref) { props.put(SERVICE, ref); props.put(SERVICE_ID, ref.getProperty(Constants.SERVICE_ID)); Object tmp = ref.getProperty(Constants.SERVICE_PID); if (tmp != null && tmp instanceof String) { props.put(SERVICE_PID, tmp); } tmp = ref.getProperty(Constants.OBJECTCLASS); if (tmp != null && tmp instanceof String[]) { props.put(SERVICE_OBJECTCLASS, tmp); } } /* ////////LISTENER METHODS//////// */ /* * (non-Javadoc) * * @see org.osgi.service.wireadmin.WireAdminListener#wireAdminEvent(org.osgi.service.wireadmin.WireAdminEvent) */ public void wireAdminEvent(WireAdminEvent event) { ServiceTracker st = eventAdminTracker; final EventAdmin eventAdmin = st == null ? null : ((EventAdmin) st.getService()); if (eventAdmin != null) { ServiceReference ref = event.getServiceReference(); if (ref == null) { throw new RuntimeException("Wire Admin ServiceReference is null"); } String topicSuffix = null; switch (event.getType()) { case WireAdminEvent.WIRE_CREATED : topicSuffix = WIRE_CREATED; break; case WireAdminEvent.WIRE_CONNECTED : topicSuffix = WIRE_CONNECTED; break; case WireAdminEvent.WIRE_UPDATED : topicSuffix = WIRE_UPDATED; break; case WireAdminEvent.WIRE_TRACE : topicSuffix = WIRE_TRACE; break; case WireAdminEvent.WIRE_DISCONNECTED : topicSuffix = WIRE_DISCONNECTED; break; case WireAdminEvent.WIRE_DELETED : topicSuffix = WIRE_DELETED; break; case WireAdminEvent.PRODUCER_EXCEPTION : topicSuffix = PRODUCER_EXCEPTION; break; case WireAdminEvent.CONSUMER_EXCEPTION : topicSuffix = CONSUMER_EXCEPTION; break; default : /* ignore: unknown/new events */ return; } String topic = WIRE_HEADER + TOPIC_SEPARATOR + topicSuffix; if (!hasServiceReferences(topic)) { if (Activator.LOG_DEBUG) log.debug(0, 10017, event.toString(), null, false); return; /* * no service references for this topic do not bother * EventAdmin */ } Hashtable props = new Hashtable(); addServiceProps(props, ref); Wire wire = event.getWire(); if (wire != null) { props.put(WA_WIRE, wire); props.put(WA_WIRE_CONNECTED, wire.isConnected() ? Boolean.TRUE : Boolean.FALSE); if (wire.getFlavors() != null) { props.put(WA_WIRE_FLAVORS, wire.getFlavors()); } if (wire.getScope() != null) { props.put(WA_WIRE_SCOPE, wire.getScope()); } props.put(WA_WIRE_VALID, wire.isValid() ? Boolean.TRUE : Boolean.FALSE); } Throwable throwable = event.getThrowable(); if (throwable != null) { addExceptionProps(props, throwable); } props.put(EVENT, event); final Event eaEvent = new Event(topic, (Dictionary) props); AccessController.doPrivileged(new PrivilegedAction() { public Object run() { eventAdmin.postEvent(eaEvent); return null; } }); if (Activator.LOG_DEBUG) log.debug(0, 10018, event.toString(), null, false); } } /** * This will return true if has at least one ServiceReference which match * given topic. * * @param topic * @return */ protected boolean hasServiceReferences(String topic) { BundleContext l_bc = bc; if (l_bc == null) { return false; } ServiceReference[] sr = null; try {/* get all handlers */ sr = l_bc.getServiceReferences(EventHandler.class.getName(), null); } catch (InvalidSyntaxException e) { return false; } if (sr != null && sr.length > 0) { TopicPermission perm = new TopicPermission(topic, TopicPermission.SUBSCRIBE); for (int i = 0; i < sr.length; i++) { try { ServiceReference sRef = sr[i]; Bundle bundle = sRef.getBundle(); if (bundle != null && (bundle.getState() != Bundle.UNINSTALLED) && bundle.hasPermission(perm)) { Object reftopic = sRef.getProperty(EventConstants.EVENT_TOPIC); if (reftopic != null) { /* * otherwise means will receive * no events */ if (reftopic instanceof String[]) { /* * even with one * element it * must be * String[] */ String topics[] = (String[]) reftopic; for (int j = 0; j < topics.length; j++) { if (matchTopic(topics[j], topic)) { return true; } } } } }/* check permission */ } catch (Throwable t) { log.error("Error while checking bundle permissions", t); } }/* for */ }/* sr != null && sr.length > 0 */ return false; } /** * Checks if a topic filter string matches the target topic string. * * @param pattern * A topic filter like "company/product/*" * @param topic * Target topic to be checked against like * "company/product/topicA" * @return true if topicProperty matches topic, false if otherwise. */ protected static boolean matchTopic(String pattern, String topic) { // //two fast checks if (pattern.length() > topic.length()) return false; if ("*".equals(pattern) || pattern.equals(topic)) return true; // //assume pattern is not NULL ... or check! int index = pattern.indexOf(TOPIC_SEPARATOR); if (index == -1 || index == 0 || index == pattern.length() - 1) { // //syntax problem // //we have no '/' or starts with '/' or ends with '/' return false; } for (index = 0; index < pattern.length(); index++) { if (pattern.charAt(index) == '*') { // //wildcard!!! if (pattern.charAt(index - 1) != TOPIC_SEPARATOR) return false; // we have not '/' before '*' if (index != pattern.length() - 1) return false; // we have something after the '*' return true; } if (pattern.charAt(index) != topic.charAt(index)) return false; } if (index != topic.length()) return false; if (pattern.charAt(index - 1) == TOPIC_SEPARATOR) return false; return true; } }