Is there a way in Spring to discover a
"plugin"-JAR from the classpath,
and load its applicationContext.xml dynamicaly?
I have achieved a plugin-like system with Spring by following this approach:
Each plug-in must contain a spring-context file with a specific name and package prefix (for example, com.example.myApp.whatever containing plugin.xml, or applicationContext.xml if you prefer).
For the plug-in to be detected in the classpath, the host application should dynamically import all the context files contributed by any jar following the previos scheme. This is achieved with a wildcard-based import in spring config:
<import resource="classpath*:/com/example/myApp/**/plugin.xml" />
Provided that each plug-in defines beans of a known interface (e.g., MyInterface). The host application can define a property of type List <MyInterface> and define the bean as autowire="byType" in order to retrieve all the beans of the MyInterfaceType in a list.
Related
This may be an impossible task, but here goes...
Is it possible to register a spring bean, by (ONLY) adding a jar to the classpath of a spring-boot application?
Scenario: I would like to create a non-intrusive plugin jar, which when imported into a spring-boot project's classpath, will automatically be picked up and provide a service (e.g. via a RestController).
Constraints
I don't want to change or reconfigure the existing spring-boot application (i.e. no additional scan paths or bean config).
I don't have any knowledge of the target spring-boot application's package structure/scan paths.
I guess I was hoping that by default Spring scan's its own package structure (i.e. org.springframework.** looking for the presence of database libs, etc) and I could piggy-back off that - I haven't had any luck (so far).
I've setup an example project in github, to further clarify/illustrate my example and attempts.
** Solution Addendum **
This bit that got it working, was to add the following file, which points to an #Configuration config file...
plugin-poc\src\main\resources\META-INF\spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=org.thirdpartyplugin.PluginConfiguration
I think in such cases you would try to add a spring auto configuration that is annotated with #ConditionalOnClass to be only evaluated if the given class is on the classpath. This class can register the bean and would just be evaluated if the conditional evaluates to true
Here is the relevant part of the spring boot documentation : Creating your own auto-configuration
I'm trying to use Spring annotations in Domino XPages Java classes. Spring works when I'm defining beans in the configuration file. However I fail to use the annotations.
To illustrate the problem, I have created two simple empty classes annotated with #Component annotation - com.geo168.a.B (#Component public class B {}) and com.geo168.a.C (#Component public class C {})
The first one I have created in Eclipse, packed and added to the application in a jar. The second one I add directly in Code/Java section.
I add a component-scan tag in the configuration file: <context:component-scan base-package="com.geo168.a"/> and try to instantiate the classes:
ApplicationContext ctx = new ClassPathXmlApplicationContext(SPRING_CONFIG);
// class defined in the jar, works ok
com.geo168.a.B b = ctx.getBean(com.geo168.a.B.class);
// class defined in Code/Java, throws exception
com.geo168.a.C c = ctx.getBean(com.geo168.a.C.class);
I get an error: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.geo168.a.C] is defined
Spring has found the annotated class only in the JAR file. It works if I add the bean explicitly to the configuration file:
<bean class="com.geo168.a.C"/>
A similar post: Is possible to add annotation #ManagedBean in XPages? seems to address only the particular JSF annotations (that do not work, because they are not implemented).
In the Spring documentation: http://docs.spring.io/spring/docs/3.1.x/spring-framework-reference/html/beans.html#beans-scanning-autodetection I find a note: The scanning of classpath packages requires the presence of corresponding directory entries in the classpath. When you build JARs with Ant, make sure that you do not activate the files-only switch of the JAR task.
Could this be somehow related? I do not know in what form Domino deploys the classes internally.
I created a cxf/spring project with:
mvn archetype:create -DarchetypeGroupId=org.apache.cxf.archetype -DarchetypeArtifactId=cxf-jaxws-javafirst
The resulting project has a HelloWorld.java interface annotated as a #WebService, and a HelloWorldImpl.java annotated with an endpointInterface=mypackage.HelloWorld.
There's a beans.xml file containing:
<jaxws:endpoint
id="helloWorld"
implementor="mypackage.HelloWorldImpl"
address="/HelloWorld" />
If I want to add more services, it looks like I'm expected to keep adding new endpoints in beans.xml. Since the classes are already annotated as #WebService, why can't it auto-discover any new services within some package / search path? Is there some way I can configure this project to do that?
Basically, I'm just trying to avoid repeating myself. The information is already going to be in the annotations so I don't want to have to edit additional files each time I add a service.
If I remove the jaxws:endpoint tag in beans.xml, and then mvn install tomcat:run, localhost:8080/myArtifactId just shows me a page saying there are not services defined.
When you use the CXF you should know that all the endpoints you added are managed by the CXFServlet that uses Spring context to find them out. beans.xml is a Spring context's config file. So if you remove the <jaxws:endpoint/> node from that file your Spring context and hence the CXFServlet will have no way to find your service endpoints.
Does Spring provide any way to output the actual content of the classpath environment variable when it is loading a resource in a context configuration file?
<!-- Import the special context -->
<import resource="classpath:mySpecialApplicationContext.xml"/>
I set the Log4J logging level to ALL for Springframework classes but this value does not appear to be logged by the framework. I am trying to figure out if Spring is loading this from a dependency, and I want to see the classpath setting during application runtime. The application is built by Maven with many dependencies.
If there are two or more mySpecialApplicationContext.xml's in the classpath, which one does Spring use?
Thank you.
You can see the relevant (I think) source code at http://goo.gl/9dK2c
In short:
No, the classpath is not logged
The details of what would be loaded when there is more than one matching resource in the classpath are ClassLoader dependent - the DefaultResourceLoader uses the thread's classloader, but typically, the first one found would be used.
I have a WAR file with the following structure:
The JSF managed bean BusinessObjectTypeListController is located in commons-web-1.0.jar in /WEB-INF/lib and referenced in BusinessObjectTypeListView.xhtml. When I run my web application and I call that view, I get the following error:
javax.servlet.ServletException: /view/common/businessObjectTypeListView.xhtml #34,94 listener="#{businessObjectTypeListController.selectData}": Target Unreachable, identifier 'businessObjectTypeListController' resolved to null
Why isn't the controller class found? It should be in the classpath, is it?
You need to have a JSF 2.0 compliant /META-INF/faces-config.xml file in the commons-web-1.0.jar file in order to get JSF to scan the JAR file for classes with JSF annotations like #ManagedBean and auto-register them.
<?xml version="1.0" encoding="UTF-8"?>
<faces-config
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_2_0.xsd"
version="2.0">
</faces-config>
JSF does namely not scan every class of every single JAR file in the classpath, that would have been too expensive. Only JARs with the above /META-INF/faces-config.xml file will be scanned.
You should also ensure that you do not have the metadata-complete="true" attribute in the <faces-config> declaration of webapp's own /WEB-INF/faces-config.xml file, otherwise JSF will assume that this faces config is complete and therefore won't auto-scan JAR files for annotations.
If none of those conditions are (or can be) met, then you need to manually register the bean as <managed-bean> in webapp's own /WEB-INF/faces-config.xml instead of relying on annotations.
See also chapter 11.4.2 of JSF 2.0 specification (emphasis mine).
11.4.2 Application Startup Behavior
...
This algorithm provides considerable flexibility for developers that are assembling the components of a JSF-based web
application. For example, an application might include one or more custom UIComponent implementations, along with
associated Renderers, so it can declare them in an application resource named “/WEB-INF/faces-config.xml”
with no need to programmatically register them with Application instance. In addition, the application might choose
to include a component library (packaged as a JAR file) that includes a “META-INF/faces-config.xml” resource.
The existence of this resource causes components, renderers, and other JSF implementation classes that are stored in this
library JAR file to be automatically registered, with no action required by the application.
I have same problem with CDI beans in my case.
I have common.jar project where i placed the CDI beans. (without beans.xml)
and
I have webapp.war that contains common.jar in it`s lib and beans.xml.
when i call a cdi bean from jsf, i get it is not reachable exception:/
project structure is created using maven :
- maven-archetype-quickstart for common.jar
- maven-archetype-webapp for webapp.war
I am using eclipse/juno en deploy to Glassfish 3.1.x.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Resolved:
For EJB and JAR packaging you should place the beans.xml in src/main/resources/META-INF/.
For WAR packaging you should place the beans.xml in src/main/webapp/WEB-INF/.
Remember that only .java files should be put in the src/main/java and src/test/java directories. Resources like .xml files should be in src/main/resources.
from topic:
CDI: beans.xml, where do I put you?
In my opinion the class BusinessObjectTypeListController is founded properly but does not instantiated.
How you create the instance of class on a view? If you use a BeanFactory review the config xml files