| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| package org.apache.felix.eventadmin.bridge.upnp; |
| |
| import java.util.Dictionary; |
| import java.util.Enumeration; |
| import java.util.HashSet; |
| import java.util.Hashtable; |
| import java.util.Iterator; |
| import java.util.Set; |
| |
| import org.osgi.framework.BundleContext; |
| import org.osgi.framework.Constants; |
| import org.osgi.framework.InvalidSyntaxException; |
| import org.osgi.framework.ServiceEvent; |
| import org.osgi.framework.ServiceListener; |
| import org.osgi.framework.ServiceReference; |
| import org.osgi.framework.ServiceRegistration; |
| import org.osgi.service.event.Event; |
| import org.osgi.service.event.EventAdmin; |
| import org.osgi.service.event.EventConstants; |
| import org.osgi.service.event.EventHandler; |
| import org.osgi.service.upnp.UPnPDevice; |
| import org.osgi.service.upnp.UPnPEventListener; |
| import org.osgi.service.upnp.UPnPService; |
| |
| /** |
| * This class registers itself as an UPnPEventListener service with the |
| * framework whenever both, at least one EventAdmin and at least one |
| * EventHandler is present and subsequently, bridges UPnPEvents received to the |
| * EventAdmin service. In order to track EventAdmin services this class |
| * registers a ServiceListener for EventAdmin services as well as a |
| * ServiceListener for EventHandlers in order to determine EventHandler |
| * availability. |
| * |
| * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a> |
| */ |
| public class UPnPEventToEventAdminBridge implements UPnPEventListener |
| { |
| private static final String EVENT_HANDLER_FILTER = "(&(" |
| + Constants.OBJECTCLASS + "=" + EventHandler.class.getName() + ")(|(" |
| + EventConstants.EVENT_TOPIC + "=\\*)(" + EventConstants.EVENT_TOPIC |
| + "=org/\\*)(" + EventConstants.EVENT_TOPIC + "=org/osgi/\\*)(" |
| + EventConstants.EVENT_TOPIC + "=org/osgi/service/\\*)(" |
| + EventConstants.EVENT_TOPIC + "=org/osgi/service/upnp/\\*)(" |
| + EventConstants.EVENT_TOPIC + "=org/osgi/service/upnp/UPnPEvent)))"; |
| |
| final Object m_lock = new Object(); |
| |
| // The references to the EventAdmins |
| final Set m_adminRefs = new HashSet(); |
| |
| // The references to the EventHandlers |
| final Set m_handlerRefs = new HashSet(); |
| |
| private final BundleContext m_context; |
| |
| private ServiceRegistration m_reg = null; |
| |
| /** |
| * This class registers itself as an UPnPEventListener service with the |
| * framework whenever both, at least one EventAdmin and at least one |
| * EventHandler is present and subsequently, bridges UPnPEvents received to |
| * the EventAdmin service. In order to track EventAdmin services this class |
| * registers a ServiceListener for EventAdmin services as well as a |
| * ServiceListener for EventHandlers in order to determine EventHandler |
| * availability. |
| * |
| * @param context |
| * The context to register with. |
| */ |
| public UPnPEventToEventAdminBridge(final BundleContext context) |
| { |
| synchronized(m_lock) |
| { |
| m_context = context; |
| |
| try |
| { |
| m_context.addServiceListener(new ServiceListener() |
| { |
| |
| public void serviceChanged(final ServiceEvent event) |
| { |
| synchronized(m_lock) |
| { |
| switch(event.getType()) |
| { |
| case ServiceEvent.REGISTERED: |
| m_adminRefs |
| .add(event.getServiceReference()); |
| break; |
| case ServiceEvent.UNREGISTERING: |
| m_adminRefs.remove(event |
| .getServiceReference()); |
| break; |
| } |
| |
| check(); |
| } |
| } |
| }, "(" + Constants.OBJECTCLASS + "=" |
| + EventAdmin.class.getName() + ")"); |
| |
| final ServiceReference[] adminRefs = m_context |
| .getServiceReferences(EventAdmin.class.getName(), null); |
| |
| if(null != adminRefs) |
| { |
| for(int i = 0; i < adminRefs.length; i++) |
| { |
| m_adminRefs.add(adminRefs[i]); |
| } |
| } |
| |
| m_context.addServiceListener(new ServiceListener() |
| { |
| public void serviceChanged(final ServiceEvent event) |
| { |
| synchronized(m_lock) |
| { |
| switch(event.getType()) |
| { |
| case ServiceEvent.REGISTERED: |
| m_handlerRefs.add(event |
| .getServiceReference()); |
| break; |
| case ServiceEvent.UNREGISTERING: |
| m_handlerRefs.remove(event |
| .getServiceReference()); |
| break; |
| } |
| |
| check(); |
| } |
| } |
| }, EVENT_HANDLER_FILTER); |
| |
| final ServiceReference[] handlerRefs = m_context |
| .getServiceReferences(EventHandler.class.getName(), |
| EVENT_HANDLER_FILTER); |
| |
| if(null != handlerRefs) |
| { |
| for(int i = 0; i < handlerRefs.length; i++) |
| { |
| m_handlerRefs.add(handlerRefs[i]); |
| } |
| } |
| } catch(InvalidSyntaxException e) |
| { |
| // This will never happen |
| } |
| |
| check(); |
| } |
| } |
| |
| // The set contains the last used filter parts. It will be null in case the |
| // last |
| // time we registered with a null property. It will be an empty HashSet in |
| // case we have been unregistered previously |
| private Set last = new HashSet(); |
| |
| // Registers itself as an UPnPEventListener with the framework in case there |
| // is both, at least one EventAdmin (i.e., !m_adminRefs.isEmpty()) and at |
| // least one EventHandler (i.e., !m_handlerRefs.isEmpty()) present and it is |
| // not already registers. Respectively, it unregisters itself in case one of |
| // the above is false. |
| void check() |
| { |
| // do we need to be registered? |
| if(m_adminRefs.isEmpty() || m_handlerRefs.isEmpty()) |
| { |
| // no we don't but do we need to unregister? |
| if(null != m_reg) |
| { |
| // yes |
| m_reg.unregister(); |
| m_reg = null; |
| last = new HashSet(); |
| } |
| } |
| else |
| // yes we need to be registered |
| { |
| final Set parts = new HashSet(); |
| final StringBuffer result = new StringBuffer().append("(|"); |
| |
| for(Iterator iter = m_handlerRefs.iterator(); iter.hasNext();) |
| { |
| final String filter = (String) ((ServiceReference) iter.next()) |
| .getProperty(EventConstants.EVENT_FILTER); |
| |
| // if any filter is not set we need to register with a null and |
| // can |
| // return |
| if(null == filter) |
| { |
| // but only if we are not currently registered with a null |
| if(last != null) |
| { |
| last = null; |
| change(null); |
| } |
| return; |
| } |
| |
| // if we don't already have this filter part we need to check if |
| // it is a valid filter |
| if(!parts.contains(filter)) |
| { |
| try |
| { |
| m_context.createFilter(filter); |
| parts.add(filter); |
| result.append(filter); |
| } catch(InvalidSyntaxException e) |
| { |
| // and it is not a valid filter - hence, drop it |
| e.printStackTrace(); |
| } |
| } |
| } |
| |
| // parts will only be empty if there is no handler with a valid |
| // filter and we only need to register with the new filter if it |
| // doesn't equals the last filter |
| if(!parts.isEmpty() && !parts.equals(last)) |
| { |
| last = parts; |
| |
| try |
| { |
| final Hashtable properties = new Hashtable(); |
| |
| properties.put(UPnPEventListener.UPNP_FILTER, |
| m_context.createFilter(replaceAll(replaceAll( |
| result.append(")").toString().toCharArray(), |
| serviceChars, UPnPService.ID).toCharArray(), |
| deviceChars, UPnPDevice.ID))); |
| |
| change(properties); |
| } catch(InvalidSyntaxException e) |
| { |
| // This will never happen |
| e.printStackTrace(); |
| } |
| } |
| } |
| } |
| |
| private static final char[] serviceChars = new char[]{'u','p','n','p','.','s','e','r','v','i','c','e','i','d'}; |
| private static final char[] deviceChars = new char[]{'u','p','n','p','.','d','e','v','i','c','e','i','d'}; |
| |
| private String replaceAll(final char[] source, final char[] pattern, final String target) |
| { |
| StringBuffer result = new StringBuffer(); |
| |
| int pos = 0, matchPos = 0; |
| |
| while(true) |
| { |
| if(pattern[matchPos] == Character.toLowerCase(source[pos])) |
| { |
| matchPos++; |
| if(matchPos == pattern.length) |
| { |
| result.append(target); |
| matchPos = 0; |
| } |
| } |
| else if(matchPos > 0 ) |
| { |
| result.append(source, pos - matchPos, matchPos + 1); |
| matchPos = 0; |
| } |
| else |
| { |
| result.append(source[pos]); |
| } |
| |
| pos++; |
| |
| if(pos >= source.length) |
| { |
| if(matchPos > 0) |
| { |
| result.append(source, pos - matchPos, matchPos); |
| } |
| |
| break; |
| } |
| } |
| |
| return result.toString(); |
| } |
| |
| private void change(final Dictionary filter) |
| { |
| if(null == m_reg) |
| { |
| m_reg = m_context.registerService( |
| UPnPEventListener.class.getName(), this, filter); |
| } |
| else |
| { |
| m_reg.setProperties(filter); |
| } |
| } |
| |
| /** |
| * Bridge any event to the EventAdmin service. |
| * |
| * @param deviceId |
| * Bridged to <tt>upnp.deviceId</tt> |
| * @param serviceId |
| * Bridged to <tt>upnp.serviceId</tt> |
| * @param events |
| * Bridged to <tt>upnp.events</tt> |
| * |
| * @see org.osgi.service.upnp.UPnPEventListener#notifyUPnPEvent(java.lang.String, |
| * java.lang.String, java.util.Dictionary) |
| */ |
| public void notifyUPnPEvent(final String deviceId, final String serviceId, |
| final Dictionary events) |
| { |
| final ServiceReference ref = m_context |
| .getServiceReference(EventAdmin.class.getName()); |
| |
| if(null != ref) |
| { |
| final EventAdmin eventAdmin = (EventAdmin) m_context |
| .getService(ref); |
| |
| if(null != eventAdmin) |
| { |
| final Dictionary immutableEvents = new Dictionary() |
| { |
| public int size() |
| { |
| return events.size(); |
| } |
| |
| public boolean isEmpty() |
| { |
| return events.isEmpty(); |
| } |
| |
| public Enumeration keys() |
| { |
| return events.keys(); |
| } |
| |
| public Enumeration elements() |
| { |
| return events.elements(); |
| } |
| |
| public Object get(Object arg0) |
| { |
| return events.get(arg0); |
| } |
| |
| public Object put(Object arg0, Object arg1) |
| { |
| throw new IllegalStateException( |
| "Event Properties may not be changed"); |
| } |
| |
| public Object remove(Object arg0) |
| { |
| throw new IllegalStateException( |
| "Event Properties may not be changed"); |
| } |
| |
| public boolean equals(Object arg0) |
| { |
| return events.equals(arg0); |
| } |
| |
| public int hashCode() |
| { |
| return events.hashCode(); |
| } |
| |
| public String toString() |
| { |
| return events.toString(); |
| } |
| }; |
| |
| final Hashtable properties = new Hashtable(); |
| |
| properties.put(UPnPDevice.ID, deviceId); |
| properties.put(UPnPService.ID, serviceId); |
| properties.put("upnp.serviceId", serviceId); |
| properties.put("upnp.deviceId", deviceId); |
| properties.put("upnp.events", immutableEvents); |
| |
| eventAdmin.postEvent( |
| new Event("org/osgi/service/upnp/UPnPEvent", properties)); |
| |
| m_context.ungetService(ref); |
| } |
| } |
| } |
| } |