How to modify JNDI Custom Resources Properties Values on the fly - java

My architecture:
GlassFish Server Open Source Edition 3.1.2.2 (5)
Java EE 6
Eclipse IDE
I created a EJB Timer, which prints a log message:
#Startup
#Singleton
public class ProgrammaticalTimerEJB {
private final Logger log = Logger.getLogger(getClass().getName());
#Resource(name = "properties/mailconfig")
private Properties mailProperties;
#Resource
private TimerService timerService;
#PostConstruct
public void createProgrammaticalTimer() {
log.log(Level.INFO, "ProgrammaticalTimerEJB initialized");
ScheduleExpression everyTenSeconds = new ScheduleExpression().second("*/10").minute("*").hour("*");
timerService.createCalendarTimer(everyTenSeconds, new TimerConfig("passed message " + new Date(), false));
}
#Timeout
public void handleTimer(final Timer timer) {
log.info(new Date().toGMTString() + " Programmatical: " + mailProperties.getProperty("to"));
}
}
This class injects my custom JNDI Resource:
#Resource(name = "properties/mailconfig")
private Properties mailProperties;
Eclipse Console:
INFO: 2 Aug 2013 10:55:40 GMT Programmatical: tim.herold#mylocal.de
INFO: 2 Aug 2013 10:55:50 GMT Programmatical: tim.herold#mylocal.de
INFO: 2 Aug 2013 10:56:00 GMT Programmatical: tim.herold#mylocal.de
Glassfish settings
asadmin> get server.resources.custom-resource.properties/mailconfig.property
server.resources.custom-resource.properties/mailconfig.property.to=tim.herold#mylocal.de
Command get executed successfully.
asadmin>
Now i want to change this property value during application runtime.
Editing it via Adminconsole or Asadmin doesnt work.
Is this possible, or is there an other/better soulution?
Many thanks in advance

There is a possibility to solve your problem:
If an application uses resource injection, the GlassFish Server invokes the JNDI API, and the application is not required to do so.
Once injected the properties are not reloaded and there is no direct posibility to reload the resource by default.
However, it is also possible for an application to locate resources by making direct calls to the JNDI API.
You need to do a JNDI Lookup for your Custom Resoruce, either scheduled or everytime before using the properties. This code worked for me:
#Timeout
public void handleTimer(final Timer timer) throws IOException, NamingException {
Context initialContext = new InitialContext();
mailProperties = (Properties)initialContext.lookup("properties/mailconfig");
log.info(new Date().toGMTString() + " Programmatical: " + mailProperties.getProperty("to"));
}

According to my understanding, the mailProperties resource is injected after the EJB is instantiated by the container which only occurs one time in the lifecycle of bean.
Therefore, futures properties changes are not available to him.
An alternative could be to try to lookup the mailProperties inside #Timeout method.

Related

NullPointerException when deploying to JBoss with #Schedule

I am using the wildfly maven plugin to deploy my Java app to a local jboss server.
I created a class with a #Scheduleannotation like this:
#Startup
#Singleton
#Slf4j
public class ClassName {
#Schedule(hour = "*", minute = "*", second = "*/20", persistent = false)
public void method() {
// Code
log.info("some log");
}
}
Now, when deploying I get the following error:
ERROR [org.jboss.as.ejb3.timer] (EJB default - 1) WFLYEJB0020: Error invoking timeout for timer: [id=879a76e7-b06d-458c-a722-70d8f1d40bc2 timedObjectId=xx-yy-server-1.0-SNAPSHOT.xx-yy-server-1.0-SNAPSHOT.ClassName auto-timer?:true persistent?:false timerService=org.jboss.as.ejb3.timerservice.TimerServiceImpl#42cdd9a initialExpiration=null intervalDuration(in milli sec)=0 nextExpiration=Tue Oct 15 17:21:00 CEST 2019 timerState=IN_TIMEOUT info=null]: javax.ejb.EJBException: java.lang.NullPointerException
Does anyone have any idea what is causing this? I am rather new to scheduling in Java so it might be something stupid, but I could not find anything online.
Thanks!
Hello,
so as this Stackoverflow question mentioned all the Timeout restrictions apply and more than it is recommended using #Stateless EJB instead of #Singleton when you have multiple timers that will be scheduled.
The #Startup annotation is to inform the EJB container to initialize the bean at the startup, you might be misusing it.

Spring autowire a class on server startup

I have a spring application. I am autowiring classes and they are working fine.
For e.g
#Controller
public class SearchController {
#Autowired
private EnvironmentControl envControl;
#Autowired
private SearchControl searchControl;
...
But now i have on server startup class called ScheduleServlet which uses init method to schedule something...
public class SchedulerServlet extends HttpServlet {
#Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
this.LOGGER.info("timer servlet is initialized ");
try {
InitialContext ic = new InitialContext();
TimerManager tm = (TimerManager) ic.lookup("java:comp/env/tm/TimerManager");
Timer timer = tm.schedule(new GlobalTemplateScheduler(), 0, 3600000);// one hour interval
System.out.println("Timer..... " + timer);
}
...
In this my GlobalTemplateScheduler class has timerExpired method which is scheduled to execute after every one hour interval.
public class GlobalTemplateScheduler implements TimerListener {
#Autowired
private TemplateControl templateControl;
#Override
public void timerExpired(Timer timer) {
try {
templateControl.updateMappings(names);
} catch (Exception e) {
this.LOGGER.error(e.getMessage());
e.printStackTrace();
}
...
So i have to autowire templateControl which i am getting null. This should happen on server startup.
Further inside updateMappings there's a datasource object which is also autowired as constructor-arg(This is working fine on browser request but need to do it on server startup).
Note: I cannot use the ApplicationListener interface.
Any suggestions would really help.
Thankyou.
On application startup beans initialization would not be completed, beans can be used after the application context refresh or after the intialization of the bean, it will make no sense to execute a logic which requires the bean on the startup unless you detect whether the bean is ready or not.
You can execute some logic using #PostConstruct in the bean which will be executed after the initialization of the bean so you can manipulate your logic in a way to do so after the intialization of the bean or you could detect and execute logic after the ContextRefreshedEvent by impelementing applicationListener and put your logic in onAppplicationEvent method.
One solution would be to use Spring's Container within your servlet. There are many implementations for this purpose, for instance the AnnotationConfigApplicationContext. Spring's documentation describes how to use the ClassPathXmlApplicationContext
Suppose GlobalTemplateScheduler is also a bean, then the key point is this:
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
GlobalTemplateScheduler sheduler = context.getBean("sheduler", GlobalTemplateScheduler.class);
sheduler.someMethod();
The content of the XML, which is used by the ClassPathXmlApplicationContext is small. But you need to enable component scan:
<context:component-scan base-package="foo.bar.baz" />
Another approach, I could suggest, is to use Spring's DispatcherServlet to wire all the beans together. It can use the same XML, it is just a matter of loading it. The benefit is that is you don't need to load the application context by yourself and launch a bean as an entry point
There are plenty of tutorials how to use this servlet.
If you dont't like to write XML, you could use the WebApplicationInitializer
As i said the beans which i was autowiring were working fine. I just needed those beans in my Scheduler Servlet.
Here's the solution which worked...
In my scheduler servlet i got the application context xml and used the beans which were required...
ApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(this.getServletContext());
GlobalTemplateControl globalTemplateControlObject = context.getBean("globalTemplateControl", GlobalTemplateControl.class);
Thanks #duffymo #Amer Qarabsa #Spindizzy for your suggestions :)

How should I name the EJB to lookup it correctly?

I have a simple problem, I have a Stateless EJB bean running in Glassfish 4. I have a client, and I want to lookup for this ejb, and I simply cannot make the right name. How should I name these correctly to work?
I just got javax.naming.NamingException, but I have no clue how to do it right.
I follow the java:global/[ear-name]/[jar-name]/[ejb-name]![fully-qualified-interface-name] convention.
Here is the client:
...
public class Main {
public static void main(String[] args) {
Calculator calculator;
Context ctx = null;
try {
Properties environment = new Properties();
environment.setProperty("org.omg.CORBA.ORBInitialHost", "127.0.0.1");
environment.setProperty("org.omg.CORBA.ORBInitialPort", "3700");
// Find the EJB with a JNDI lookup
ctx = new InitialContext(environment);
calculator = (Calculator)ctx.lookup(
"java:global/calculator-application/calculator-ejb/calcBean!eak.Calculator"
);
} catch(NamingException ex) {
ex.printStackTrace();
return;
}
...
}
}
Here are the annotations of my EJB component:
#Stateless(name="calcBean", mappedName="calc")
#Remote(Calculator.class)
public class CalculatorBean implements Calculator {
...
And I run what jndi names are in my Glassfish server:
C:\javaee\glassfish4\glassfish\bin>asadmin.bat list-jndi-entries
UserTransaction: com.sun.enterprise.transaction.startup.TransactionLifecycleServ
ice$2
ejb: com.sun.enterprise.naming.impl.TransientContext
java:global: com.sun.enterprise.naming.impl.TransientContext
calc__3_x_Internal_RemoteBusinessHome__: javax.naming.Reference
calc: javax.naming.Reference
jdbc: com.sun.enterprise.naming.impl.TransientContext
concurrent: com.sun.enterprise.naming.impl.TransientContext
com.sun.enterprise.container.common.spi.util.InjectionManager: com.sun.enterpris
e.container.common.impl.util.InjectionManagerImpl
jms: com.sun.enterprise.naming.impl.TransientContext
calc#eak.Calculator: javax.naming.Reference
Command list-jndi-entries executed successfully.
You'r configuration looks valid to me. I tested your example and it worked just fine. Please make sure that the gf-client.jar from glassfish4/glassfish/lib is in your classpath. Please also confirm that your application, module and bean names are as you see in the Glassfish console. In my case JNDI lookup string java:global/ear-1.0-SNAPSHOT/my-ejb-jar-1.0-SNAPSHOT/calcBean!Echo worked perfectly.

JAX-WS & Tomcat: storing in InitialContext not possible?

Intro I'm writing a web service using JAX-WS, and deploying it in Tomcat. With a lot of difficulties I finally had some code written.
Problem Unfortunately, when trying to run it I get the following error:
Context is read only
Setting I'm writing a web service that queries multiple databases and returns a single result. For that purpose, in the init() method (marked with #PostConstruct), I create a series of DataSources that I add to the context.
This is how I create the pool (based on Tomcat documentation) and after its creation I add it to the context (based on this tutorial):
#PostConstruct
private void init(){
PoolProperties props = new PoolProperties();
props.setUrl("jdbc:postgresql://" + ...);
props.setUsername(...);
props.setPassword(...);
props.setJdbcInterceptors("org.apache.tomcat.jdbc.pool.interceptor.ConnectionState;" +
"org.apache.tomcat.jdbc.pool.interceptor.StatementFinalizer");
DataSource dataSource = new DataSource();
dataSource.setPoolProperties(propos);
Context ctx = new InitialContext(env);
ctx.bind("java:/comp/env/dbpool", dataSource);
}
And later I use it:
#WebMethod
public Result performQuery(QueryParameters params){
Context ctx = new InitialContext(env);
(DataSource) source = (DataSource) ctx.lookup("java:/comp/env/dbpool");
}
I deploy the web service on a Tomcat 7 server.
Question I understand after Google-ing that I cannot write into the Context on Tomcat. But how else could I solve this? From what I understand about JAX-WS I cannot just have a private variable holding a DataSource, right? I have to pass that DataSource using a Context, right?
The InitialContext is writable from the Tomcat code only, not from your client code. You must add your DataSource to your context.xml and it'll work.

Is using VM arguments for servlet configuration 'clean'?

This relates to this answer:
System.getProperty("catalina.base") There can be scenario where client may use any other server
another server-independent system
property yourself, you can set as a VM
argument.
-Dconfig.location=/path/to/folder
In case of Tomcat, you can set it as
JAVA_OPTS environment variable, or
edit the catalina.bat startup file or
edit the Windows Service settings
(when it's installed as Windows
Service), etc. Other servers supports
similar constructs as well.
Is this considered 'clean'? We've been doing this for years, just want to know if this is acceptable, or there is a better way to configure runtime environment.
It feels maybe dirty, but there are apart from putting it in the classpath really no better ways if the requirement is to untouch the WAR whenever you want to change the location of external configuration files.
If untouching the WAR is not a strict requirement and rebuilding the WAR is allowed (e.g. you're using an inhouse developed application with continuous integration and serveradmins are in the same line, etc), then you could also use a <context-param> in web.xml instead.
<context-param>
<param-name>config.location<param-name>
<param-value>/path/to/file</param-value>
</context-param>
It's then in any Servlet (or better, ServletContextListener) available by ServletContext#getInitParameter():
String configLocation = servletContext.getInitParameter("config.location");
File configFile = new File(configLocation, "config.properties");
// ...
My understanding is that "more clean" would be using either <servlet-param> <init-param> in web.xml or some kind of IoC solution, like Spring.
I feel this is not the cleanest of ways to attain what you want. You can use the web.xml init params or servlet params tags.
Another way is using properties file or an XML configuration file.
I just solved a similar problem in a slightly different way. Our customer wants to configure database connection details, integration server locations and ports etc. without rebuilding the war. Using environment property to point an external file containing the information may or may not be okay, but it felt a bit dirty trick. Anyway, here's a slightly more enterprisey way.
For database connections we use JNDI lookup and below is the current solution for integration server parametrization. The parameters can come from at least three different sources now:
properties-file, which is overridable with Maven profiles and requires single line of xml in spring configuration to be accessible. This is obviously inside the war file.
web.xml context-param. This is also, of course, inside the war file.
Tomcat server can override the init parameters with context.xml which can be outside the war. This happens to be the same file where JNDI context is defined, which is nice.
Below is the implementation for configuration accessor bean. It can run in servlet context and also without one (for some unit tests it makes little sense to kickstart a full-blown web server, but we nevertheless need to satisfy spring bean injections).
I don't mean this to be a perfect solution, but it is one. Didn't find anything like this with google.
#Service
public class IntegrationConfigurationImpl implements
IntegrationConfiguration, InitializingBean,
ServletContextAware, ApplicationContextAware {
private static final String SERVER_HOST_PROPERTY = "integration.server.host";
private static final String SERVER_PORT_PROPERTY = "integration.server.port";
private static final String PROPERTY_BEAN_NAME = "integrationProperties";
private ServletContext servletContext;
private ApplicationContext applicationContext;
private static final Logger log = LoggerFactory.getLogger(IntegrationConfigurationImpl.class);
private String serverHost = "foo";
private int serverPort = 42;
#Override
public String getServerHost() {
return serverHost;
}
#Override
public int getServerPort() {
return serverPort;
}
#Override
public void setServletContext(ServletContext servletContext) {
this.servletContext = servletContext;
}
#Override
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
#Override
public void afterPropertiesSet() throws Exception {
// konfiguraation validointi..
if (servletContext == null) {
log.info("servlet context not set, not running as a web application. Trying to get properties from application context");
if (applicationContext.containsBean(PROPERTY_BEAN_NAME)) {
Properties p = (Properties)applicationContext.getBean(PROPERTY_BEAN_NAME);
serverHost = p.getProperty(SERVER_HOST_PROPERTY);
serverPort = Integer.valueOf(p.getProperty(SERVER_PORT_PROPERTY)).intValue();
} else {
log.info("Property bean not found :" + PROPERTY_BEAN_NAME);
}
} else {
serverHost = servletContext.getInitParameter(SERVER_HOST_PROPERTY);
serverPort = Integer.valueOf(servletContext.getInitParameter(SERVER_PORT_PROPERTY)).intValue();
}
log.info("Using integration server " + getServerHost() + ", port " + getServerPort());
}
}
The disadvantage with having system property is you need to restart the container to modify the system parameter.
Having it as init-param in web.xml, can allow you to modify by just restarting the web app.
Having in init-param is a better way.

Categories

Resources