blob: dc43801f8bd83b0d8a5ef0605981493701930b08 [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.tamaya.servicecontext.internal;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleEvent;
import org.osgi.framework.BundleListener;
import org.osgi.framework.Constants;
import org.osgi.framework.ServiceFactory;
import org.osgi.framework.ServiceRegistration;
import org.osgi.util.tracker.ServiceTracker;
/**
* An bundle listener that registers services defined in META-INF/services, when a bundle is starting.
*
* @author anatole@apache.org
*/
public class OSGIServiceLoader implements BundleListener {
// Provide logging
private static final Logger log = Logger.getLogger(OSGIServiceLoader.class.getName());
private Map<Class, ServiceTracker<Object,Object>> services = new ConcurrentHashMap<>();
@Override
public void bundleChanged(BundleEvent bundleEvent) {
// Parse and create metadta on STARTING
if (bundleEvent.getType() == BundleEvent.STARTED) {
Bundle bundle = bundleEvent.getBundle();
if (bundle.getEntry("META-INF/services/") == null) {
return;
}
Enumeration<String> entryPaths = bundle.getEntryPaths("META-INF/services/");
while (entryPaths.hasMoreElements()) {
String entryPath = entryPaths.nextElement();
if(!entryPath.endsWith("/")) {
processEntryPath(bundle, entryPath);
}
}
}
}
private void processEntryPath(Bundle bundle, String entryPath) {
try {
String serviceName = entryPath.substring("META-INF/services/".length());
Class<?> serviceClass = bundle.loadClass(serviceName);
URL child = bundle.getEntry(entryPath);
InputStream inStream = child.openStream();
BufferedReader br = new BufferedReader(new InputStreamReader(inStream, "UTF-8"));
String implClassName = br.readLine();
while (implClassName != null){
int hashIndex = implClassName.indexOf("#");
if (hashIndex > 0) {
implClassName = implClassName.substring(0, hashIndex-1);
}
else if (hashIndex == 0) {
implClassName = "";
}
implClassName = implClassName.trim();
if (implClassName.length() > 0) {
try {
// Load the service class
Class<?> implClass = bundle.loadClass(implClassName);
if (!serviceClass.isAssignableFrom(implClass)) {
log.warning("Configured service: " + implClassName + " is not assignble to " +
serviceClass.getName());
continue;
}
// Provide service properties
Hashtable<String, String> props = new Hashtable<>();
props.put(Constants.VERSION_ATTRIBUTE, bundle.getVersion().toString());
String vendor = bundle.getHeaders().get(Constants.BUNDLE_VENDOR);
props.put(Constants.SERVICE_VENDOR, (vendor != null ? vendor : "anonymous"));
// Translate annotated @Priority into a service ranking
props.put(Constants.SERVICE_RANKING,
String.valueOf(PriorityServiceComparator.getPriority(implClass)));
// Register the service factory on behalf of the intercepted bundle
JDKUtilServiceFactory factory = new JDKUtilServiceFactory(implClass);
BundleContext bundleContext = bundle.getBundleContext();
bundleContext.registerService(serviceName, factory, props);
}
catch(Exception e){
log.log(Level.SEVERE,
"Failed to load service class using ServiceLoader logic: " + implClassName, e);
}
}
implClassName = br.readLine();
}
br.close();
}
catch (RuntimeException rte) {
throw rte;
}
catch (Exception e) {
log.log(Level.SEVERE, "Failed to read services from: " + entryPath, e);
}
}
/**
* Service factory simply instantiating the configured service.
*/
static class JDKUtilServiceFactory implements ServiceFactory
{
private final Class<?> serviceClass;
public JDKUtilServiceFactory(Class<?> serviceClass) {
this.serviceClass = serviceClass;
}
@Override
public Object getService(Bundle bundle, ServiceRegistration registration) {
try {
return serviceClass.newInstance();
}
catch (Exception ex) {
ex.printStackTrace();
throw new IllegalStateException("Cannot instanciate service", ex);
}
}
@Override
public void ungetService(Bundle bundle, ServiceRegistration registration, Object service) {
}
}
}