/*******************************************************************************
* Copyright (c) 2010-2014 SAP AG and others.
* 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:
* SAP AG - initial API and implementation
*******************************************************************************/
package org.eclipse.skalli.services.extension;
import java.net.URL;
import java.text.MessageFormat;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.eclipse.skalli.model.ExtensionEntityBase;
import org.eclipse.skalli.services.BundleFilter;
import org.eclipse.skalli.services.FilterMode;
import org.eclipse.skalli.services.Services;
import org.osgi.service.component.ComponentConstants;
import org.osgi.service.component.ComponentContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Convenience class for accessing {@link ExtensionService extension services} currently
* registered in the OSGi runtime. Extension services can be retrieved by their short names,
* or by the {@link ExtensionEntityBase extension classes} they represent.
* <p>
* Note, this class tracks extension service by means of OSGi's declarative service
* mechanisms and caches the currently registered services in internal maps.
* Except {@link #findExtensionResources(String, String, boolean)} all methods
* take their result from the internal caches.
*/
public class ExtensionServices {
private static final Logger LOG = LoggerFactory.getLogger(ExtensionServices.class);
private static Map<String, ExtensionService<?>> byShortName =
new ConcurrentHashMap<String, ExtensionService<?>>();
private static Map<String, ExtensionService<?>> byExtensionClassName =
new ConcurrentHashMap<String, ExtensionService<?>>();
protected void activate(ComponentContext context) {
LOG.info(MessageFormat.format("[ExtensionServices] {0} : activated",
(String) context.getProperties().get(ComponentConstants.COMPONENT_NAME)));
}
protected void deactivate(ComponentContext context) {
LOG.info(MessageFormat.format("[ExtensionServices] {0} : deactivated",
(String) context.getProperties().get(ComponentConstants.COMPONENT_NAME)));
}
protected void bindExtensionService(ExtensionService<?> extensionService) {
String shortName = extensionService.getShortName();
if (byShortName.containsKey(shortName)) {
throw new RuntimeException(MessageFormat.format(
"There is already an extension registered with the short name ''{0}''", shortName));
}
byShortName.put(extensionService.getShortName(), extensionService);
String extensionClassName = extensionService.getExtensionClass().getName();
byExtensionClassName.put(extensionClassName, extensionService);
LOG.info(MessageFormat.format("[ExtensionServices][registered {0}]", extensionClassName));
}
protected void unbindExtensionService(ExtensionService<?> extensionService) {
String extensionClassName = extensionService.getExtensionClass().getName();
byExtensionClassName.remove(extensionClassName);
byShortName.remove(extensionService.getShortName());
LOG.info(MessageFormat.format("[ExtensionServices][unregistered {0}]", extensionClassName));
}
/**
* Returns all currently registered extension services.
*
* @return a collection of extension services, or an empty collection.
*/
public static Collection<ExtensionService<?>> getAll() {
return Collections.unmodifiableCollection(byShortName.values());
}
/**
* Returns all currently registered extension services by their short names.
*
* @return a map of extension services with short names as keys,
* or an empty map.
*/
public static Map<String, ExtensionService<?>> getByShortNames() {
return Collections.unmodifiableMap(byShortName);
}
/**
* Returns the {@link ExtensionService} instance matching a given short name.
*
* @param shortName the {@link {@link ExtensionService#getShortName() short name}.
* @return the extension service instance, or <code>null</code> if there is no instance
* for the given short name available.
*/
public static ExtensionService<?> getByShortName(String shortName) {
ExtensionService<?> extensionService = byShortName.get(shortName);
return extensionService != null? extensionService : null;
}
/**
* Returns all currently registered extension services by the names
* of the {@link ExtensionEntityBase extension classes} with which they
* are associated.
*
* @return a map of extension services with (fully qualified) names
* of extension classes as keys, or an empty map.
*/
public static Map<String, ExtensionService<?>> getByExtensionClassNames() {
return Collections.unmodifiableMap(byExtensionClassName);
}
/**
* Returns the {@link ExtensionService} instance matching a given
* {@link ExtensionEntityBase extension class} name.
*
* @param extensionClass the extension class.
* @return the extension service instance, or <code>null</code> if there is no instance
* for the given extension class available.
*/
@SuppressWarnings("unchecked")
public static <T extends ExtensionEntityBase> ExtensionService<T> getByExtensionClassName(String extensionClassName) {
ExtensionService<?> extensionService = byExtensionClassName.get(extensionClassName);
return extensionService != null? (ExtensionService<T>)extensionService : null;
}
/**
* Returns the {@link ExtensionService} instance matching a given
* {@link ExtensionEntityBase extension class}.
*
* @param extensionClass the extension class.
* @return the extension service instance, or <code>null</code> if there is no instance
* for the given extension class available.
*/
public static <T extends ExtensionEntityBase> ExtensionService<T> getByExtensionClass(Class<T> extensionClass) {
return getByExtensionClassName(extensionClass.getName());
}
/**
* Scans all bundles providing an {@link ExtensionService} for resources
* matching the given <code>path</code> and <code>pattern</code>.
*
* @param path The path name in which to look. The path is always relative
* to the root of this bundle and may begin with "/". A
* path value of "/" indicates the root of this bundle.
* @param pattern The file name pattern for selecting entries in the
* specified path. The pattern is only matched against the last
* element of the entry path. If the entry is a directory then the
* trailing "/" is not used for pattern matching. Substring
* matching is supported, as specified in the Filter specification,
* using the wildcard character ("*"). If null is
* specified, this is equivalent to "*" and matches all
* files.
* @param recursive If {@code true}, recurse into subdirectories. Otherwise
* only return entries from the specified path.
* @return a list of matching resources (which may be empty).
*
* @see Services#findResources(String, String, boolean, FilterMode, BundleFilter...)
* @see FilterMode#SHORT_CIRCUIT
*/
public static List<URL> findExtensionResources(String path, String pattern, boolean recursive) {
return Services.findResources(path, pattern, recursive, FilterMode.ALL, new ExtensionBundleFilter());
}
/**
* Filter that accepts all bundles providing {@link ExtensionService extension services}.
* @see Services#getBundles(BundleFilter...)
*/
public static class ExtensionBundleFilter extends BundleFilter.AcceptService {
public ExtensionBundleFilter() {
super(ExtensionService.class);
}
}
}