blob: a766d2d2f4bd936ef409e16af763bdaa608140bf [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.connect;
import java.io.File;
import java.io.IOException;
import java.net.JarURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Dictionary;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleEvent;
import org.osgi.framework.BundleException;
import org.osgi.framework.Constants;
import org.osgi.framework.Filter;
import org.osgi.framework.FrameworkEvent;
import org.osgi.framework.FrameworkListener;
import org.osgi.framework.FrameworkUtil;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceEvent;
import org.osgi.framework.ServiceListener;
import org.osgi.framework.ServiceReference;
import org.osgi.framework.ServiceRegistration;
import org.osgi.framework.Version;
import org.osgi.framework.VersionRange;
import org.osgi.framework.startlevel.FrameworkStartLevel;
import org.osgi.framework.wiring.BundleCapability;
import org.osgi.framework.wiring.BundleRevision;
import org.osgi.framework.wiring.BundleWiring;
import org.osgi.service.packageadmin.ExportedPackage;
import org.osgi.service.packageadmin.PackageAdmin;
import org.osgi.service.packageadmin.RequiredBundle;
import org.osgi.service.startlevel.StartLevel;
import org.apache.felix.connect.felix.framework.ServiceRegistry;
import org.apache.felix.connect.felix.framework.util.EventDispatcher;
import org.apache.felix.connect.launch.BundleDescriptor;
import org.apache.felix.connect.launch.ClasspathScanner;
import org.apache.felix.connect.launch.PojoServiceRegistry;
import org.apache.felix.connect.launch.PojoServiceRegistryFactory;
public class PojoSR implements PojoServiceRegistry
{
private final BundleContext m_context;
private final ServiceRegistry m_registry = new ServiceRegistry(
new ServiceRegistry.ServiceRegistryCallbacks()
{
public void serviceChanged(ServiceEvent event, Dictionary<?, ?> oldProps)
{
m_dispatcher.fireServiceEvent(event, (Dictionary<String, ?>) oldProps, m_bundles.get(0l));
}
});
private final EventDispatcher m_dispatcher = new EventDispatcher(m_registry);
private final Map<Long, Bundle> m_bundles = new HashMap<Long, Bundle>();
private final Map<String, Object> bundleConfig;
private final boolean m_hasVFS;
public static BundleDescriptor createSystemBundle() {
final Map<String, String> headers = new HashMap<String, String>();
headers.put(Constants.BUNDLE_SYMBOLICNAME, "org.apache.felix.connect");
headers.put(Constants.BUNDLE_VERSION, "0.0.0");
headers.put(Constants.BUNDLE_NAME, "System Bundle");
headers.put(Constants.BUNDLE_MANIFESTVERSION, "2");
headers.put(Constants.BUNDLE_VENDOR, "Apache Software Foundation");
Revision revision = new Revision()
{
final long lastModified = System.currentTimeMillis();
@Override
public long getLastModified()
{
return lastModified;
}
@Override
public Enumeration<String> getEntries()
{
return Collections.enumeration(Collections.EMPTY_LIST);
}
@Override
public URL getEntry(String entryName)
{
return getClass().getClassLoader().getResource(entryName);
}
};
Map<Class, Object> services = new HashMap<Class, Object>();
services.put(FrameworkStartLevel.class, new FrameworkStartLevelImpl());
return new BundleDescriptor(
PojoSR.class.getClassLoader(),
"System Bundle",
headers,
revision,
services
);
}
public PojoSR(Map<String, ?> config) throws Exception
{
this(config, null);
}
public PojoSR(Map<String, ?> config, BundleDescriptor systemBundle) throws Exception
{
if (systemBundle == null) {
systemBundle = createSystemBundle();
}
bundleConfig = new HashMap<String, Object>(config);
final Bundle b = new PojoSRBundle(
m_registry,
m_dispatcher,
m_bundles,
systemBundle.getUrl(),
0,
"org.apache.felix.connect",
new Version(0, 0, 1),
systemBundle.getRevision(),
systemBundle.getClassLoader(),
systemBundle.getHeaders(),
systemBundle.getServices(),
bundleConfig)
{
@Override
public synchronized void start() throws BundleException
{
if (m_state != Bundle.RESOLVED)
{
return;
}
m_dispatcher.startDispatching();
m_state = Bundle.STARTING;
m_dispatcher.fireBundleEvent(new BundleEvent(BundleEvent.STARTING, this));
m_context = new PojoSRBundleContext(this, m_registry, m_dispatcher, m_bundles, bundleConfig);
int i = 0;
for (Bundle b : m_bundles.values())
{
i++;
try
{
if (b != this)
{
b.start();
}
}
catch (Throwable t)
{
System.out.println("Unable to start bundle: " + i);
t.printStackTrace();
}
}
m_state = Bundle.ACTIVE;
m_dispatcher.fireBundleEvent(new BundleEvent(BundleEvent.STARTED, this));
m_dispatcher.fireFrameworkEvent(new FrameworkEvent(FrameworkEvent.STARTED, this, null));
super.start();
}
@Override
public synchronized void stop() throws BundleException
{
if ((m_state == Bundle.STOPPING) || m_state == Bundle.RESOLVED)
{
return;
}
else if (m_state != Bundle.ACTIVE)
{
throw new BundleException("Can't stop pojosr because it is not ACTIVE");
}
final Bundle systemBundle = this;
Runnable r = new Runnable()
{
public void run()
{
m_dispatcher.fireBundleEvent(new BundleEvent(BundleEvent.STOPPING, systemBundle));
for (Bundle b : m_bundles.values())
{
try
{
if (b != systemBundle)
{
b.stop();
}
}
catch (Throwable t)
{
t.printStackTrace();
}
}
m_dispatcher.fireBundleEvent(new BundleEvent(BundleEvent.STOPPED, systemBundle));
m_state = Bundle.RESOLVED;
m_dispatcher.stopDispatching();
}
};
m_state = Bundle.STOPPING;
if ("true".equalsIgnoreCase(System.getProperty("org.apache.felix.connect.events.sync")))
{
r.run();
}
else
{
new Thread(r).start();
}
}
};
m_bundles.put(0l, b);
b.start();
b.getBundleContext().registerService(StartLevel.class.getName(), new StartLevelImpl(), null);
b.getBundleContext().registerService(PackageAdmin.class.getName(), new PackageAdminImpl(), null);
m_context = b.getBundleContext();
boolean hasVFS;
try
{
hasVFS = org.jboss.vfs.VFS.class != null;
} catch (Throwable t) {
hasVFS = false;
}
m_hasVFS = hasVFS;
Collection<BundleDescriptor> scan = (Collection<BundleDescriptor>) config.get(PojoServiceRegistryFactory.BUNDLE_DESCRIPTORS);
if (scan != null)
{
Object autoStart = config.get(PojoServiceRegistryFactory.BUNDLES_AUTOSTART);
if(autoStart == null || Boolean.TRUE.equals(autoStart))
{
startBundles(scan);
}
else
{
for (BundleDescriptor desc : scan) {
registerBundle(desc);
}
}
}
}
public void startBundles(Collection<BundleDescriptor> scan) throws Exception {
List<Bundle> bundles = new LinkedList<Bundle>();
for (BundleDescriptor desc : scan)
{
Bundle bundle = registerBundle(desc);
bundles.add(bundle);
}
for (Bundle bundle : bundles)
{
try
{
bundle.start();
}
catch (Throwable e)
{
System.out.println("Unable to start bundle: " + bundle);
e.printStackTrace();
}
}
}
public Bundle registerBundle(BundleDescriptor desc) throws Exception
{
Revision revision = desc.getRevision();
if (revision == null)
{
revision = buildRevision(desc);
}
Map<String, String> bundleHeaders = desc.getHeaders();
Version osgiVersion;
try
{
osgiVersion = Version.parseVersion(bundleHeaders.get(Constants.BUNDLE_VERSION));
}
catch (Exception ex)
{
ex.printStackTrace();
osgiVersion = Version.emptyVersion;
}
String sym = bundleHeaders.get(Constants.BUNDLE_SYMBOLICNAME);
if (sym != null)
{
int idx = sym.indexOf(';');
if (idx > 0)
{
sym = sym.substring(0, idx);
}
sym = sym.trim();
}
Bundle bundle = new PojoSRBundle(
m_registry,
m_dispatcher,
m_bundles,
desc.getUrl(),
m_bundles.size(),
sym,
osgiVersion,
revision,
desc.getClassLoader(),
bundleHeaders,
desc.getServices(),
bundleConfig);
m_bundles.put(bundle.getBundleId(), bundle);
return bundle;
}
private Revision buildRevision(BundleDescriptor desc) throws IOException
{
Revision r;
URL url = new URL(desc.getUrl());
URL u = new URL(desc.getUrl() + "META-INF/MANIFEST.MF");
String extF = u.toExternalForm();
if (extF.startsWith("file:"))
{
File root = new File(URLDecoder.decode(url.getFile(), "UTF-8"));
r = new DirRevision(root);
}
else
{
URLConnection uc = u.openConnection();
if (uc instanceof JarURLConnection)
{
String target = ((JarURLConnection) uc).getJarFileURL().toExternalForm();
String prefix = null;
if (!("jar:" + target + "!/").equals(desc.getUrl()) && desc.getUrl().startsWith("jar:" + target + "!/"))
{
System.out.println(desc.getUrl() + " " + target);
prefix = desc.getUrl().substring(("jar:" + target + "!/").length());
}
r = new JarRevision(
((JarURLConnection) uc).getJarFile(),
((JarURLConnection) uc).getJarFileURL(),
prefix,
uc.getLastModified());
}
else if (m_hasVFS && extF.startsWith("vfs"))
{
r = new VFSRevision(url, url.openConnection().getLastModified());
}
else
{
r = new URLRevision(url, url.openConnection().getLastModified());
}
}
return r;
}
public static void main(String[] args) throws Exception
{
Filter filter = null;
Class<?> main = null;
for (int i = 0; (args != null) && (i < args.length) && (i < 2); i++)
{
try
{
filter = FrameworkUtil.createFilter(args[i]);
}
catch (InvalidSyntaxException ie)
{
try
{
main = PojoSR.class.getClassLoader().loadClass(args[i]);
}
catch (Exception ex)
{
throw new IllegalArgumentException("Argument is neither a filter nor a class: " + args[i]);
}
}
}
Map<String, Object> config = new HashMap<String, Object>();
config.put(
PojoServiceRegistryFactory.BUNDLE_DESCRIPTORS,
(filter != null) ? new ClasspathScanner()
.scanForBundles(filter.toString()) : new ClasspathScanner()
.scanForBundles());
new PojoServiceRegistryFactoryImpl().newPojoServiceRegistry(config);
if (main != null)
{
int count = 0;
if (filter != null)
{
count++;
}
count++;
String[] newArgs = args;
if (count > 0)
{
newArgs = new String[args.length - count];
System.arraycopy(args, count, newArgs, 0, newArgs.length);
}
main.getMethod("main", String[].class).invoke(null, newArgs);
}
}
public BundleContext getBundleContext()
{
return m_context;
}
@Override
public void addServiceListener(ServiceListener listener, String filter) throws InvalidSyntaxException
{
m_context.addServiceListener(listener, filter);
}
@Override
public void addServiceListener(ServiceListener listener)
{
m_context.addServiceListener(listener);
}
@Override
public void removeServiceListener(ServiceListener listener)
{
m_context.removeServiceListener(listener);
}
@Override
public ServiceRegistration<?> registerService(String[] clazzes, Object service, Dictionary<String, ?> properties)
{
return m_context.registerService(clazzes, service, properties);
}
@Override
public ServiceRegistration<?> registerService(String clazz, Object service, Dictionary<String, ?> properties)
{
return m_context.registerService(clazz, service, properties);
}
@Override
public ServiceReference<?>[] getServiceReferences(String clazz, String filter) throws InvalidSyntaxException
{
return m_context.getServiceReferences(clazz, filter);
}
@Override
public ServiceReference<?> getServiceReference(String clazz)
{
return m_context.getServiceReference(clazz);
}
@Override
public <S> S getService(ServiceReference<S> reference)
{
return m_context.getService(reference);
}
@Override
public boolean ungetService(ServiceReference<?> reference)
{
return m_context.ungetService(reference);
}
private static class FrameworkStartLevelImpl implements FrameworkStartLevel, BundleAware
{
private Bundle bundle;
@Override
public void setBundle(Bundle bundle)
{
this.bundle = bundle;
}
@Override
public int getStartLevel()
{
return 0;
}
@Override
public void setStartLevel(int startlevel, FrameworkListener... listeners)
{
}
@Override
public int getInitialBundleStartLevel()
{
return 0;
}
@Override
public void setInitialBundleStartLevel(int startlevel)
{
}
@Override
public Bundle getBundle()
{
return bundle;
}
}
private static class StartLevelImpl implements StartLevel
{
@Override
public void setStartLevel(int startlevel)
{
// TODO Auto-generated method stub
}
@Override
public void setInitialBundleStartLevel(int startlevel)
{
// TODO Auto-generated method stub
}
@Override
public void setBundleStartLevel(Bundle bundle, int startlevel)
{
// TODO Auto-generated method stub
}
@Override
public boolean isBundlePersistentlyStarted(Bundle bundle)
{
// TODO Auto-generated method stub
return true;
}
@Override
public boolean isBundleActivationPolicyUsed(Bundle bundle)
{
// TODO Auto-generated method stub
return false;
}
@Override
public int getStartLevel()
{
// TODO Auto-generated method stub
return 1;
}
@Override
public int getInitialBundleStartLevel()
{
// TODO Auto-generated method stub
return 1;
}
@Override
public int getBundleStartLevel(Bundle bundle)
{
// TODO Auto-generated method stub
return 1;
}
}
private class PackageAdminImpl implements PackageAdmin
{
@Override
public boolean resolveBundles(Bundle[] bundles)
{
return true;
}
@Override
public void refreshPackages(Bundle[] bundles)
{
FrameworkEvent event = new FrameworkEvent(FrameworkEvent.PACKAGES_REFRESHED, m_bundles.get(0l), null);
m_dispatcher.fireFrameworkEvent(event);
}
@Override
public RequiredBundle[] getRequiredBundles(String symbolicName)
{
List list = new ArrayList();
for (Bundle bundle : PojoSR.this.m_bundles.values())
{
if ((symbolicName == null) || (symbolicName.equals(bundle.getSymbolicName())))
{
list.add(new RequiredBundleImpl(bundle));
}
}
return (list.isEmpty())
? null
: (RequiredBundle[]) list.toArray(new RequiredBundle[list.size()]);
}
@Override
public Bundle[] getHosts(Bundle bundle)
{
// TODO Auto-generated method stub
return null;
}
@Override
public Bundle[] getFragments(Bundle bundle)
{
// TODO Auto-generated method stub
return null;
}
@Override
public ExportedPackage[] getExportedPackages(String name)
{
// TODO Auto-generated method stub
return null;
}
@Override
public ExportedPackage[] getExportedPackages(Bundle bundle)
{
List<ExportedPackage> list = new ArrayList<ExportedPackage>();
// If a bundle is specified, then return its
// exported packages.
if (bundle != null)
{
getExportedPackages(bundle, list);
}
// Otherwise return all exported packages.
else
{
for (Bundle b : m_bundles.values())
{
getExportedPackages(b, list);
}
}
return list.isEmpty() ? null : list.toArray(new ExportedPackage[list.size()]);
}
private void getExportedPackages(Bundle bundle, List<ExportedPackage> list)
{
// Since a bundle may have many revisions associated with it,
// one for each revision in the cache, search each revision
// to get all exports.
for (BundleCapability cap : bundle.adapt(BundleWiring.class).getCapabilities(null))
{
if (cap.getNamespace().equals(BundleRevision.PACKAGE_NAMESPACE))
{
list.add(new ExportedPackageImpl(cap));
}
}
}
@Override
public ExportedPackage getExportedPackage(String name)
{
// TODO Auto-generated method stub
return null;
}
@Override
public Bundle[] getBundles(String symbolicName, String versionRange)
{
Set<Bundle> result = new HashSet<Bundle>();
VersionRange range = versionRange != null ? new VersionRange(versionRange) : null;
for (Bundle bundle : m_bundles.values())
{
if (symbolicName != null && !bundle.getSymbolicName().equals(symbolicName))
{
continue;
}
if (range != null && !range.includes(bundle.getVersion()))
{
continue;
}
result.add(bundle);
}
return result.isEmpty() ? null : result.toArray(new Bundle[result.size()]);
}
@Override
public int getBundleType(Bundle bundle)
{
return bundle.adapt(BundleRevision.class).getTypes();
}
@Override
public Bundle getBundle(Class clazz)
{
return m_context.getBundle();
}
}
}