Skip to content

Commit

Permalink
OSGI framework wiring support as required by latest Eclipse internal …
Browse files Browse the repository at this point in the history
…platform (diffplug#414)

Support of OSGI framework wiring as require by Eclipse internal platform since 4.12.
  • Loading branch information
fvgh authored Jul 1, 2019
1 parent 92749a5 commit dbf857a
Show file tree
Hide file tree
Showing 7 changed files with 251 additions and 10 deletions.
4 changes: 4 additions & 0 deletions _ext/eclipse-base/CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# spotless-eclipse-base

### Version 3.2.0 - June 30th 2019 ([artifact]([jcenter](https://bintray.com/diffplug/opensource/spotless-eclipse-base)))

* Added support of Eclipse 4.12 framework wiring. ([#413](https://github.com/diffplug/spotless/issues/413))

### Version 3.1.1 - June 4th 2019 ([artifact]([jcenter](https://bintray.com/diffplug/opensource/spotless-eclipse-base)))

* Fixed problem handling URL escaped characters in JAR file location. ([#401](https://github.com/diffplug/spotless/issues/401))
Expand Down
4 changes: 2 additions & 2 deletions _ext/eclipse-base/gradle.properties
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Mayor versions correspond to the supported Eclipse core version.
# Minor version is incremented for features or incompatible changes (including changes to supported dependency versions).
# Patch version is incremented for backward compatible patches of this library.
ext_version=3.1.1
ext_version=3.2.0
ext_artifactId=spotless-eclipse-base
ext_description=Eclipse bundle controller and services for Spotless

Expand All @@ -12,7 +12,7 @@ ext_group=com.diffplug.spotless
ext_VER_JAVA=1.8

# Compile dependencies
VER_ECLIPSE_CORE_RESOURCES=[3.11.1,4.0.0[
VER_ECLIPSE_CORE_RESOURCES=[3.13.400,4.0.0[

# Provided dependencies
VER_SLF4J=[1.6,2.0[
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@

import javax.xml.parsers.SAXParserFactory;

import org.eclipse.core.internal.runtime.InternalPlatform;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
Expand Down Expand Up @@ -226,6 +227,13 @@ private void addPlugin(int state, BundleActivator plugin) throws BundleException
if (!coreConfigStarted) {
//The SAXParserFactory.class is required for parsing the plugin XML files
addMandatoryServiceIfMissing(SAXParserFactory.class, SAXParserFactory.newInstance());
/*
* Since org.eclipse.core.runtime version 3.15.300, the Eclipse bundle look-up is accomplished
* via the wiring framework, which requires a stat of the InternalPlatform.
* The internal platform initialization is customized by the services
* registered to the controller.
*/
InternalPlatform.getDefault().start(controller);
startFrameworkBundles();
coreConfigStarted = true;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import org.osgi.framework.Filter;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceReference;
import org.osgi.framework.wiring.FrameworkWiring;

/**
* OSGi bundle controller allowing a minimal Eclipse platform setup
Expand Down Expand Up @@ -63,8 +64,11 @@ public BundleController() throws BundleException {
bundles.add(systemBundle);

services = new ServiceCollection(systemBundle, properties);

//Eclipse core (InternalPlatform) still uses PackageAdmin for looking up bundles
services.add(org.osgi.service.packageadmin.PackageAdmin.class, new EclipseBundleLookup(bundles));
EclipseBundleLookup bundleLookup = new EclipseBundleLookup(systemBundle, bundles);
services.add(org.osgi.service.packageadmin.PackageAdmin.class, bundleLookup);
services.add(FrameworkWiring.class, bundleLookup);

//Redirect framework activator requests to the the org.eclipse.osgi bundle to this instance.
bundles.add(new SimpleBundle(systemBundle, ECLIPSE_LAUNCHER_SYMBOLIC_NAME, Bundle.ACTIVE));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,26 +15,48 @@
*/
package com.diffplug.spotless.extra.eclipse.base.osgi;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.eclipse.osgi.internal.framework.FilterImpl;
import org.osgi.framework.Bundle;
import org.osgi.framework.FrameworkListener;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.namespace.IdentityNamespace;
import org.osgi.framework.wiring.BundleCapability;
import org.osgi.framework.wiring.FrameworkWiring;
import org.osgi.resource.Namespace;
import org.osgi.resource.Requirement;
import org.osgi.service.packageadmin.ExportedPackage;
import org.osgi.service.packageadmin.PackageAdmin;
import org.osgi.service.packageadmin.RequiredBundle;

/**
* {@link PackageAdmin} service for bundle look-up and bypassing wiring.
*
* {@link PackageAdmin} and {@link FrameworkWiring} service for bundle look-up.
* <p>
* The wiring information will always claim that all required bundles are present.
* Other functionality is not supported.
* Unsupported methods are marked as deprecated and throw an {@link UnsupportedOperationException}.
* The wiring information will always claim that all required bundles are present, since
* Spotlss does on purpose not provide all dependencies requested by plugins, since
* only small parts of the plugins are used.
* Removal and addition requests for bundles will always claim that there is nothing to do.
* <p>
* Interface is deprecated, but for example the InternalPlatform still uses PackageAdmin.
* PackageAdmin interface is deprecated, but might still be used by bundles.
* It is kept for backward compatibility until removed from Eclipse.
*/
@SuppressWarnings("deprecation")
class EclipseBundleLookup implements PackageAdmin {
class EclipseBundleLookup implements FrameworkWiring, PackageAdmin {

private static final Set<String> OSGI_KEYS_FOR_SYMBOLIC_NAMES = Collections.unmodifiableSet(Stream.of(IdentityNamespace.IDENTITY_NAMESPACE, IdentityNamespace.TYPE_BUNDLE).collect(Collectors.toSet()));
private final Bundle systemBundle;
private final BundleSet bundles;

EclipseBundleLookup(final BundleSet bundles) {
EclipseBundleLookup(final Bundle systemBundle, final BundleSet bundles) {
this.systemBundle = systemBundle;
this.bundles = bundles;
}

Expand Down Expand Up @@ -97,4 +119,64 @@ public int getBundleType(Bundle bundle) {
return 0; //No fragments
}

@Override
public Bundle getBundle() {
return systemBundle;
}

@Override
public void refreshBundles(Collection<Bundle> bundles, FrameworkListener... listeners) {
//Spotless bundles cannot be loaded dynamically
}

@Override
public boolean resolveBundles(Collection<Bundle> bundles) {
return true;
}

@Override
public Collection<Bundle> getRemovalPendingBundles() {
return Collections.emptyList(); //Nothing to remove
}

@Override
public Collection<Bundle> getDependencyClosure(Collection<Bundle> bundles) {
return Collections.emptyList(); //No dependencies
}

@Override
public Collection<BundleCapability> findProviders(Requirement requirement) {
// requirement must not be null (according to interface description)!
String filterSpec = requirement.getDirectives().get(Namespace.REQUIREMENT_FILTER_DIRECTIVE);
if (null == filterSpec) {
throw new IllegalArgumentException("Requirement filter diretive '" + Namespace.REQUIREMENT_FILTER_DIRECTIVE + "' not found.");
}
try {
FilterImpl requirementFilter = FilterImpl.newInstance(filterSpec);
Collection<String> requiredSymbolicNames = getRequestedSymbolicNames(requirementFilter);
Collection<BundleCapability> capabilities = new ArrayList<BundleCapability>(requiredSymbolicNames.size());
requiredSymbolicNames.forEach(symbolicName -> {
Bundle bundle = bundles.get(symbolicName);
if (bundle != null) {
capabilities.add(new SimpleBundleCapability(bundle));
}
});
return capabilities;
} catch (InvalidSyntaxException e) {
throw new IllegalArgumentException("Filter specifiation invalid:\n" + filterSpec, e);
}
}

/**
* Simplified parser irgnoreing the version.
* Parser is incomplete since it ignores the filter operation.
* It basicall implements the bespoke way Eclipse maps its old style bundle handling to OSGI.
*/
private static Collection<String> getRequestedSymbolicNames(FilterImpl filter) {
List<String> symbolicNames = filter.getStandardOSGiAttributes().entrySet().stream().filter(entry -> OSGI_KEYS_FOR_SYMBOLIC_NAMES.contains(entry.getKey())).map(entry -> entry.getValue()).collect(Collectors.toList());
filter.getChildren().forEach(childFilter -> {
symbolicNames.addAll(getRequestedSymbolicNames(childFilter));
});
return symbolicNames;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,35 @@ private SimpleBundle(BundleContext context, int state, ResourceAccessor resource
name = master.name;
}

@Override
public <A> A adapt(Class<A> type) {
/*
* The adaptation is currently used by the InternalPlugin to get the framework wiring
* implementation from the system bundle.
* The original purpose to provide more specialized access to the Bundle object,
* seems not be used by Eclipse at all.
* Hence the call is mapped to old-style Eclipse services.
*/
try {

ServiceReference<?>[] references = context.getAllServiceReferences(type.getName(), "");
if ((null != references) && (0 != references.length)) {
if (1 != references.length) {
throw new IllegalArgumentException("Multiple services found for " + type.getName()); //In Spotless services should always be unique
}
Object obj = context.getService(references[0]);
try {
return type.cast(obj);
} catch (ClassCastException e) {
throw new IllegalArgumentException("Received unexpected class for reference filter " + type.getName(), e);
}
}
return null;
} catch (InvalidSyntaxException e) {
throw new IllegalArgumentException("Unexpected syntax exception", e); //Should never be thrown by Spotless bundle controller
}
}

@Override
public int getState() {
return state;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
/*
* Copyright 2016 DiffPlug
*
* 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.
*/
package com.diffplug.spotless.extra.eclipse.base.osgi;

import java.util.Collections;
import java.util.List;
import java.util.Map;

import org.osgi.framework.Bundle;
import org.osgi.framework.Version;
import org.osgi.framework.wiring.BundleCapability;
import org.osgi.framework.wiring.BundleRequirement;
import org.osgi.framework.wiring.BundleRevision;
import org.osgi.framework.wiring.BundleWiring;
import org.osgi.resource.Capability;
import org.osgi.resource.Requirement;

/**
* Simplified bundle capability ignoring internal wiring and versions
* <p/>
* Since multiple versions/implementations of bundles for the same
* capability is not supported a split of bundle capability and revision is not required.
*/
class SimpleBundleCapability implements BundleCapability, BundleRevision {
private final Bundle bundle;

SimpleBundleCapability(Bundle bundle) {
this.bundle = bundle;
}

@Override
public BundleRevision getRevision() {
return this;
}

@Override
public String getNamespace() {
return this.getClass().getName(); //All bundles live in th same namespace
}

@Override
public Map<String, String> getDirectives() {
return Collections.emptyMap();
}

@Override
public Map<String, Object> getAttributes() {
return Collections.emptyMap();
}

@Override
public BundleRevision getResource() {
return this;
}

@Override
public Bundle getBundle() {
return bundle;
}

@Override
public String getSymbolicName() {
return bundle.getSymbolicName();
}

@Override
public Version getVersion() {
return bundle.getVersion();
}

@Override
public List<BundleCapability> getDeclaredCapabilities(String namespace) {
return Collections.emptyList();
}

@Override
public List<BundleRequirement> getDeclaredRequirements(String namespace) {
return Collections.emptyList();
}

@Override
public int getTypes() {
return 0; //It does not matter whether this bunddle is a fragment of not since all bundles are initially provided
}

@Override
public BundleWiring getWiring() {
return null; //No wiring information
}

@Override
public List<Capability> getCapabilities(String namespace) {
return Collections.emptyList();
}

@Override
public List<Requirement> getRequirements(String namespace) {
return Collections.emptyList();
}

}

0 comments on commit dbf857a

Please sign in to comment.