FELIX-4252 Make Extender's ThreadPool size configurable
* Through "org.apache.felix.ipojo.extender.ThreadPoolSize" System property
* Through ConfigurationAdmin (PID: org.apache.felix.ipojo.extender.ExecutorQueueService)
* Added a ThreadGroup iPOJO Extender's threads
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1527573 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/AbstractService.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/AbstractService.java
index e4f201a..e07455f 100644
--- a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/AbstractService.java
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/AbstractService.java
@@ -81,4 +81,8 @@
protected Dictionary<String, ?> getServiceProperties() {
return null;
}
+
+ protected ServiceRegistration<?> getRegistration() {
+ return m_registration;
+ }
}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/Extender.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/Extender.java
index 4caa387..df03d2e 100644
--- a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/Extender.java
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/Extender.java
@@ -19,11 +19,15 @@
package org.apache.felix.ipojo.extender.internal;
+import java.util.concurrent.ThreadFactory;
+
import org.apache.felix.ipojo.ConfigurationTracker;
import org.apache.felix.ipojo.EventDispatcher;
import org.apache.felix.ipojo.extender.internal.linker.DeclarationLinker;
import org.apache.felix.ipojo.extender.internal.processor.*;
import org.apache.felix.ipojo.extender.internal.queue.ExecutorQueueService;
+import org.apache.felix.ipojo.extender.internal.queue.GroupThreadFactory;
+import org.apache.felix.ipojo.extender.internal.queue.NamingThreadFactory;
import org.apache.felix.ipojo.extender.internal.queue.PrefixedThreadFactory;
import org.apache.felix.ipojo.extender.internal.queue.SynchronousQueueService;
import org.apache.felix.ipojo.extender.internal.queue.pref.HeaderPreferenceSelection;
@@ -136,8 +140,17 @@
Preference.SYNC,
m_logger);
} else {
+ // Build a thread factory that will groups extender's thread together
+ ThreadFactory threadFactory = new GroupThreadFactory(new ThreadGroup("iPOJO Extender"));
+ threadFactory = new NamingThreadFactory(threadFactory);
+ threadFactory = new PrefixedThreadFactory(threadFactory, "[iPOJO] ");
+
+ // Create the queue services
SynchronousQueueService sync = new SynchronousQueueService(context);
- ExecutorQueueService async = new ExecutorQueueService(context, 1, new PrefixedThreadFactory("[iPOJO] "));
+ ExecutorQueueService async = new ExecutorQueueService(context,
+ Integer.getInteger(ExecutorQueueService.THREADPOOL_SIZE_PROPERTY,
+ 1), // default to 1 if no system property is set
+ threadFactory);
m_queueService = new PreferenceQueueService(new HeaderPreferenceSelection(), sync, async);
extensionBundleProcessor = new QueuingActivationProcessor(extensionBundleProcessor, m_queueService);
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/queue/ExecutorQueueService.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/queue/ExecutorQueueService.java
index 9e801fb..6cbb2c8 100644
--- a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/queue/ExecutorQueueService.java
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/queue/ExecutorQueueService.java
@@ -25,6 +25,9 @@
import org.apache.felix.ipojo.extender.queue.JobInfo;
import org.apache.felix.ipojo.extender.queue.QueueService;
import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.service.cm.ConfigurationException;
+import org.osgi.service.cm.ManagedService;
import java.util.*;
import java.util.concurrent.*;
@@ -32,7 +35,17 @@
/**
* An asynchronous implementation of the queue service. This implementation relies on an executor service.
*/
-public class ExecutorQueueService extends AbstractService implements LifecycleQueueService {
+public class ExecutorQueueService extends AbstractService implements LifecycleQueueService, ManagedService {
+
+ /**
+ * Property name used to configure this ThreadPool's size (usable as System Property or ConfigAdmin property).
+ */
+ public static final String THREADPOOL_SIZE_PROPERTY = "org.apache.felix.ipojo.extender.ThreadPoolSize";
+
+ /**
+ * Service PID used to identify service with ConfigAdmin.
+ */
+ public static final String EXECUTOR_QUEUE_SERVICE_PID = "org.apache.felix.ipojo.extender.ExecutorQueueService";
/**
* The default thread pool size (3).
@@ -42,7 +55,7 @@
/**
* The executor service.
*/
- private final ExecutorService m_executorService;
+ private final ThreadPoolExecutor m_executorService;
/**
* The statistics populated by this queue service.
@@ -50,6 +63,16 @@
private final Statistic m_statistic = new Statistic();
/**
+ * Store service properties (used when updating their values)
+ */
+ private Hashtable<String, Object> m_properties;
+
+ /**
+ * Initial thread pool size.
+ */
+ private final int initialSize;
+
+ /**
* Creates the queue service using the default pool size.
*
* @param bundleContext the bundle context.
@@ -65,7 +88,7 @@
* @param size the thread pool size.
*/
public ExecutorQueueService(BundleContext bundleContext, int size) {
- this(bundleContext, Executors.newFixedThreadPool(size));
+ this(bundleContext, (ThreadPoolExecutor) Executors.newFixedThreadPool(size));
}
/**
@@ -76,7 +99,7 @@
* @param threadFactory the thread factory
*/
public ExecutorQueueService(BundleContext bundleContext, int size, ThreadFactory threadFactory) {
- this(bundleContext, Executors.newFixedThreadPool(size, threadFactory));
+ this(bundleContext, (ThreadPoolExecutor) Executors.newFixedThreadPool(size, threadFactory));
}
@@ -87,9 +110,11 @@
* @param bundleContext the bundle context
* @param executorService the executor service we have to use
*/
- private ExecutorQueueService(BundleContext bundleContext, ExecutorService executorService) {
+ private ExecutorQueueService(BundleContext bundleContext, ThreadPoolExecutor executorService) {
super(bundleContext, QueueService.class);
m_executorService = executorService;
+ initialSize = executorService.getCorePoolSize();
+ m_properties = getDefaultProperties();
}
/**
@@ -108,9 +133,15 @@
@Override
protected Dictionary<String, ?> getServiceProperties() {
- Hashtable<String, Object> properties = new Hashtable<String, Object>();
- properties.put(QueueService.QUEUE_MODE_PROPERTY, QueueService.ASYNCHRONOUS_QUEUE_MODE);
- return properties;
+ return m_properties;
+ }
+
+ private Hashtable<String, Object> getDefaultProperties() {
+ Hashtable<String, Object> initial = new Hashtable<String, Object>();
+ initial.put(Constants.SERVICE_PID, EXECUTOR_QUEUE_SERVICE_PID);
+ initial.put(QueueService.QUEUE_MODE_PROPERTY, QueueService.ASYNCHRONOUS_QUEUE_MODE);
+ initial.put(THREADPOOL_SIZE_PROPERTY, initialSize);
+ return initial;
}
public int getFinished() {
@@ -154,4 +185,56 @@
return submit(callable, "No description");
}
+ public void updated(Dictionary properties) throws ConfigurationException {
+
+ // Default configuration
+ if (properties == null) {
+ properties = getDefaultProperties();
+ }
+
+ boolean changed = false;
+
+ // Try to read configuration
+ Object o = properties.get(THREADPOOL_SIZE_PROPERTY);
+ if (o != null) {
+ // Convert value
+ Integer newSize = getIntegerProperty(o, DEFAULT_QUEUE_SIZE);
+
+ if (newSize != m_executorService.getMaximumPoolSize()) {
+ // Apply configuration change
+ m_executorService.setCorePoolSize(newSize);
+ m_executorService.setMaximumPoolSize(newSize);
+ m_properties.put(THREADPOOL_SIZE_PROPERTY, newSize);
+ changed = true;
+ }
+ }
+
+ if (changed) {
+ // Transfer unrecognized values in service properties as per spec. recommendation
+ for (Object key : Collections.list(properties.keys())) {
+ if (!THREADPOOL_SIZE_PROPERTY.equals(key)) {
+ m_properties.put(key.toString(), properties.get(key));
+ }
+ }
+
+ // Update registration object
+ getRegistration().setProperties(m_properties);
+ }
+
+ }
+
+ private Integer getIntegerProperty(final Object value, final Integer defaultValue) throws ConfigurationException {
+ Integer newSize = null;
+ if (value instanceof Integer) {
+ newSize = (Integer) value;
+ } else {
+ // Try to convert the value
+ try {
+ newSize = Integer.parseInt(value.toString());
+ } catch (NumberFormatException e) {
+ return defaultValue;
+ }
+ }
+ return newSize;
+ }
}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/queue/GroupThreadFactory.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/queue/GroupThreadFactory.java
new file mode 100644
index 0000000..b1b5b5c
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/queue/GroupThreadFactory.java
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.felix.ipojo.extender.internal.queue;
+
+import java.util.concurrent.ThreadFactory;
+
+/**
+ * A thread factory that groups produced threads inside a given {@link java.lang.ThreadGroup}.
+ */
+public class GroupThreadFactory implements ThreadFactory {
+
+ /**
+ * Group for produced Threads.
+ */
+ private final ThreadGroup m_threadGroup;
+
+ public GroupThreadFactory() {
+ this(defaultThreadGroup());
+ }
+
+ /**
+ * Returns the default thread group just like {@link java.util.concurrent.Executors#defaultThreadFactory()}.
+ */
+ private static ThreadGroup defaultThreadGroup() {
+ SecurityManager s = System.getSecurityManager();
+ if (s != null) {
+ return s.getThreadGroup();
+ } else {
+ return Thread.currentThread().getThreadGroup();
+ }
+ }
+
+ /**
+ * @param threadGroup group to be used for produced threads.
+ */
+ public GroupThreadFactory(final ThreadGroup threadGroup) {
+ m_threadGroup = threadGroup;
+ }
+
+ /**
+ * Creates a new thread.
+ * Prepend the prefix to the thread name
+ *
+ * @param r the runnable
+ * @return the thread object
+ */
+ public Thread newThread(Runnable r) {
+ return new Thread(m_threadGroup, r);
+ }
+}
diff --git a/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/queue/NamingThreadFactory.java b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/queue/NamingThreadFactory.java
new file mode 100644
index 0000000..82f8540
--- /dev/null
+++ b/ipojo/runtime/core/src/main/java/org/apache/felix/ipojo/extender/internal/queue/NamingThreadFactory.java
@@ -0,0 +1,92 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.felix.ipojo.extender.internal.queue;
+
+import static java.lang.String.format;
+
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * A thread factory setting the name of the created thread.
+ * This thread factory delegates the thread creation on another factory and format the name.
+ */
+public class NamingThreadFactory implements ThreadFactory {
+
+ /**
+ * Unique identifier generator.
+ */
+ private static final AtomicInteger IDENTIFIERS = new AtomicInteger(1);
+
+ /**
+ * Per-factory counter
+ */
+ private final AtomicInteger threadNumber = new AtomicInteger(1);
+
+ /**
+ * The wrapped thread factory on which creation is delegated.
+ */
+ private final ThreadFactory m_threadFactory;
+
+ /**
+ * Pool identifier.
+ */
+ private final int m_identifier;
+
+ /**
+ * For test only.
+ */
+ public static void reset() {
+ IDENTIFIERS.set(1);
+ }
+
+ /**
+ * Creates the object delegating to the given thread factory.
+ *
+ * @param threadFactory the thread factory
+ */
+ public NamingThreadFactory(ThreadFactory threadFactory) {
+ this(threadFactory, IDENTIFIERS.getAndIncrement());
+ }
+
+ /**
+ * Creates the object delegating to the given thread factory.
+ *
+ * @param threadFactory the thread factory
+ * @param identifier the pool identifier
+ */
+ private NamingThreadFactory(final ThreadFactory threadFactory, final int identifier) {
+ m_threadFactory = threadFactory;
+ m_identifier = identifier;
+ }
+
+ /**
+ * Creates a new thread.
+ * Format the Thread name
+ *
+ * @param r the runnable
+ * @return the thread object
+ */
+ public Thread newThread(Runnable r) {
+ Thread thread = m_threadFactory.newThread(r);
+ thread.setName(format("pool-%d-thread-%d", m_identifier, threadNumber.getAndIncrement()));
+ return thread;
+ }
+}
diff --git a/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/extender/internal/queue/ExecutorQueueServiceTestCase.java b/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/extender/internal/queue/ExecutorQueueServiceTestCase.java
index 363eaab..6699cd4 100644
--- a/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/extender/internal/queue/ExecutorQueueServiceTestCase.java
+++ b/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/extender/internal/queue/ExecutorQueueServiceTestCase.java
@@ -22,9 +22,12 @@
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
import java.util.Dictionary;
+import java.util.Hashtable;
import java.util.concurrent.Future;
import org.apache.felix.ipojo.extender.internal.queue.callable.SleepingCallable;
@@ -32,6 +35,8 @@
import org.apache.felix.ipojo.extender.queue.Callback;
import org.apache.felix.ipojo.extender.queue.JobInfo;
import org.apache.felix.ipojo.extender.queue.QueueService;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
@@ -54,6 +59,9 @@
@Mock
private Callback<String> m_callback;
+ @Captor
+ private ArgumentCaptor<Dictionary<String, ?>> m_captor;
+
@Override
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
@@ -114,6 +122,73 @@
queueService.stop();
}
+ public void testConfigurationUpdate() throws Exception {
+ ExecutorQueueService queueService = new ExecutorQueueService(m_bundleContext, 1);
+
+ Mockito.<ServiceRegistration<?>>when(m_bundleContext.registerService(eq(QueueService.class.getName()),
+ eq(queueService),
+ any(Dictionary.class)))
+ .thenReturn(m_registration);
+
+ queueService.start();
+
+ verify(m_bundleContext).registerService(eq(QueueService.class.getName()), eq(queueService), m_captor.capture());
+
+ // Verify initial value is 1
+ Dictionary<String, ?> initial = m_captor.getValue();
+ assertEquals(1, initial.get(ExecutorQueueService.THREADPOOL_SIZE_PROPERTY));
+
+ Dictionary<String, Object> update = new Hashtable<String, Object>();
+ update.put(ExecutorQueueService.THREADPOOL_SIZE_PROPERTY, 3);
+ queueService.updated(update);
+
+ verify(m_registration).setProperties(m_captor.capture());
+ assertEquals(3, m_captor.getValue().get(ExecutorQueueService.THREADPOOL_SIZE_PROPERTY));
+ }
+
+ public void testManagedServiceUpdatedWithNull() throws Exception {
+ ExecutorQueueService queueService = new ExecutorQueueService(m_bundleContext, 1);
+
+ Mockito.<ServiceRegistration<?>>when(m_bundleContext.registerService(eq(QueueService.class.getName()),
+ eq(queueService),
+ any(Dictionary.class)))
+ .thenReturn(m_registration);
+
+ queueService.start();
+
+ verify(m_bundleContext).registerService(eq(QueueService.class.getName()), eq(queueService), m_captor.capture());
+
+ // Verify initial value is 1
+ Dictionary<String, ?> initial = m_captor.getValue();
+ assertEquals(1, initial.get(ExecutorQueueService.THREADPOOL_SIZE_PROPERTY));
+
+ // Change the value once and then apply a null configuration
+ // Service should use it's default configuration
+ Dictionary<String, Object> update = new Hashtable<String, Object>();
+ update.put(ExecutorQueueService.THREADPOOL_SIZE_PROPERTY, 3);
+ queueService.updated(update);
+ queueService.updated(null);
+
+ verify(m_registration, times(2)).setProperties(m_captor.capture());
+ assertEquals(1, m_captor.getValue().get(ExecutorQueueService.THREADPOOL_SIZE_PROPERTY));
+ }
+
+ public void testConfigurationUpdatedWithNoChanges() throws Exception {
+ ExecutorQueueService queueService = new ExecutorQueueService(m_bundleContext, 1);
+
+ Mockito.<ServiceRegistration<?>>when(m_bundleContext.registerService(eq(QueueService.class.getName()),
+ eq(queueService),
+ any(Dictionary.class)))
+ .thenReturn(m_registration);
+
+ queueService.start();
+
+ Dictionary<String, Object> update = new Hashtable<String, Object>();
+ update.put(ExecutorQueueService.THREADPOOL_SIZE_PROPERTY, 1);
+ queueService.updated(update);
+
+ verifyZeroInteractions(m_registration);
+ }
}
diff --git a/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/extender/internal/queue/GroupThreadFactoryTestCase.java b/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/extender/internal/queue/GroupThreadFactoryTestCase.java
new file mode 100644
index 0000000..93834bc
--- /dev/null
+++ b/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/extender/internal/queue/GroupThreadFactoryTestCase.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.felix.ipojo.extender.internal.queue;
+
+import junit.framework.TestCase;
+
+/**
+ * User: guillaume
+ * Date: 30/09/13
+ * Time: 15:37
+ */
+public class GroupThreadFactoryTestCase extends TestCase {
+
+ public void testThreadsAreInTheSpecifiedGroup() throws Exception {
+ ThreadGroup group = new ThreadGroup("Test");
+ GroupThreadFactory factory = new GroupThreadFactory(group);
+
+ Thread thread = factory.newThread(new EmptyRunnable());
+
+ assertEquals(group, thread.getThreadGroup());
+ }
+
+ private static class EmptyRunnable implements Runnable {
+ public void run() {
+ // do nothing
+ }
+ }
+}
diff --git a/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/extender/internal/queue/NamingThreadFactoryTestCase.java b/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/extender/internal/queue/NamingThreadFactoryTestCase.java
new file mode 100644
index 0000000..c9bd2bd
--- /dev/null
+++ b/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/extender/internal/queue/NamingThreadFactoryTestCase.java
@@ -0,0 +1,89 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.apache.felix.ipojo.extender.internal.queue;
+
+import java.util.concurrent.ThreadFactory;
+
+import junit.framework.TestCase;
+
+/**
+ * User: guillaume
+ * Date: 30/09/13
+ * Time: 15:46
+ */
+public class NamingThreadFactoryTestCase extends TestCase {
+
+ @Override
+ public void setUp() throws Exception {
+ // Ensure pool numbering starts at 1 for each test
+ NamingThreadFactory.reset();
+ }
+
+ public void testSequentialThreadNaming() throws Exception {
+ NamingThreadFactory factory = new NamingThreadFactory(new DefaultThreadFactory());
+
+ Thread t1 = factory.newThread(new EmptyRunnable());
+ Thread t2 = factory.newThread(new EmptyRunnable());
+ Thread t3 = factory.newThread(new EmptyRunnable());
+ Thread t4 = factory.newThread(new EmptyRunnable());
+
+ assertEquals("pool-1-thread-1", t1.getName());
+ assertEquals("pool-1-thread-2", t2.getName());
+ assertEquals("pool-1-thread-3", t3.getName());
+ assertEquals("pool-1-thread-4", t4.getName());
+ }
+
+ public void testMultiThreadNamingFactories() throws Exception {
+ NamingThreadFactory factory1 = new NamingThreadFactory(new DefaultThreadFactory());
+ NamingThreadFactory factory2 = new NamingThreadFactory(new DefaultThreadFactory());
+
+ // Interleaved invocations
+ Thread t21 = factory2.newThread(new EmptyRunnable());
+ Thread t11 = factory1.newThread(new EmptyRunnable());
+ Thread t12 = factory1.newThread(new EmptyRunnable());
+ Thread t13 = factory1.newThread(new EmptyRunnable());
+ Thread t22 = factory2.newThread(new EmptyRunnable());
+ Thread t23 = factory2.newThread(new EmptyRunnable());
+ Thread t14 = factory1.newThread(new EmptyRunnable());
+ Thread t24 = factory2.newThread(new EmptyRunnable());
+
+ assertEquals("pool-1-thread-1", t11.getName());
+ assertEquals("pool-1-thread-2", t12.getName());
+ assertEquals("pool-1-thread-3", t13.getName());
+ assertEquals("pool-1-thread-4", t14.getName());
+
+ assertEquals("pool-2-thread-1", t21.getName());
+ assertEquals("pool-2-thread-2", t22.getName());
+ assertEquals("pool-2-thread-3", t23.getName());
+ assertEquals("pool-2-thread-4", t24.getName());
+ }
+
+ private static class DefaultThreadFactory implements ThreadFactory {
+ public Thread newThread(final Runnable r) {
+ return new Thread(r);
+ }
+ }
+
+ private static class EmptyRunnable implements Runnable {
+ public void run() {
+ // do nothing
+ }
+ }
+}
diff --git a/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/extender/internal/queue/PrefixedThreadFactoryTestCase.java b/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/extender/internal/queue/PrefixedThreadFactoryTestCase.java
index ec00530..e101ee1 100644
--- a/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/extender/internal/queue/PrefixedThreadFactoryTestCase.java
+++ b/ipojo/runtime/core/src/test/java/org/apache/felix/ipojo/extender/internal/queue/PrefixedThreadFactoryTestCase.java
@@ -26,8 +26,6 @@
import java.util.concurrent.ThreadFactory;
-import org.apache.felix.ipojo.extender.internal.queue.PrefixedThreadFactory;
-
import junit.framework.TestCase;
/**