Skip to content

Instantly share code, notes, and snippets.

@aslakknutsen
Created May 9, 2011 10:26
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save aslakknutsen/962338 to your computer and use it in GitHub Desktop.
Save aslakknutsen/962338 to your computer and use it in GitHub Desktop.
JOops Test Impl, Auto discover dependencies (based on examples-domain from Arquillian-Core)
/*
* JBoss, Home of Professional Open Source
* Copyright 2011 Red Hat Inc. and/or its affiliates and other contributors
* as indicated by the @authors tag. All rights reserved.
* 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 com.acme.deps;
import java.util.HashSet;
import java.util.Set;
import junit.framework.Assert;
import oops.Analyzer;
import oops.DependencyVisitor;
import org.jboss.shrinkwrap.impl.base.URLPackageScanner;
import org.jboss.shrinkwrap.impl.base.URLPackageScanner.Callback;
import org.junit.Test;
import com.acme.cdi.translate.NorwegianTranslator;
import com.acme.cdi.translate.SentenceParser;
import com.acme.cdi.translate.TextTranslator;
import com.acme.cdi.translate.TranslateController;
import com.acme.cdi.translate.Translator;
import com.acme.ejb.GreetingManager;
import com.acme.ejb.GreetingManagerBean;
/**
* Simple test case to demonstrate auto dependency resolving issues
*
* @author <a href="mailto:aslak@redhat.com">Aslak Knutsen</a>
* @version $Revision: $
*/
public class OppsTestCase
{
/*
* A EJB Interface does not depend on it's implementation
*/
@Test
public void ejb_shouldFindImplementationBasedOnInterface() throws Exception
{
Set<String> result = scan(
new String[]{GreetingManager.class.getName()});
Assert.assertTrue(result.contains(GreetingManager.class.getName()));
// not found when scanning the interface, No EJB deployed
Assert.assertTrue(result.contains(GreetingManagerBean.class.getName()));
}
/*
* Can't resolve Bean implementations or Observers based on interfaces
*/
@Test
public void cdi_shouldFindImplementationBasedOnInterface() throws Exception
{
Set<String> result = scan(
new String[]{TranslateController.class.getName()});
Assert.assertTrue(result.contains(Translator.class.getName()));
Assert.assertTrue(result.contains(TranslateController.class.getName()));
Assert.assertTrue(result.contains(TextTranslator.class.getName()));
Assert.assertTrue(result.contains(SentenceParser.class.getName()));
// not found, no one directly depend on it, UnResolvedBeanException
Assert.assertTrue(result.contains(NorwegianTranslator.class.getName()));
}
/*
* Scanning the whole package works in this case(so does archive.addPackage()), but it will most likely depend on another package
* based on interfaces, and we'll have the same issue as above.
*/
@Test
public void cdi_scanPackage_shouldFindImplementationBasedOnInterface() throws Exception
{
Set<String> result = scan(
scanPackage(TranslateController.class.getPackage().getName()));
Assert.assertTrue(result.contains(Translator.class.getName()));
Assert.assertTrue(result.contains(TranslateController.class.getName()));
Assert.assertTrue(result.contains(TextTranslator.class.getName()));
Assert.assertTrue(result.contains(SentenceParser.class.getName()));
Assert.assertTrue(result.contains(NorwegianTranslator.class.getName()));
}
private Set<String> scan(String[] classesToScan)
{
final Set<String> foundClasses = new HashSet<String>();
final long start = System.currentTimeMillis();
Analyzer.analyze(new DependencyVisitor()
{
@Override
public void success(String className)
{
if(!className.matches("(java|javax|sun|com\\.sun|org\\.xml|org\\.w3c)\\..*"))
{
System.out.println("Found: " + className);
foundClasses.add(className);
}
}
@Override
public void fail(String className)
{
System.out.println("Not found: " + className);
}
@Override
public void end()
{
System.out.println("End Scan in " + (System.currentTimeMillis() - start) + " ms");
}
},
classesToScan);
return foundClasses;
}
private String[] scanPackage(String packageName)
{
final Set<String> found = new HashSet<String>();
URLPackageScanner scanner = URLPackageScanner.newInstance(true, OppsTestCase.class.getClassLoader(), new Callback()
{
public void classFound(String name)
{
found.add(name);
}
},
packageName);
scanner.scanPackage();
return found.toArray(new String[]{});
}
}
@aslakknutsen
Copy link
Author

Output:

Found: com.acme.ejb.GreetingManager
End Scan in 2643 ms
Found: com.acme.cdi.translate.TranslateController
Found: com.acme.cdi.translate.SentenceParser
Found: com.acme.cdi.translate.Translator
Found: com.acme.cdi.translate.TextTranslator
End Scan in 1662 ms
Found: com.acme.cdi.translate.SentenceParser
Found: com.acme.cdi.translate.TranslateController
Found: com.acme.cdi.translate.NorwegianTranslator
Found: com.acme.cdi.translate.TextTranslator
Found: com.acme.cdi.translate.Translator
End Scan in 1612 ms

The two main problems:

  • Interface does not depend on it's implementation
  • Speed

Interface not depending on implementation

We could probably fix the 80% case by using a very simple hack:

  • Scan the given class
  • Scan the result Packages
    ** Include anything that depends on the given class or it's dependencies

Example:
acme.Interface
acme.Impl extends Interface

  • Given acme.Interface (depends on nothing)
  • Scan package acme
  • Include acme.Impl since it depends on acme.Interface

Speed

Adding another 2 sec pr @deployment is a bit much, but a very simple performance gain would be to move the filter up to the scanner, instead of Filtering the scanner result. JOops is not optimized for the kind of scanning we need, it finds EVERYTHING,
hench the regexp "(java|javax|sun|com.sun|org.xml|org.w3c)..*" in success.

But if we can stop scanning the class hierarchy when we hit the Filter, we should in most cases only scan a couple of classes deep.

Of course another issue is, what should the filter be?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment