# HG changeset patch # User Jesse Glick # Date 1264719696 18000 Issue #179473: ability to run NB modules in an OSGi container. diff --git a/apisupport.harness/nbproject/project.properties b/apisupport.harness/nbproject/project.properties --- a/apisupport.harness/nbproject/project.properties +++ b/apisupport.harness/nbproject/project.properties @@ -84,6 +84,7 @@ org/netbeans/nbbuild/MakeListOfNBM*.class,\ org/netbeans/nbbuild/MakeMasterJNLP*.class,\ org/netbeans/nbbuild/MakeNBM*.class,\ + org/netbeans/nbbuild/MakeOSGi*.class,\ org/netbeans/nbbuild/MakeUpdateDesc*.class,\ org/netbeans/nbbuild/ModuleListParser*.class,\ org/netbeans/nbbuild/ModuleSelector*.class,\ diff --git a/apisupport.harness/release/osgi.xml b/apisupport.harness/release/osgi.xml new file mode 100644 --- /dev/null +++ b/apisupport.harness/release/osgi.xml @@ -0,0 +1,107 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apisupport.harness/release/suite.xml b/apisupport.harness/release/suite.xml --- a/apisupport.harness/release/suite.xml +++ b/apisupport.harness/release/suite.xml @@ -510,7 +510,19 @@ - + + + + + + + + + + + + + diff --git a/apisupport.harness/taskdefs.properties b/apisupport.harness/taskdefs.properties --- a/apisupport.harness/taskdefs.properties +++ b/apisupport.harness/taskdefs.properties @@ -43,6 +43,7 @@ makenbm=org.netbeans.nbbuild.MakeNBM makejnlp=org.netbeans.nbbuild.MakeJNLP makemasterjnlp=org.netbeans.nbbuild.MakeMasterJNLP +makeosgi=org.netbeans.nbbuild.MakeOSGi parseprojectxml=org.netbeans.nbbuild.ParseProjectXml genlist=org.netbeans.nbbuild.MakeListOfNBM arch=org.netbeans.nbbuild.Arch diff --git a/apisupport.project/src/org/netbeans/modules/apisupport/project/queries/GlobalSourceForBinaryImpl.java b/apisupport.project/src/org/netbeans/modules/apisupport/project/queries/GlobalSourceForBinaryImpl.java --- a/apisupport.project/src/org/netbeans/modules/apisupport/project/queries/GlobalSourceForBinaryImpl.java +++ b/apisupport.project/src/org/netbeans/modules/apisupport/project/queries/GlobalSourceForBinaryImpl.java @@ -111,6 +111,7 @@ } } } + // XXX $suite/build/osgi/$cnb-*.jar => $cnb in $suite NbPlatform supposedPlaf = null; for (NbPlatform plaf : NbPlatform.getPlatforms()) { // XXX more robust condition? diff --git a/core.netigso/manifest.mf b/core.netigso/manifest.mf --- a/core.netigso/manifest.mf +++ b/core.netigso/manifest.mf @@ -5,5 +5,5 @@ OpenIDE-Module-Specification-Version: 1.1 OpenIDE-Module-Recommends: org.osgi.framework.launch.FrameworkFactory AutoUpdate-Essential-Module: true +X-Comment: various OSGi headers are set by the MakeOSGi task - diff --git a/core.netigso/nbproject/project.xml b/core.netigso/nbproject/project.xml --- a/core.netigso/nbproject/project.xml +++ b/core.netigso/nbproject/project.xml @@ -73,6 +73,14 @@ + org.openide.filesystems + + + + 7.33 + + + org.openide.modules diff --git a/core.netigso/src/org/netbeans/core/osgi/Activator.java b/core.netigso/src/org/netbeans/core/osgi/Activator.java new file mode 100644 --- /dev/null +++ b/core.netigso/src/org/netbeans/core/osgi/Activator.java @@ -0,0 +1,164 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2010 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2010 Sun Microsystems, Inc. + */ + +package org.netbeans.core.osgi; + +import java.net.URL; +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.openide.modules.ModuleInstall; +import org.openide.util.SharedClassObject; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleActivator; +import org.osgi.framework.BundleContext; +import org.osgi.framework.BundleEvent; +import org.osgi.framework.BundleListener; +import org.osgi.framework.Constants; +import org.osgi.framework.FrameworkEvent; +import org.osgi.framework.FrameworkListener; + +/** + * Initializes critical NetBeans infrastructure inside an OSGi container. + */ +public class Activator implements BundleActivator, BundleListener, FrameworkListener { + + public Activator() {} + + public @Override void start(BundleContext context) throws Exception { + if (System.getProperty("netbeans.home") != null) { + throw new IllegalStateException("Should not be run from inside regular NetBeans module system"); + } + String storage = context.getProperty(Constants.FRAMEWORK_STORAGE); + if (storage != null) { + System.setProperty("netbeans.user", storage); + } + OSGiMainLookup.initialize(context); + context.addFrameworkListener(this); + context.addBundleListener(this); + System.err.println("XXX processing already loaded bundles..."); + for (Bundle b : context.getBundles()) { + switch (b.getState()) { + case Bundle.ACTIVE: + // XXX coalesce these layer events + bundleChanged(new BundleEvent(BundleEvent.RESOLVED, b)); + // XXX should resolve all first, then start all: + bundleChanged(new BundleEvent(BundleEvent.STARTED, b)); + break; + case Bundle.RESOLVED: + case Bundle.STARTING: + case Bundle.STOPPING: + bundleChanged(new BundleEvent(BundleEvent.RESOLVED, b)); + break; + } + } + System.err.println("XXX done processing already loaded bundles."); + } + + public @Override void stop(BundleContext context) throws Exception {} + + public @Override void bundleChanged(BundleEvent event) { + Bundle bundle = event.getBundle(); + switch (event.getType()) { + case BundleEvent.RESOLVED: + System.err.println("XXX resolved " + bundle.getSymbolicName()); + OSGiMainLookup.bundleResolved(bundle); + OSGiRepository.DEFAULT.addLayers(layersFor(bundle)); + // XXX install URLStreamHandlerService (OSGi spec 11.4); delegate to META-INF/namedservices//java.net.URLStreamHandler + break; + case BundleEvent.UNRESOLVED: + System.err.println("XXX unresolved " + bundle.getSymbolicName()); + OSGiMainLookup.bundleUnresolved(bundle); + OSGiRepository.DEFAULT.removeLayers(layersFor(bundle)); + break; + case BundleEvent.STARTED: + System.err.println("XXX started " + bundle.getSymbolicName()); + ModuleInstall mi = installerFor(bundle); + if (mi != null) { + System.err.println("XXX running " + mi.getClass().getName() + ".restored()"); + mi.restored(); + } + // XXX if o.n.core (or o.n.m.settings?) tell OSGiMainLookup to use CoreBridge.getDefault().lookupCacheLoad() + break; + case BundleEvent.STOPPED: + System.err.println("XXX stopped " + bundle.getSymbolicName()); + mi = installerFor(bundle); + if (mi != null) { + System.err.println("XXX running " + mi.getClass().getName() + ".uninstalled()"); + mi.uninstalled(); + } + break; + } + } + + public @Override void frameworkEvent(FrameworkEvent event) { + System.err.println("XXX framework event " + event.getType() + " on " + event.getBundle().getSymbolicName()); + // XXX perhaps defer processing various things until the framework is started + } + + private static URL[] layersFor(Bundle b) { + List layers = new ArrayList(2); + String explicit = (String) b.getHeaders().get("OpenIDE-Module-Layer"); + if (explicit != null) { + layers.add(b.getResource(explicit)); + // XXX could also add localized/branded variants + } + URL generated = b.getResource("META-INF/generated-layer.xml"); + if (generated != null) { + layers.add(generated); + } + return layers.toArray(new URL[layers.size()]); + } + + private static ModuleInstall installerFor(Bundle b) { + String respath = (String) b.getHeaders().get("OpenIDE-Module-Install"); + if (respath != null) { + String fqn = respath.replaceFirst("[.]class$", "").replace('/', '.'); + try { + return SharedClassObject.findObject(((Class) b.loadClass(fqn)).asSubclass(ModuleInstall.class), true); + } catch (Exception x) { // CNFE, CCE, ... + Logger.getLogger(Activator.class.getName()).log(Level.WARNING, "Could not load " + fqn, x); + return null; + } + } + return null; + } + +} diff --git a/core.netigso/src/org/netbeans/core/osgi/OSGiClassLoader.java b/core.netigso/src/org/netbeans/core/osgi/OSGiClassLoader.java new file mode 100644 --- /dev/null +++ b/core.netigso/src/org/netbeans/core/osgi/OSGiClassLoader.java @@ -0,0 +1,110 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2010 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2010 Sun Microsystems, Inc. + */ + +package org.netbeans.core.osgi; + +import java.io.IOException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Enumeration; +import java.util.List; +import org.openide.util.Enumerations; +import org.openide.util.NbCollections; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; + +/** + * Delegates to all loaded bundles. + */ +class OSGiClassLoader extends ClassLoader { + + private final BundleContext context; + + public OSGiClassLoader(BundleContext context) { + this.context = context; + } + + protected @Override Class findClass(String name) throws ClassNotFoundException { + for (Bundle b : context.getBundles()) { + if (b.getState() == Bundle.INSTALLED) { + continue; + } + try { + return b.loadClass(name); + } catch (ClassNotFoundException x) { + // normal, try next one + } + } + return super.findClass(name); + } + + protected @Override URL findResource(String name) { + for (Bundle b : context.getBundles()) { + if (b.getState() == Bundle.INSTALLED) { + continue; + } + URL resource = b.getResource(name); + if (resource != null) { + return resource; + } + } + return super.findResource(name); + } + + protected @Override Enumeration findResources(String name) throws IOException { + List> resourcess = new ArrayList>(); + for (Bundle b : context.getBundles()) { + if (b.getState() == Bundle.INSTALLED) { + continue; + } + Enumeration resourcesRaw = b.getResources(name); + if (resourcesRaw == null) { + // Oddly, this is permitted. + continue; + } + Enumeration resources = NbCollections.checkedEnumerationByFilter(resourcesRaw, URL.class, true); + if (resources != null) { + resourcess.add(resources); + } + } + return Enumerations.concat(Collections.enumeration(resourcess)); + } + +} diff --git a/core.netigso/src/org/netbeans/core/osgi/OSGiMainLookup.java b/core.netigso/src/org/netbeans/core/osgi/OSGiMainLookup.java new file mode 100644 --- /dev/null +++ b/core.netigso/src/org/netbeans/core/osgi/OSGiMainLookup.java @@ -0,0 +1,111 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2010 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2010 Sun Microsystems, Inc. + */ + +package org.netbeans.core.osgi; + +import java.util.ArrayList; +import java.util.List; +import org.openide.util.Lookup; +import org.openide.util.lookup.Lookups; +import org.openide.util.lookup.ProxyLookup; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; + +/** + * Default lookup when running inside an OSGi container. + */ +public class OSGiMainLookup extends ProxyLookup { + + private static BundleContext context; + + private static OSGiMainLookup get() { + return (OSGiMainLookup) Lookup.getDefault(); + } + + public static void initialize(BundleContext _context) throws Exception { + System.setProperty(Lookup.class.getName(), OSGiMainLookup.class.getName()); + context = _context; + OSGiMainLookup lkp; + ClassLoader oldCCL = Thread.currentThread().getContextClassLoader(); + Thread.currentThread().setContextClassLoader(OSGiMainLookup.class.getClassLoader()); + try { + lkp = get(); + } finally { + Thread.currentThread().setContextClassLoader(oldCCL); + } + lkp.postInit(); + } + + static void bundleResolved(Bundle bundle) { + // XXX extend existing classLoader + get().setClassLoader(); + } + + static void bundleUnresolved(Bundle bundle) { + get().setClassLoader(); + } + + private ClassLoader classLoader; + private final List nonClassLoaderDelegates = new ArrayList(); + + public OSGiMainLookup() {} + + private void postInit() { + nonClassLoaderDelegates.add(Lookups.forPath("Services/")); + nonClassLoaderDelegates.add(Lookups.fixed(OSGiRepository.DEFAULT)); + // XXX add ModuleInfo objects corresponding to bundles + // XXX InstalledFileLocator impl for OSGI-INF/files/* + setClassLoader(); + } + + private void setClassLoader() { + classLoader = new OSGiClassLoader(context); + // XXX should it be set as thread CCL? would help some NB APIs, but might break OSGi conventions + setDelegates(); + } + + private void setDelegates() { + Lookup[] delegates = new Lookup[nonClassLoaderDelegates.size() + 2]; + nonClassLoaderDelegates.toArray(delegates); + delegates[delegates.length - 2] = Lookups.metaInfServices(classLoader); + delegates[delegates.length - 1] = Lookups.singleton(classLoader); + setLookups(delegates); + } + +} diff --git a/core.netigso/src/org/netbeans/core/osgi/OSGiRepository.java b/core.netigso/src/org/netbeans/core/osgi/OSGiRepository.java new file mode 100644 --- /dev/null +++ b/core.netigso/src/org/netbeans/core/osgi/OSGiRepository.java @@ -0,0 +1,118 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2010 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2010 Sun Microsystems, Inc. + */ + +package org.netbeans.core.osgi; + +import java.net.URL; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.openide.filesystems.FileSystem; +import org.openide.filesystems.MultiFileSystem; +import org.openide.filesystems.Repository; +import org.openide.filesystems.XMLFileSystem; +import org.xml.sax.SAXException; + +/** + * Default repository to which layers can be added or removed. + */ +class OSGiRepository extends Repository { + + public static final OSGiRepository DEFAULT = new OSGiRepository(); + + private final LayerFS fs; + + private OSGiRepository() { + this(new LayerFS()); + } + + private OSGiRepository(LayerFS fs) { + super(fs); + this.fs = fs; + } + + public void addLayers(URL... resources) { + if (resources.length > 0) { + System.err.println("XXX adding layers: " + Arrays.toString(resources)); + fs.addLayers(resources); + } + } + + public void removeLayers(URL... resources) { + if (resources.length > 0) { + System.err.println("XXX removing layers: " + Arrays.toString(resources)); + fs.removeLayers(resources); + } + } + + private static final class LayerFS extends MultiFileSystem { + + private final Map fss = new HashMap(); + + LayerFS() { + reset(); + } + + private synchronized void addLayers(URL... resources) { + for (URL resource : resources) { + try { + fss.put(resource, new XMLFileSystem(resource)); + } catch (SAXException x) { + Logger.getLogger(OSGiRepository.class.getName()).log(Level.WARNING, "Could not parse layer: " + resource, x); + } + } + reset(); + } + + private synchronized void removeLayers(URL... resources) { + fss.keySet().removeAll(Arrays.asList(resources)); + reset(); + } + + private void reset() { + setDelegates(fss.values().toArray(new FileSystem[fss.size()])); + // XXX add ${netbeans.user}/config as a writable layer + // XXX also include FileSystem's from lookup + } + + } + +} diff --git a/core.netigso/src/org/netbeans/core/osgi/package-info.java b/core.netigso/src/org/netbeans/core/osgi/package-info.java new file mode 100644 --- /dev/null +++ b/core.netigso/src/org/netbeans/core/osgi/package-info.java @@ -0,0 +1,44 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2010 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2010 Sun Microsystems, Inc. + */ + +/** + * Runtime services for use by NetBeans modules translated to bundles + * running inside an OSGi container. + */ +package org.netbeans.core.osgi; diff --git a/libs.felix/external/binaries-list b/libs.felix/external/binaries-list --- a/libs.felix/external/binaries-list +++ b/libs.felix/external/binaries-list @@ -1,1 +1,2 @@ 8F69CD29825A30B6AA774BD79576C909AEC07668 felix-2.0.3.jar +BD5615C6A15497B60A0AAA9A04D4F05E2BC42D07 felix-main-2.0.2.jar diff --git a/libs.felix/external/felix-2.0.3-license.txt b/libs.felix/external/felix-2.0.3-license.txt --- a/libs.felix/external/felix-2.0.3-license.txt +++ b/libs.felix/external/felix-2.0.3-license.txt @@ -3,6 +3,7 @@ Description: Apache Felix OSGi container (minus org.osgi.** packages). License: Apache-2.0 OSR: 13360 +Files: felix-2.0.3.jar felix-main-2.0.2.jar Origin: http://archive.apache.org/dist/felix/ Apache License diff --git a/libs.felix/nbproject/project.properties b/libs.felix/nbproject/project.properties --- a/libs.felix/nbproject/project.properties +++ b/libs.felix/nbproject/project.properties @@ -38,6 +38,7 @@ # made subject to such option by the copyright holder. release.external/felix-2.0.3.jar=modules/ext/felix-2.0.3.jar +release.external/felix-main-2.0.2.jar=modules/ext/felix-main-2.0.2.jar javac.source=1.6 javac.compilerargs=-Xlint -Xlint:-serial diff --git a/nbbuild/antsrc/org/netbeans/nbbuild/MakeOSGi.java b/nbbuild/antsrc/org/netbeans/nbbuild/MakeOSGi.java new file mode 100644 --- /dev/null +++ b/nbbuild/antsrc/org/netbeans/nbbuild/MakeOSGi.java @@ -0,0 +1,378 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2010 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2010 Sun Microsystems, Inc. + */ + +package org.netbeans.nbbuild; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.URI; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Set; +import java.util.jar.Attributes; +import java.util.jar.JarFile; +import java.util.jar.JarOutputStream; +import java.util.jar.Manifest; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.zip.CRC32; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.Task; +import org.apache.tools.ant.types.ResourceCollection; +import org.apache.tools.ant.types.resources.FileResource; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; +import org.xml.sax.InputSource; + +/** + * Converts a set of NetBeans modules into OSGi bundles. + */ +public class MakeOSGi extends Task { + + private File destdir; + private List modules = new ArrayList(); + + /** + * Mandatory destination directory. Bundles will be created here. + */ + public void setDestdir(File destdir) { + this.destdir = destdir; + } + + /** + * Adds a set of module JARs. + * It is permitted for them to be JARs anywhere on disk, + * but it is best if they are in a cluster structure + * with ../update_tracking/*.xml present + * so that associated files can be included in the bundle. + */ + public void add(ResourceCollection modules) { + this.modules.add(modules); + } + + public @Override void execute() throws BuildException { + if (destdir == null) { + throw new BuildException("missing destdir"); + } + for (ResourceCollection rc : modules) { + Iterator it = rc.iterator(); + while (it.hasNext()) { + File jar = ((FileResource) it.next()).getFile(); + try { + process(jar); + } catch (Exception x) { + throw new BuildException("Could not process " + jar + ": " + x, x, getLocation()); + } + } + } + } + + private void process(File module) throws Exception { + JarFile jar = new JarFile(module); + try { + Manifest netbeans = jar.getManifest(); + Manifest osgi = new Manifest(); + if (netbeans.getMainAttributes().getValue("Bundle-SymbolicName") != null) { + // XXX copy it as is! + } + osgi.getMainAttributes().putValue("Manifest-Version", "1.0"); // workaround for JDK bug + osgi.getMainAttributes().putValue("Bundle-ManifestVersion", "2"); + String codename = netbeans.getMainAttributes().getValue("OpenIDE-Module"); + String cnb = codename.replaceFirst("/\\d+$", ""); + if (cnb.equals("org.netbeans.core.netigso")) { + // special handling... + netbeans.getMainAttributes().remove(new Attributes.Name("OpenIDE-Module-Install")); + netbeans.getMainAttributes().remove(new Attributes.Name("Class-Path")); + osgi.getMainAttributes().putValue("Bundle-Activator", "org.netbeans.core.osgi.Activator"); + osgi.getMainAttributes().putValue("Import-Package", "org.osgi.framework"); + } + osgi.getMainAttributes().putValue("Bundle-SymbolicName", cnb); + String spec = netbeans.getMainAttributes().getValue("OpenIDE-Module-Specification-Version"); + String bundleVersion = null; + if (spec != null) { + bundleVersion = threeDotsWithMajor(spec, codename); + String buildVersion = netbeans.getMainAttributes().getValue("OpenIDE-Module-Build-Version"); + if (buildVersion == null) { + buildVersion = netbeans.getMainAttributes().getValue("OpenIDE-Module-Implementation-Version"); + } + if (buildVersion != null) { + bundleVersion += "." + buildVersion.replaceAll("[^a-zA-Z0-9_-]", "_"); + } + osgi.getMainAttributes().putValue("Bundle-Version", bundleVersion); + } + File bundleFile = new File(destdir, cnb + (bundleVersion != null ? "-" + bundleVersion : "") + ".jar"); + if (bundleFile.lastModified() > module.lastModified()) { + log("Skipping " + module + " since " + bundleFile + " is newer", Project.MSG_VERBOSE); + return; + } + log("Processing " + module + " into " + bundleFile); + String pp = netbeans.getMainAttributes().getValue("OpenIDE-Module-Public-Packages"); + if (pp != null && !pp.equals("-")) { + // XXX handle .** (subpackages) + // XXX if have an integer OpenIDE-Module-Specification-Version, export all packages + osgi.getMainAttributes().putValue("Export-Package", pp.replaceAll("\\.\\*", "")); + // OpenIDE-Module-Friends is ignored since OSGi has no apparent equivalent + } + for (String attrToCopy : new String[] {"OpenIDE-Module-Layer", "OpenIDE-Module-Install"}) { + String val = netbeans.getMainAttributes().getValue(attrToCopy); + if (val != null) { + osgi.getMainAttributes().putValue(attrToCopy, val); + } + } + StringBuilder requireBundles = new StringBuilder(); + /* XXX does not work, perhaps because of cyclic dependencies: + // do not need to import any API, just need it to be started: + requireBundles.append("org.netbeans.core.netigso"); + */ + String dependencies = netbeans.getMainAttributes().getValue("OpenIDE-Module-Module-Dependencies"); + if (dependencies != null) { + for (String dependency : dependencies.split(" *, *")) { + if (requireBundles.length() > 0) { + requireBundles.append(", "); + } + translateDependency(requireBundles, dependency); + } + } + if (requireBundles.length() > 0) { + osgi.getMainAttributes().putValue("Require-Bundle", requireBundles.toString()); + } + // XXX OpenIDE-Module-Java-Dependencies => Bundle-RequiredExecutionEnvironment: JavaSE-1.6 + // XXX OpenIDE-Module-Package-Dependencies => Import-Package + // OpenIDE-Module-{Provides,Requires,Needs} are ignored since OSGi has no apparent equivalent + // (achievable by exposing generic provide/require mechanisms of Felix and/or Equinox, + // but this would not be part of the OSGi R4.1 spec, but could be raised as an issue for R5 since it was discussed for R4) + // autoload, eager status are ignored since OSGi has no apparent equivalent + Properties localizedStrings = new Properties(); + String locbundle = netbeans.getMainAttributes().getValue("OpenIDE-Module-Localizing-Bundle"); + if (locbundle != null) { + InputStream is = jar.getInputStream(jar.getEntry(locbundle)); + try { + localizedStrings.load(is); + } finally { + is.close(); + } + osgi.getMainAttributes().putValue("Bundle-Localization", locbundle.replaceFirst("[.]properties$", "")); + } + handleDisplayAttribute(localizedStrings, netbeans.getMainAttributes(), osgi.getMainAttributes(), + "OpenIDE-Module-Name", "Bundle-Name"); + handleDisplayAttribute(localizedStrings, netbeans.getMainAttributes(), osgi.getMainAttributes(), + "OpenIDE-Module-Display-Category", "Bundle-Category"); + handleDisplayAttribute(localizedStrings, netbeans.getMainAttributes(), osgi.getMainAttributes(), + "OpenIDE-Module-Short-Description", "Bundle-Description"); + Map bundledFiles = findBundledFiles(module, cnb); + // XXX any use for OpenIDE-Module-Long-Description? + String classPath = netbeans.getMainAttributes().getValue("Class-Path"); + if (classPath != null) { + StringBuilder bundleCP = new StringBuilder(); + for (String entry : classPath.split("[, ]+")) { + String clusterPath = new URI(module.getParentFile().getName() + "/" + entry).normalize().toString(); + if (bundledFiles.containsKey(clusterPath)) { + bundleCP.append("/OSGI-INF/files/").append(clusterPath).append(","); + } else { + log("Class-Path entry " + entry + " from " + module + " does not correspond to any apparent cluster file", Project.MSG_WARN); + } + } + osgi.getMainAttributes().putValue("Bundle-Classpath", bundleCP + "."); + } + // XXX modules/lib/*.dll/so => Bundle-NativeCode (but syntax is rather complex) + OutputStream bundle = new FileOutputStream(bundleFile); + try { + ZipOutputStream zos = new JarOutputStream(bundle, osgi); + Set parents = new HashSet(); + Enumeration entries = jar.entries(); + while (entries.hasMoreElements()) { + ZipEntry entry = entries.nextElement(); + String path = entry.getName(); + if (path.endsWith("/") || path.equals("META-INF/MANIFEST.MF")) { + continue; + } + InputStream is = jar.getInputStream(entry); + try { + writeEntry(zos, path, is, parents); + } finally { + is.close(); + } + } + for (Map.Entry bundledFile : bundledFiles.entrySet()) { + InputStream is = new FileInputStream(bundledFile.getValue()); + try { + // XXX need matching IFL impl in netigso + writeEntry(zos, "OSGI-INF/files/" + bundledFile.getKey(), is, parents); + } finally { + is.close(); + } + } + zos.finish(); + zos.close(); + } finally { + bundle.close(); + } + } finally { + jar.close(); + } + } + + private static void writeEntry(ZipOutputStream zos, String path, InputStream data, Set parents) throws IOException { + int size = Math.max(data.available(), 100); + ByteArrayOutputStream baos = new ByteArrayOutputStream(size); + byte[] buf = new byte[size]; + int read; + while ((read = data.read(buf)) != -1) { + baos.write(buf, 0, read); + } + writeEntry(zos, path, baos.toByteArray(), parents); + } + private static void writeEntry(ZipOutputStream zos, String path, byte[] data, Set parents) throws IOException { + assert path.length() > 0 && !path.endsWith("/") && !path.startsWith("/") && path.indexOf("//") == -1 : path; + for (int i = 0; i < path.length(); i++) { + if (path.charAt(i) == '/') { + String parent = path.substring(0, i + 1); + if (parents.add(parent)) { + ZipEntry ze = new ZipEntry(parent); + ze.setMethod(ZipEntry.STORED); + ze.setSize(0); + ze.setCrc(0); + zos.putNextEntry(ze); + zos.closeEntry(); + } + } + } + ZipEntry ze = new ZipEntry(path); + ze.setMethod(ZipEntry.STORED); + ze.setSize(data.length); + CRC32 crc = new CRC32(); + crc.update(data); + ze.setCrc(crc.getValue()); + zos.putNextEntry(ze); + zos.write(data, 0, data.length); + zos.closeEntry(); + } + + // copied from NetigsoModuleFactory + private static String threeDotsWithMajor(String version, String withMajor) { + int indx = withMajor.indexOf('/'); + int major = 0; + if (indx > 0) { + major = Integer.parseInt(withMajor.substring(indx + 1)); + } + String[] segments = (version + ".0.0.0").split("\\."); + assert segments.length >= 3 && segments[0].length() > 0; + return (Integer.parseInt(segments[0]) + major * 100) + "." + segments[1] + "." + segments[2]; + } + + static void translateDependency(StringBuilder b, String dependency) throws IOException { + Matcher m = Pattern.compile("([^/ >=]+)(?:/(\\d+)(?:-(\\d+))?)? *(?:(=|>) *(.+))?").matcher(dependency); + if (!m.matches()) { + throw new IOException("bad dep: " + dependency); + } + String depCnb = m.group(1); + String depMajLo = m.group(2); + String depMajHi = m.group(3); + String comparison = m.group(4); + String version = m.group(5); + b.append(depCnb); + if (!"=".equals(comparison)) { + if (version == null) { + version = "0"; + } + String targetVersion = threeDotsWithMajor(version, depMajLo == null ? "" : "x/" + depMajLo); + b.append(";bundle-version=\"[").append(targetVersion).append(","); + b.append(100 * (Integer.parseInt(targetVersion.replaceFirst("[.].+", "")) / 100 + 1)); + b.append(")\""); + } + } + + private void handleDisplayAttribute(Properties props, Attributes netbeans, Attributes osgi, String netbeansHeader, String osgiHeader) throws IOException { + String val = netbeans.getValue(netbeansHeader); + if (val != null) { + osgi.putValue(osgiHeader, val); + } else if (props.containsKey(netbeansHeader)) { + osgi.putValue(osgiHeader, "%" + netbeansHeader); + } + } + + private Map findBundledFiles(File module, String cnb) throws Exception { + Map result = new HashMap(); + if (module.getParentFile().getName().matches("modules|core|lib")) { + File cluster = module.getParentFile().getParentFile(); + File updateTracking = new File(new File(cluster, "update_tracking"), cnb.replace('.', '-') + ".xml"); + if (updateTracking.isFile()) { + Document doc = XMLUtil.parse(new InputSource(updateTracking.toURI().toString()), false, false, null, null); + NodeList nl = doc.getElementsByTagName("file"); + for (int i = 0; i < nl.getLength(); i++) { + String path = ((Element) nl.item(i)).getAttribute("name"); + if (path.matches("config/Modules/.+[.]xml")) { + continue; + } + File f = new File(cluster, path); + if (f.equals(module)) { + continue; + } + // XXX exclude lib/nbexec{,.dll,.exe}, core/*felix*.jar + if (f.isFile()) { + result.put(path, f); + } else { + log("did not find " + f + " specified in " + updateTracking, Project.MSG_WARN); + } + } + } else { + log("did not find expected " + updateTracking, Project.MSG_WARN); + } + } else { + log("JAR " + module + " not found in expected cluster layout", Project.MSG_WARN); + } + return result; + } + +} diff --git a/nbbuild/test/unit/src/org/netbeans/nbbuild/MakeOSGiTest.java b/nbbuild/test/unit/src/org/netbeans/nbbuild/MakeOSGiTest.java new file mode 100644 --- /dev/null +++ b/nbbuild/test/unit/src/org/netbeans/nbbuild/MakeOSGiTest.java @@ -0,0 +1,64 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2010 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2010 Sun Microsystems, Inc. + */ + +package org.netbeans.nbbuild; + +import org.netbeans.junit.NbTestCase; + +public class MakeOSGiTest extends NbTestCase { + + public MakeOSGiTest(String n) { + super(n); + } + + public void testTranslateDependency() throws Exception { + assertTranslateDependency("org.openide.util;bundle-version=\"[8.0.0,100)\"", "org.openide.util > 8.0"); + assertTranslateDependency("org.netbeans.modules.lexer;bundle-version=\"[201.4.0,300)\"", "org.netbeans.modules.lexer/2 > 1.4"); + assertTranslateDependency("what.ever;bundle-version=\"[0.0.0,100)\"", "what.ever"); + assertTranslateDependency("org.netbeans.modules.java.sourceui", "org.netbeans.modules.java.sourceui = 15"); + // XXX interpret depMajHi, e.g. /0-1 > 1.7 => [1.7.0,200) + // XXX 3 or more items in sequence + } + private void assertTranslateDependency(String expected, String dependency) throws Exception { + StringBuilder b = new StringBuilder(); + MakeOSGi.translateDependency(b, dependency); + assertEquals(expected, b.toString()); + } + +}