Skip to content

Commit

Permalink
WELD-2450: Allow to register BeanArchiveHandler using ServiceLoader
Browse files Browse the repository at this point in the history
- handlers with higher priority have precedence
  • Loading branch information
mkouba committed Jan 11, 2018
1 parent 127ecb6 commit 0898115
Show file tree
Hide file tree
Showing 11 changed files with 411 additions and 8 deletions.
Expand Up @@ -17,23 +17,30 @@
package org.jboss.weld.environment.deployment.discovery;

import java.lang.annotation.Annotation;
import java.util.AbstractMap.SimpleEntry;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.Set;

import org.jboss.logging.Logger;
import javax.annotation.Priority;

import org.jboss.weld.bootstrap.api.Bootstrap;
import org.jboss.weld.bootstrap.spi.BeansXml;
import org.jboss.weld.bootstrap.spi.Metadata;
import org.jboss.weld.environment.deployment.WeldBeanDeploymentArchive;
import org.jboss.weld.environment.deployment.discovery.BeanArchiveScanner.ScanResult;
import org.jboss.weld.environment.logging.CommonLogger;
import org.jboss.weld.exceptions.UnsupportedOperationException;
import org.jboss.weld.resources.spi.ClassFileServices;
import org.jboss.weld.resources.spi.ResourceLoader;
import org.jboss.weld.util.ServiceLoader;

/**
*
Expand All @@ -43,8 +50,6 @@
*/
public abstract class AbstractDiscoveryStrategy implements DiscoveryStrategy {

private static final Logger logger = Logger.getLogger(AbstractDiscoveryStrategy.class);

protected final ResourceLoader resourceLoader;

protected final Bootstrap bootstrap;
Expand Down Expand Up @@ -83,6 +88,8 @@ public Set<WeldBeanDeploymentArchive> performDiscovery() {
final List<BeanArchiveBuilder> beanArchiveBuilders = new ArrayList<BeanArchiveBuilder>();
final Set<String> processedRefs = new HashSet<String>();

List<BeanArchiveHandler> beanArchiveHandlers = initBeanArchiveHandlers();

for (ScanResult scanResult : scanner.scan()) {
final String ref = scanResult.getBeanArchiveRef();
if (processedRefs.contains(ref)) {
Expand All @@ -91,7 +98,7 @@ public Set<WeldBeanDeploymentArchive> performDiscovery() {
CommonLogger.LOG.processingBeanArchiveReference(ref);
processedRefs.add(ref);
BeanArchiveBuilder builder = null;
for (BeanArchiveHandler handler : handlers) {
for (BeanArchiveHandler handler : beanArchiveHandlers) {
builder = handler.handle(ref);
if (builder != null) {
CommonLogger.LOG.beanArchiveReferenceHandled(ref, handler);
Expand All @@ -102,7 +109,7 @@ public Set<WeldBeanDeploymentArchive> performDiscovery() {
}
}
if (builder == null) {
CommonLogger.LOG.beanArchiveReferenceCannotBeHandled(ref, handlers);
CommonLogger.LOG.beanArchiveReferenceCannotBeHandled(ref, beanArchiveHandlers);
}
}

Expand Down Expand Up @@ -150,7 +157,7 @@ protected void addToArchives(Set<WeldBeanDeploymentArchive> deploymentArchives,
}
if (bda.isEmpty()) {
// Most probably an unsuccessful candidate for an implicit bean archive with no beans.xml
logger.debugv("Empty bean deployment archive ignored: {0}", bda.getId());
CommonLogger.LOG.debugv("Empty bean deployment archive ignored: {0}", bda.getId());
return;
}
deploymentArchives.add(bda);
Expand Down Expand Up @@ -194,4 +201,41 @@ public void registerHandler(BeanArchiveHandler handler) {
handlers.add(handler);
}

List<BeanArchiveHandler> initBeanArchiveHandlers() {
List<SimpleEntry<Integer, BeanArchiveHandler>> entries = new ArrayList<>();

// Add programatically added handlers
for (ListIterator<BeanArchiveHandler> iterator = handlers.listIterator(); iterator.hasNext();) {
entries.add(new SimpleEntry<>(handlers.size() - iterator.nextIndex(), iterator.next()));
}

// Load additional bean archive handlers - use Weld ServiceLoader so that we can use the given ResourceLoader
for (Metadata<BeanArchiveHandler> meta : ServiceLoader.load(BeanArchiveHandler.class, resourceLoader)) {
BeanArchiveHandler handler = meta.getValue();
CommonLogger.LOG.debugv("Additional BeanArchiveHandler loaded: {0}", handler.getClass());
entries.add(new SimpleEntry<>(getPriority(handler), handler));
}

Collections.sort(entries, new Comparator<SimpleEntry<Integer, BeanArchiveHandler>>() {
@Override
public int compare(SimpleEntry<Integer, BeanArchiveHandler> o1, SimpleEntry<Integer, BeanArchiveHandler> o2) {
return Integer.compare(o2.getKey(), o1.getKey());
}
});

List<BeanArchiveHandler> beanArchiveHandlers = new ArrayList<>(entries.size());
for (SimpleEntry<Integer, BeanArchiveHandler> entry : entries) {
beanArchiveHandlers.add(entry.getValue());
}
return beanArchiveHandlers;
}

private static int getPriority(BeanArchiveHandler handler) {
Priority priority = handler.getClass().getAnnotation(Priority.class);
if (priority != null) {
return priority.value();
}
return 0;
}

}
Expand Up @@ -16,10 +16,22 @@
*/
package org.jboss.weld.environment.deployment.discovery;

import java.util.ServiceLoader;

/**
* Handles the reference to a bean archive.
* <p>
* The standard way to register a handler is via {@link DiscoveryStrategy#registerHandler(BeanArchiveHandler)}. Alternatively, handlers may be registered using
* the {@link ServiceLoader} mechanism.
* </p>
* <p>
* Additionaly, handlers could specify their priority using {@code javax.annotation.Priority}. Handlers with higher priority have precedence. The default
* priority is 0. Handlers registered programatically have the default priority {@code registeredHandlers.size - index}, i.e. derived from the order they were
* inserted.
* </p>
*
* @author Martin Kouba
* @see DiscoveryStrategy#registerHandler(BeanArchiveHandler)
*/
public interface BeanArchiveHandler {

Expand Down
Expand Up @@ -114,7 +114,7 @@ protected String getBeanArchiveReference(URL url) {
}
ref = getBeanArchiveReferenceForJar(ref, url);
} else {
logger.warnv("Unable to adapt URL: {0}, using its external form instead", url);
logger.infov("Unable to adapt URL: {0}, using its external form instead", url);
ref = url.toExternalForm();
}
logger.debugv("Resolved bean archive reference: {0} for URL: {1}", ref, url);
Expand Down Expand Up @@ -167,7 +167,7 @@ protected String getBeanArchiveReferenceForJar(String path, URL fallback) {
CommonLogger.LOG.jnlpClassLoaderInvocationException(iacce);
}
}
logger.warnv("Unable to adapt JAR file URL: {0}, using its external form instead", path);
logger.infov("Unable to adapt JAR file URL: {0}, using its external form instead", path);
return fallback.toExternalForm();
}
}
@@ -0,0 +1,56 @@
/*
* JBoss, Home of Professional Open Source
* Copyright 2018, Red Hat, Inc., and individual contributors
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* 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 org.jboss.weld.environment.deployment.discovery;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

import java.lang.annotation.Annotation;
import java.util.Collections;
import java.util.List;

import org.jboss.weld.resources.ClassLoaderResourceLoader;
import org.junit.Test;

/**
*
* @author Martin Kouba
*/
public class DiscoveryStrategyTest {

@Test
public void testBeanArchiveHandlers() {
AbstractDiscoveryStrategy strategy = (AbstractDiscoveryStrategy) DiscoveryStrategyFactory
.create(new ClassLoaderResourceLoader(getClass().getClassLoader()), null, Collections.<Class<? extends Annotation>>emptySet(), true);
strategy.registerHandler(new TestHandler2());
List<BeanArchiveHandler> handlers = strategy.initBeanArchiveHandlers();
assertEquals(3, handlers.size());
assertTrue(handlers.get(0) instanceof TestHandler);
assertTrue(handlers.get(1) instanceof FileSystemBeanArchiveHandler);
assertTrue(handlers.get(2) instanceof TestHandler2);
}

static class TestHandler2 implements BeanArchiveHandler {

@Override
public BeanArchiveBuilder handle(String beanArchiveReference) {
return null;
}

}

}
@@ -0,0 +1,13 @@
package org.jboss.weld.environment.deployment.discovery;

import javax.annotation.Priority;

@Priority(10)
class TestHandler implements BeanArchiveHandler {

@Override
public BeanArchiveBuilder handle(String beanArchiveReference) {
return null;
}

}
@@ -0,0 +1 @@
org.jboss.weld.environment.deployment.discovery.TestHandler
@@ -0,0 +1,155 @@
/*
* JBoss, Home of Professional Open Source
* Copyright 2018, Red Hat, Inc., and individual contributors
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* 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 org.jboss.weld.environment.se.test.discovery.handler;

import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLStreamHandler;
import java.net.URLStreamHandlerFactory;
import java.util.Collection;
import java.util.Collections;

import org.jboss.arquillian.container.se.api.ClassPath;
import org.jboss.arquillian.container.test.api.Deployment;
import org.jboss.arquillian.junit.Arquillian;
import org.jboss.shrinkwrap.api.Archive;
import org.jboss.shrinkwrap.api.BeanArchive;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.weld.environment.deployment.AbstractWeldDeployment;
import org.jboss.weld.environment.deployment.discovery.BeanArchiveHandler;
import org.jboss.weld.environment.se.Weld;
import org.jboss.weld.environment.se.WeldContainer;
import org.jboss.weld.resources.spi.ResourceLoader;
import org.jboss.weld.resources.spi.ResourceLoadingException;
import org.junit.Test;
import org.junit.runner.RunWith;

/**
*
* @author Martin Kouba
*/
@RunWith(Arquillian.class)
public class AdditionalBeanArchiveHandlerTest {

@Deployment
public static Archive<?> createTestArchive() {
return ClassPath.builder().add(ShrinkWrap.create(BeanArchive.class).addPackage(AdditionalBeanArchiveHandlerTest.class.getPackage())).build();
}

@Test
public void testAdditionalBeanArchiveHandlerUsed() {

URL.setURLStreamHandlerFactory(new URLStreamHandlerFactory() {
@Override
public URLStreamHandler createURLStreamHandler(String protocol) {
return new TestURLStreamHandler();
}
});

try (WeldContainer container = new Weld().setResourceLoader(new TestResourceLoader()).initialize()) {
// Bar is available, Foo is not (TestBeanArchiveHandler2 has higher priority)
assertTrue(container.select(Bar.class).isResolvable());
assertFalse(container.select(Foo.class).isResolvable());
}
}

static class TestResourceLoader implements ResourceLoader {

@Override
public void cleanup() {
}

@Override
public Class<?> classForName(String name) {
try {
return getClass().getClassLoader().loadClass(name);
} catch (ClassNotFoundException e) {
throw new ResourceLoadingException(e);
}
}

@Override
public URL getResource(String name) {
if (name.equals(AbstractWeldDeployment.BEANS_XML)) {
try {
return new URL("uberjar://some/beans.xml");
} catch (MalformedURLException e) {
throw new ResourceLoadingException(e);
}
}
return null;
}

@Override
public Collection<URL> getResources(String name) {
if (name.equals(AbstractWeldDeployment.BEANS_XML)) {
try {
return Collections.singleton(new URL("uberjar://some/beans.xml"));
} catch (MalformedURLException e) {
throw new ResourceLoadingException(e);
}
}
if (name.contains(BeanArchiveHandler.class.getName())) {
try {
return Collections.singleton(new URL("uberjar://some/services"));
} catch (MalformedURLException e) {
throw new ResourceLoadingException(e);
}
}
return Collections.emptyList();
}

}

static class TestURLStreamHandler extends URLStreamHandler {

@Override
protected URLConnection openConnection(URL u) throws IOException {
return new TestURLConnection(u);
}

}

static class TestURLConnection extends URLConnection {

protected TestURLConnection(URL url) {
super(url);
}

@Override
public void connect() throws IOException {
}

@Override
public InputStream getInputStream() throws IOException {
if (url.toString().contains("beans.xml")) {
return new ByteArrayInputStream("".getBytes());
} else if (url.toString().contains("services")) {
return new ByteArrayInputStream((TestBeanArchiveHandler1.class.getName() + "\n" + TestBeanArchiveHandler2.class.getName()).getBytes());
}
return null;
}

}
}

0 comments on commit 0898115

Please sign in to comment.