blob: 659d3e83b3d3ed6448b3179303818b27561d591f [file] [log] [blame]
/*******************************************************************************
* 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, new Boolean(wire.isConnected()));
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, new Boolean(wire.isValid()));
}
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;
}
}