Consider the following mule configuration and having mule embedded in a Web (Java EE) Application:
<jms:connector
name="jmsConnector"
connectionFactory-ref="jmsConnectionFactory"> <!-- From spring -->
<!-- JNDI Name Resover here? -->
</jms:connector>
<flow name="mainTestFlow">
<jms:inbound-endpoint connector-ref="jmsConnector"
queue="jms/MessageQueue" />
<logger level="INFO" category="mule.message.logging" message="Message arrived." />
</flow>
jmsConnectionFactory refers to a JMS Connection Factory defined in Spring, from:
<bean id="jmsConnectionFactory" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="jms/QueueConnectionFactory" />
</bean>
The Queue Connection Factory was tested and is working.
The jms/MessageQueue queue name refers to a resource-ref defined in the web application web.xml file. This JNDI reference is bound at the container level to a javax.jms.Queue managed by the application server and connected to a proper messaging server (ActiveMQ, in this case).
However, Mule doesn't treat the queue="" attribute as a JNDI destination, but as the queue name itself. So, when the above code is initialized, it actually creates a new queue in ActiveMQ named "jms/MessageQueue". What I really wanted was that it correctly retrieved the queue from the JNDI reference in the Web Application descriptor.
Ok, you could say, all I had to do was to configure a JNDI Name Resolver at the JMS Connector and also add the jndiDestinations="true" and forceJndiDestinations="true" attributes to it.
This is acceptable:
<jms:default-jndi-name-resolver
jndiProviderUrl="tcp://localhost:1099"
jndiInitialFactory="???"/>
The real problem is that I don't want to place the real Initial Context Factory class name in the jndiInitialFactory, because it would fall into a container-specific definition. However, my application is sometimes deployed into JBoss 4.2.3, and sometimes, into WebSphere 7. Having 2 configurations and 2 EAR packages is not an option, due to our development process.
Anyway, is it anyhow possible to either tell Mule-ESB to assume the current container (as it is in embedded mode) as the default JNDI Initial Factory for lookups or provide a "generic" JNDI Initial Factory that would recognize the container's JNDI environment? That shouldn't be a problem, because a web application can refer to it's container JNDI environment without additional (or even visible) configuration.
If not possible, can I have my jms:inbound-endpoint refer to a javax.jms.Queue defined in Spring, just as the jms:connector does with the JMS Connection Factory? That would actually be rather elegant and clean, as Mule is Spring-friendly.
Thank you all in advance!
Solution
After much thought and consideration, I finally solved my problem by creating a custom JndiNameResolver wired up to spring JNDI facilities (JndiTemplate, for instance). This is far from the best solution, but I found that to be the one that would least interfere and tamper with Mule's and Spring's inner intricacies.
That said, here is the class:
package com.filcobra.mule;
import javax.naming.NamingException;
import org.mule.transport.jms.jndi.AbstractJndiNameResolver;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.jndi.JndiObjectLocator;
import org.springframework.jndi.JndiTemplate;
public class SpringJndiNameResolver extends AbstractJndiNameResolver implements InitializingBean {
private static Logger logger = LoggerFactory.getLogger(SpringJndiNameResolver.class);
private JndiTemplate jndiTemplate;
#Override
public void afterPropertiesSet() throws Exception {
if (jndiTemplate == null) {
jndiTemplate = new JndiTemplate();
}
}
#Override
public Object lookup(String name) throws NamingException {
Object object = null;
if (name != null) {
logger.debug("Looking up name "+name);
object = jndiTemplate.lookup(name);
logger.debug("Object "+object+" found for name "+name);
}
return object;
}
public JndiTemplate getJndiTemplate() {
return jndiTemplate;
}
public void setJndiTemplate(JndiTemplate jndiTemplate) {
this.jndiTemplate = jndiTemplate;
}
}
With that, the configuration falls back into the usual:
<spring:bean id="jndiTemplate" class="org.springframework.jndi.JndiTemplate" />
<jms:connector
name="jmsConnector"
connectionFactoryJndiName="java:comp/env/jms/MyConnectionFactory" <!-- from Resource-Ref -->
jndiDestinations="true"
forceJndiDestinations="true"
specification="1.1" >
<jms:custom-jndi-name-resolver class="com.filcobra.mule.SpringJndiNameResolver">
<spring:property name="jndiTemplate" ref="jndiTemplate"/>
</jms:custom-jndi-name-resolver>
</jms:connector>
With that, I was finally able to not have my Mule ESB installation tied up to a specific JMS vendor/implementation. In fact, the JMS (queues and factories) configuration is all left under the application server responsibility.
Nevertheless, one thing remained odd. I expected that the JMS endpoints also used my Jndi Name Resolver in order to lookup the queue from a resource-reference, or its JNDI Name, the same way it did with the Connection Factory. That wouldn't work whatsoever. I finally worked around that by placing the queue name itself, as created in the JMS server:
<flow name="mainTestFlow">
<jms:inbound-endpoint connector-ref="jmsConnector" queue="queue/myQueue"/> <!-- Queue Name, not JNDI Name -->
That worked. So, I'm assuming the JMS Connector doesn't try and look up the queue, but simply uses the connection factory (looked up or not) to directly access the JMS Server.
Regards!
I see the problem in the source code: basically if you provide an externally created connection factory, jndiDestinations and forceJndiDestinations are forcefully set to false.
I haven't messed with JNDI enough recently to provide a generic solution to your problem, which indeed would be the best.
What I would try would be to sub-class org.mule.transport.jms.Jms11Support, inject my Spring looked-up queues in it, rewire it internally to use these queues and, lastly, inject it in the Mule JMS connector itself.
Related
Hello I have a web application running on apache-tomee-plus-8.0.1. My problem is about getting an Environment variable from a runnable in a custom executor. The variable is defined in /conf/context.xml:
<?xml version="1.0" encoding="UTF-8"?>
<Context>
<!-- Default set of monitored resources. If one of these changes, the -->
<!-- web application will be reloaded. -->
<WatchedResource>WEB-INF/web.xml</WatchedResource>
<WatchedResource>WEB-INF/tomcat-web.xml</WatchedResource>
<WatchedResource>${catalina.base}/conf/web.xml</WatchedResource>
<!-- disable the scan in order to avoid errors at startup due to ora18n.jar-->
<JarScanner scanManifest="false" scanClassPath="false" scanBootstrapClassPath="false"></JarScanner>
<!-- base64 from user:pass -->
<Environment name="myCreds"
value="toto" type="java.lang.String" />
</Context>
The function I use to get the variable "myCreds"
private static String getCredentialsFromContext() throws NamingException {
Context initialContext = new InitialContext();
Context environmentContext = (Context) initialContext.lookup("java:comp/env");
return (String) environmentContext.lookup("myCreds");
}
This function is called via a JAX-RS endpoint which is used to start long background maintenance tasks of the server application. The progress of the background task is then available on another endpoint.
If I do
#GET
#Path("/testOK")
public static String testOK() {
return getCredentialsFromContext(); // works fine
}
But when I use an executor, the lookup fails with
javax.naming.NameNotFoundException: Name [comp/env] is not bound in this Context. Unable to find [comp].
private static ExecutorService index_executor;
#GET
#Path("/testKO")
public static Response testKO() {
if (index_executor == null){
index_executor = Executors.newFixedThreadPool(5);
}
index_executor.submit(new Runnable() {
#Override
public void run() {
System.out.println(getCredentialsFromContext()); // FAIL
}
});
return Response.ok.build()
}
It looks like the InitialContext is not the same when called from the runnable. I would like to avoid to pass through args the value of "myCreds". I tried to move the declaration of "myCreds" in the context.xml of the webapp but it didn't help. Using JNDIContext also fails.
Do you understand what is the problem and why the context is different?
Thanks :)
JNDI lookups depend on some context information on the running thread, usually the context class loader.
On a Java EE/Jakarta EE server you should not spawn new (unmanaged) threads yourself, but use the ManagedExecutorService provided by the container. This service automatically propagates some kinds of contexts from the calling thread:
The types of contexts to be propagated from a contextualizing application component include JNDI naming context, classloader, and security information. Containers must support propagation of these context types.
(Jakarta Concurrency Specification, emphasis mine)
You can inject a ManagedExecutorService using a #Resource annotation:
#Resource
private ManagedExecutorService executorService;
Using a ManagedExecutorService works on Wildfly, but on TomEE there is IMHO a bug that prevents the propagation of the naming context: JAX-RS resources use CxfContainerClassLoader as context classloader, which wraps the real classloader, preventing it from propagating to the managed thread.
A workaround would consist in switching temporarily to the wrapped classloader:
final ClassLoader tccl = Thread.currentThread().getContextClassLoader();
if (tccl instanceof org.apache.openejb.util.classloader.Unwrappable) {
final ClassLoader cl = ((org.apache.openejb.util.classloader.Unwrappable) tccl).unwrap();
Thread.currentThread().setContextClassLoader(cl);
}
executorService.submit(...);
Thread.currentThread().setContextClassLoader(tccl);
Edit: actually, it is enough to mark the JAX-RS resource as #Stateless for the correct propagation of the JNDI naming context.
I'm newbie to Spring JMX. And i want to monitor the prototype beans in my project through Spring JMX, I created a sample project to register a bean(Singleton) with Spring's MbeanExporter that is working. Then i googled to register the Non-Singleton bean with Spring JMX and monitor it but i didn't found any thing helpful.
I came across a Spring forum post that describes my problem but that answer is not to the point.
I kept googling for this issue and i found few posts on the stackoverlow itself that really helped me. Just copying the code here:-
#Component("MyPrototypeScopedBeanName")
#Scope(value = "prototype")
#ManagedResource
public class MyPrototypeScopedBeanName implements SelfNaming
#Autowired
MBeanExporter exporter;
.
.
#PostConstruct
private void init() throws Exception {
exporter.registerManagedResource(this);
}
.
.
.
#Override
public ObjectName getObjectName() throws MalformedObjectNameException {
return new ObjectName("com.foobar", "name", this.toString());
}
Also, you may want to configure your exporter to ignore this during autodetect, because the way autodetect works with prototypes, it will create yet another instance for itself that will add an additional instance to your JMX console.
<property name="autodetect" value="true"/>
<!-- Done to prevent creation of additional prototype during autodetect routine -->
<property name="excludedBeans">
<list>
<value>MyPrototypeScopedBeanName</value>
</list>
</property>
Stackoverflow link
Another link
Courtesy:- #theJC
What is the best approach for creating services that load a property set at runtime (bean is passed "xyz" and loads xyz.properties)? These properties files need to be able to be dropped into a folder outside the classpath before a command is entered to start the service (edit: this could happen at any time while the program is running).
I already have a system to do this that we've been using for over a year, but I'm migrating to spring to make the code more modular (customize services more easily through DI) and easier to maintain. My current method of creating an environment and then passing it with "this" to the dependencies just seems upside down from an IoC standpoint.
Is there a way to use a PropertyPlaceholderConfigurer without hardcoding the name of the property file? Maybe just a reference to a variable I pass into the constructor of the service that its dependencies can load? So far, it looks like I will have to create a service and inject its dependencies without any config and then call a separate load method for each to pass in the properties, but that just seems like I'm not really using spring.
USE CASE: The app will pool client connections to various servers and will forward requests from other applications to these servers. New profiles must be able to be added by non-programmers without taking down or restarting the app. The profiles will include basic things like host, port, and login info, but also more complex things like whether to use tcp/http, ssl/https (which will determine which client type to use), and timeouts and pool min/max/etc (which will need default values).
I tried with PropertyPlaceholderConfigurer and frankly, I couldn't wrap my head around it, somehow. It's easy enough to use when you use the existing options but I couldn't extend the framework.
So my approach was much more simple:
Create an annotation #InjectConfig which takes a config key as parameter.
In your beans/services, annotate fields or public setters with this annotation.
Write a BeanPostProcessor which takes options from a "config provider" and injects them into the fields / setters.
Now all you need is a config provider. Inject that into the post processor and you're done.
Note: I prefer annotating setters because that means you can easily configure your services from tests (just call the setters) without having to come up with smart names for 238576 config files.
EDIT If you have many configs, then a config factory might be a better choice:
Create a key to describe a config bundle (I usually use an enum or a new type here to prevent typos)
Put this key into the service when you create it (manually or via Spring)
Write a config factory that can return Properties or a Map for a config key.
Inject this factory into your service
In the init code of your service, use the key to lookup your config via the factory.
Using this approach, you can have a dummy factory that always returns the same thing in tests and a more complex factory for production.
The real factory can then be configured via spring so it knows where to look for configuration files. One approach is to register a java.io.File per config key. Now your concerns (configuring a service and loading configs) are completely separated.
PropertyPlaceholderConfigurer reads and initialize files on application context initialization and only once. So most probably you cannot configure it at runtime.
But you can have variables. For example, for my case I have default properties and user specific properties. So PropertyPlaceholderConfigurer loads properties from classpath first and after that is trying to find additional properties at defined location (user home folder). I user's property file exists so configurer loads it and override properties.
Here is my example:
<bean id="config" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="ignoreResourceNotFound" value="true"/> <!-- do not throw exception if file not found -->
<property name="locations">
<list>
<value>classpath:server.properties</value>
<value>file:${user.home}/user.properties</value>
</list>
</property>
</bean>
I'm not sure that this answer is what you're exactly need. But I'm trying to guess what is your actual task. So if you need re-read properties runtime each time you access them you have to do it manually like you did before because spring application context helps you configure your application initial configuration.
It seems like the best approach may be to use a ServiceManager that contains the main ApplicationContext and then have each Service initialize its own FileSystemXmlApplicationContext with the main context as the parent like so:
public class ServiceManager {
ApplicationContext appContext;
String APP_HOME = System.getProperty("user.home") + File.separator;
public void init() {
//set main spring context
appContext = new AnnotationConfigApplicationContext(AppConfig.class);
}
public void start(String serviceName) throws Exception {
ApplicationContext serviceContext = new FileSystemXmlApplicationContext(
new String[]{APP_HOME + serviceName + ".xml"}, //path to child ctx
appContext); //reference to parent ctx to build hierarchy
Service service = (Service) serviceContext.getBean("service");
service.start();
}
}
The ApplicationContext is a bit heavy to be duplicating, but memory is pretty cheap these days and this provides total separation of concerns. I still have shared logging and an event system managed by the parent context, and each service is now simplified in its own config. I built a proof of concept using two services, and it seems to work fine so far. I'll add another comment once I finish the other services and finish testing.
reference:
http://techo-ecco.com/blog/spring-application-context-hierarchy-and-contextsingletonbeanfactorylocator/
Helo masters, I have to create a JNDI Datasource dynamically, I tried to do it with a listener called SetupApplicationListener. Here is the beginning of WEB-LIB/web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee">
<display-name>pri-web</display-name>
<!-- Listeners -->
<listener>
<listener-class>org.apache.myfaces.webapp.StartupServletContextListener</listener-class>
</listener>
<listener>
<listener-class>myapp.SetupApplicationListener</listener-class>
</listener>
The code of the listener:
public class SetupApplicationListener implements ServletContextListener {
public static Log LOG = null;
public void contextInitialized(ServletContextEvent ctx){
try {
createOracleDataSource();
.....
}
}
private void createOracleDataSource() throws SQLException, NamingException {
OracleDataSource ds = new OracleDataSource();
ds.setDriverType(...);
ds.setServerName(...);
ds.setPortNumber(...);
ds.setDatabaseName(...);
ds.setUser(...);
ds.setPassword(...);
new InitialContext().bind("java:comp/env/jdbc/myDS", ds);
}
.....
}
And there is the error:
[ERROR] 29/01/2013 09:44:50,517 (SetupApplicationListener.java:86) -> Error
javax.naming.NamingException: Context is read only
at org.apache.naming.NamingContext.checkWritable(NamingContext.java:903)
at org.apache.naming.NamingContext.bind(NamingContext.java:831)
at org.apache.naming.NamingContext.bind(NamingContext.java:171)
at org.apache.naming.NamingContext.bind(NamingContext.java:187)
at org.apache.naming.SelectorContext.bind(SelectorContext.java:186)
at javax.naming.InitialContext.bind(InitialContext.java:359)
at myapp.SetupApplicationListener.createOracleDataSource(SetupApplicationListener.java:102)
Can I set the read-only properties of the Context to "true"? Thanks! :)
Tomcat 6.0
Oracle 11g
jdk1.5
EDIT: Don't need to be dynamically, i have to define a jndi datasource internally I can't modify the server files because it is a shared server. It must be jndi because other modules use it in that way, thanks.
If you need to create a datasource dynamically is there really any need for a JNDI lookup? JNDI is designed to make the connection external to the application, while in your scenario its tightly coupled to the application due to a legitimate requirement. Why not just use a JDBC connection?
You need to create a ServletContextListener and there you can make the InitialContext writable - it's not the way it should be done, but if you really need it, this is one way you can do it.
This also works with Java Melody!
protected void makeJNDIContextWritable(ServletContextEvent sce) {
try {
Class<?> contextAccessControllerClass = sce.getClass().getClassLoader().loadClass("org.apache.naming.ContextAccessController");
Field readOnlyContextsField = contextAccessControllerClass.getDeclaredField("readOnlyContexts");
readOnlyContextsField.setAccessible(true);
Hashtable readOnlyContexts = (Hashtable) readOnlyContextsField.get(null);
String context = null;
for (Object key : readOnlyContexts.keySet()) {
String keyString = key + "";
if (keyString.endsWith(sce.getServletContext().getContextPath())) {
context = keyString;
}
}
readOnlyContexts.remove(context);
} catch (Exception ex) {
ex.printStackTrace();
}
}
I haven't got this problem before since I usually defined JNDI in application server(tomcat, weblogic and etc). Just like what Kevin said, this is exactly what JNDI was designed for; separating datasource config from your source code and retrieving JNDI resources through lookup and inject;
Back to your question, I think tomcat has every strict rules on modifying JNDI at runtime. In another word, you cannot re-bind or remove jndi from Context. If you go through the tomcat specification you will probably see some thing about jndi lookup but no re-bind.
From section EE.5.3.4 of the EE 6 platform specification (JSR 316):
The container must ensure that the application component instances
have only read access to their naming context. The container must
throw the javax.naming.OperationNotSupportedException from all the
methods of the javax.naming.Context interface that modify the
environment naming context and its subcontexts.
Note that "their naming context" in this section is referring to java:comp.
I solved this problem when found that I was closing environmentContext object
For example:
Context context=new InitialContext();
Context environmentContext=(Context) context.lookup("java:comp/env");
And my code was:
environmentContext.close();
After removing close function from environmentContext problem was solded for me;
I also had this problem, but being new to Tomee, I didn't know that there is a simple solution. When I deployed my web app to the webapps folder, the app worked fine, but when I deployed it to a service folder, I got the same abort. The problem was that the folder name did not match the war name (minus the .war). Once I fixed that, the app worked fine. Make sure the war name, folder name and service name are identical. This problem produces several different errors, including Context is read only and Error merging Java EE JNDI entries.
I solved this issue by setting useNaming="false" in my context.xml.
From the documentation:
useNaming : Set to true (the default) to have Catalina enable a JNDI InitialContext for this web application that is compatible with Java2 Enterprise Edition (J2EE) platform conventions.
I'm writing a Java servlet that needs to read some site-specific
configuration data; I would like it to be easily accessible/modifiable
by the sysadmins at deployment time. There is no sensible default,
so the data has to be provided by the site admin.
It consists of a few string key/value pairs (think Properties).
It would only be read once (at initialization time).
I'm aware of this SO question
and the ServletContext.getInitParameter() mechanism, but as far as
my understanding goes, they require the data to be bundled in the
servlet package (either as a properties file, or specified in the
web.xml), which makes it inconvenient to upgrade the servlet code.
Is there any "standard" interface for a servlet to get this kind of
key/value configuration data? It would be ok if the programming
interface is the same everywhere, but the actual way of setting the
configuration data depends on the actual servlet container being used.
I'm looking preferably at portable solutions, but I'd be content with
something that only works in Tomcat and Jetty.
The recommended way to configure an application server for a web application is per JNDI.
Every application server (including Jetty and Tomcat) allows you to configure JNDI parameters.
For Jetty you can add the following to your jetty.xml to add the JNDI parameter param.file:
<!-- JNDI java:comp/env -->
<New id="param.file" class="org.mortbay.jetty.plus.naming.EnvEntry">
<Arg>param.file</Arg>
<Arg type="java.lang.String"><SystemProperty name="jetty.home" default="."/>etc/config.properties</Arg>
<Arg type="boolean">true</Arg>
</New>
Then in your servlet you can read the JNDI parameter:
import javax.naming.InitialContext;
import javax.naming.NamingException;
...
public Object readJndi(String paramName) {
Object jndiValue = null;
try {
final InitialContext ic = new InitialContext();
jndiValue = ic.lookup("java:comp/env/" + paramName);
} catch (NamingException e) {
// handle exception
}
return jndiValue;
}
public String getConfigPath() {
return (String) readJndi("param.file");
}
The way to set JNDI values differs for other application servers but the code to read the configuration is always the same.
The Servlet init parameters are the right (and standardized) way of defining properties which can be configured by the administrator. Many of the application servers provide a GUI backend where the parameters can be configured.
For an example for Tomcat, see Defining Tomcat servlet context parameters
Configure the external location of the properties - either via a jvm argument (when starting the servlet container), or in the web.xml
in the external location use config.properties and read it with java.util.Properties
You may take Preferences or hack with user.home, user.dir, etc. But for a few key/value keep things simple.
Write a small Singleton to wrap around Properties and load them from a fix & absolute location
public class LocalConfig extends Properties {
public static LocalConfig $ = new LocalConfig();
private LocalConfig() throws IOException {
load(new File("/etc/myconfig.properties"));
}
}