/* * #%L * Gravia :: Runtime :: Embedded * %% * Copyright (C) 2013 - 2014 JBoss by Red Hat * %% * Licensed 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. * #L% */ package org.jboss.gravia.runtime.embedded.internal; import static org.jboss.gravia.runtime.spi.RuntimeLogger.LOGGER; import java.util.ArrayList; import java.util.Collections; import java.util.Dictionary; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.atomic.AtomicLong; import org.jboss.gravia.runtime.Filter; import org.jboss.gravia.runtime.Module; import org.jboss.gravia.runtime.ModuleContext; import org.jboss.gravia.runtime.ServiceEvent; import org.jboss.gravia.runtime.ServiceFactory; import org.jboss.gravia.runtime.ServiceReference; import org.jboss.gravia.runtime.spi.AbstractModule; import org.jboss.gravia.runtime.spi.NoFilter; import org.jboss.gravia.runtime.spi.RuntimeEventsManager; import org.osgi.framework.Bundle; /** * Manages the services in hte runtime * * @author thomas.diesler@jboss.com * @since 27-Sep-2013 * * @ThreadSafe */ final class RuntimeServicesManager { private final RuntimeEventsManager frameworkEvents; private final Map<String, List<ServiceState<?>>> serviceMap = new ConcurrentHashMap<String, List<ServiceState<?>>>(); private final AtomicLong identityGenerator = new AtomicLong(); RuntimeServicesManager(RuntimeEventsManager frameworkEvents) { this.frameworkEvents = frameworkEvents; } void fireServiceEvent(Module module, int type, ServiceState<?> serviceState) { frameworkEvents.fireServiceEvent(module, type, serviceState); } private long getNextServiceId() { return identityGenerator.incrementAndGet(); } /** * Registers the specified service object with the specified properties under the specified class names into the Framework. * A <code>ServiceRegistration</code> object is returned. The <code>ServiceRegistration</code> object is for the private use * of the module registering the service and should not be shared with other modules. The registering module is defined to * be the context module. * * @param classNames The class names under which the service can be located. * @param serviceValue The service object or a <code>ServiceFactory</code> object. * @param properties The properties for this service. * @return A <code>ServiceRegistration</code> object for use by the module registering the service */ @SuppressWarnings({ "rawtypes" }) ServiceState registerService(ModuleContext context, String[] classNames, final Object serviceValue, Dictionary properties) { assert classNames != null && classNames.length > 0 : "Null service classes"; ServiceState.ValueProvider<Object> valueProvider = new ServiceState.ValueProvider<Object>() { @Override public boolean isFactoryValue() { return serviceValue instanceof ServiceFactory; } @Override public Object getValue() { return serviceValue; } }; long serviceId = getNextServiceId(); ServiceState<?> serviceState = new ServiceState<Object>(this, context.getModule(), serviceId, classNames, valueProvider, properties); LOGGER.debug("Register service: {}", serviceState); for (String className : classNames) { List<ServiceState<?>> serviceStates; synchronized (serviceMap) { serviceStates = serviceMap.get(className); if (serviceStates == null) { serviceStates = new CopyOnWriteArrayList<ServiceState<?>>(); serviceMap.put(className, serviceStates); } } serviceStates.add(serviceState); } //module.addRegisteredService(serviceState); // This event is synchronously delivered after the service has been registered with the Framework. frameworkEvents.fireServiceEvent(context.getModule(), ServiceEvent.REGISTERED, serviceState); return serviceState; } /** * Returns a <code>ServiceReference</code> object for a service that implements and was registered under the specified * class. * * @param clazz The class name with which the service was registered. * @return A <code>ServiceReference</code> object, or <code>null</code> */ ServiceState<?> getServiceReference(ModuleContext context, String clazz) { assert clazz != null : "Null clazz"; List<ServiceState<?>> result = getServiceReferencesInternal(context, clazz, NoFilter.INSTANCE, true); if (result.isEmpty()) return null; int lastIndex = result.size() - 1; return result.get(lastIndex); } /** * Returns an array of <code>ServiceReference</code> objects. The returned array of <code>ServiceReference</code> objects * contains services that were registered under the specified class, match the specified filter expression. * * If checkAssignable is true, the packages for the class names under which the services were registered match the context * module's packages as defined in {@link ServiceReference#isAssignableTo(Bundle, String)}. * * * @param clazz The class name with which the service was registered or <code>null</code> for all services. * @param filterStr The filter expression or <code>null</code> for all services. * @return A potentially empty list of <code>ServiceReference</code> objects. */ List<ServiceState<?>> getServiceReferences(ModuleContext context, String clazz, String filterStr, boolean checkAssignable) { Filter filter = NoFilter.INSTANCE; if (filterStr != null) filter = context.createFilter(filterStr); List<ServiceState<?>> result = getServiceReferencesInternal(context, clazz, filter, checkAssignable); return result; } private List<ServiceState<?>> getServiceReferencesInternal(final ModuleContext module, String className, Filter filter, boolean checkAssignable) { assert module != null : "Null module"; assert filter != null : "Null filter"; Set<ServiceState<?>> initialSet = new HashSet<ServiceState<?>>(); if (className != null) { List<ServiceState<?>> list = serviceMap.get(className); if (list != null) { initialSet.addAll(list); } } else { for (List<ServiceState<?>> list : serviceMap.values()) { initialSet.addAll(list); } } if (initialSet.isEmpty()) return Collections.emptyList(); Set<ServiceState<?>> resultset = new HashSet<ServiceState<?>>(); for (ServiceState<?> serviceState : initialSet) { if (isMatchingService(module, serviceState, className, filter, checkAssignable)) { resultset.add(serviceState); } } // Sort the result List<ServiceState<?>> resultList = new ArrayList<ServiceState<?>>(resultset); if (resultList.size() > 1) Collections.sort(resultList, ServiceReferenceComparator.getInstance()); return Collections.unmodifiableList(resultList); } private boolean isMatchingService(ModuleContext context, ServiceState<?> serviceState, String clazzName, Filter filter, boolean checkAssignable) { if (serviceState.isUnregistered() || filter.match(serviceState) == false) return false; if (checkAssignable == false || clazzName == null) return true; return serviceState.isAssignableTo(context.getModule(), clazzName); } /** * Returns the service object referenced by the specified <code>ServiceReference</code> object. * * @return A service object for the service associated with <code>reference</code> or <code>null</code> */ <S> S getService(ModuleContext context, ServiceState<S> serviceState) { // If the service has been unregistered, null is returned. if (serviceState.isUnregistered()) return null; // Add the given service ref to the list of used services serviceState.addUsingModule((AbstractModule) context.getModule()); S value = serviceState.getScopedValue(context.getModule()); // If the factory returned an invalid value // restore the service usage counts if (value == null) { serviceState.removeUsingModule((AbstractModule) context.getModule()); } return value; } /** * Unregister the given service. */ void unregisterService(ServiceState<?> serviceState) { if (serviceState.isUnregistered()) return; LOGGER.debug("Unregister service: {}", serviceState); for (String className : serviceState.getClassNames()) { try { List<ServiceState<?>> serviceStates = serviceMap.get(className); if (serviceStates != null) { serviceStates.remove(serviceState); } } catch (RuntimeException ex) { LOGGER.error("Cannot unregister service: " + className, ex); } } Module serviceOwner = serviceState.getServiceOwner(); // This event is synchronously delivered before the service has completed unregistering. frameworkEvents.fireServiceEvent(serviceOwner, ServiceEvent.UNREGISTERING, serviceState); // Remove from using modules for (AbstractModule module : serviceState.getUsingModulesInternal()) { while (ungetService(module, serviceState)) { } } } /** * Releases the service object referenced by the specified <code>ServiceReference</code> object. If the context module's use * count for the service is zero, this method returns <code>false</code>. Otherwise, the context module's use count for the * service is decremented by one. * * @return <code>false</code> if the context module's use count for the service is zero or if the service has been * unregistered; <code>true</code> otherwise. */ boolean ungetService(AbstractModule module, ServiceState<?> serviceState) { serviceState.ungetScopedValue(module); int useCount = module.removeServiceInUse(serviceState); if (useCount == 0) { serviceState.removeUsingModule(module); } return useCount >= 0; } }