blob: b50a969277c2e62baf6ba0c45ac35b3bee7293fd [file] [log] [blame]
/*
* 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;
}
}
}