0

I have a jar with the model DTOs. I want these classes (marked with @XmlRootElement) to be available to Jersey 1 HTTP client. My initialization code is:

ClientConfig cc = new DefaultClientConfig(MyContextResolver.class);
Client client = Client.create(cc);

In MyContextResolver (which implements ContextResolver<JAXBContext>), I've tried:

jaxbContext = JAXBContext.newInstance(Class1.class, Class2.class);

It works, but it's not dynamic (I have to add classes manually, one by one).

I also used Spring and it worked, but I want a Spring free solution.

Is there a better solution? One that automatically scans all classes in the jar and adds them to the context? Thanks.

Community
  • 1
  • 1
Luís Soares
  • 5,726
  • 4
  • 39
  • 66

2 Answers2

1

Is there a reason why you want to have a context that handles all the classes at the same time? You are risking an exception if you have names clashes.

Why not simply have a ContextResolver that works like:

Map<Class,JAXBContect> cache = new ConcurrentHashMap<>();
getContext(Class<?> type) {
 JAXBContect  context = cache.get(type);
 if (context ==  null) }
    context = JAXBContext.newInstance(type);
    cache.put(type,context);
 }
 return context;    
}

JAXB will resolve the necessary classes dependencies (the only problem are subclasses but those should be marked @XMLSeeAlso in the parrent class).

The only potential problem is that you will find out of any JAXB problem during runtime not during startup. But at the same time any error will affect only the clients dependent on wrongly annotated classes and the rest will work.

Zielu
  • 8,312
  • 4
  • 28
  • 41
  • That is a good solution but the problem is that my response class is always the same. Let's call it Response. This class can have a list of items of the same type but that type is dynamic. Why? Because my rest service is a CRUD-like generic list method. To avoid collisions I have used xml namespaces. – Luís Soares Feb 24 '15 at 22:49
  • By using XmlSeeAlso I would need to list all possible types explicitly, even if I add a new one. Is this too meta? Because I have lots a possible classes to be listed. – Luís Soares Feb 24 '15 at 22:56
  • I see, You could use ObjectFactories from each packages to register your classes in the context, but it is still not 'dynamic' just saves you the manually adding classes to a long list in case you have a new package memmber. You can look at jar scanning http://stackoverflow.com/questions/2903082/scan-class-or-jar-file-in-java. As for XmlSeeAlso it is rather for the Child/Parent/Generic reletionship than batch loading. – Zielu Feb 24 '15 at 22:59
  • I don't add packages often so that solution may be interesting. Do you have any example using object factories? (Regarding XmlSeeAlso, any if my classes can be a child of Response.) – Luís Soares Feb 24 '15 at 23:03
  • You use ObjectFactory instead of your classes in the JAXBContext.newInstance(). As how they are constructed/annotated use jaxb to generate classes from a schema and you will get one (faster and easier than reading docs). You will still need to update them manually. Finally, you kind of know what the actual type of your Response will be cause you call CRUD methods for a particular type. Maybe you can use this information to inialized context correctly. – Zielu Feb 24 '15 at 23:08
  • The type is just a query string parameter passed to the server-side list method. I have it when I make the request, but at that time it's too late to change context. I can't change the providers after the client initialization. Or can I? (I'll give you the skeleton code tomorrow when I'm in front of the code.) – Luís Soares Feb 24 '15 at 23:12
0

I ended up scanning the jar manually...

public static List<Class> listClassesInPackage(String jarName, String packageName) throws IOException {
    packageName = packageName.replaceAll("\\.", "/");
    JarInputStream jarFile = new JarInputStream(new FileInputStream(jarName));
    JarEntry jarEntry = null;
    List<Class> classes = new ArrayList();
    do {
        try {
            jarEntry = jarFile.getNextJarEntry();
            if (jarEntry != null && jarEntry.getName().startsWith(packageName) && jarEntry.getName().endsWith(".class")) {
                Class<?> forName = Class.forName(jarEntry.getName().replaceAll("/", "\\.").replaceAll(".class", ""));
                XmlRootElement xmlAnnotation = forName.getAnnotation(XmlRootElement.class);
                if (xmlAnnotation != null) {
                    classes.add(forName);
                }
            }
        } catch (ClassNotFoundException | IOException ex) {
            // ignore this class
        }
    } while (jarEntry != null);
    return classes;
}

invocation:

List<Class> packageClasses = listClassesInPackage(
                Meta.class.getProtectionDomain().getCodeSource().getLocation().getPath(), "pt.iol.model2");
jaxbContext = JAXBContext.newInstance(packageClasses.toArray(new Class[0]));
Luís Soares
  • 5,726
  • 4
  • 39
  • 66