Weld on Java SE does not use alternative - java

I have projectA, projectB, and projectC Eclipse Maven projects.
ProjectA contains:
IMyApi interface.
"Empty" META-INF\beans.xml file.
ProjectB contains:
IMyConfig interface.
MyConfigJndi implementation of IMyConfig.
MyApiImpl implementation of IMyApi, with a property #Inject private IMyConfig config;.
"Empty" META-INF\beans.xml file.
ProjectC contains:
a MyConfigAlter implementation of IMyConfig, marked as #Alternative.
a Main class (and method) that initializes Weld SE and retrieves a IMyApi bean.
a META-INF\beans.xml where MyConfigAlter is listed in the alternatives section.
Now, I run the Main class, and the IMyApi bean is successfully retrieved (as a MyApiImpl instance). But such an instance has been, in its config property, injected with a MyConfigJndi instance, instead of the alternative version (MyConfigAlter)
I am using Eclipse Luna + M2Eclipse.
What am I doing wrong?
UPDATE: I found out that using #Specializes instead of #Alternative solves the issue, but I still think it is not the proper solution (in some situation I may not have access to the "default" implementation).
UPDATE 2:
I am using Weld-se, 2.2.10.Final:
<dependency>
<groupId>org.jboss.weld.se</groupId>
<artifactId>weld-se</artifactId>
<version>2.2.10.Final</version>
<scope>runtime</scope>
</dependency>
And the initialization is simply
WeldContainer weld =
new Weld().
initialize();
IMyApi myApi =
weld.
instance().
select(
IMyApi.
class).
get();

Selecting an alternative using the alternatives element in the beans.xml descriptor only affects the corresponding bean archive, i.e. ProjectC in your case, as documented in Declaring selected alternatives for a bean archive. Based on that, this is logical that the ProjectB bean archive gets the MyConfigJndi implementation injected.
Since CDI 1.2, it is possible to select an alternative globally for the application using the #Priority annotation as documented in Declaring selected alternatives for an application.
So in your case, you could write:
#Priority(Interceptor.Priority.Application)
#Alternative
class MyConfigAlter {
}

Another way to solve this is to use -Dorg.jboss.weld.se.archive.isolation=false - from http://docs.jboss.org/weld/reference/2.2.11.Final/en-US/html/environments.html#_bean_archive_isolation_2
The reason that this happens is that each JAR on the classpath becomes its own bean archive. Since the CDI spec as of 1.2 does not include an SE specification there is no definition of how the classpath operates in this mode. This isn't necessarily how an SE app would be designed since you don't have unique classloaders for each JAR.

Related

Define stereotype to use on different project

I have 2 java-ee (helidon-mp) projects (Lets say A and B), both share some logic which is encapsulated on a separate project/jar/dependency, the projects are similar yet not identical, to handle those differences I consider it would be a good idea to create a Stereotype which would allow me to specify when do I want to create/provide instances tuned for either A or B project. Given the shared dependency project creates beans it has its own beans.xml file, just as both of the projects that depend on it.
The problem
I would like to be able to specify the desired stereotype to use within the beans.xml file of each project, yet when I specify the stereotype at the project level, such configuration is not passed down to the dependency project; this throws an exception with a message similar to the following one:
WELD-001408: Unsatisfied dependencies for type NeededClassForAProject with qualifiers #Default
at injection point [BackedAnnotatedParameter] Parameter 1 of [BackedAnnotatedConstructor] #Inject public the.project.class.injecting.dependency.ProjectA(NeededClassForAProject)
at the.project.class.injecting.dependency.ProjectA.<init>(ProjectA.java:0)
When I define the stereotype within the dependency beans.xml everything works like a charm yet this causes that whenever I need to work with Project A I need to update (and rebuild) the dependency project changing its beans.xml indicating I want to use the configuration for A and same goes whenever I switch to working with project B
The question itself:
Is it possible to keep my configuration at the project level beans.xml and make the dependency project recognize such configuration from the parent project? how
The answer is here
where it clearly states:
By default, #Alternative beans are disabled. We need to enable an alternative in the beans.xml descriptor of a bean archive to make it available for instantiation and injection. However, this activation only applies to the beans in that archive.
From CDI 1.1 onwards the alternative can be enabled for the whole application using #Priority annotation.

Possible to ignore configuration classes on the classpath?

I have a Spring Boot application that works as expected when ran with embedded tomcat, but I noticed that if I try to run it from an existing tomcat instance that I'm using with a previous project then it fails with a NoClassDefFoundError for a class that I don't use anywhere in my application.
I noticed in the /lib directory I had a single jar that contained a few Spring annotated classes, so as a test I cleaned out the /lib directory which resolved the issue. My assumption is that Spring is seeing some of the configurations/beans/imports on the classpath due to them existing in the /lib directory and either trying to autoconfigure something on its own, or is actually trying to instantiate some of these classes.
So then my question is - assuming I can't always fully control the contents of everything on the classpath, how can I prevent errors like this from occurring?
EDIT
For a little more detail - the class not being found is DefaultCookieSerializer which is part of the spring-session-implementation dependency. It is pulled into one of the classes in the jar located in /lib, but it is not any part of my application.
Check for features provided by #EnableAutoConfiguration. You can explicitly configure set of auto-configuration classes for your application. This tutorial can be a good starting point.
You can remove the #SpringBootApplication annotation from the main class and replace it with an #ComponentScan annotation and an #Import annotation that explicitly lists only the configuration classes you want to load. For example, in a Spring boot MVC app that uses metrics, web client, rest template, Jackson, etc, I was able to replace the #SpringBootApplication annotation with below code and get it working exactly as it was before, with all functional tests passing:
#Import({ MetricsAutoConfiguration.class,
InfluxMetricsExportAutoConfiguration.class,
ServletWebServerFactoryAutoConfiguration.class,
DispatcherServletAutoConfiguration.class,
WebMvcAutoConfiguration.class,
JacksonAutoConfiguration.class,
WebClientAutoConfiguration.class,
RestTemplateAutoConfiguration.class,
RefreshAutoConfiguration.class,
ValidationAutoConfiguration.class
})
#ComponentScan
The likely culprit of mentioned exception are incompatible jars on the classpath.
As we don't know with what library you have the issue we cant tell you the exact reason, but the situation looks like that:
One of Spring-Boot autoconfiguration classes is being triggered by the presence of class on the classpath
Trigerred configuration tries to create some bean of class that is not present in the jar you have (but it is in the specific version mentioned in the Spring BOM)
Version incompatibilities may also cause MethodNotFound exceptions.
That's one of the reasons why it is good practice not to run Spring Boot applications inside the container (make jar not war), but as a runnable jar with an embedded container.
Even before Spring Boot it was preferred to take account of libraries being present on runtime classpath and mark them as provided inside your project. Having different versions of the library on a classpath may cause weird ClassCastExceptions where on both ends names match, but the rest doesn't.
You could resolve specific cases by disabling autoconfiguration that causes your issue. You can do that either by adding exclude to your #SpringBootApplication or using a property file.
Edit:
If you don't use very broad package scan (or use package name from outside of your project in package scan) in your Spring Boot application it is unlikely that Spring Boot simply imports configuration from the classpath.
As I have mentioned before it is rather some autoconfiguration that is being triggered by existence of a class in the classpath.
Theoretical solution:
You could use maven shade plugin to relocate all packages into your own package space: see docs.
The problems is you'd have face:
Defining very broad relocation pattern that would exclude JEE classes that need to be used so that container would know how to run your application.
Relocation most likely won't affect package names used as strings in the Spring Boot annotations (like annotations #PackageScan or #ConditionalOnClass). As far as I know it is not implemented yet. You'd have to implement that by yourself - maybe as some kind of shade plugin resource processor.
When relocating classes you'd have to replace package names in all relevant configuration located in the jars. Possibly also merge some of those.
You'd also have to take into account how libraries that you use, or spring uses use package names or files.
This is definitely not a trivial tasks with many traps ahead. But if done right, then it would possibly allow you to disregard what is on the containers classpath. Spring Boot would also look for classes in relocated packages, and you wouldn't have those in ordinary jars.

Hibernate and Jersey dependency conflict (javassist) - can anyone explain how this works?

I'm currently using hibernate-4.1.4 and jersey-2.22. These have javassist-3.15 and javassist-3.18 respectively.
I included both hibernate and jersey in my project and to my surprise, there were no dependency conflicts between the said javassists.
I was wondering how Java tells hibernate to use 3.15 and how it tell jersey to use 3.18 since both are in the build path. Is one javassist not being used?
Follow up question: Let's say that javassist-3.15 and javassist-3.18 have a conflict with each other. How do I resolve this? Do I disable both javassists and include one externally?
EDIT: My app is a web app that runs on Tomcat 7. We don't use Maven/Gradle. We just configure the dependencies by putting the jars in the build path using Eclipse.
JAVA loads classes through ClassLoaders ... Many applications servers, as Tomcat or Wildfly, implement and use their own class loaders (not the regular ones of the common JDK) ... So you must check the Tomcat documentation to read about its classloading behaviour...
After saying that, is very likely that Tomcat is loading libraries in alphabetical order. I Explain...
Suppose that you use a class named: Dummy, and this class is contained at the libraries: dummy-1.0.jar and dummy-1.1.jar ... when the class Dummy is requested, the Tomcat ClassLoader search for that class definition, looking first at dummy-1.0.jar and later at dummy-1.1.jar ... given that dummy-1.0.jar contains that class, Tomcat stops looking a returns that class version ... If dummy-1.0.jar would not have the target class, the dummy-1.1.jar class version would be returned instead...
(I suggest to try this to validate the container behaivour, it's not so hard to implement)...
And yes, if javassist-3.15 and javassist-3.18 conflicts with each other, you should remove them and pick the javassist JAR more
suitable for both libraries (jersey and hibernate).
As thumb rule, I tend to pick the newest library (the one with greater version), but this scheme not always work...

JAXB package-info.java declarations are ignored in separate maven modul

I use package-info.java to specify #XmlAccessorType(XmlAccessType.NONE) and some xml java adapters using #XmlJavaTypeAdapters. Model objects (with JAXB annotations) are placed in separate maven module shared by other modules. The configuration in package-info.java is not discovered if model objects are in separate maven module. If I move for testing purposes model objects to same maven module everything is OK. I think separate maven module can be considered equivalent to 3rd party lib from JAXBContext point of view. I use JDK1.7 JAXB reference implementation. Any ideas how configuration may differ?
I also encounter this problem, in my case qualified/unqualified property from package-info.java was ignored. I managed to find two way to workaround this:
like Pavla wrote, copy all JAXB classes with package-info.java locally
include module as a dependency with compile scope (which gives similar result that classes are in module. In my case I created separate jar lib with JAXB classes)
I also spotted that it do not work only in case of creating WebServices (creating object and sending to WS works fine in different modules).
I am using Jbossas7.1.1 and cxf 2.4.6. In the time of registering service Jboss created wsdl from JAXB (in my case path /opt/jboss/jboss-as-7.1.1.Final/standalone/data/wsdl/module.war/SubmitMessage.wsdl). In local setting file is generated properly.
Any ideas why creating WS behaves like this?
I hit this issue recently and the actual problem (with Java 8, i.e. no Java modules involved) was that I had on the classpath two *.jar files which both contained the same package - in one JAR, there was package-info.class with JAXB annotations and in the other one, there wasn't.
In that case, I guess that if package-info.class file is discovered depends on the classpath ordering (which is very brittle and only semi-deterministic).

Set the JAXB context factory initialization class to be used

I have updated our projects (Java EE based running on Websphere 8.5) to use a new release of a company internal framework (and Ejb 3.x deployment descriptors rather than the 2.x ones). Since then my integration Tests fail with the following exception:
[java.lang.ClassNotFoundException: com.ibm.xml.xlxp2.jaxb.JAXBContextFactory]
I can build the application with the previous framework release and everything works fine.
While debugging i noticed that within the ContextFinder (javax.xml.bind) there are two different behaviours:
Previous Version (Everything works just fine): None of the different places brings up a factory class so the default factory class gets loaded which is com.sun.xml.internal.bind.v2.ContextFactory (defined as String constant within the class).
Upgraded Version (ClassNotFound): There is a resource "META-INF/services/javax.xml.bind.JAXBContext" beeing loaded successfully and the first line read makes the ContextFinder attempt to load "com.ibm.xml.xlxp2.jaxb.JAXBContextFactory" which causes the error.
I now have two questions:
What sort is that resource? Because inside our EAR there is two WARs and none of those two contains a folder services in its
META-INF directory.
Where could that value be from otherwise? Because a filediff showed me no new or changed properties files.
No need to say i am going to read all about the JAXB configuration possibilities but if you have first insights on what could have gone wrong or help me out with that resource (is it a real file i have to look for?) id appreciate a lot. Many Thanks!
EDIT (according to comments Input/Questions):
Out of curiosity, does your framework include JAXB JARs? Did the old version of your framework include jaxb.properties?
Indeed (i am a bit surprised) the framework has a customized eclipselink-2.4.1-.jar inside the EAR that includes both a JAXB implementation and a jaxb.properties file that shows the following entry in both versions (the one that finds the factory as well as in the one that throws the exception):
javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
I think this is has nothing to do with the current issue since the jar stayed exactly the same in both EARs (the one that runs/ the one with the expection)
It's also not clear to me why the old version of the framework was ever selecting the com.sun implementation
There is a class javax.xml.bind.ContextFinder which is responsible for initializing the JAXBContextFactory. This class searches various placess for the existance of a jaxb.properties file or a "javax.xml.bind.JAXBContext" resource. If ALL of those places dont show up which Context Factory to use there is a deault factory loaded which is hardcoded in the class itself:
private static final String PLATFORM_DEFAULT_FACTORY_CLASS = "com.sun.xml.internal.bind.v2.ContextFactory";
Now back to my problem:
Building with the previous version of the framework (and EJB 2.x deployment descriptors) everything works fine). While debugging i can see that there is no configuration found and thatfore above mentioned default factory is loaded.
Building with the new version of the framework (and EJB 3.x deployment descriptors so i can deploy) ONLY A TESTCASE fails but the rest of the functionality works (like i can send requests to our webservice and they dont trigger any errors). While debugging i can see that there is a configuration found. This resource is named "META-INF/services/javax.xml.bind.JAXBContext". Here are the most important lines of how this resource leads to the attempt to load 'com.ibm.xml.xlxp2.jaxb.JAXBContextFactory' which then throws the ClassNotFoundException. This is simplified source of the mentioned javax.xml.bind.ContextFinder class:
URL resourceURL = ClassLoader.getSystemResource("META-INF/services/javax.xml.bind.JAXBContext");
BufferedReader r = new BufferedReader(new InputStreamReader(resourceURL.openStream(), "UTF-8"));
String factoryClassName = r.readLine().trim();
The field factoryClassName now has the value 'com.ibm.xml.xlxp2.jaxb.JAXBContextFactory'
Because this has become a super lager question i will also add a bounty :)
I will work the entire day on this and let you know if there is any news.
Update/ Solution
This question has been solved. The original problem has occured because misconfiguration of complexly build multi model maven projects which one dependency used a updated version of a customized eclipse link jar that contained a definition for a JAXBFactory not available in the component where the error occured. Setting the JAXB context factory in most cases is configured with a jaxb.propertie file or JAXBContext file that contains the same definition. Detailed loading process of the appropriate JAXBContextFactory happens in javax.xml.bind.ContextFinder.
The error has not yet been solved (during the fact over 4 major EE/SE Applications lead to the error) and there is no general answer but that defined JAXBContextFactorys must exist in your classpath (wow what a wonder...) so you either have a that ClassNotFound Error because youre missing resources (well thats the acctual cause) or because you have a wrong JAXBContextFactory defined in any of the above mentioned propertie files which contain a definition according to the below answer.
Very many thanks for your great comments and support, i realy appreciate!
You can include a jaxb.properties file in the same package as your domain model to specify the JAXB (JSR-222) implementation you wish to use. For example it would look like the following to specify EclipseLink MOXy as your JAXB provider.
javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
For More Information
http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html
Another quick and dirty solution (a workaround, really) that worked for me is to explicitly include a JAXB implementation to the maven build. For example
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.2.7</version>
</dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-impl</artifactId>
<version>2.2.7</version>
</dependency>
Note that this adds a somehow unnecessary dependency to your build, as JAXB obviously already is part of each JRE >= version 6.
Most likely this will only work when the WAS classloader is set to parent last.

Categories