blob: b57c962932be229114d19fc384e8db659b204bf9 [file] [log] [blame]
/*
* 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.framework;
import junit.framework.TestCase;
import org.osgi.framework.Bundle;
import org.osgi.framework.Constants;
import org.osgi.framework.launch.Framework;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.jar.JarOutputStream;
import java.util.jar.Manifest;
public class ConcurrentClassLoaderTest extends TestCase
{
private static final int CONCURRENCY_LEVEL = 1000;
private File m_cacheDir;
private Framework m_felix;
public void testCanResolveClassInParallel() throws Exception
{
m_cacheDir = createCacheDir();
m_felix = createFramework(m_cacheDir);
m_felix.init();
m_felix.getBundleContext().installBundle(createBundleWithDynamicImportPackage());
m_felix.start();
Bundle[] bundles = m_felix.getBundleContext().getBundles();
assertEquals("Two, system and mine: " + Arrays.toString(bundles), 2, bundles.length);
final Bundle bundle = bundles[1];
// This latch ensures that all threads start at the same time
final CountDownLatch latch = new CountDownLatch(CONCURRENCY_LEVEL);
final AtomicInteger doneCount = new AtomicInteger();
for (int i = 0; i < CONCURRENCY_LEVEL; i++) {
new Thread()
{
public void run()
{
try {
latch.countDown();
latch.await();
bundle.loadClass("com.sun.this.class.does.not.exist.but.asking.for.it.must.not.block");
} catch (Exception e) {
// ignore
} finally {
doneCount.incrementAndGet();
}
}
}.start();
}
// Wait for 1 minute for all threads to catch up
long timeout = System.currentTimeMillis() + TimeUnit.MINUTES.toMillis(1);
while (System.currentTimeMillis() < timeout && doneCount.get() != CONCURRENCY_LEVEL) {
Thread.sleep(50);
}
assertEquals("Class resolution all started", 0, latch.getCount());
assertEquals("Class resolution all finished", CONCURRENCY_LEVEL, doneCount.get());
}
@Override
protected void tearDown() throws Exception
{
super.tearDown();
m_felix.stop();
m_felix.waitForStop(1000);
m_felix = null;
delete(m_cacheDir);
}
private static File createCacheDir() throws IOException
{
File cacheDir = File.createTempFile("felix-cache", ".dir");
cacheDir.delete();
cacheDir.mkdirs();
return cacheDir;
}
private static Framework createFramework(File cacheDir)
{
Map<String, String> params = new HashMap<String, String>();
params.put(Constants.FRAMEWORK_SYSTEMPACKAGES, "org.osgi.framework; version=1.4.0,"
+ "org.osgi.service.packageadmin; version=1.2.0,"
+ "org.osgi.service.startlevel; version=1.1.0,"
+ "org.osgi.util.tracker; version=1.3.3,"
+ "org.osgi.service.url; version=1.0.0");
params.put("felix.cache.profiledir", cacheDir.getPath());
params.put("felix.cache.dir", cacheDir.getPath());
params.put(Constants.FRAMEWORK_STORAGE, cacheDir.getPath());
return new Felix(params);
}
private static String createBundleWithDynamicImportPackage() throws IOException
{
String manifest = "Bundle-SymbolicName: boot.test\n" + "Bundle-Version: 1.1.0\n" + "Bundle-ManifestVersion: 2\n" + "DynamicImport-Package: com.sun.*";
File f = File.createTempFile("felix-bundle", ".jar");
f.deleteOnExit();
Manifest mf = new Manifest(new ByteArrayInputStream(manifest.getBytes("utf-8")));
mf.getMainAttributes().putValue("Manifest-Version", "1.0");
JarOutputStream os = new JarOutputStream(new FileOutputStream(f), mf);
os.close();
return f.toURI().toString();
}
private static void delete(File file) throws IOException {
if (file.isDirectory()) {
File[] children = file.listFiles();
if (children != null) {
for (File child : children) {
delete(child);
}
}
}
file.delete();
}
}