diff --git a/runtime/bundles/org.eclipse.e4.core.contexts/META-INF/MANIFEST.MF b/runtime/bundles/org.eclipse.e4.core.contexts/META-INF/MANIFEST.MF
index 80aa2e6bc5b..1adb07d5902 100644
--- a/runtime/bundles/org.eclipse.e4.core.contexts/META-INF/MANIFEST.MF
+++ b/runtime/bundles/org.eclipse.e4.core.contexts/META-INF/MANIFEST.MF
@@ -9,7 +9,7 @@ Bundle-ActivationPolicy: lazy
Require-Bundle: org.eclipse.e4.core.di
Bundle-RequiredExecutionEnvironment: JavaSE-17
Import-Package: jakarta.inject;version="[2.0.0,3.0.0)",
- javax.inject;version="[1.0.0,2.0.0)",
+ javax.inject;version="[1.0.0,2.0.0)";resolution:=optional,
org.osgi.framework;version="[1.5.0,2.0.0)",
org.osgi.service.event;version="[1.3.0,2.0.0)"
Export-Package: org.eclipse.e4.core.contexts;version="1.7.0",
diff --git a/runtime/bundles/org.eclipse.e4.core.contexts/src/org/eclipse/e4/core/contexts/ContextInjectionFactory.java b/runtime/bundles/org.eclipse.e4.core.contexts/src/org/eclipse/e4/core/contexts/ContextInjectionFactory.java
index 60d79af3371..3e35c137198 100644
--- a/runtime/bundles/org.eclipse.e4.core.contexts/src/org/eclipse/e4/core/contexts/ContextInjectionFactory.java
+++ b/runtime/bundles/org.eclipse.e4.core.contexts/src/org/eclipse/e4/core/contexts/ContextInjectionFactory.java
@@ -34,13 +34,14 @@
*
* If annotations are supported by the runtime, matching of methods and fields
* to be injected is also performed using the annotations defined in packages
- * javax.inject and org.eclipse.e4.core.di.annotations.
+ * jakarta.inject and org.eclipse.e4.core.di.annotations.
*
*
* The injection of values is generally done as a number of calls. User objects
* that want to finalize the injected data (for instance, to perform
* calculations based on multiple injected values) can place such calculations
- * in a method with the javax.annotation.PostConstruct
annotation.
+ * in a method with the jakarta.annotation.PostConstruct
+ * annotation.
*
*
* When injecting values, all fields are injected prior to injection of methods.
@@ -55,7 +56,7 @@
*
* When a context is disposed, the injection factory will attempt to notify all
* injected objects by calling methods with the
- * javax.annotation.PreDestroy
annotation.
+ * jakarta.annotation.PreDestroy
annotation.
*
* This class is not intended to be extended by clients.
*
diff --git a/runtime/bundles/org.eclipse.e4.core.contexts/src/org/eclipse/e4/core/internal/contexts/ContextObjectSupplier.java b/runtime/bundles/org.eclipse.e4.core.contexts/src/org/eclipse/e4/core/internal/contexts/ContextObjectSupplier.java
index c49528b6520..cfbf8409aab 100644
--- a/runtime/bundles/org.eclipse.e4.core.contexts/src/org/eclipse/e4/core/internal/contexts/ContextObjectSupplier.java
+++ b/runtime/bundles/org.eclipse.e4.core.contexts/src/org/eclipse/e4/core/internal/contexts/ContextObjectSupplier.java
@@ -26,6 +26,7 @@
import org.eclipse.e4.core.di.suppliers.IObjectDescriptor;
import org.eclipse.e4.core.di.suppliers.IRequestor;
import org.eclipse.e4.core.di.suppliers.PrimaryObjectSupplier;
+import org.eclipse.e4.core.internal.di.AnnotationLookup;
import org.eclipse.e4.core.internal.di.Requestor;
public class ContextObjectSupplier extends PrimaryObjectSupplier {
@@ -188,13 +189,9 @@ else if (targetContext.containsKey(keys[i]))
}
private String getKey(IObjectDescriptor descriptor) {
- if (descriptor.hasQualifier(javax.inject.Named.class)) {
- javax.inject.Named namedAnnotation = descriptor.getQualifier(javax.inject.Named.class);
- return namedAnnotation.value();
- }
- if (descriptor.hasQualifier(jakarta.inject.Named.class)) {
- jakarta.inject.Named namedAnnotation = descriptor.getQualifier(jakarta.inject.Named.class);
- return namedAnnotation.value();
+ String value = AnnotationLookup.getQualifierValue(descriptor);
+ if (value != null) {
+ return value;
}
Type elementType = descriptor.getDesiredType();
return typeToString(elementType);
diff --git a/runtime/bundles/org.eclipse.e4.core.di.annotations/META-INF/MANIFEST.MF b/runtime/bundles/org.eclipse.e4.core.di.annotations/META-INF/MANIFEST.MF
index df1ef997d2f..7d106de6331 100644
--- a/runtime/bundles/org.eclipse.e4.core.di.annotations/META-INF/MANIFEST.MF
+++ b/runtime/bundles/org.eclipse.e4.core.di.annotations/META-INF/MANIFEST.MF
@@ -6,6 +6,6 @@ Bundle-Version: 1.8.200.qualifier
Bundle-RequiredExecutionEnvironment: JavaSE-17
Export-Package: org.eclipse.e4.core.di.annotations;version="1.6.0"
Import-Package: jakarta.inject;version="[2.0.0,3.0.0)",
- javax.inject;version="[1.0.0,2.0.0)"
+ javax.inject;version="[1.0.0,2.0.0)";resolution:=optional
Bundle-Vendor: %Bundle-Vendor
Automatic-Module-Name: org.eclipse.e4.core.di.annotations
diff --git a/runtime/bundles/org.eclipse.e4.core.di.extensions/META-INF/MANIFEST.MF b/runtime/bundles/org.eclipse.e4.core.di.extensions/META-INF/MANIFEST.MF
index 53bd676b15e..b5fb4853dbf 100644
--- a/runtime/bundles/org.eclipse.e4.core.di.extensions/META-INF/MANIFEST.MF
+++ b/runtime/bundles/org.eclipse.e4.core.di.extensions/META-INF/MANIFEST.MF
@@ -9,5 +9,5 @@ Bundle-RequiredExecutionEnvironment: JavaSE-17
Export-Package: org.eclipse.e4.core.di.extensions;version="0.16.0"
Bundle-Localization: fragment
Import-Package: jakarta.inject;version="[2.0.0,3.0.0)",
- javax.inject;version="[1.0.0,2.0.0)"
+ javax.inject;version="[1.0.0,2.0.0)";resolution:=optional
Automatic-Module-Name: org.eclipse.e4.core.di.extensions
diff --git a/runtime/bundles/org.eclipse.e4.core.di/META-INF/MANIFEST.MF b/runtime/bundles/org.eclipse.e4.core.di/META-INF/MANIFEST.MF
index 7c67322d83a..017bb7e81d8 100644
--- a/runtime/bundles/org.eclipse.e4.core.di/META-INF/MANIFEST.MF
+++ b/runtime/bundles/org.eclipse.e4.core.di/META-INF/MANIFEST.MF
@@ -13,10 +13,10 @@ Export-Package: org.eclipse.e4.core.di;version="1.7.0",
org.eclipse.e4.core.internal.di.osgi;x-internal:=true,
org.eclipse.e4.core.internal.di.shared;x-friends:="org.eclipse.e4.core.contexts,org.eclipse.e4.core.di.extensions.supplier"
Require-Bundle: org.eclipse.e4.core.di.annotations;bundle-version="[1.4.0,2.0.0)";visibility:=reexport
-Import-Package: javax.annotation;version="[1.3.5,2.0.0)",
- javax.inject;version="[1.0.0,2.0.0)",
+Import-Package: jakarta.annotation;version="[2,3)",
jakarta.inject;version="[2,3)",
- jakarta.annotation;version="[2,3)",
+ javax.annotation;version="[1.3.0,2.0.0)";resolution:=optional,
+ javax.inject;version="[1.0.0,2.0.0)";resolution:=optional,
org.eclipse.osgi.framework.log;version="1.1.0",
org.osgi.framework;version="[1.8.0,2.0.0)",
org.osgi.util.tracker;version="[1.5.1,2.0.0)"
diff --git a/runtime/bundles/org.eclipse.e4.core.di/src/org/eclipse/e4/core/di/suppliers/IObjectDescriptor.java b/runtime/bundles/org.eclipse.e4.core.di/src/org/eclipse/e4/core/di/suppliers/IObjectDescriptor.java
index 5fcd714a752..e1ca06194dd 100644
--- a/runtime/bundles/org.eclipse.e4.core.di/src/org/eclipse/e4/core/di/suppliers/IObjectDescriptor.java
+++ b/runtime/bundles/org.eclipse.e4.core.di/src/org/eclipse/e4/core/di/suppliers/IObjectDescriptor.java
@@ -23,7 +23,7 @@
* set of optional qualifiers.
*
*
- * @see javax.inject.Qualifier
+ * @see jakarta.inject.Qualifier
* @noextend This interface is not intended to be extended by clients.
* @noimplement This interface is not intended to be implemented by clients.
* @since 1.7
diff --git a/runtime/bundles/org.eclipse.e4.core.di/src/org/eclipse/e4/core/internal/di/AnnotationLookup.java b/runtime/bundles/org.eclipse.e4.core.di/src/org/eclipse/e4/core/internal/di/AnnotationLookup.java
new file mode 100644
index 00000000000..767f6346dcd
--- /dev/null
+++ b/runtime/bundles/org.eclipse.e4.core.di/src/org/eclipse/e4/core/internal/di/AnnotationLookup.java
@@ -0,0 +1,185 @@
+/*******************************************************************************
+ * Copyright (c) 2023, 2023 Hannes Wellmann and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Hannes Wellmann - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.e4.core.internal.di;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.AnnotatedElement;
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.function.Function;
+import java.util.function.Supplier;
+import org.eclipse.e4.core.di.IInjector;
+import org.eclipse.e4.core.di.suppliers.IObjectDescriptor;
+import org.eclipse.e4.core.di.suppliers.PrimaryObjectSupplier;
+
+/**
+ * A utility class to ease the look-up of jakarta/javax.inject and
+ * jakarta/javax.annotation annotations and types as mutual replacements, while
+ * being able to handle the absence of javax-classes in the runtime.
+ *
+ * If support for javax-annotations is removed, this class can be simplified to
+ * only handle jakarta-annotations, then all method can be inlined and this
+ * class eventually deleted, together with the entire test-project
+ * org.eclipse.e4.core.tests.
+ */
+public class AnnotationLookup {
+ private AnnotationLookup() {
+ }
+
+ public static record AnnotationProxy(List> classes) {
+ public AnnotationProxy {
+ classes = List.copyOf(classes);
+ }
+
+ public boolean isPresent(AnnotatedElement element) {
+ for (Class extends Annotation> annotationClass : classes) {
+ if (element.isAnnotationPresent(annotationClass)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+
+ static final AnnotationProxy INJECT = createProxyForClasses(jakarta.inject.Inject.class,
+ () -> javax.inject.Inject.class);
+ static final AnnotationProxy SINGLETON = createProxyForClasses(jakarta.inject.Singleton.class,
+ () -> javax.inject.Singleton.class);
+ static final AnnotationProxy QUALIFIER = createProxyForClasses(jakarta.inject.Qualifier.class,
+ () -> javax.inject.Qualifier.class);
+
+ static final AnnotationProxy PRE_DESTROY = createProxyForClasses(jakarta.annotation.PreDestroy.class,
+ () -> javax.annotation.PreDestroy.class);
+ public static final AnnotationProxy POST_CONSTRUCT = createProxyForClasses(jakarta.annotation.PostConstruct.class,
+ () -> javax.annotation.PostConstruct.class);
+
+ static final AnnotationProxy OPTIONAL = createProxyForClasses(org.eclipse.e4.core.di.annotations.Optional.class,
+ null);
+
+ private static AnnotationProxy createProxyForClasses(Class extends Annotation> jakartaAnnotationClass,
+ Supplier> javaxAnnotationClass) {
+ List> classes = getAvailableClasses(jakartaAnnotationClass, javaxAnnotationClass);
+ @SuppressWarnings({ "rawtypes", "unchecked" })
+ List> annotationClasses = (List) classes;
+ return new AnnotationProxy(annotationClasses);
+ }
+
+ private static final List> PROVIDER_TYPES = getAvailableClasses(jakarta.inject.Provider.class,
+ () -> javax.inject.Provider.class);
+
+ static boolean isProvider(Type type) {
+ for (Class> clazz : PROVIDER_TYPES) {
+ if (clazz.equals(type)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @FunctionalInterface
+ private interface ProviderFactory {
+ Object create(IObjectDescriptor descriptor, IInjector injector, PrimaryObjectSupplier provider);
+ }
+
+ private static final ProviderFactory PROVIDER_FACTORY;
+ static {
+ ProviderFactory factory;
+ try {
+ /**
+ * This subclass solely exists for the purpose to not require the presence of
+ * the javax.inject.Provider interface in the runtime when the base-class is
+ * loaded. This can be deleted when support for javax is removed form the
+ * E4-injector.
+ */
+ class JavaxCompatibilityProviderImpl extends ProviderImpl implements javax.inject.Provider {
+ public JavaxCompatibilityProviderImpl(IObjectDescriptor descriptor, IInjector injector,
+ PrimaryObjectSupplier provider) {
+ super(descriptor, injector, provider);
+ }
+ }
+ factory = JavaxCompatibilityProviderImpl::new;
+ // Attempt to load the class early in order to enforce an early class-loading
+ // and to be able to handle the NoClassDefFoundError below in case
+ // javax-Provider is not available in the runtime:
+ factory.create(null, null, null);
+ } catch (NoClassDefFoundError e) {
+ factory = ProviderImpl::new;
+ }
+ PROVIDER_FACTORY = factory;
+ }
+
+ public static Object getProvider(IObjectDescriptor descriptor, IInjector injector, PrimaryObjectSupplier provider) {
+ return PROVIDER_FACTORY.create(descriptor, injector, provider);
+ }
+
+ public static String getQualifierValue(IObjectDescriptor descriptor) {
+ var annotations = NAMED_ANNOTATION2VALUE_GETTER.entrySet();
+ for (Entry, Function> entry : annotations) {
+ Class extends Annotation> annotationClass = entry.getKey();
+ if (descriptor.hasQualifier(annotationClass)) {
+ Annotation namedAnnotation = descriptor.getQualifier(annotationClass);
+ return entry.getValue().apply(namedAnnotation);
+ }
+ }
+ return null;
+ }
+
+ private static final Map, Function> NAMED_ANNOTATION2VALUE_GETTER;
+
+ static {
+ Map, Function> annotation2valueGetter = new HashMap<>();
+ annotation2valueGetter.put(jakarta.inject.Named.class, a -> ((jakarta.inject.Named) a).value());
+ loadJavaxClass(
+ () -> annotation2valueGetter.put(javax.inject.Named.class, a -> ((javax.inject.Named) a).value()));
+ NAMED_ANNOTATION2VALUE_GETTER = Map.copyOf(annotation2valueGetter);
+ }
+
+ private static List> getAvailableClasses(Class> jakartaClass, Supplier extends Class>> javaxClass) {
+ List> classes = new ArrayList<>();
+ classes.add(jakartaClass);
+ if (javaxClass != null) {
+ loadJavaxClass(() -> classes.add(javaxClass.get()));
+ }
+ return classes;
+ }
+
+ private static boolean javaxWarningPrinted = false;
+
+ private static void loadJavaxClass(Runnable run) {
+ try {
+ run.run();
+ if (!javaxWarningPrinted) {
+ if (Boolean.parseBoolean(System.getProperty("eclipse.e4.inject.javax.warning", "true"))) { //$NON-NLS-1$//$NON-NLS-2$
+ @SuppressWarnings("nls")
+ String message = """
+ WARNING: Annotation classes from the 'javax.inject' or 'javax.annotation' package found.
+ It is recommended to migrate to the corresponding replacements in the jakarta namespace.
+ The Eclipse E4 Platform will remove support for those javax-annotations in a future release.
+ To suppress this warning set the VM property: -Declipse.e4.inject.javax.warning=false
+ """;
+ System.err.println(message);
+ }
+ javaxWarningPrinted = true;
+ }
+ } catch (NoClassDefFoundError e) {
+ // Ignore exception: javax-annotation seems to be unavailable in the runtime
+ }
+ }
+
+}
diff --git a/runtime/bundles/org.eclipse.e4.core.di/src/org/eclipse/e4/core/internal/di/InjectorImpl.java b/runtime/bundles/org.eclipse.e4.core.di/src/org/eclipse/e4/core/internal/di/InjectorImpl.java
index 55fd0155d50..5b44a53c9d1 100644
--- a/runtime/bundles/org.eclipse.e4.core.di/src/org/eclipse/e4/core/internal/di/InjectorImpl.java
+++ b/runtime/bundles/org.eclipse.e4.core.di/src/org/eclipse/e4/core/internal/di/InjectorImpl.java
@@ -27,7 +27,6 @@
import java.security.CodeSource;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
@@ -38,6 +37,7 @@
import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;
+import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.eclipse.e4.core.di.IBinding;
import org.eclipse.e4.core.di.IInjector;
@@ -48,6 +48,7 @@
import org.eclipse.e4.core.di.suppliers.IObjectDescriptor;
import org.eclipse.e4.core.di.suppliers.IRequestor;
import org.eclipse.e4.core.di.suppliers.PrimaryObjectSupplier;
+import org.eclipse.e4.core.internal.di.AnnotationLookup.AnnotationProxy;
import org.eclipse.e4.core.internal.di.osgi.LogHelper;
import org.osgi.framework.Bundle;
import org.osgi.framework.FrameworkUtil;
@@ -72,9 +73,9 @@ public class InjectorImpl implements IInjector {
private final Map>> injectedObjects = new WeakHashMap<>();
private final Set>> injectedClasses = new LinkedHashSet<>();
- private final HashMap, Object> singletonCache = new HashMap<>();
+ private final Map, Object> singletonCache = new HashMap<>();
private final Map, Set> bindings = new HashMap<>();
- private final Map, Map> annotationsPresent = new HashMap<>();
+ private final Map> annotationsPresent = new HashMap<>();
// Performance improvement:
private final Map, Method[]> methodsCache = Collections.synchronizedMap(new WeakHashMap<>());
@@ -132,13 +133,10 @@ private void internalInject(Object object, PrimaryObjectSupplier objectSupplier,
}
rememberInjectedObject(object, objectSupplier);
- // We call @javax.annotation.PostConstruct after injection. This means that is
- // is called
- // as a part of both #make() and #inject().
- processAnnotated(javax.annotation.PostConstruct.class, object, object.getClass(), objectSupplier, tempSupplier,
+ // We call @jakarta.annotation.PostConstruct after injection. This means that is
+ // is called as a part of both #make() and #inject().
+ processAnnotated(AnnotationLookup.POST_CONSTRUCT, object, object.getClass(), objectSupplier, tempSupplier,
new ArrayList<>(5));
- processAnnotated(jakarta.annotation.PostConstruct.class, object, object.getClass(), objectSupplier,
- tempSupplier, new ArrayList<>(5));
// remove references to the temporary suppliers
for (Requestor> requestor : requestors) {
@@ -180,12 +178,10 @@ public void uninject(Object object, PrimaryObjectSupplier objectSupplier) {
try {
if (!forgetInjectedObject(object, objectSupplier))
return; // not injected at this time
- processAnnotated(javax.annotation.PreDestroy.class, object, object.getClass(), objectSupplier, null,
- new ArrayList<>(5));
- processAnnotated(jakarta.annotation.PreDestroy.class, object, object.getClass(), objectSupplier, null,
+ processAnnotated(AnnotationLookup.PRE_DESTROY, object, object.getClass(), objectSupplier, null,
new ArrayList<>(5));
- ArrayList> requestors = new ArrayList<>();
+ List> requestors = new ArrayList<>();
processClassHierarchy(object, objectSupplier, null, true /* track */, false /* inverse order */, requestors);
for (Requestor> requestor : requestors) {
@@ -353,8 +349,7 @@ private Object internalMake(Class> clazz, PrimaryObjectSupplier objectSupplier
if (shouldDebug)
classesBeingCreated.add(clazz);
- boolean isSingleton = isAnyAnnotationPresent(clazz,
- List.of(javax.inject.Singleton.class, jakarta.inject.Singleton.class));
+ boolean isSingleton = isAnnotationPresent(clazz, AnnotationLookup.SINGLETON);
if (isSingleton) {
synchronized (singletonCache) {
if (singletonCache.containsKey(clazz))
@@ -375,11 +370,10 @@ private Object internalMake(Class> clazz, PrimaryObjectSupplier objectSupplier
continue;
// unless this is the default constructor, it has to be tagged
- if (!isAnyAnnotationPresent(constructor,
- List.of(javax.inject.Inject.class, jakarta.inject.Inject.class))
- && constructor.getParameterTypes().length != 0)
+ if (!isAnnotationPresent(constructor, AnnotationLookup.INJECT)
+ && constructor.getParameterTypes().length != 0) {
continue;
-
+ }
ConstructorRequestor requestor = new ConstructorRequestor(constructor, this, objectSupplier, tempSupplier);
Object[] actualArgs = resolveArgs(requestor, objectSupplier, tempSupplier, false, true, false);
if (unresolved(actualArgs) != -1)
@@ -437,9 +431,7 @@ public void disposed(PrimaryObjectSupplier objectSupplier) {
Object object = objects[i];
if (!forgetInjectedObject(object, objectSupplier))
continue; // not injected at this time
- processAnnotated(javax.annotation.PreDestroy.class, object, object.getClass(), objectSupplier, null,
- new ArrayList<>(5));
- processAnnotated(jakarta.annotation.PreDestroy.class, object, object.getClass(), objectSupplier, null,
+ processAnnotated(AnnotationLookup.PRE_DESTROY, object, object.getClass(), objectSupplier, null,
new ArrayList<>(5));
}
forgetSupplier(objectSupplier);
@@ -494,9 +486,10 @@ private Object[] resolveArgs(Requestor> requestor, PrimaryObjectSupplier objec
// 1) check if we have a Provider
for (int i = 0; i < actualArgs.length; i++) {
Class> providerClass = getProviderType(descriptors[i].getDesiredType());
- if (providerClass == null)
+ if (providerClass == null) {
continue;
- actualArgs[i] = new ProviderImpl>(descriptors[i], this, objectSupplier);
+ }
+ actualArgs[i] = AnnotationLookup.getProvider(descriptors[i], this, objectSupplier);
}
// 2) try extended suppliers
@@ -689,7 +682,7 @@ private boolean processFields(Object userObject, PrimaryObjectSupplier objectSup
continue;
injectedStatic = true;
}
- if (!isAnyAnnotationPresent(field, List.of(javax.inject.Inject.class, jakarta.inject.Inject.class))) {
+ if (!isAnnotationPresent(field, AnnotationLookup.INJECT)) {
continue;
}
requestors.add(new FieldRequestor(field, this, objectSupplier, tempSupplier, userObject, track));
@@ -708,9 +701,8 @@ private boolean processMethods(final Object userObject, PrimaryObjectSupplier ob
for (Method method : methods) {
Boolean isOverridden = null;
- Map methodMap = null;
Class> originalClass = userObject.getClass();
- methodMap = isOverriddenCache.get(originalClass);
+ Map methodMap = isOverriddenCache.get(originalClass);
if (methodMap != null) {
isOverridden = methodMap.get(method);
}
@@ -732,7 +724,7 @@ private boolean processMethods(final Object userObject, PrimaryObjectSupplier ob
}
injectedStatic = true;
}
- if (!isAnyAnnotationPresent(method, List.of(javax.inject.Inject.class, jakarta.inject.Inject.class))) {
+ if (!isAnnotationPresent(method, AnnotationLookup.INJECT)) {
continue;
}
requestors.add(new MethodRequestor(method, this, objectSupplier, tempSupplier, userObject, track));
@@ -743,7 +735,7 @@ private boolean processMethods(final Object userObject, PrimaryObjectSupplier ob
/**
* Checks if a given method is overridden with an injectable method.
*/
- private boolean isOverridden(Method method, ArrayList> classHierarchy) {
+ private boolean isOverridden(Method method, List> classHierarchy) {
int modifiers = method.getModifiers();
if (Modifier.isPrivate(modifiers))
return false;
@@ -848,8 +840,9 @@ private Class> getProviderType(Type type) {
if (!(type instanceof ParameterizedType))
return null;
Type rawType = ((ParameterizedType) type).getRawType();
- if (!javax.inject.Provider.class.equals(rawType) && !jakarta.inject.Provider.class.equals(rawType))
+ if (!AnnotationLookup.isProvider(rawType)) {
return null;
+ }
Type[] actualTypes = ((ParameterizedType) type).getActualTypeArguments();
if (actualTypes.length != 1)
return null;
@@ -893,17 +886,12 @@ private Binding findBinding(IObjectDescriptor descriptor) {
if (desiredClass == null)
desiredClass = getDesiredClass(descriptor.getDesiredType());
synchronized (bindings) {
- if (!bindings.containsKey(desiredClass))
- return null;
Set collection = bindings.get(desiredClass);
- String desiredQualifierName = null;
- if (descriptor.hasQualifier(jakarta.inject.Named.class)) {
- jakarta.inject.Named namedAnnotation = descriptor.getQualifier(jakarta.inject.Named.class);
- desiredQualifierName = namedAnnotation.value();
- } else if (descriptor.hasQualifier(javax.inject.Named.class)) {
- javax.inject.Named namedAnnotation = descriptor.getQualifier(javax.inject.Named.class);
- desiredQualifierName = namedAnnotation.value();
- } else {
+ if (collection == null) {
+ return null;
+ }
+ String desiredQualifierName = AnnotationLookup.getQualifierValue(descriptor);
+ if (desiredQualifierName == null) {
Annotation[] annotations = descriptor.getQualifiers();
if (annotations != null) {
for (Annotation annotation : annotations) {
@@ -941,7 +929,9 @@ private boolean eq(String str1, String str2) {
return str1.equals(str2);
}
- private void processAnnotated(Class extends Annotation> annotation, Object userObject, Class> objectClass, PrimaryObjectSupplier objectSupplier, PrimaryObjectSupplier tempSupplier, ArrayList> classHierarchy) {
+ private void processAnnotated(AnnotationProxy annotation, Object userObject, Class> objectClass,
+ PrimaryObjectSupplier objectSupplier, PrimaryObjectSupplier tempSupplier,
+ List> classHierarchy) {
Class> superClass = objectClass.getSuperclass();
if (superClass != null && !superClass.getName().equals(JAVA_OBJECT)) {
classHierarchy.add(objectClass);
@@ -953,14 +943,16 @@ private void processAnnotated(Class extends Annotation> annotation, Object use
if (!isAnnotationPresent(method, annotation)) {
if (shouldDebug) {
for (Annotation a : method.getAnnotations()) {
- if (annotation.getName().equals(a.annotationType().getName())) {
+ if (annotation.classes().stream().map(Class::getName)
+ .anyMatch(a.annotationType().getName()::equals)) {
StringBuilder tmp = new StringBuilder();
tmp.append("Possbible annotation mismatch: method \""); //$NON-NLS-1$
tmp.append(method.toString());
tmp.append("\" annotated with \""); //$NON-NLS-1$
tmp.append(describeClass(a.annotationType()));
tmp.append("\" but was looking for \""); //$NON-NLS-1$
- tmp.append(describeClass(annotation));
+ tmp.append(annotation.classes().stream().map(InjectorImpl::describeClass)
+ .collect(Collectors.joining(System.lineSeparator() + " or "))); //$NON-NLS-1$
tmp.append("\""); //$NON-NLS-1$
LogHelper.logWarning(tmp.toString(), null);
}
@@ -975,8 +967,9 @@ private void processAnnotated(Class extends Annotation> annotation, Object use
Object[] actualArgs = resolveArgs(requestor, objectSupplier, tempSupplier, false, false, false);
int unresolved = unresolved(actualArgs);
if (unresolved != -1) {
- if (isAnnotationPresent(method, Optional.class))
+ if (isAnnotationPresent(method, AnnotationLookup.OPTIONAL)) {
continue;
+ }
reportUnresolvedArgument(requestor, unresolved);
}
requestor.setResolvedArgs(actualArgs);
@@ -985,7 +978,7 @@ private void processAnnotated(Class extends Annotation> annotation, Object use
}
/** Provide a human-meaningful description of the provided class */
- private String describeClass(Class> cl) {
+ private static String describeClass(Class> cl) {
Bundle b = FrameworkUtil.getBundle(cl);
if (b != null) {
return b.getSymbolicName() + ":" + b.getVersion() + ":" + cl.getName(); //$NON-NLS-1$ //$NON-NLS-2$
@@ -1005,32 +998,10 @@ public void setDefaultSupplier(PrimaryObjectSupplier objectSupplier) {
defaultSupplier = objectSupplier;
}
- private boolean isAnyAnnotationPresent(AnnotatedElement annotatedElement,
- Collection> annotation) {
- for (Class extends Annotation> a : annotation) {
- if (isAnnotationPresent(annotatedElement, a)) {
- return true;
- }
- }
- return false;
- }
-
- private boolean isAnnotationPresent(AnnotatedElement annotatedElement,
- Class extends Annotation> annotation) {
- Map cache = annotationsPresent.get(annotation);
- if (cache == null) {
- cache = Collections.synchronizedMap(new WeakHashMap<>());
- annotationsPresent.put(annotation, cache);
- }
-
- Boolean present = cache.get(annotatedElement);
- if (present != null) {
- return present;
- }
-
- boolean isPresent = annotatedElement.isAnnotationPresent(annotation);
- cache.put(annotatedElement, isPresent);
- return isPresent;
+ private boolean isAnnotationPresent(AnnotatedElement annotatedElement, AnnotationProxy lookUp) {
+ Map cache = annotationsPresent.computeIfAbsent(lookUp,
+ a -> Collections.synchronizedMap(new WeakHashMap<>()));
+ return cache.computeIfAbsent(annotatedElement, lookUp::isPresent);
}
/**
diff --git a/runtime/bundles/org.eclipse.e4.core.di/src/org/eclipse/e4/core/internal/di/ObjectDescriptor.java b/runtime/bundles/org.eclipse.e4.core.di/src/org/eclipse/e4/core/internal/di/ObjectDescriptor.java
index e03d80932a3..1994eb27041 100644
--- a/runtime/bundles/org.eclipse.e4.core.di/src/org/eclipse/e4/core/internal/di/ObjectDescriptor.java
+++ b/runtime/bundles/org.eclipse.e4.core.di/src/org/eclipse/e4/core/internal/di/ObjectDescriptor.java
@@ -96,9 +96,9 @@ private Annotation[] qualifiers(Annotation[] allAnnotations) {
Annotation[] result;
List qualifiers = new ArrayList<>();
for (Annotation annotation : allAnnotations) {
- if (annotation.annotationType().isAnnotationPresent(javax.inject.Qualifier.class)
- || annotation.annotationType().isAnnotationPresent(jakarta.inject.Qualifier.class))
+ if (AnnotationLookup.QUALIFIER.isPresent(annotation.annotationType())) {
qualifiers.add(annotation);
+ }
}
if (qualifiers.isEmpty())
return null;
diff --git a/runtime/bundles/org.eclipse.e4.core.di/src/org/eclipse/e4/core/internal/di/ProviderImpl.java b/runtime/bundles/org.eclipse.e4.core.di/src/org/eclipse/e4/core/internal/di/ProviderImpl.java
index cf0347c34ed..d137cec1c7e 100644
--- a/runtime/bundles/org.eclipse.e4.core.di/src/org/eclipse/e4/core/internal/di/ProviderImpl.java
+++ b/runtime/bundles/org.eclipse.e4.core.di/src/org/eclipse/e4/core/internal/di/ProviderImpl.java
@@ -13,11 +13,12 @@
*******************************************************************************/
package org.eclipse.e4.core.internal.di;
+import jakarta.inject.Provider;
import org.eclipse.e4.core.di.IInjector;
import org.eclipse.e4.core.di.suppliers.IObjectDescriptor;
import org.eclipse.e4.core.di.suppliers.PrimaryObjectSupplier;
-public class ProviderImpl implements javax.inject.Provider, jakarta.inject.Provider {
+public class ProviderImpl implements Provider {
final private PrimaryObjectSupplier objectProvider;
final private IObjectDescriptor objectDescriptor;
diff --git a/runtime/bundles/org.eclipse.e4.core.services/META-INF/MANIFEST.MF b/runtime/bundles/org.eclipse.e4.core.services/META-INF/MANIFEST.MF
index 7b70cf56aba..534b78bd7d9 100644
--- a/runtime/bundles/org.eclipse.e4.core.services/META-INF/MANIFEST.MF
+++ b/runtime/bundles/org.eclipse.e4.core.services/META-INF/MANIFEST.MF
@@ -9,8 +9,7 @@ Bundle-ActivationPolicy: lazy
Bundle-RequiredExecutionEnvironment: JavaSE-17
Import-Package: jakarta.annotation;version="[2.0.0,3.0.0)",
jakarta.inject;version="[2.0.0,3.0.0)",
- javax.annotation;version="[1.3.0,2.0.0)",
- javax.inject;version="[1.0.0,2.0.0)",
+ javax.inject;version="[1.0.0,2.0.0)";resolution:=optional,
org.eclipse.osgi.service.debug;version="1.1.0",
org.eclipse.osgi.service.localization;version="1.1.0",
org.eclipse.osgi.util;version="[1.1.0,2.0.0)",
diff --git a/runtime/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/internal/services/MessageFactoryServiceImpl.java b/runtime/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/internal/services/MessageFactoryServiceImpl.java
index 703c2f24db8..4405769d301 100644
--- a/runtime/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/internal/services/MessageFactoryServiceImpl.java
+++ b/runtime/bundles/org.eclipse.e4.core.services/src/org/eclipse/e4/core/internal/services/MessageFactoryServiceImpl.java
@@ -250,12 +250,12 @@ private M createInstance(Locale locale, Class messages, Message annotatio
* @param messageClass
* The type of the message class whose instance is requested.
*/
+ @SuppressWarnings("restriction")
private void processPostConstruct(Object messageObject, Class> messageClass) {
if (messageObject != null) {
Method[] methods = messageClass.getDeclaredMethods();
for (Method method : methods) {
- if (!method.isAnnotationPresent(javax.annotation.PostConstruct.class)
- && !method.isAnnotationPresent(jakarta.annotation.PostConstruct.class)) {
+ if (!org.eclipse.e4.core.internal.di.AnnotationLookup.POST_CONSTRUCT.isPresent(method)) {
continue;
} else {
try {
diff --git a/runtime/tests/org.eclipse.e4.core.tests/META-INF/MANIFEST.MF b/runtime/tests/org.eclipse.e4.core.tests/META-INF/MANIFEST.MF
index 03725a4dc15..8ee486122c5 100644
--- a/runtime/tests/org.eclipse.e4.core.tests/META-INF/MANIFEST.MF
+++ b/runtime/tests/org.eclipse.e4.core.tests/META-INF/MANIFEST.MF
@@ -21,12 +21,14 @@ Import-Package: jakarta.annotation;version="[2.1.0,3.0.0)",
org.eclipse.osgi.service.datalocation,
org.eclipse.osgi.service.debug,
org.junit;version="[4.13.0,5.0.0)",
+ org.junit.function;version="[4.13.0,5.0.0)",
org.junit.runner;version="[4.13.0,5.0.0)",
org.junit.runners;version="[4.13.0,5.0.0)",
org.osgi.framework;version="[1.10.0,2.0.0)",
org.osgi.service.component;version="1.3.0",
org.osgi.service.event;version="1.3.0",
org.osgi.util.tracker;version="[1.5.0,2.0.0)"
+DynamicImport-Package: javax.annotation, javax.inject
Export-Package: org.eclipse.e4.core.internal.tests;x-internal:=true,
org.eclipse.e4.core.internal.tests.about;x-internal:=true,
org.eclipse.e4.core.internal.tests.contexts;x-internal:=true,
diff --git a/runtime/tests/org.eclipse.e4.core.tests/src/org/eclipse/e4/core/internal/tests/di/extensions/InjectionOSGiTest.java b/runtime/tests/org.eclipse.e4.core.tests/src/org/eclipse/e4/core/internal/tests/di/extensions/InjectionOSGiTest.java
index 1731b8e8e39..822550ef5cb 100644
--- a/runtime/tests/org.eclipse.e4.core.tests/src/org/eclipse/e4/core/internal/tests/di/extensions/InjectionOSGiTest.java
+++ b/runtime/tests/org.eclipse.e4.core.tests/src/org/eclipse/e4/core/internal/tests/di/extensions/InjectionOSGiTest.java
@@ -18,6 +18,7 @@
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import org.eclipse.e4.core.contexts.ContextInjectionFactory;
@@ -106,6 +107,20 @@ public void setUp() throws Exception {
localContext);
}
+ @Test
+ public void ensureJavaxIsNotAvailable() {
+ // Ensure that the providing bundles of the following classes are absent of the
+ // test-runtime and thus the mentioned classes cannot be loaded
+ assertThrows(ClassNotFoundException.class, () -> Class.forName("javax.inject.Inject"));
+ assertThrows(ClassNotFoundException.class, () -> Class.forName("javax.inject.Singleton"));
+ assertThrows(ClassNotFoundException.class, () -> Class.forName("javax.inject.Qualifier"));
+ assertThrows(ClassNotFoundException.class, () -> Class.forName("javax.inject.Provider"));
+ assertThrows(ClassNotFoundException.class, () -> Class.forName("javax.inject.Named"));
+
+ assertThrows(ClassNotFoundException.class, () -> Class.forName("javax.annotation.PreDestroy"));
+ assertThrows(ClassNotFoundException.class, () -> Class.forName("javax.annotation.PostConstruct"));
+ }
+
@Test
public void testInject() {
assertTrue(target.hasContext());