Exporting Spring #Bean objects using JMX - java

I'm using Spring's #Configuration, and I noticed that a #Bean does not get registered using JMX.
The bean is wired as
#Bean
protected CountingHttpInterceptor countingHttpInterceptor() {
return new CountingHttpInterceptor();
}
and the class definition is
#ManagedResource
public class CountingHttpInterceptor implements HttpRequestInterceptor, HttpResponseInterceptor { /* code here*/ }
This #Configuration file is processed after the main , XML-based, application context is built, and does not have the chance to take part in the discovery process which is activated using XML bean definitions ( org.springframework.jmx.export.annotation.AnnotationJmxAttributeSource and frieds ).
How can I JMX-enable the beans from the #Configuration file?
Update: the xml configuration
<bean id="jmxExporter" class="org.springframework.jmx.export.MBeanExporter">
<property name="assembler" ref="assembler"/>
<property name="namingStrategy" ref="namingStrategy"/>
<property name="autodetect" value="true"/>
</bean>
<bean id="jmxAttributeSource" class="org.springframework.jmx.export.annotation.AnnotationJmxAttributeSource"/>
<bean id="assembler" class="org.springframework.jmx.export.assembler.MetadataMBeanInfoAssembler">
<property name="attributeSource" ref="jmxAttributeSource"/>
</bean>
<bean id="namingStrategy" class="org.springframework.jmx.export.naming.MetadataNamingStrategy">
<property name="attributeSource" ref="jmxAttributeSource"/>
</bean>

Despite the temptations of the #Configuration- based approach, some things remain better done with XML config. In particular, the namespace-based config such as <context:mbean-export>. These essentially represent "macros" consisting of complex arrangements of interacting objects.
Now, you could replicate this logic in your #Configuration class, but it's really more trouble than it's worth. Instead, I suggest putting such system-level stuff into XML, and importing it from your #Configuration class:
#ImportResource("/path/to/beans.xml")
public class MyConfig {
#Bean
protected CountingHttpInterceptor countingHttpInterceptor() {
return new CountingHttpInterceptor();
}
}
and then in /path/to/beans.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
">
<context:mbean-export/>
</beans>

Everything you have is correct. In your #Configuration class you need to add one more annotation to export your MBeans, #EnableMBeanExport.
Your configuration class would look something like this...
#Configuration
#EnableMBeanExport
public class SpringConfiguration {
#Bean
protected CountingHttpInterceptor countingHttpInterceptor() {
return new CountingHttpInterceptor();
}
}

Related

Why is autowired properties file null?

I autowired some beans from my testConfig.xml, and it works fine but when I want to autowire a properties file it gives null, the properties file is near of my xml in selenium folder. My testConfig.xml looks like this :
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.2.xsd" >
<bean id="base" class="java.net.URI">
<constructor-arg value="http://localhost:8080/MySite" />
</bean>
<bean id="siteBase" class="java.net.URI">
<constructor-arg value="http://localhost:8080/MySite/site" />
</bean>
<bean id="adminBase" class="java.net.URI">
<constructor-arg value="http://localhost:8080/MySite/admin" />
</bean>
<bean id="firefoxDriver" class="org.openqa.selenium.firefox.FirefoxDriver" destroy-method="quit"/>
<context:annotation-config/>
<util:properties id="seleniumSelectors" location="classpath:selenium/selenium-selectors.properties"/>
</beans>
and here I want to autowire it:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "/testConfig.xml" })
public abstract class AbstractUITest extends TestCase{
#Autowired
protected URI base;
#Autowired
protected URI siteBase;
#Autowired
protected URI adminBase;
#Autowired
protected WebDriver firefoxDriver;
#Autowired
#Qualifier("seleniumSelectors")
protected Properties selectors;
protected By getBySelectorKey(String key){
if(key.endsWith(".xpath")){
return By.xpath(selenium.getProperty(key));
}
}
Just the selectors object is null and I don`t know why, any suggest?
UPDATE 2:
I made a mistake everithing is null when the selector is null.I cheked Initialization in my test runs before autowired, somehow this
public class AdminCandidatesPageUITest extends AbstractAdminUITest {
private By COMPONENT_QUERY_TEXTBOX_EMAIL = getBySelectorKey("admin.candidates.edit.textbox.email.xpath");
private By COMPONENT_QUERY_TEXTBOX_EMAIL_ERROR = getBySelectorKey("admin.candidates.edit.textbox.email.errors.xpath");
should run after seleniumSelector is autowired. Any suggest?
Try to move tag <context:annotation-config/> one line down. I am not sure but it seems that wiring triggered by this tag happens before creating of seleniumSelectors, so it is still null at the moment.
BTW if you mark your properties as #Required the context will fail to start and throw exception because one of the properties is not wired. This is probably preferable than failing later at runtime.

Spring Conditional Bean creation based on another bean

I'm trying to find a way to only create a bean if the value of another bean/property is true, using Spring 3.2 and XML configurations.
<bean id="isEnabled" class="java.lang.Boolean">
<bean factory-bean="configurationService" factory-method="getBooleanValue">
<constructor-arg index="0">
<util:constant static-field="org.code.ConfigurationKeys.ENABLED"/>
</constructor-arg>
</bean>
</bean>
<if isEnabled=true>
..... create some beans
</if>
I've seen some slightly similar examples using Spring EL but nothing that does this exactly...
You can use profiles.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd" >
<!-- here goes common beans -->
<beans profile="Prof_1">
<import resource="./first-config.xml" />
</beans>
<beans profile="Prof_2">
<import resource="./second-config.xml" />
</beans>
</beans>
One can activate multiple profile at same time or choose not to activate any. To activate there are multiple ways but to programtaically do this we need to add a initializer in web.xml
<context-param>
<param-name>contextInitializerClasses</param-name>
<param-value>com.test.MyCustomInitializer</param-value>
</context-param>
MyCustomInitializer looks like following
public class MyCustomInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
#Override
public void initialize(ConfigurableApplicationContext applicationContext) {
try {
String activeProf;
// some logic to either read file/env variable/setting to determine which profile to activate
applicationContext.getEnvironment().setActiveProfiles( activeProf );
} catch (IOException e) {
e.printStackTrace();
}
}
}
Why you don't use the factory to create the objects when are required and make them lazy.
<bean id="second "class="xxx.xxx.Class" lazy-init="true" scope="prototype"/>
There is no way to introduce if statement within the spring configuration, profiles could work but at more related to environment not a programmatic configuration.

How can i add a sub Tag to an Xml file already defined using Java?

I have an XML file that contains the configration of a Spring project and I want to dynamically add a new bean. I have to modify the initial xML file and add my new bean definition:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="config"
class="myclass">
<property name="configXml">
<value>config.xml</value>
</property>
</bean>
<-- here a want to add a new bean definition <bean>....</bean> -->
</beans>
Has anyone got an idea?
I'm still not 100% what you mean, but here's two cases to try.
If you want to specify properties of a bean based on some derived value you can use the Spring Expression Language
http://static.springsource.org/spring/docs/3.0.x/reference/expressions.html
<bean id="someBean" class="example.SomeBean">
<property name="foo" value="#{config.whatever}"/>
</bean>
If you want to something more complex you can construct a bean programatically using a factory bean
<bean id="someBean" class="example.SomeBeanFactory">
<property name="config" ref="config"/>
</bean>
with something like
class SomeBeanFactory implements FactoryBean<SomeBean> {
public void setConfig(MyClass config) { ... }
public SomeBean getObject() {
...
}
}

Converting XML Spring config for JMX to Java config

I have a small test app for exposing a "Bean" to JMX using Spring. It uses an XML based config and everything works fine:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd">
<context:component-scan base-package="com.dmclaughlin.spring" />
<context:property-placeholder location="classpath:test.properties"/>
<bean id="SimpleJmxController" class="com.dmclaughlin.spring.jmx.SimpleJmxBean">
<property name="activated" value="${some.activated}"/>
</bean>
<!-- Spring JMX -->
<bean id="exporter" class="org.springframework.jmx.export.MBeanExporter" lazy-init="false">
<property name="autodetect" value="true"></property>
<property name="namingStrategy" ref="namingStrategy"></property>
<property name="assembler" ref="assembler"></property>
</bean>
<bean id="attributeSource"
class="org.springframework.jmx.export.annotation.AnnotationJmxAttributeSource"/>
<bean id="assembler"
class="org.springframework.jmx.export.assembler.MetadataMBeanInfoAssembler">
<property name="attributeSource" ref="attributeSource"/>
</bean>
<bean id="namingStrategy"
class="org.springframework.jmx.export.naming.MetadataNamingStrategy">
<property name="attributeSource" ref="attributeSource"/>
</bean>
But the application I need to add this functionality to, uses #Configuration style, and I'm trying to convert the above XML to work. I added something like this:
#Bean
public MetadataNamingStrategy getNamingStrategy() {
MetadataNamingStrategy strategy = new MetadataNamingStrategy();
strategy.setAttributeSource(new AnnotationJmxAttributeSource());
return strategy;
}
#Bean
public MetadataMBeanInfoAssembler getMbeanInfoAssembler() {
return new MetadataMBeanInfoAssembler(new AnnotationJmxAttributeSource());
}
#Bean
public MBeanExporter getExporter() {
MBeanExporter exporter = new MBeanExporter();
exporter.setAutodetect(true);
exporter.setNamingStrategy(getNamingStrategy());
exporter.setAssembler(getMbeanInfoAssembler());
return exporter;
}
And everything compiles, but when I load up JConsole my Bean annotated with #ManagedResource and #ManagedAttribute isn't exposed. Am I missing something simple here?
Edit: the answer below didn't fix my problem (the problem was I was testing my XML in a Tomcat environment, but testing my non-XML config in a standalone application, which meant there was no JMXServer present.. d'oh), but it did help me simplify once I debugged what I messed up.
For me it was enough to add:
#Bean
public AnnotationMBeanExporter annotationMBeanExporter() {
return new AnnotationMBeanExporter();
}
you should configure your mbeanexporter with "eager"
#Bean
#Lazy(false)
public MBeanExporter getExporter() {
...
}
greetings
AccLess

spring autowiring not working

Hi,
I am using spring 3.0 with Quartz in a scheduler class. I have created the application context by
private static final ClassPathXmlApplicationContext applicationContext;
static {
applicationContext = new
ClassPathXmlApplicationContext("config/applicationContext.xml");
}
The problem is that none of the #Autowired beans actually get auto-wired, so I have to manually set dependencies like this:
<bean class="com.spr.service.RegistrationServiceImpl" id="registrationService">
<property name="userService" ref="userService" />
</bean>
Example of where I'm using #Autowired:
public class RegistrationService {
#AutoWired private UserService userService;
// setter for userService;
}
public class UserService{
// methods
}
I also made sure to enable the annotation configuration in my Spring config:
<context:annotation-config/>
<bean id="registrationSevice" class="RegistrationService"/>
<bean id="userService" class="UserService"/>
Why is #Autowired not working for me?
You haven't provided the UserService class source code so I can't be sure about your problem. Looks like the UserService class is missing a 'stereotype' annotation like #Component or #Service. You also have to configure the Spring classpath scanning using the following configuration:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<!-- Add your classes base package here -->
<context:component-scan base-package="your.package"/>
</beans>
Your beans must include one of the Spring stereotype annotations like:
package your.package;
#Service
public class UserService{
}
Atlast i got it resolved by adding the
<context:component-scan base-package="your.package"/>
in my applicationContext.xml. Thank u all for your support.

Categories