Skip to content

Commit

Permalink
Merge pull request #6 from gastaldi/FURNACE-74
Browse files Browse the repository at this point in the history
FURNACE-74: SimpleContainer now supports Producers
  • Loading branch information
gastaldi committed Aug 21, 2015
2 parents 6c6fa2f + c74a3aa commit 3e81723
Show file tree
Hide file tree
Showing 10 changed files with 395 additions and 21 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* Copyright 2014 Red Hat, Inc. and/or its affiliates.
*
* Licensed under the Eclipse Public License version 1.0, available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.jboss.forge.furnace.container.simple;

/**
* Interface used to register a Service to be a producer.
*
* <p>
* To register a {@link Producer}, register it as a {@link Service} or {@link SingletonService}.
* <p>
*
* <pre>
* public class ExampleService extends ProducerService<MyObject>
* {
* &#64;Override
* public MyObject get()
* {
* // r MyObject
* }
* }
* </pre>
* <p>
* Example registration file:
* </p>
*
* <pre>
* META-INF/services/org.jboss.forge.furnace.container.simple.Service
* -------
* org.example.ExampleService
* org.example.ExampleService2
* org.my.custom.MyService
* -------
* </pre>
*
*
* <h2>Warning: from JDK 1.8.0_20 onwards, javac changed its behavior in order to check for transitive references.
* Producer services must NEVER be directly accessed outside of the addon in which they are defined. See
* <a href="https://issues.jboss.org/browse/FORGE-2019">this link</a> for more information</h2>
*
* @author <a href="mailto:ggastald@redhat.com">George Gastaldi</a>
*/
public interface Producer<T>
{
T get();
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
*/
package org.jboss.forge.furnace.container.simple.impl;

import org.jboss.forge.furnace.Furnace;
import org.jboss.forge.furnace.addons.Addon;
import org.jboss.forge.furnace.exception.ContainerException;
import org.jboss.forge.furnace.proxy.ClassLoaderInterceptor;
Expand All @@ -17,14 +16,13 @@
* @author <a href="mailto:lincolnbaxter@gmail.com">Lincoln Baxter, III</a>
*
*/
public class SimpleExportedInstanceImpl<T> implements ExportedInstance<T>
public class SimpleExportedInstance<T> implements ExportedInstance<T>
{
private final Addon addon;
private final Class<T> type;

public SimpleExportedInstanceImpl(Furnace furnace, Addon addon, Class<T> clazz)
public SimpleExportedInstance(Addon addon, Class<T> clazz)
{
// TODO remove unused parameter
this.addon = addon;
this.type = clazz;
}
Expand Down Expand Up @@ -89,7 +87,7 @@ public boolean equals(Object obj)
return false;
if (getClass() != obj.getClass())
return false;
SimpleExportedInstanceImpl other = (SimpleExportedInstanceImpl) obj;
SimpleExportedInstance other = (SimpleExportedInstance) obj;
if (addon == null)
{
if (other.addon != null)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
/*
* Copyright 2013 Red Hat, Inc. and/or its affiliates.
*
* Licensed under the Eclipse Public License version 1.0, available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.jboss.forge.furnace.container.simple.impl;

import org.jboss.forge.furnace.addons.Addon;
import org.jboss.forge.furnace.container.simple.Producer;
import org.jboss.forge.furnace.exception.ContainerException;
import org.jboss.forge.furnace.proxy.ClassLoaderInterceptor;
import org.jboss.forge.furnace.proxy.Proxies;
import org.jboss.forge.furnace.spi.ExportedInstance;

/**
* @author <a href="mailto:lincolnbaxter@gmail.com">Lincoln Baxter, III</a>
*
*/
public class SimpleProducerExportedInstance<T> implements ExportedInstance<T>
{
private final Addon addon;
private final Class<T> type;
private final Class<? extends Producer<T>> producer;
private final boolean singleton;
private T delegate;

public SimpleProducerExportedInstance(Addon addon, Class<T> type, Class<? extends Producer<T>> producer,
boolean singleton)
{
this.addon = addon;
this.type = type;
this.producer = producer;
this.singleton = singleton;
}

@Override
public T get()
{
if (singleton)
{
if (delegate == null)
{
delegate = newInstance();
}
return delegate;
}
else
{
return newInstance();
}
}

private T newInstance()
{
try
{
T delegate = producer.newInstance().get();
delegate = Proxies.enhance(addon.getClassLoader(), delegate,
new ClassLoaderInterceptor(addon.getClassLoader(),
delegate));
return delegate;
}
catch (Exception e)
{
throw new ContainerException("Could not create instance of [" + type.getName() + "] through reflection.",
e);
}
}

@Override
public void release(T instance)
{
// no action required
}

@Override
public String toString()
{
return type.getName() + " from " + addon;
}

@Override
public Class<? extends T> getActualType()
{
return type;
}

@Override
public Addon getSourceAddon()
{
return addon;
}

@Override
public int hashCode()
{
final int prime = 31;
int result = 1;
result = prime * result + ((addon == null) ? 0 : addon.hashCode());
result = prime * result + ((type == null) ? 0 : type.hashCode());
return result;
}

@Override
public boolean equals(Object obj)
{
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
SimpleProducerExportedInstance other = (SimpleProducerExportedInstance) obj;
if (addon == null)
{
if (other.addon != null)
return false;
}
else if (!addon.equals(other.addon))
return false;
if (type == null)
{
if (other.type != null)
return false;
}
else if (!type.equals(other.type))
return false;
return true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.net.URL;
import java.util.Collections;
import java.util.Enumeration;
Expand All @@ -20,9 +22,9 @@
import java.util.logging.Level;
import java.util.logging.Logger;

import org.jboss.forge.furnace.Furnace;
import org.jboss.forge.furnace.addons.Addon;
import org.jboss.forge.furnace.container.simple.EventListener;
import org.jboss.forge.furnace.container.simple.Producer;
import org.jboss.forge.furnace.container.simple.Service;
import org.jboss.forge.furnace.container.simple.SingletonService;
import org.jboss.forge.furnace.spi.ExportedInstance;
Expand All @@ -38,25 +40,65 @@ public class SimpleServiceRegistry implements ServiceRegistry
private static final Logger log = Logger.getLogger(SimpleServiceRegistry.class.getName());

private final Addon addon;
private final Set<Class<?>> serviceTypes;
private final Set<Class<?>> singletonServiceTypes;

private final Set<Class<?>> serviceTypes = new HashSet<>();
private final Set<Class<?>> singletonServiceTypes = new HashSet<>();

private final Map<String, ExportedInstance<?>> instancesCache = new ConcurrentHashMap<>();

public SimpleServiceRegistry(Furnace furnace, Addon addon)
@SuppressWarnings("unchecked")
public SimpleServiceRegistry(Addon addon)
{
this.addon = addon;
// Maintaining legacy behavior
this.serviceTypes = locateServices(addon, Service.class, EventListener.class);
this.singletonServiceTypes = locateServices(addon, SingletonService.class);
for (Class<?> type : serviceTypes)
// Simple services
for (Class<?> type : locateServices(addon, Service.class, EventListener.class))
{
instancesCache.put(type.getName(), new SimpleExportedInstanceImpl<>(furnace, addon, type));
Class<?> serviceType = type;
ExportedInstance<?> exportedInstance;
if (Producer.class.isAssignableFrom(type))
{
serviceType = extractProducesType(type);
exportedInstance = new SimpleProducerExportedInstance(addon, serviceType, type, false);
}
else
{
exportedInstance = new SimpleExportedInstance<>(addon, type);
}

serviceTypes.add(serviceType);
instancesCache.put(serviceType.getName(), exportedInstance);
}
for (Class<?> type : singletonServiceTypes)

// Singleton services
for (Class<?> type : locateServices(addon, SingletonService.class))
{
Class<?> serviceType = type;
ExportedInstance<?> exportedInstance;
if (Producer.class.isAssignableFrom(type))
{
serviceType = extractProducesType(type);
exportedInstance = new SimpleProducerExportedInstance(addon, serviceType, type, true);
}
else
{
exportedInstance = new SimpleSingletonExportedInstance<>(addon, type);
}
singletonServiceTypes.add(serviceType);
instancesCache.put(serviceType.getName(), exportedInstance);
}
}

static Class<?> extractProducesType(Class<?> service)
{
Type[] genericInterfaces = service.getGenericInterfaces();
for (Type type : genericInterfaces)
{
instancesCache.put(type.getName(), new SimpleSingletonExportedInstanceImpl<>(furnace, addon, type));
if (type instanceof ParameterizedType && ((ParameterizedType) type).getRawType() == Producer.class)
{
return (Class<?>) ((ParameterizedType) type).getActualTypeArguments()[0];
}
}
return null;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
*/
package org.jboss.forge.furnace.container.simple.impl;

import org.jboss.forge.furnace.Furnace;
import org.jboss.forge.furnace.addons.Addon;
import org.jboss.forge.furnace.exception.ContainerException;
import org.jboss.forge.furnace.proxy.ClassLoaderInterceptor;
Expand All @@ -17,13 +16,13 @@
* @author <a href="mailto:lincolnbaxter@gmail.com">Lincoln Baxter, III</a>
*
*/
public class SimpleSingletonExportedInstanceImpl<T> implements ExportedInstance<T>
public class SimpleSingletonExportedInstance<T> implements ExportedInstance<T>
{
private final Addon addon;
private final Class<T> type;
private T delegate = null;

public SimpleSingletonExportedInstanceImpl(Furnace furnace, Addon addon, Class<T> clazz)
public SimpleSingletonExportedInstance(Addon addon, Class<T> clazz)
{
this.addon = addon;
this.type = clazz;
Expand Down Expand Up @@ -93,7 +92,7 @@ public boolean equals(Object obj)
return false;
if (getClass() != obj.getClass())
return false;
SimpleSingletonExportedInstanceImpl other = (SimpleSingletonExportedInstanceImpl) obj;
SimpleSingletonExportedInstance other = (SimpleSingletonExportedInstance) obj;
if (addon == null)
{
if (other.addon != null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public void initialize(Furnace furnace, AddonRegistry registry, Addon self) thro
public void start(Addon addon) throws Exception
{
SimpleContainer.start(addon, furnace);
this.serviceRegistry = new CachedServiceRegistry(new SimpleServiceRegistry(furnace, addon));
this.serviceRegistry = new CachedServiceRegistry(new SimpleServiceRegistry(addon));
this.eventManager = new SimpleEventManagerImpl(addon, serviceRegistry);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/**
* Copyright 2015 Red Hat, Inc. and/or its affiliates.
*
* Licensed under the Eclipse Public License version 1.0, available at
* http://www.eclipse.org/legal/epl-v10.html
*/

package org.jboss.forge.furnace.container.simple.impl;

import org.jboss.forge.furnace.container.simple.Producer;
import org.junit.Assert;
import org.junit.Test;

/**
*
* @author <a href="mailto:ggastald@redhat.com">George Gastaldi</a>
*/
public class SimpleServiceRegistryTest
{

@Test
public void testExtractProducesType()
{
Class<?> type = SimpleServiceRegistry.extractProducesType(MyProducer.class);
Assert.assertSame(String.class, type);
}

class MyProducer implements Producer<String>
{
@Override
public String get()
{
return null;
}
}
}

0 comments on commit 3e81723

Please sign in to comment.