Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Backports to 2.1.x #154

Merged
merged 4 commits into from
Feb 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 5 additions & 10 deletions .github/workflows/maven.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#
# Copyright (c) 2021, 2022 Contributors to the Eclipse Foundation
# Copyright (c) 2021, 2024 Contributors to the Eclipse Foundation
#
# This program and the accompanying materials are made available under the
# terms of the Eclipse Public License v. 2.0 which is available at
Expand Down Expand Up @@ -29,21 +29,16 @@ jobs:

strategy:
matrix:
java_version: [ 11, 17-ea ]
java_version: [ 17, 21 ]

steps:
- name: Checkout for build
uses: actions/checkout@v2.3.4
uses: actions/checkout@v4
- name: Set up JDK
uses: actions/setup-java@v2
uses: actions/setup-java@v4
with:
distribution: 'zulu'
java-version: ${{ matrix.java_version }}
- name: Cache Maven repository
uses: actions/cache@v2
with:
path: ~/.m2
key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
restore-keys: ${{ runner.os }}-m2
cache: 'maven'
- name: Verify
run: cd api && mvn -B -V -U -C -Poss-release clean verify org.glassfish.copyright:glassfish-copyright-maven-plugin:check -Dgpg.skip=true -Dcopyright.ignoreyear=true
8 changes: 4 additions & 4 deletions .github/workflows/web.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#
# Copyright (c) 2022 Contributors to the Eclipse Foundation
# Copyright (c) 2022, 2024 Contributors to the Eclipse Foundation
#
# This program and the accompanying materials are made available under the
# terms of the Eclipse Public License v. 2.0 which is available at
Expand Down Expand Up @@ -40,7 +40,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Add static content
run: |
export VERSION=`curl https://repo1.maven.org/maven2/jakarta/activation/jakarta.activation-api/maven-metadata.xml | tac | grep -o -m 1 "<version>[[:digit:]]\.[[:digit:]]\.[[:digit:]]</version" | cut -f 2 -d ">" | cut -f 1 -d "<"`
Expand All @@ -50,7 +50,7 @@ jobs:
unzip -q -d ./www/api jakarta.activation-api-javadoc.zip -x "META-INF/*"
cp -Rfv CONTRIBUTING.md doc/spec/* ./www/
- name: Setup Pages
uses: actions/configure-pages@v2
uses: actions/configure-pages@v3
- name: Build with Jekyll
uses: actions/jekyll-build-pages@v1
with:
Expand All @@ -69,4 +69,4 @@ jobs:
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v1
uses: actions/deploy-pages@v2
3 changes: 2 additions & 1 deletion api/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
<parent>
<groupId>org.eclipse.ee4j</groupId>
<artifactId>project</artifactId>
<version>1.0.7</version>
<version>1.0.9</version>
</parent>

<modelVersion>4.0.0</modelVersion>
Expand All @@ -35,6 +35,7 @@
<activation.recompile.skip>false</activation.recompile.skip>
<!-- for the osgiversion-maven-plugin -->
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.build.outputTimestamp>2024-02-14T00:00:00Z</project.build.outputTimestamp>
<javadoc.title>Jakarta Activation API documentation</javadoc.title>
<legal.doc.source>${project.basedir}/..</legal.doc.source>
<copyright.exclude>${project.basedir}/../etc/copyright-exclude</copyright.exclude>
Expand Down
175 changes: 106 additions & 69 deletions api/src/main/java/jakarta/activation/FactoryFinder.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2021, 2024 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0, which is available at
Expand All @@ -13,6 +13,7 @@
import java.lang.reflect.Method;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Arrays;
import java.util.Iterator;
import java.util.logging.Level;
import java.util.logging.Logger;
Expand All @@ -26,48 +27,41 @@ class FactoryFinder {
new ServiceLoaderUtil.ExceptionHandler<RuntimeException>() {
@Override
public RuntimeException createException(Throwable throwable, String message) {
return new RuntimeException(message, throwable);
return new IllegalStateException(message, throwable);
}
};

/**
* Finds the implementation {@code Class} object for the given
* factory type. If it fails and {@code tryFallback} is {@code true}
* finds the {@code Class} object for the given default class name.
* The arguments supplied must be used in order
* Note the default class name may be needed even if fallback
* is not to be attempted in order to check if requested type is fallback.
* factory type.
* <P>
* This method is package private so that this code can be shared.
*
* @param factoryClass factory abstract class or interface to be found
* @return the {@code Class} object of the specified message factory;
* may not be {@code null}
*
* @param factoryClass factory abstract class or interface to be found
* @param defaultClassName the implementation class name, which is
* to be used only if nothing else
* is found; {@code null} to indicate
* that there is no default class name
* @param tryFallback whether to try the default class as a
* fallback
* @exception RuntimeException if there is no factory found
* may not be {@code null}
* @throws IllegalStateException if there is no factory found
*/
static <T> T find(Class<T> factoryClass,
String defaultClassName,
boolean tryFallback) throws RuntimeException {
static <T> T find(Class<T> factoryClass) throws RuntimeException {
for (ClassLoader l : getClassLoaders(
Thread.class,
FactoryFinder.class,
System.class)) {
T f = find(factoryClass, l);
if (f != null) {
return f;
}
}

ClassLoader tccl = ServiceLoaderUtil.contextClassLoader(EXCEPTION_HANDLER);
String factoryId = factoryClass.getName();
throw EXCEPTION_HANDLER.createException((Throwable) null,
"Provider for " + factoryClass.getName() + " cannot be found");
}

static <T> T find(Class<T> factoryClass, ClassLoader loader) throws RuntimeException {
// Use the system property first
String className = fromSystemProperty(factoryId);
String className = fromSystemProperty(factoryClass.getName());
if (className != null) {
T result = newInstance(className, defaultClassName, tccl);
if (result != null) {
return result;
}
// try api loader
result = newInstance(className, defaultClassName, FactoryFinder.class.getClassLoader());
T result = newInstance(className, factoryClass, loader);
if (result != null) {
return result;
}
Expand All @@ -76,40 +70,29 @@ static <T> T find(Class<T> factoryClass,
// standard services: java.util.ServiceLoader
T factory = ServiceLoaderUtil.firstByServiceLoader(
factoryClass,
loader,
logger,
EXCEPTION_HANDLER);
if (factory != null) {
return factory;
}

// handling Glassfish/OSGi (platform specific default)
if (isOsgi()) {
T result = lookupUsingOSGiServiceLoader(factoryId);
if (result != null) {
return result;
}
T result = lookupUsingHk2ServiceLoader(factoryClass, loader);
if (result != null) {
return result;
}

// If not found and fallback should not be tried, throw RuntimeException.
if (!tryFallback) {
throw new RuntimeException(
"Provider for " + factoryId + " cannot be found", null);
}

// We didn't find the class through the usual means so try the default
// (built in) factory if specified.
if (defaultClassName == null) {
throw new RuntimeException(
"Provider for " + factoryId + " cannot be found", null);
}
return newInstance(defaultClassName, defaultClassName, tccl);
return null;
}

private static <T> T newInstance(String className, String defaultClassName, ClassLoader tccl) throws RuntimeException {
private static <T> T newInstance(String className,
Class<? extends T> service, ClassLoader loader)
throws RuntimeException {
return ServiceLoaderUtil.newInstance(
className,
defaultClassName,
tccl,
service,
loader,
EXCEPTION_HANDLER);
}

Expand Down Expand Up @@ -138,31 +121,85 @@ private static void logFound(String value) {
}
}

private static final String OSGI_SERVICE_LOADER_CLASS_NAME = "org.glassfish.hk2.osgiresourcelocator.ServiceLoader";
private static Class<?>[] getHk2ServiceLoaderTargets(Class<?> factoryClass) {
ClassLoader[] loaders = getClassLoaders(Thread.class, factoryClass, System.class);

Class<?>[] classes = new Class<?>[loaders.length];
int w = 0;
for (ClassLoader loader : loaders) {
if (loader != null) {
try {
classes[w++] = Class.forName("org.glassfish.hk2.osgiresourcelocator.ServiceLoader", false, loader);
} catch (Exception | LinkageError ignored) {
} //GlassFish class loaders can throw undocumented exceptions
}
}

private static boolean isOsgi() {
try {
Class.forName(OSGI_SERVICE_LOADER_CLASS_NAME);
return true;
} catch (ClassNotFoundException ignored) {
if (classes.length != w) {
classes = Arrays.copyOf(classes, w);
}
return false;
return classes;
}

@SuppressWarnings({"unchecked"})
private static <T> T lookupUsingOSGiServiceLoader(String factoryId) {
try {
// Use reflection to avoid having any dependency on HK2 ServiceLoader class
Class<?> serviceClass = Class.forName(factoryId);
Class<?>[] args = new Class<?>[]{serviceClass};
Class<?> target = Class.forName(OSGI_SERVICE_LOADER_CLASS_NAME);
Method m = target.getMethod("lookupProviderInstances", Class.class);
Iterator<?> iter = ((Iterable<?>) m.invoke(null, (Object[]) args)).iterator();
return iter.hasNext() ? (T) iter.next() : null;
} catch (Exception ignored) {
// log and continue
return null;
private static <T> T lookupUsingHk2ServiceLoader(Class<T> factoryClass, ClassLoader loader) {
for (Class<?> target : getHk2ServiceLoaderTargets(factoryClass)) {
try {
// Use reflection to avoid having any dependency on HK2 ServiceLoader class
Class<?> serviceClass = Class.forName(factoryClass.getName(), false, loader);
Class<?>[] args = new Class<?>[]{serviceClass};
Method m = target.getMethod("lookupProviderInstances", Class.class);
Iterable<?> iterable = ((Iterable<?>) m.invoke(null, (Object[]) args));
if (iterable != null) {
Iterator<?> iter = iterable.iterator();
if (iter.hasNext()) {
return factoryClass.cast(iter.next()); //Verify classloader.
}
}
} catch (Exception ignored) {
// log and continue
}
}
return null;
}

private static ClassLoader[] getClassLoaders(final Class<?>... classes) {
return AccessController.doPrivileged(
new PrivilegedAction<ClassLoader[]>() {
@Override
public ClassLoader[] run() {
ClassLoader[] loaders = new ClassLoader[classes.length];
int w = 0;
for (Class<?> k : classes) {
ClassLoader cl = null;
if (k == Thread.class) {
try {
cl = Thread.currentThread().getContextClassLoader();
} catch (SecurityException ex) {
}
} else if (k == System.class) {
try {
cl = ClassLoader.getSystemClassLoader();
} catch (SecurityException ex) {
}
} else {
try {
cl = k.getClassLoader();
} catch (SecurityException ex) {
}
}

if (cl != null) {
loaders[w++] = cl;
}
}

if (loaders.length != w) {
loaders = Arrays.copyOf(loaders, w);
}
return loaders;
}
}
);
}
}
Loading