Skip to content

Commit

Permalink
WELDX-19: Implemented ThreadContext and @ThreadScoped
Browse files Browse the repository at this point in the history
  • Loading branch information
peteroyle committed Dec 10, 2009
1 parent bebaeee commit cc1ddb4
Show file tree
Hide file tree
Showing 11 changed files with 467 additions and 15 deletions.
2 changes: 0 additions & 2 deletions src/main/java/org/jboss/weld/environment/se/Weld.java
Expand Up @@ -17,8 +17,6 @@
package org.jboss.weld.environment.se;

import javax.annotation.PostConstruct;
import javax.inject.Inject;
import javax.jws.soap.InitParam;
import org.jboss.weld.bootstrap.api.Bootstrap;
import org.jboss.weld.bootstrap.api.Environments;
import org.jboss.weld.bootstrap.spi.BeanDeploymentArchive;
Expand Down
Expand Up @@ -16,26 +16,46 @@
*/
package org.jboss.weld.environment.se;

import org.jboss.weld.environment.se.threading.RunnableDecorator;
import javax.enterprise.event.Observes;
import javax.enterprise.inject.spi.AfterBeanDiscovery;
import javax.enterprise.inject.spi.BeanManager;
import javax.enterprise.inject.spi.BeforeBeanDiscovery;
import javax.enterprise.inject.spi.Extension;
import org.jboss.weld.context.api.BeanStore;
import org.jboss.weld.context.beanstore.HashMapBeanStore;
import org.jboss.weld.environment.se.beans.InstanceManager;
import org.jboss.weld.environment.se.beans.ParametersFactory;
import org.jboss.weld.environment.se.contexts.ThreadContext;

/**
* Explicitly registers all of the 'built-in' Java SE related beans.
* Explicitly registers all of the 'built-in' Java SE related beans and contexts.
* @author Peter Royle
*/
public class WeldSEBeanRegistrant implements Extension
{

public void registerWeldSEBeans(@Observes BeforeBeanDiscovery event, BeanManager beanManager)
public static ThreadContext THREAD_CONTEXT = null;

public void registerWeldSEBeans(@Observes BeforeBeanDiscovery event, BeanManager manager)
{
event.addAnnotatedType(beanManager.createAnnotatedType(ShutdownManager.class));
event.addAnnotatedType(beanManager.createAnnotatedType(ParametersFactory.class));
event.addAnnotatedType(beanManager.createAnnotatedType(InstanceManager.class));
event.addAnnotatedType(beanManager.createAnnotatedType(Weld.class));
event.addAnnotatedType(manager.createAnnotatedType(ShutdownManager.class));
event.addAnnotatedType(manager.createAnnotatedType(ParametersFactory.class));
event.addAnnotatedType(manager.createAnnotatedType(InstanceManager.class));
event.addAnnotatedType(manager.createAnnotatedType(Weld.class));
event.addAnnotatedType(manager.createAnnotatedType(RunnableDecorator.class));
}

public void registerWeldSEContexts(@Observes AfterBeanDiscovery event)
{
// set up this thread's bean store
BeanStore beanStore = new HashMapBeanStore();
final ThreadContext threadContext = new ThreadContext();
threadContext.setBeanStore(beanStore);

// activate and add context
threadContext.setActive(true);
event.addContext(threadContext);
THREAD_CONTEXT = threadContext;
}
}
@@ -0,0 +1,59 @@
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
*
* Use is subject to license terms.
*
* JBoss, Home of Professional Open Source
* Copyright 2008, Red Hat Middleware LLC, 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.contexts;

import org.jboss.weld.context.AbstractThreadLocalMapContext;

/**
* The thread context. Works with @ThreadScoped beans, maintaining a separate
* context for each thread.
*
* @author Nicklas Karlsson
* @author Peter Royle
*/
public class ThreadContext extends AbstractThreadLocalMapContext
{

/**
* Constructor
*/
public ThreadContext()
{
super(ThreadScoped.class);
}

@Override
public String toString()
{
String active = isActive() ? "Active " : "Inactive ";
String beanStoreInfo = getBeanStore() == null ? "" : getBeanStore().toString();
return active + "thread context " + beanStoreInfo;
}

@Override
protected boolean isCreationLockRequired()
{
return false;
}

}
@@ -0,0 +1,41 @@
/*
* JBoss, Home of Professional Open Source
* Copyright 2008, Red Hat Middleware LLC, 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.contexts;

import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

import java.lang.annotation.Documented;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import javax.enterprise.context.NormalScope;

/**
* Used to represent that a managed bean is scoped to the current thread.
* @author Peter Royle
*/
@Target( { TYPE, METHOD, FIELD })
@Retention(RUNTIME)
@Documented
@NormalScope
@Inherited
public @interface ThreadScoped
{
}
@@ -0,0 +1,53 @@
/*
* JBoss, Home of Professional Open Source
* Copyright 2008, Red Hat Middleware LLC, 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.threading;

import javax.decorator.Decorator;
import javax.decorator.Delegate;
import javax.enterprise.inject.Any;
import javax.inject.Inject;
import org.jboss.weld.context.beanstore.HashMapBeanStore;
import org.jboss.weld.environment.se.WeldSEBeanRegistrant;
import org.jboss.weld.environment.se.contexts.ThreadContext;

/**
* Decorator for all beans which implements Runnable. It intercepts the call
* to the run() method to set up the ThreadContext for the new thread so that
* instances of @ThreadScoped beans can be correctly resolved.
* @author Peter Royle
*/
@Decorator
public class RunnableDecorator implements Runnable {

@Inject @Delegate @Any Runnable runnable;

/**
* Set up the ThreadContet and delegate.
*/
public void run()
{
// set up context for this thread
final ThreadContext threadContext = WeldSEBeanRegistrant.THREAD_CONTEXT;
threadContext.setBeanStore(new HashMapBeanStore());
threadContext.setActive(true);
// run the original thread
runnable.run();
}



}
@@ -0,0 +1,91 @@
/**
* JBoss, Home of Professional Open Source
* Copyright 2009, Red Hat, Inc. and/or its affiliates, 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;

import java.util.ArrayList;

import java.util.List;
import org.jboss.weld.environment.se.ShutdownManager;
import org.jboss.weld.environment.se.Weld;
import org.jboss.weld.environment.se.WeldContainer;
import org.testng.annotations.Test;

import static org.testng.Assert.assertEquals;

/**
* Tests for ThreadContext, @ThreadScoped and the RunnableDecorator. The
* decorator is enabled in the META-INF/beans.xml of the test resources.
* @author Peter Royle
*/
public class ThreadContextTest
{

static final int NUM_THREADS = 10;
static final int NUM_LOOPS = 10;

@Test
public void testThreadContext()
{
WeldContainer weld = new Weld().initialize();

List<ThreadRunner> threadRunners = new ArrayList<ThreadRunner>(NUM_THREADS);
List<Thread> threads = new ArrayList<Thread>(NUM_THREADS);
for (int threadIdx = 0; threadIdx < NUM_THREADS; threadIdx++)
{
final ThreadRunner threadRunner = weld.instance().select(ThreadRunner.class).get();
threadRunner.setName("ThreadRunner thread #" + threadIdx);

Thread thread = new Thread(threadRunner);
thread.start();
threads.add(thread);
threadRunners.add(threadRunner);
}

// wait for all threads to complete
assertEquals(NUM_THREADS, threads.size());
for (Thread thread : threads)
{
try
{
thread.join();
} catch (InterruptedException ex)
{
throw new RuntimeException(ex);
}
}

// bubble any exception from other threads to the surface
assertEquals(NUM_THREADS, threadRunners.size());
for (ThreadRunner threadRunner : threadRunners)
{
for (Exception e : threadRunner.getExceptions())
{
throw new RuntimeException(e);
}
}

shutdownManager(weld);
}

private void shutdownManager(WeldContainer weld)
{
ShutdownManager shutdownManager = weld.instance().select(ShutdownManager.class).get();
shutdownManager.shutdown();
}


}
87 changes: 87 additions & 0 deletions src/test/java/org/jboss/weld/environment/se/test/ThreadRunner.java
@@ -0,0 +1,87 @@
/**
* JBoss, Home of Professional Open Source
* Copyright 2009, Red Hat, Inc. and/or its affiliates, 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;

import java.util.ArrayList;
import java.util.List;
import javax.inject.Inject;
import org.jboss.weld.environment.se.test.beans.threading.SingletonCounter;
import org.jboss.weld.environment.se.test.beans.threading.ThreadCounter;

import static org.testng.Assert.assertEquals;

/**
* An bean which implements Runnable and therefore can be run in a separate thread.
* All such beans, when passed to Thread.start(), will be decorated by the
* RunnableDecorator which will take care of making ThreadContext available to
* that thread for resolution of @ThreadScoped beans.
* @author Peter Royle
*/
public class ThreadRunner implements Runnable
{

// an application scoped counter
@Inject private SingletonCounter appCounter;
// a thread scoped counter
@Inject private ThreadCounter threadCounter;
// a name for logging
private String name = "Unnamed";
// gather exceptions encountered for re-throwing in the test class
private List<Exception> exceptions = new ArrayList<Exception>();

/**
* Run a loop, incrementing both the thread-scoped and application scoped
* counters with each iteration.
*/
public void run()
{
try
{

// Thread scoped counter should start at zero ...
assertEquals(0, threadCounter.getCount());

for (int loop = 1; loop <= ThreadContextTest.NUM_LOOPS; loop++)
{
final int appCount = appCounter.increment();
final int threadCount = threadCounter.increment();
System.out.println(name + " : " + appCount + ", " + threadCount);
assertEquals(loop, threadCount);
}
// ... and end at the number of loops
assertEquals(ThreadContextTest.NUM_LOOPS, threadCounter.getCount());
} catch (Exception e)
{
this.exceptions.add(e);
}
}

public String getName()
{
return name;
}

public void setName(String name)
{
this.name = name;
}

public List<Exception> getExceptions()
{
return exceptions;
}
}

0 comments on commit cc1ddb4

Please sign in to comment.