/* * 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.ipojo.handlers.providedservice; import org.apache.felix.ipojo.*; import org.apache.felix.ipojo.util.Callback; import org.apache.felix.ipojo.util.Property; import org.apache.felix.ipojo.util.SecurityHelper; import org.osgi.framework.*; import org.osgi.service.cm.ConfigurationAdmin; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.*; /** * Provided Service represent a provided service by the component. * * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a> */ public class ProvidedService implements ServiceFactory { /** * Service State : REGISTRED. */ public static final int REGISTERED = 1; /** * Service State : UNREGISTRED. */ public static final int UNREGISTERED = 0; /** * Factory Policy : SINGLETON_FACTORY. */ public static final int SINGLETON_STRATEGY = 0; /** * Factory policy : SERVICE_FACTORY. */ public static final int SERVICE_STRATEGY = 1; /** * Factory policy : STATIC_FACTORY. */ public static final int STATIC_STRATEGY = 2; /** * Factory policy : INSTANCE. * Creates one service object per instance consuming the service. */ public static final int INSTANCE_STRATEGY = 3; /** * Factory policy : CUSTOMIZED. * Custom creation strategy */ public static final int CUSTOM_STRATEGY = -1; public static final String ALL_SPECIFICATIONS_FOR_CONTROLLERS = "ALL"; /** * At this time, it is only the java interface full name. */ private String[] m_serviceSpecifications = new String[0]; /** * The service registration. is null when the service is not registered. * m_serviceRegistration : ServiceRegistration */ private ServiceRegistration m_serviceRegistration; /** * Link to the owner handler. m_handler : Provided Service Handler */ private ProvidedServiceHandler m_handler; /** * The map of properties. */ private Map<String, Property> m_properties = new TreeMap<String, Property>(); /** * Service providing policy. */ private final int m_policy; /** * Service Object creation policy. */ private final CreationStrategy m_strategy; /** * Were the properties updated during the processing. */ private volatile boolean m_wasUpdated; /** * Service Controller. */ private Map<String, ServiceController> m_controllers = new HashMap<String, ServiceController>(); /** * Post-Registration callback. */ private Callback m_postRegistration; /** * Post-Unregistration callback. */ private Callback m_postUnregistration; /** * The published properties. */ private Dictionary m_publishedProperties = new Properties(); /** * The provided service listeners. */ private List<ProvidedServiceListener> m_listeners = new ArrayList<ProvidedServiceListener>(); /** * Creates a provided service object. * * @param handler the the provided service handler. * @param specification the specifications provided by this provided service * @param factoryPolicy the service providing policy * @param creationStrategyClass the customized service object creation strategy. * @param conf the instance configuration. */ public ProvidedService(ProvidedServiceHandler handler, String[] specification, int factoryPolicy, Class creationStrategyClass, Dictionary conf) { CreationStrategy strategy; m_handler = handler; m_serviceSpecifications = specification; if (creationStrategyClass == null) { m_policy = factoryPolicy; } else { m_policy = CUSTOM_STRATEGY; } // Add instance name, factory name and factory version is set. try { addProperty(new Property(Factory.INSTANCE_NAME_PROPERTY, null, null, handler.getInstanceManager().getInstanceName(), String.class.getName(), handler.getInstanceManager(), handler)); addProperty(new Property("factory.name", null, null, handler.getInstanceManager().getFactory().getFactoryName(), String.class.getName(), handler.getInstanceManager(), handler)); if (handler.getInstanceManager().getFactory().getVersion() != null) { addProperty(new Property(Factory.FACTORY_VERSION_PROPERTY, null, null, handler.getInstanceManager().getFactory().getVersion(), String.class.getName(), handler.getInstanceManager(), handler)); } // Add the service.* if defined if (conf.get(Constants.SERVICE_PID) != null) { addProperty(new Property(Constants.SERVICE_PID, null, null, (String) conf.get(Constants.SERVICE_PID), String.class.getName(), handler.getInstanceManager(), handler)); } if (conf.get(Constants.SERVICE_RANKING) != null) { addProperty(new Property(Constants.SERVICE_RANKING, null, null, (String) conf.get(Constants.SERVICE_RANKING), "int", handler.getInstanceManager(), handler)); } if (conf.get(Constants.SERVICE_VENDOR) != null) { addProperty(new Property(Constants.SERVICE_VENDOR, null, null, (String) conf.get(Constants.SERVICE_VENDOR), String.class.getName(), handler.getInstanceManager(), handler)); } if (conf.get(Constants.SERVICE_DESCRIPTION) != null) { addProperty(new Property(Constants.SERVICE_DESCRIPTION, null, null, (String) conf.get(Constants.SERVICE_DESCRIPTION), String.class.getName(), handler.getInstanceManager(), handler)); } } catch (ConfigurationException e) { m_handler.error("An exception occurs when adding instance.name and factory.name property : " + e.getMessage()); } if (creationStrategyClass != null) { try { strategy = (CreationStrategy) creationStrategyClass.newInstance(); } catch (IllegalAccessException e) { strategy = null; m_handler.error("[" + m_handler.getInstanceManager().getInstanceName() + "] The customized service object creation policy " + "(" + creationStrategyClass.getName() + ") is not accessible: " + e.getMessage(), e); getInstanceManager().stop(); } catch (InstantiationException e) { strategy = null; m_handler.error("[" + m_handler.getInstanceManager().getInstanceName() + "] The customized service object creation policy " + "(" + creationStrategyClass.getName() + ") cannot be instantiated: " + e.getMessage(), e); getInstanceManager().stop(); } } else { switch (factoryPolicy) { case SINGLETON_STRATEGY: strategy = new SingletonStrategy(); break; case SERVICE_STRATEGY: case STATIC_STRATEGY: // In this case, we need to try to create a new pojo object, // the factory method will handle the creation. strategy = new FactoryStrategy(); break; case INSTANCE_STRATEGY: strategy = new PerInstanceStrategy(); break; // Other policies: // Thread : one service object per asking thread // Consumer : one service object per consumer default: strategy = null; List specs = Arrays.asList(m_serviceSpecifications); m_handler.error("[" + m_handler.getInstanceManager().getInstanceName() + "] Unknown creation policy for " + specs + " : " + factoryPolicy); getInstanceManager().stop(); break; } } m_strategy = strategy; } /** * Add properties to the provided service. * * @param props : the properties to attached to the service registration */ protected void setProperties(Property[] props) { for (Property prop : props) { addProperty(prop); } } /** * Add the given property to the property list. * * @param prop : the element to add */ private synchronized void addProperty(Property prop) { m_properties.put(prop.getName(), prop); } /** * Remove a property. * * @param name : the property to remove * @return <code>true</code> if the property was removed, * <code>false</code> otherwise. */ private synchronized boolean removeProperty(String name) { return m_properties.remove(name) != null; } /** * Get the service reference of the service registration. * * @return the service reference of the provided service (null if the * service is not published). */ public ServiceReference getServiceReference() { if (m_serviceRegistration == null) { return null; } else { return m_serviceRegistration.getReference(); } } /** * Returns a service object for the dependency. * * @param bundle : the bundle * @param registration : the service registration of the registered service * @return a new service object or a already created service object (in the case of singleton) or <code>null</code> * if the instance is no more valid. * @see org.osgi.framework.ServiceFactory#getService(org.osgi.framework.Bundle, org.osgi.framework.ServiceRegistration) */ public Object getService(Bundle bundle, ServiceRegistration registration) { if (getInstanceManager().getState() == InstanceManager.VALID) { return m_strategy.getService(bundle, registration); } else { return null; } } /** * The unget method. * * @param bundle : bundle * @param registration : service registration * @param service : service object * @see org.osgi.framework.ServiceFactory#ungetService(org.osgi.framework.Bundle, * org.osgi.framework.ServiceRegistration, java.lang.Object) */ public void ungetService(Bundle bundle, ServiceRegistration registration, Object service) { m_strategy.ungetService(bundle, registration, service); } /** * Registers the service. The service object must be able to serve this * service. * This method also notifies the creation strategy of the publication. */ public void registerService() { ServiceRegistration reg = null; Properties serviceProperties = null; // Do not have to be in the synchronized block, immutable. final BundleContext bc = m_handler.getInstanceManager().getContext(); synchronized (this) { if (m_serviceRegistration != null) { return; } else { if (m_handler.getInstanceManager().getState() == ComponentInstance.VALID && isAtLeastAServiceControllerValid()) { // Security check if (SecurityHelper.hasPermissionToRegisterServices( m_serviceSpecifications, bc) && SecurityHelper.canRegisterService(bc)) { serviceProperties = getServiceProperties(); } else { throw new SecurityException("The bundle " + bc.getBundle().getBundleId() + " does not have the" + " permission to register the services " + Arrays.asList(m_serviceSpecifications)); } } else { // We don't have to do anything. return; } } } // Registration must be done outside of the synchronized block. m_strategy.onPublication(getInstanceManager(), getServiceSpecificationsToRegister(), serviceProperties); reg = bc.registerService( getServiceSpecificationsToRegister(), this, (Dictionary) serviceProperties); boolean update = false; synchronized (this) { if (m_serviceRegistration != null) { // Oh oh the service was registered twice. Unregister the last one reg.unregister(); return; } else { m_serviceRegistration = reg; } // An update may happen during the registration, re-check and apply. // This must be call outside the synchronized block. // If the registration is null, the security helper returns false. if (m_wasUpdated && SecurityHelper.canUpdateService(reg)) { serviceProperties = getServiceProperties(); update = true; } } if (update) { reg.setProperties(serviceProperties); } synchronized (this) { m_publishedProperties = serviceProperties; m_wasUpdated = false; // Call the post-registration callback in the same thread holding // the monitor lock. // This allows to be sure that the callback is called once per // registration. // But the callback must take care to not create a deadlock if (m_postRegistration != null) { try { m_postRegistration .call(new Object[]{m_serviceRegistration .getReference()}); } catch (Exception e) { m_handler.error( "Cannot invoke the post-registration callback " + m_postRegistration.getMethod(), e); } } } // Notify: ProvidedServiceListeners.serviceRegistered() notifyListeners(+1); } /** * Withdraws the service from the service registry. */ public void unregisterService() { ServiceReference ref = null; synchronized (this) { // Create a copy of the service reference in the case we need // to inject it to the post-unregistration callback. if (m_serviceRegistration != null) { ref = m_serviceRegistration.getReference(); m_serviceRegistration.unregister(); m_serviceRegistration = null; } m_strategy.onUnpublication(); // Call the post-unregistration callback in the same thread holding the monitor lock. // This allows to be sure that the callback is called once per unregistration. // But the callback must take care to not create a deadlock if (m_postUnregistration != null && ref != null) { try { m_postUnregistration.call(new Object[]{ref}); } catch (Exception e) { m_handler.error("Cannot invoke the post-unregistration callback " + m_postUnregistration.getMethod(), e); } } } // Notify: ProvidedServiceListeners.serviceUnregistered() if (ref != null) { notifyListeners(-1); } } /** * Get the current provided service state. * * @return The state of the provided service. */ public int getState() { if (m_serviceRegistration == null) { return UNREGISTERED; } else { return REGISTERED; } } protected InstanceManager getInstanceManager() { return m_handler.getInstanceManager(); } /** * Return the list of properties attached to this service. This list * contains only property where a value are assigned. * <p/> * This method is called while holding the monitor lock. * * @return the properties attached to the provided service. */ private Properties getServiceProperties() { // Build the service properties list Properties serviceProperties = new Properties(); for (Property p : m_properties.values()) { final Object value = p.getValue(); if (value != null && value != Property.NO_VALUE) { serviceProperties.put(p.getName(), value); } } return serviceProperties; } /** * Get the list of properties attached to the service registration. * * @return the properties attached to the provided service. */ public synchronized Property[] getProperties() { return m_properties.values().toArray(new Property[m_properties.size()]); } /** * Update the service properties. The new list of properties is sent to * the service registry. */ public void update() { boolean doCallListener = false; synchronized (this) { // Update the service registration if (m_serviceRegistration != null) { Properties updated = getServiceProperties(); Dictionary oldProps = (Dictionary) ((Properties) m_publishedProperties).clone(); Dictionary newProps = (Dictionary) (updated.clone()); // Remove keys that must not be compared newProps.remove(Factory.INSTANCE_NAME_PROPERTY); oldProps.remove(Factory.INSTANCE_NAME_PROPERTY); newProps.remove(Constants.SERVICE_ID); oldProps.remove(Constants.SERVICE_ID); newProps.remove(Constants.SERVICE_PID); oldProps.remove(Constants.SERVICE_PID); newProps.remove("factory.name"); oldProps.remove("factory.name"); newProps.remove(ConfigurationAdmin.SERVICE_FACTORYPID); oldProps.remove(ConfigurationAdmin.SERVICE_FACTORYPID); // Trigger the update only if the properties have changed. // First check, are the size equals if (oldProps.size() != newProps.size()) { if (SecurityHelper.canUpdateService(m_serviceRegistration)) { m_handler.info("Updating Registration : " + oldProps.size() + " / " + newProps.size()); m_publishedProperties = updated; m_serviceRegistration.setProperties(updated); doCallListener = true; } } else { // Check changes Enumeration keys = oldProps.keys(); boolean hasChanged = false; while (!hasChanged && keys.hasMoreElements()) { String k = (String) keys.nextElement(); Object val = oldProps.get(k); if (!val.equals(updated.get(k))) { hasChanged = true; } } if (hasChanged && SecurityHelper.canUpdateService(m_serviceRegistration)) { m_handler.info("Updating Registration : " + updated); m_publishedProperties = updated; m_serviceRegistration.setProperties(updated); doCallListener = true; } } } else { // Need to be updated later. m_wasUpdated = true; } } if (doCallListener) { // Notify: ProvidedServiceListeners.serviceUpdated() notifyListeners(0); } } /** * Add properties to the list. * * @param props : properties to add */ protected void addProperties(Dictionary props) { Enumeration keys = props.keys(); boolean updated = false; while (keys.hasMoreElements()) { String key = (String) keys.nextElement(); Object value = props.get(key); // m_properties can be modified by another thread, we need to make a stack confinement Property property; synchronized (this) { property = m_properties.get(key); } if (property != null) { // Already existing property if (property.getValue() == null || !value.equals(property.getValue())) { property.setValue(value); updated = true; } } else { try { // Create the property. property = new Property(key, null, null, value, getInstanceManager(), m_handler); addProperty(property); updated = true; } catch (ConfigurationException e) { m_handler.error("The propagated property " + key + " cannot be created correctly : " + e.getMessage()); } } } if (updated) { m_handler.info("Update triggered by adding properties " + props); update(); } } /** * Remove properties from the list. * * @param props : properties to remove */ protected void deleteProperties(Dictionary props) { Enumeration keys = props.keys(); boolean mustUpdate = false; while (keys.hasMoreElements()) { String key = (String) keys.nextElement(); mustUpdate = mustUpdate || removeProperty(key); } if (mustUpdate) { m_handler.info("Update triggered when removing properties : " + props); update(); } } /** * Get the published service specifications. * * @return the list of provided service specifications (i.e. java * interface). */ public String[] getServiceSpecifications() { return m_serviceSpecifications; } /** * Get the service registration. * * @return the service registration of this service. */ public ServiceRegistration getServiceRegistration() { return m_serviceRegistration; } /** * Sets the service controller on this provided service. * * @param field the field attached to this controller * @param value the value the initial value * @param specification the target specification, if <code>null</code> * affect all specifications. */ public void setController(String field, boolean value, String specification) { if (specification == null) { m_controllers.put(ALL_SPECIFICATIONS_FOR_CONTROLLERS, new ServiceController(field, value)); } else { m_controllers.put(specification, new ServiceController(field, value)); } } /** * Gets the service controller attached to the given field. * * @param field the field name * @return the service controller, {@code null} if there is no service controller attached to the given field * name. */ public ServiceController getController(String field) { for (ServiceController controller : m_controllers.values()) { if (field.equals(controller.m_field)) { return controller; } } return null; } /** * Gets the service controller handling the service publishing the given specification. * * @param spec the specification (qualified class name) * @return the service controller, {@code null} if there is no service controller handling the service publishing * the given service specification */ public ServiceController getControllerBySpecification(String spec) { return m_controllers.get(spec); } /** * Checks if at least one service controller is valid. * * @return <code>true</code> if one service controller at least * is valid. */ private boolean isAtLeastAServiceControllerValid() { // No controller if (m_controllers.isEmpty()) { return true; } for (ServiceController controller : m_controllers.values()) { if (controller.getValue()) { return true; } } return false; } private String[] getServiceSpecificationsToRegister() { if (m_controllers.isEmpty()) { return m_serviceSpecifications; } ArrayList<String> l = new ArrayList<String>(); if (m_controllers.containsKey(ALL_SPECIFICATIONS_FOR_CONTROLLERS)) { ServiceController ctrl = m_controllers.get(ALL_SPECIFICATIONS_FOR_CONTROLLERS); if (ctrl.m_value) { l.addAll(Arrays.asList(m_serviceSpecifications)); } } for (String spec : m_controllers.keySet()) { ServiceController ctrl = m_controllers.get(spec); if (ctrl.m_value) { if (!ALL_SPECIFICATIONS_FOR_CONTROLLERS.equals(spec)) { // Already added. if (!l.contains(spec)) { l.add(spec); } } } else { l.remove(spec); } } return l.toArray(new String[l.size()]); } public void setPostRegistrationCallback(Callback cb) { m_postRegistration = cb; } public void setPostUnregistrationCallback(Callback cb) { m_postUnregistration = cb; } public int getPolicy() { return m_policy; } public Class<? extends CreationStrategy> getCreationStrategy() { return m_strategy.getClass(); } /** * Add the given listener to the provided service handler's list of listeners. * * @param listener the {@code ProvidedServiceListener} object to be added * @throws NullPointerException if {@code listener} is {@code null} */ public void addListener(ProvidedServiceListener listener) { if (listener == null) { throw new NullPointerException("null listener"); } synchronized (m_listeners) { m_listeners.add(listener); } } /** * Remove the given listener from the provided service handler's list of listeners. * * @param listener the {@code ProvidedServiceListener} object to be removed * @throws NullPointerException if {@code listener} is {@code null} * @throws NoSuchElementException if {@code listener} wasn't present the in provided service handler's list of listeners */ public void removeListener(ProvidedServiceListener listener) { if (listener == null) { throw new NullPointerException("null listener"); } synchronized (m_listeners) { // We definitely cannot rely on listener's equals method... // ...so we need to manually search for the listener, using ==. int i = -1; for (int j = m_listeners.size() - 1; j >= 0; j--) { if (m_listeners.get(j) == listener) { // Found! i = j; break; } } if (i != -1) { m_listeners.remove(i); } else { throw new NoSuchElementException("no such listener"); } } } /** * Notify all listeners that a change has occurred in this provided service. * * @param direction the "direction" of the change (+1:registration, 0:update, -1:unregistration) */ private void notifyListeners(int direction) { // Get a snapshot of the listeners List<ProvidedServiceListener> tmp; synchronized (m_listeners) { tmp = new ArrayList<ProvidedServiceListener>(m_listeners); } // Do notify, outside the m_listeners lock for (ProvidedServiceListener l : tmp) { try { if (direction > 0) { l.serviceRegistered(m_handler.getInstanceManager(), this); } else if (direction < 0) { l.serviceUnregistered(m_handler.getInstanceManager(), this); } else { l.serviceModified(m_handler.getInstanceManager(), this); } } catch (Throwable e) { // Put a warning on the logger, and continue m_handler.warn( String.format( "[%s] A ProvidedServiceListener has failed: %s", m_handler.getInstanceManager().getInstanceName(), e.getMessage()) , e); } } } /** * Removes all the listeners from this provided service before it gets disposed. */ public void cleanup() { synchronized (m_listeners) { m_listeners.clear(); } } /** * Service Controller. */ class ServiceController { /** * The controller value. */ private boolean m_value; /** * The field attached to this controller. */ private final String m_field; /** * Creates a ServiceController. * * @param field the field * @param value the initial value */ public ServiceController(String field, boolean value) { m_field = field; m_value = value; } public String getField() { return m_field; } /** * Gets the value. * * @return the value */ public boolean getValue() { synchronized (ProvidedService.this) { return m_value; } } /** * Sets the value. * * @param value the value */ public void setValue(Boolean value) { synchronized (ProvidedService.this) { if (value != m_value) { // If there is a change to the ServiceController value then // we will // need to modify the registrations. m_value = value; unregisterService(); if (getServiceSpecificationsToRegister().length != 0) { registerService(); } } } } } /** * Singleton creation strategy. * This strategy just creates one service object and * returns always the same. */ private class SingletonStrategy extends CreationStrategy { /** * The service is going to be registered. * * @param instance the instance manager * @param interfaces the published interfaces * @param props the properties * @see org.apache.felix.ipojo.handlers.providedservice.CreationStrategy#onPublication(InstanceManager, java.lang.String[], java.util.Properties) */ public void onPublication(InstanceManager instance, String[] interfaces, Properties props) { } /** * The service was unpublished. * * @see org.apache.felix.ipojo.handlers.providedservice.CreationStrategy#onUnpublication() */ public void onUnpublication() { } /** * A service object is required. * * @param arg0 the bundle requiring the service object. * @param arg1 the service registration. * @return the first pojo object. * @see org.osgi.framework.ServiceFactory#getService(org.osgi.framework.Bundle, org.osgi.framework.ServiceRegistration) */ public Object getService(Bundle arg0, ServiceRegistration arg1) { return m_handler.getInstanceManager().getPojoObject(); } /** * A service object is released. * * @param arg0 the bundle * @param arg1 the service registration * @param arg2 the get service object. * @see org.osgi.framework.ServiceFactory#ungetService(org.osgi.framework.Bundle, org.osgi.framework.ServiceRegistration, java.lang.Object) */ public void ungetService(Bundle arg0, ServiceRegistration arg1, Object arg2) { } } /** * Service object creation policy following the OSGi Service Factory * policy {@link ServiceFactory}. */ private class FactoryStrategy extends CreationStrategy { /** * The service is going to be registered. * * @param instance the instance manager * @param interfaces the published interfaces * @param props the properties * @see org.apache.felix.ipojo.handlers.providedservice.CreationStrategy#onPublication(InstanceManager, java.lang.String[], java.util.Properties) */ public void onPublication(InstanceManager instance, String[] interfaces, Properties props) { } /** * The service is unpublished. * * @see org.apache.felix.ipojo.handlers.providedservice.CreationStrategy#onUnpublication() */ public void onUnpublication() { } /** * OSGi Service Factory getService method. * Returns a new service object per asking bundle. * This object is then cached by the framework. * * @param arg0 the bundle requiring the service * @param arg1 the service registration * @return the service object for the asking bundle * @see org.osgi.framework.ServiceFactory#getService(org.osgi.framework.Bundle, org.osgi.framework.ServiceRegistration) */ public Object getService(Bundle arg0, ServiceRegistration arg1) { return m_handler.getInstanceManager().createPojoObject(); } /** * OSGi Service Factory unget method. * Deletes the created object for the asking bundle. * * @param arg0 the asking bundle * @param arg1 the service registration * @param arg2 the created service object returned for this bundle * @see org.osgi.framework.ServiceFactory#ungetService(org.osgi.framework.Bundle, org.osgi.framework.ServiceRegistration, java.lang.Object) */ public void ungetService(Bundle arg0, ServiceRegistration arg1, Object arg2) { m_handler.getInstanceManager().deletePojoObject(arg2); } } /** * Service object creation policy creating a service object per asking iPOJO component * instance. This creation policy follows the iPOJO Service Factory interaction pattern * and does no support 'direct' invocation. */ private class PerInstanceStrategy extends CreationStrategy implements IPOJOServiceFactory, InvocationHandler { /** * Map [ComponentInstance->ServiceObject] storing created service objects. */ private Map/*<ComponentInstance, ServiceObject>*/ m_instances = new HashMap(); /** * A method is invoked on the proxy object. * If the method is the {@link IPOJOServiceFactory#getService(ComponentInstance)} * method, this method creates a service object if not already created for the asking * component instance. * If the method is {@link IPOJOServiceFactory#ungetService(ComponentInstance, Object)} * the service object is unget (i.e. removed from the map and deleted). * In all other cases, a {@link UnsupportedOperationException} is thrown as this policy * requires to use the {@link IPOJOServiceFactory} interaction pattern. * * @param arg0 the proxy object * @param arg1 the called method * @param arg2 the arguments * @return the service object attached to the asking instance for 'get', * <code>null</code> for 'unget', * a {@link UnsupportedOperationException} for all other methods. * @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object[]) */ public Object invoke(Object arg0, Method arg1, Object[] arg2) { if (isGetServiceMethod(arg1)) { return getService((ComponentInstance) arg2[0]); } if (isUngetServiceMethod(arg1)) { ungetService((ComponentInstance) arg2[0], arg2[1]); return null; } // Regular methods from java.lang.Object : equals and hashCode if (arg1.getName().equals("equals") && arg2 != null && arg2.length == 1) { return this.equals(arg2[0]); } if (arg1.getName().equals("hashCode")) { return this.hashCode(); } throw new UnsupportedOperationException("This service requires an advanced creation policy. " + "Before calling the service, call the getService(ComponentInstance) method to get " + "the service object. - Method called: " + arg1.getName()); } /** * A service object is required. * This policy returns a service object per asking instance. * * @param instance the instance requiring the service object * @return the service object for this instance * @see org.apache.felix.ipojo.IPOJOServiceFactory#getService(org.apache.felix.ipojo.ComponentInstance) */ public Object getService(ComponentInstance instance) { Object obj = m_instances.get(instance); if (obj == null) { obj = m_handler.getInstanceManager().createPojoObject(); m_instances.put(instance, obj); } return obj; } /** * A service object is unget. * The service object is removed from the map and deleted. * * @param instance the instance releasing the service * @param svcObject the service object * @see org.apache.felix.ipojo.IPOJOServiceFactory#ungetService(org.apache.felix.ipojo.ComponentInstance, java.lang.Object) */ public void ungetService(ComponentInstance instance, Object svcObject) { Object pojo = m_instances.remove(instance); m_handler.getInstanceManager().deletePojoObject(pojo); } /** * The service is going to be registered. * * @param instance the instance manager * @param interfaces the published interfaces * @param props the properties * @see org.apache.felix.ipojo.handlers.providedservice.CreationStrategy#onPublication(InstanceManager, java.lang.String[], java.util.Properties) */ public void onPublication(InstanceManager instance, String[] interfaces, Properties props) { } /** * The service is going to be unregistered. * The instance map is cleared. Created object are disposed. * * @see org.apache.felix.ipojo.handlers.providedservice.CreationStrategy#onUnpublication() */ public void onUnpublication() { Collection col = m_instances.values(); Iterator it = col.iterator(); while (it.hasNext()) { m_handler.getInstanceManager().deletePojoObject(it.next()); } m_instances.clear(); } /** * OSGi Service Factory getService method. * * @param arg0 the asking bundle * @param arg1 the service registration * @return a proxy implementing the {@link IPOJOServiceFactory} * @see org.osgi.framework.ServiceFactory#getService(org.osgi.framework.Bundle, org.osgi.framework.ServiceRegistration) */ public Object getService(Bundle arg0, ServiceRegistration arg1) { Object proxy = Proxy.newProxyInstance(getInstanceManager().getClazz().getClassLoader(), getSpecificationsWithIPOJOServiceFactory(m_serviceSpecifications, m_handler.getInstanceManager().getContext()), this); return proxy; } /** * OSGi Service factory unget method. * Does nothing. * * @param arg0 the asking bundle * @param arg1 the service registration * @param arg2 the service object created for this bundle. * @see org.osgi.framework.ServiceFactory#ungetService(org.osgi.framework.Bundle, org.osgi.framework.ServiceRegistration, java.lang.Object) */ public void ungetService(Bundle arg0, ServiceRegistration arg1, Object arg2) { } /** * Utility method returning the class array of provided service * specification and the {@link IPOJOServiceFactory} interface. * * @param specs the published service interface * @param bc the bundle context, used to load classes * @return the class array containing provided service specification and * the {@link IPOJOServiceFactory} class. */ private Class[] getSpecificationsWithIPOJOServiceFactory(String[] specs, BundleContext bc) { Class[] classes = new Class[specs.length + 1]; int i = 0; for (i = 0; i < specs.length; i++) { try { classes[i] = bc.getBundle().loadClass(specs[i]); } catch (ClassNotFoundException e) { // Should not happen. } } classes[i] = IPOJOServiceFactory.class; return classes; } } }