How to expose a property in MBean description - java

The following managed operation exists in the project:
#ManagedOperation(description = "Some description")
#ManagedOperationParameters({
#ManagedOperationParameter(name = "key", description = "Some description"),
})
public void foo(String key) {
// some logic
}
Also there is a property which can be used in Spring context by surrounding it with dollar sign and square brackets:
"${some.property.key}"
Is it possible to use the value of aforementioned property key in the managed operation annotation description? Something like:
#ManagedOperationParameter(name = "key",
description = "Some description, please note that the key is ${some.property.key}")

Not out-of-the-box, but it's pretty easy to customize...
public class CustomAttributeSource extends AnnotationJmxAttributeSource implements EmbeddedValueResolverAware {
private StringValueResolver embeddedValueResolver;
#Override
public void setEmbeddedValueResolver(StringValueResolver resolver) {
this.embeddedValueResolver = resolver;
}
#Override
public ManagedAttribute getManagedAttribute(Method method) throws InvalidMetadataException {
ManagedAttribute managedAttribute = super.getManagedAttribute(method);
if (this.embeddedValueResolver != null) {
managedAttribute
.setDescription(this.embeddedValueResolver.resolveStringValue(managedAttribute.getDescription()));
}
return managedAttribute;
}
#Override
public ManagedOperation getManagedOperation(Method method) throws InvalidMetadataException {
ManagedOperation managedOperation = super.getManagedOperation(method);
if (this.embeddedValueResolver != null) {
managedOperation
.setDescription(this.embeddedValueResolver.resolveStringValue(managedOperation.getDescription()));
}
return managedOperation;
}
}
Then...
<bean class="org.springframework.jmx.export.annotation.AnnotationMBeanExporter">
<property name="assembler">
<bean class="org.springframework.jmx.export.assembler.MetadataMBeanInfoAssembler">
<property name="attributeSource">
<bean class="foo.CustomAttributeSource" />
</property>
</bean>
</property>
</bean>

Related

Mybatis null configuration object inside custom TypeHandler

I was writing a custom JsonTypeHandler, code works fine but I wanted access to configuration field of extended BaseTypeHandler which seems always null.
Why is it null? Am I missing something here?
Custom JsonTypeHandler: You may ignore methods inside below code.
#MappedTypes({ JsonObject.class })
#MappedJdbcTypes({JdbcType.NVARCHAR})
public class JsonTypeHandler extends BaseTypeHandler<JsonObject> {
private String sqlDialect;
#Override
public void setNonNullParameter(PreparedStatement ps, int i, JsonObject parameter, JdbcType jdbcType)
throws SQLException {
String parameterAsString = new Gson().toJson(parameter, JsonObject.class);
ps.setString(i, parameterAsString);
}
#Override
public JsonObject getNullableResult(ResultSet rs, String columnName) throws SQLException {
String sqlJson = rs.getString(columnName);
if (null != sqlJson) {
return new Gson().fromJson(sqlJson, JsonObject.class);
}
return null;
}
#Override
public JsonObject getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
String sqlJson = rs.getString(columnIndex);
if (null != sqlJson) {
return new Gson().fromJson(sqlJson, JsonObject.class);
}
return null;
}
#Override
public JsonObject getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
String sqlJson = cs.getString(columnIndex);
if (null != sqlJson) {
return new Gson().fromJson(sqlJson, JsonObject.class);
}
return null;
}
}
mybatis-config.xml
<configuration>
<settings>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="jdbcTypeForNull" value="NULL"/>
</settings>
<typeHandlers>
<typeHandler handler="com.dummy.JsonTypeHandler" javaType="com.google.gson.JsonObject"/>
</typeHandlers>
<databaseIdProvider type="DB_VENDOR">
<property name="PostgreSql" value="postgres"></property>
</databaseIdProvider>
<mappers>
<!--mappers-->
</mappers>
</configuration>
spring-context.xml
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="configLocation"
value="classpath:com/dummy/mybatis/mybatis-config.xml"/>
<property name="dataSource" ref="postgresDataSource"/>
</bean>
Versions:
dependency 'org.mybatis:mybatis:3.4.1'
mavenBom 'org.springframework:spring-framework-bom:5.2.6.RELEASE'
My intention is to get databaseId from configuration object inside BaseTypeHandler. Hope snippets are reproducible.
As explained in this issue, the field BaseTypeHandler.configuration is added by mistake and is planned to be removed.
You can register an instance of the type handler after setting necessary properties instead of registering the Class.
It would look something like this:
JsonTypeHandler handler = new JsonTypeHandler(configuration);
configuration.getTypeHandlerRegistry().register(..., handler, ...);

Application context not loading after Spring 4 upgrade

I'm in the process of upgrading the spring framework version used in our webapp from 3.1.4 to 4.1.8. With the new Spring version, A few of our unit tests are failing because #Autowired is no longer working. This is one of the failing tests:
#ContextConfiguration(locations={"/math-application-context.xml"})
public class MathematicaMathServiceTest extends JavaMathServiceTest{
#Autowired
private KernelLinkPool mathematicalKernelPool;
protected static String originalServiceType = System.getProperty("calculation.math.service.type");
#AfterClass
public static void unsetMathServiceType(){
System.clearProperty("calculation.math.service.type");
}
#BeforeClass
public static void setMathServiceType(){
System.setProperty("calculation.math.service.type","Mathematica");
}
#Test
public void testMathematicaService() throws Exception{
try {
acquireKernelAndExecute(0);
Assert.assertEquals(0, mathematicalKernelPool.getBorrowingThreadsCount());
} catch(UnsatisfiedLinkError e) {
System.out.println("Mathematica not installed. Skipping test");
}catch(Exception ex){
if (!ExceptionFormatter.hasCause(ex, MathServiceNotConfiguredException.class)){throw ex;}
if (System.getProperty(MathService.SERVICE_CONFIGURED_SYSTEM_VARIABLE) != null){
throw ex;
}
logger.error("Cannot execute test. Math service is not configured");
}
}
}
This is the KernelLinkPool class:
public class KernelLinkPool extends GenericObjectPool implements InitializingBean{
private static final int RETRY_TIMEOUT_MS = 5000;
private static final long STARTUP_WAIT_TIME_MS = 10000;
private boolean mathematicaConfigured = true;
private PoolableObjectFactory factory;
// ensures that multiple requests from the same thread will be given the same KernelLink object
private static ThreadLocal<KernelLink> threadBoundKernel = new ThreadLocal<KernelLink>();
// holds the number of requests issued on each thread
private static ThreadLocal<Integer> callDepth = new ThreadLocal<Integer>();
private long maxBorrowWait;
private Integer maxKernels;
private boolean releaseLicenseOnReturn;
private Logger logger = LoggerFactory.getLogger(this.getClass());
// (used only for unit testing at this point)
private Map<String,Integer> borrowingThreads = new ConcurrentHashMap<String,Integer>();
public KernelLinkPool(PoolableObjectFactory factory) {
super(factory);
this.factory = factory;
this.setMaxWait(maxBorrowWait);
}
#Override
public Object borrowObject() throws Exception{
return borrowObject(this.maxBorrowWait);
}
public Object borrowObject(long waitTime) throws Exception {
long starttime = System.currentTimeMillis();
if (!mathematicaConfigured){
throw new MathServiceNotConfiguredException();
}
try{
if (callDepth.get() == null){
callDepth.set(1);
}else{
callDepth.set(callDepth.get()+1);
}
KernelLink link = null;
if (threadBoundKernel.get() != null){
link = threadBoundKernel.get();
}else{
//obtain kernelLink from object pool
//retry when borrowObject fail until
//maxBorrowWait is reached
while(true){
try{
logger.debug("Borrowing MathKernel from object pool");
link = (KernelLink) super.borrowObject();
break;
}catch(KernelLinkCreationException ex){
long timeElapsed = System.currentTimeMillis() - starttime;
logger.info("Failed to borrow MathKernel. Time elapsed [" + timeElapsed + "] ms", ex);
if(timeElapsed >= waitTime){
logger.info("Retry timeout reached");
throw ex;
}
Thread.sleep(RETRY_TIMEOUT_MS);
}
}
logger.debug("borrowed [" + link + "]");
threadBoundKernel.set(link);
}
borrowingThreads.put(Thread.currentThread().getName(),callDepth.get());
return link;
}catch(Exception ex){
logger.error("Failed to acquire Mathematica kernel. Borrowing threads [" + borrowingThreads + "]");
throw ex;
}
}
public void returnObject(Object obj) throws Exception {
callDepth.set(callDepth.get()-1);
if (callDepth.get() <= 0){
threadBoundKernel.set(null);
borrowingThreads.remove(Thread.currentThread().getName());
if (releaseLicenseOnReturn){
// will destroy obj
super.invalidateObject(obj);
}
else{
// will park obj in the pool of idle objects
super.returnObject(obj);
}
}else{
borrowingThreads.put(Thread.currentThread().getName(),callDepth.get());
}
}
#Override
public void afterPropertiesSet() throws Exception {
try{
if (maxKernels == 0){
List<KernelLink> links = new ArrayList<KernelLink>();
while (true){
try{
links.add((KernelLink)factory.makeObject());
}catch(KernelLinkCreationException ex){
break;
}
}
if(links.isEmpty()){
logger.warn("No available Mathematica license!");
mathematicaConfigured = false;
return;
}
for (KernelLink link : links){
factory.destroyObject(link);
}
logger.info("Detected number of available Mathematica license = [" + links.size() + "]");
setMaxActive(links.size());
setMaxIdle(links.size());
}else{
if(maxKernels < 0){
logger.info("Set number of Mathematica license to no limit");
}else{
logger.info("Set number of Mathematica license to [" + maxKernels + "]");
}
setMaxActive(maxKernels);
setMaxIdle(maxKernels);
}
Object ob = borrowObject(STARTUP_WAIT_TIME_MS);
returnObject(ob);
mathematicaConfigured = true;
}catch(Throwable ex){
logger.warn("Mathematica kernel pool could not be configured: ", ex.getMessage());
mathematicaConfigured = false;
}
}
public int getBorrowingThreadsCount() {
return borrowingThreads.size();
}
public Integer getMaxKernels() {
return maxKernels;
}
public void setMaxKernels(Integer maxKernels) {
this.maxKernels = maxKernels;
}
public boolean isMathematicaConfigured(){
return mathematicaConfigured;
}
public boolean isReleaseLicenseOnReturn() {
return releaseLicenseOnReturn;
}
public void setReleaseLicenseOnReturn(boolean releaseLicenseOnReturn) {
this.releaseLicenseOnReturn = releaseLicenseOnReturn;
}
public long getMaxBorrowWait() {
return maxBorrowWait;
}
public void setMaxBorrowWait(long maxBorrowWait) {
this.maxBorrowWait = maxBorrowWait;
}
}
The tests are failing with this exception:
org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.etse.math.wolfram.KernelLinkPool] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
This is the math-application-context file:
<?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-3.1.xsd">
<beans profile="unitTest,integratedTest,activeServer">
<bean class="org.springframework.jmx.export.MBeanExporter"
lazy-init="false">
<property name="registrationBehaviorName" value="REGISTRATION_IGNORE_EXISTING" />
<property name="beans">
<map>
<entry key="etse.math:name=MathematicalKernelFactory"
value-ref="mathematicalKernelFactory" />
<entry key="etse.math:name=MathematicalKernelPool" value-ref="mathematicalKernelPool" />
</map>
</property>
</bean>
<bean id="mathService" class="com.etse.math.MathServiceFactoryBean">
<property name="mathServiceType" value="${calculation.math.service.type}"/>
<property name="mathematicaService" ref="mathematicaService"/>
</bean>
<bean id="mathematicaService" class="com.etse.math.wolfram.MathematicaService">
<property name="kernelPool" ref="mathematicalKernelPool" />
<property name="minParallelizationSize" value="${calculation.mathematica.kernel.parallel.batch.size}" />
</bean>
<bean id="mathematicalKernelPool" class="com.etse.math.wolfram.KernelLinkPool"
destroy-method="close">
<constructor-arg ref="mathematicalKernelFactory" />
<property name="maxKernels" value="${calculation.mathematica.max.kernels}" />
<property name="maxBorrowWait"
value="${calculation.mathematica.kernel.borrow.max.wait}" />
<property name="releaseLicenseOnReturn"
value="${calculation.mathematica.kernel.release.license.on.return}" />
</bean>
<bean id="mathematicalKernelFactory" class="com.etse.math.wolfram.KernelLinkFactory">
<property name="debugPackets" value="false" />
<property name="linkMode" value="launch" />
<property name="mathematicaKernelLocation" value="${calculation.mathematica.kernel.location}" />
<property name="mathematicaLibraryLocation" value="${calculation.mathematica.library.location}" />
<property name="mathematicaAddOnsDirectory" value="${calculation.mathematica.addons.directory}" />
<property name="linkProtocol" value="sharedMemory" />
</bean>
</beans>
<beans profile="passiveServer,thickClient,tools">
<bean id="mathService" class="com.etse.math.DummyMathService"/>
</beans>
I also tried using the application context to load the bean, but that failed with the following exception:
org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'mathematicalKernelPool' is defined
If I remove the autowired field, the test fails with a NoSuchBeanDefinitionException for another bean (mathService) that is loaded via the application context in a super class. So it appears that the application context from math-application-context is not loaded for some reason. Any idea of what could be happening here? Thank you.
UPDATE:
I took a look at the beans defined in the application context and confirmed that none of the beans defined in math-application-context are present. The application context contains only beans defined in another context file loaded by the super class. Why would it fail to load math-application-context?
At this point I would honestly get rid of the XML config and go total annotation/code based. Create a Config class and have it create any beans you need to be autowired.
It was a profile issue. The super class to the test was using:
#ProfileValueSourceConfiguration(TestProfileValueSource.class)
to set the profile, but it was not working. After removing that annotation I added:
#ActiveProfiles(resolver=TestProfileValueSource.class) and now its working again.

Spring SpEL - How to use SpEL to parse a message

I'm trying to use spring SpEL to parse a message received in UDP.
Just to understand how to use Spring SpEL I have written this:
context.xml:
<bean id="message" class="springsimulator.Message">
<property name="strMessage" value="$TEST,11,22,33"/>
</bean>
<bean id="nmeamessage" class="springsimulator.NMEAMessage">
<property name="fields" value="#{message.strMessage.split(',')}"/>
</bean>
<bean id="parser" class="springsimulator.SPELParser">
<property name="values">
<map>
<entry key="val1" value="#{nmeamessage.fields[1]}"/>
<entry key="val2" value="#{nmeamessage.fields[2]}"/>
</map>
</property>
</bean>
Message.java:
public class Message {
public String strMessage;
public void setStrMessage(String strMessage) {
this.strMessage = strMessage;
}
}
NMEAMessage:
public class NMEAMessage {
public String[] fields;
public void setFields(String[] fields) {
this.fields = fields;
}
}
Parser:
public class Parser {
Map<String,String> values;
public void setValues(Map<String, String> values) {
this.values = values;
}
public String toString() {
String message = "";
for (Entry<String, String> entry : values.entrySet()) {
message += entry.getKey() + ":" + entry.getValue() + "\n";
}
return message;
}
}
main:
ExpressionParser parser = new SpelExpressionParser();
ConfigurableApplicationContext context = new ClassPathXmlApplicationContext("context.xml");
Parser myparser = (Parser) context.getBean("parser");
System.out.println(myparser);
This works, I have parsed my message.
But now, I want to evaluate the SpEL expression in a loop each time I receive a message
while(running) {
socket.receive(message)
//Split message to get fields
//set fields into a map
}
Is there a proper way to do this using SpEL and context.xml ?
Thanks
To parse SpEL expressions at runtime, do something like this:
// Setup the SpEL parser: do this once
SpelParserConfiguration spelParserConfiguration = new SpelParserConfiguration(SpelCompilerMode.IMMEDIATE, getClass().getClassLoader());
ExpressionParser expressionParser = new SpelExpressionParser(spelParserConfiguration);
StandardEvaluationContext evaluationContext = new StandardEvaluationContext();
// Parse (compile) the expression: try to do this once
Expression expression = expressionParser.parseExpression(unevaluatedExpression)
// Then within the loop ...
// Supply context, like the value of your namespace
evaluationContext.setVariable(variableName, value);
// Evaluate an expression as many times as you like
Object result = expression.getValue(evaluationContext);

How do I register a Hibernate saveUpdate listener when using Spring's AnnoationSessionFactoryBean

I'm trying to add a custom save event listener for hibernate. My goal is to have hibernate set the last update and created timestamp values on certain entities. I have read from other posts that JPA annotations will do it but if you are using a Hibernate Session then you need to extend DefaultSaveOrUpdateEventListener. I did this and it hasn't worked. Every example I see is using a hibernate config file. My sessionFactory is configured with Spring.
<bean id="mySessionFactory"
class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="dataSource">
<ref bean="myDataSource"/>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
<prop key="hibernate.show_sql">true</prop>
</props>
</property>
<property name="packagesToScan">
<list>
<value>com.mypackages</value>
</list>
</property>
<property name="eventListeners">
<map>
<entry key="save-update">
<ref local="saveEventListener" />
</entry>
</map>
</property>
</bean>
<bean id="saveEventListener" class="com.mypackage.event.SaveOrUpdateDateListener" />
I set a breakpoint and it doesn't go through the listener. My last updated and created fields are not being set in the database.
I have worked with the similar issue and finally resolved.
I have a base pojo with properties for Auditing , Every entity extends this base pojo. on call to save or update, the methods of Event Listeners get triggered where I update the entity with auditing information.
#Component
public class EntitySaveListener implements PersistEventListener, MergeEventListener,
PreInsertEventListener {
private static final long serialVersionUID = 1L;
static final Logger logger = LoggerFactory
.getLogger(EntitySaveListener.class);
#Autowired
private LocalEntityManagerFactoryBean entityManagerFactory;
public EntitySaveListener() {
logger.info("EntitySaveListener created");
}
public void onPersist(PersistEvent event) throws HibernateException {
if (SecurityContextHolder.getContext() != null
&& SecurityContextHolder.getContext().getAuthentication() != null) {
Object principal = SecurityContextHolder.getContext()
.getAuthentication().getPrincipal();
if (principal != null && principal instanceof V2VUserDetails) {
User user = ((V2VUserDetails) principal).getUser();
if (event.getObject() instanceof ModificationTracker &&
user != null) {
ModificationTracker entity = (ModificationTracker) event.getObject();
entity.setCreatedDate(new Date());
entity.setCreatedBy(user);
entity.setLastUpdated(new Date());
entity.setLastUpdatedBy(user);
}
}
}
}
#SuppressWarnings("rawtypes")
#Override
public void onPersist(PersistEvent event, Map arg1)
throws HibernateException {
// TODO Auto-generated method stub
}
#Override
public void onMerge(MergeEvent event) throws HibernateException {
if (SecurityContextHolder.getContext() != null
&& SecurityContextHolder.getContext().getAuthentication() != null) {
Object principal = SecurityContextHolder.getContext()
.getAuthentication().getPrincipal();
if (principal != null && principal instanceof V2VUserDetails) {
User user = ((V2VUserDetails) principal).getUser();
if (event.getEntity() instanceof ModificationTracker
&& user != null) {
ModificationTracker entity = (ModificationTracker) event
.getEntity();
entity.setLastUpdated(new Date());
entity.setLastUpdatedBy(user);
}
}
}
}
#SuppressWarnings("rawtypes")
#Override
public void onMerge(MergeEvent arg0, Map arg1) throws HibernateException {
// TODO Auto-generated method stub
}
#Override
public boolean onPreInsert(PreInsertEvent arg0) {
// TODO Auto-generated method stub
return false;
}
}

SpringPwdDecrypter how to call it from spring framework as per below

I have the following in my beans spring XML configuration file. here they are trying to get the password from a properties file run time and decrypt the password by calling the class "SpringPwdDecrypterUtil" which extends spring FactoryBean.
Question: in properties file where they mentioned with user and password. But the password is decrypted one which will be decrpted by the below configuration. I want to know how they(existing prod applicaiton) would have generated the password(decrpyted) and placed in the text file !
bean.xml
<property name="password">
<bean class="com.cname.SpringPwdDecrypterUtil">
<property name="password">
<value>${db.password}</value>
</property>
</bean>
</property>
--SpringPwdDecrypterUtil.java
package com.cname;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.FactoryBean;
public class SpringPwdDecrypterUtil
implements FactoryBean
{
private String password = null;
private String key = null;
public void setPassword(String password) {
this.password = password;
}
public void setKey(String key) {
this.key = key;
}
public Object getObject() {
if (StringUtils.isNotEmpty(this.key)) {
return CryptoUtils.decryptAndEncodeBase64(this.key, this.password);
}
return CryptoUtils.decryptAndEncodeBase64(this.password);
}
public Class getObjectType() {
return String.class;
}
public boolean isSingleton() {
return true;
}
}

Categories