Saturday, April 19, 2008

How do i use my own log4j properties or xml file in JBoss

This article is mainly for those who had a hard time configuring "their own" log4j.properties or log4j.xml within their application on JBoss. For a brief background on this, please read through this JBoss wiki. As explained in that wiki, JBoss uses its own version of log4j.xml and log4j jar, making it difficult to use your own version of the log4j properties/xml files. The wiki also lists down the steps required to configure your application to use its own log4j config file. However, i have seen people complain in the forums that those steps either dont work for them or the steps are too involved and not worth a try.

People at JBoss had taken note of these issues (see the related JIRA) and starting JBoss-4.2.x, the steps involved to configure your own log4j config file in JBoss are pretty simple and straight-forward. In this example, i will try to explain these steps for a EAR file as well as a WAR file. The version that i am using here is JBoss-4.2.2GA (which is the latest stable version at this point). In the examples here, i use a log4j.properties file. The steps are the same even for a log4j.xml.

So here's what is required to get it working in a EAR file:

1) Create a log4j.properties and package it with your application and make it available in the classpath (i placed the log4j.properties file at the root of the EAR file).

2) Also package the appropriate version of log4j jar with your application (this step and the next step are important because you will have to ensure that log4j initializes separately for your application and picks up the log4j.properties file). Place this log4j jar file in the application package so that it is made available in the classpath (i placed it in a folder named lib of the EAR file - remember that any jars in the lib folder of the EAR are by default added to the classpath and made available to the entire application, starting JBoss-4.2.x version).

3) Enable classloader isolation for your application. This is necessary as explained in step#2. You will have to create a jboss-app.xml file and place it in the META-INF folder of the EAR. The jboss-app.xml looks like:


<jboss-app>
<loader-repository>
org.myapp:loader=MyClassLoader
<loader-repository-config>
java2ParentDelegation=false
</loader-repository-config>
</loader-repository>

</jboss-app>






This is how the EAR file would look like:



myApp.ear
|
|--- log4j.properties
|
|--- META-INF
| |
| |--- application.xml
| |
| |--- jboss-app.xml
|
|--- lib
| |
| |--- log4jxxx.jar






That's it! Your application now will use it own version of log4j config file as well log4j jar.

Steps involved for a WAR file are similar (If the WAR file is part of an EAR file, then you don't have to follow the steps below. The steps mentioned above for an EAR file are sufficient for a WAR inside an EAR):

1) Create a log4j.properties and package it with your application and make it available in the classpath (i placed the log4j.properties file in the WEB-INF/classes folder of the WAR).

2) Also package the appropriate version of log4j jar with your application (this step and the next step are important because you will have to ensure that log4j initializes separately for your application and picks up the log4j.properties file). Place this log4j jar file in the application package so that it is made available in the classpath (i placed it in a WEB-INF/lib of the WAR file).

3) Enable classloader isolation for your application. This is necessary as explained in step#2. You will have to create a jboss-web.xml file and place it in the WEB-INF folder of the WAR. The jboss-web.xml looks like:



<jboss-web>
<class-loading java2ClassLoadingCompliance="false">
<loader-repository>
org.myapp:loader=MyClassLoader
<loader-repository-config>java2ParentDelegation=false</loader-repository-config>
</loader-repository>
</class-loading>
</jboss-web>





This is how the WAR file would look like:


myApp.war
|
|--- WEB-INF
| |
| |--- web.xml
| |
| |--- jboss-web.xml
| |
| |--- classes
| | |
| | |--- log4j.properties
|
|
|--- lib
| |
| |--- log4jxxx.jar




Saturday, October 06, 2007

Why do i get NameNotFoundException while doing a JNDI lookup?

Many a times when you are doing a lookup in the JNDI tree, you see javax.naming.NameNotFoundException. A simple code that does the lookup will look something like:


Context ctx = new InitialContext();
Object obj = ctx.lookup("somepath/somename");


This code just looks up the JNDI tree to get an object bound by the name "somepath/somename". Looks simple. However, chances are that you might even see this exception:


javax.naming.NameNotFoundException: somepath not bound
at org.jnp.server.NamingServer.getBinding(NamingServer.java:529)
at org.jnp.server.NamingServer.getBinding(NamingServer.java:537)
at org.jnp.server.NamingServer.getObject(NamingServer.java:543)
at org.jnp.server.NamingServer.lookup(NamingServer.java:267)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:585)
at sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:294)
at sun.rmi.transport.Transport$1.run(Transport.java:153)
at java.security.AccessController.doPrivileged(Native Method)
at sun.rmi.transport.Transport.serviceCall(Transport.java:149)
at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:460)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:701)
at java.lang.Thread.run(Thread.java:595)
at sun.rmi.transport.StreamRemoteCall.exceptionReceivedFromServer(StreamRemoteCall.java:247)
at sun.rmi.transport.StreamRemoteCall.executeCall(StreamRemoteCall.java:223)
at sun.rmi.server.UnicastRef.invoke(UnicastRef.java:126)
at org.jnp.server.NamingServer_Stub.lookup(Unknown Source)
at org.jnp.interfaces.NamingContext.lookup(NamingContext.java:625)
at org.jnp.interfaces.NamingContext.lookup(NamingContext.java:587)
at javax.naming.InitialContext.lookup(InitialContext.java:351)


Look closely at the stacktrace. It shows that while looking up the JNDI tree it could not find the jndi name "somepath" (this name may vary). The reason is simple, the JNDI tree does not have any object bound by this name.

To quote the javadocs of this exception "This exception is thrown when a component of the name cannot be resolved because it is not bound."

So how do i know, what's the name to which my object is bound? Each application server, usually provides a JNDI view which can be used to see the contents of the JNDI tree. If you know what object you are looking for (ex: the name of the bean), then you can traverse this JNDI tree to see what name it is bound to. The JNDI view is specific to every application server.

To give an example, JBoss provides its JDNI tree view, through the JMX console. Here are the steps, one has to follow to check the JNDI tree contents on JBoss:

- Go to http://< server>:< port>/jmx-console (Ex: http://localhost:8080/jmx-console)
- Search for service=JNDIView on the jmx-console page
- Click on that link
- On the page that comes up click on the Invoke button beside the list() method
- The page that comes up will show the contents of the JNDI tree.

Here's an sample of how the output looks like(just a small part of the entire output):


java: Namespace

+- XAConnectionFactory (class: org.jboss.mq.SpyXAConnectionFactory)
+- DefaultDS (class: org.jboss.resource.adapter.jdbc.WrapperDataSource)
+- SecurityProxyFactory (class: org.jboss.security.SubjectSecurityProxyFactory)
+- DefaultJMSProvider (class: org.jboss.jms.jndi.JNDIProviderAdapter)
+- comp (class: javax.naming.Context)
+- JmsXA (class: org.jboss.resource.adapter.jms.JmsConnectionFactoryImpl)
+- ConnectionFactory (class: org.jboss.mq.SpyConnectionFactory)
+- jaas (class: javax.naming.Context)
| +- dukesbank (class: org.jboss.security.plugins.SecurityDomainContext)
| +- HsqlDbRealm (class: org.jboss.security.plugins.SecurityDomainContext)
| +- jbossmq (class: org.jboss.security.plugins.SecurityDomainContext)
| +- JmsXARealm (class: org.jboss.security.plugins.SecurityDomainContext)


Global JNDI Namespace

+- ebankTxController (proxy: $Proxy79 implements interface com.sun.ebank.ejb.tx.TxControllerHome,interface javax.ejb.Handle)
+- ebankAccountController (proxy: $Proxy75 implements interface com.sun.ebank.ejb.account.AccountControllerHome,interface javax.ejb.Handle)
+- TopicConnectionFactory (class: org.jboss.naming.LinkRefPair)
+- jmx (class: org.jnp.interfaces.NamingContext)
| +- invoker (class: org.jnp.interfaces.NamingContext)
| | +- RMIAdaptor (proxy: $Proxy48 implements interface org.jboss.jmx.adaptor.rmi.RMIAdaptor,interface org.jboss.jmx.adaptor.rmi.RMIAdaptorExt)
| +- rmi (class: org.jnp.interfaces.NamingContext)
| | +- RMIAdaptor[link -> jmx/invoker/RMIAdaptor] (class: javax.naming.LinkRef)
+- HTTPXAConnectionFactory (class: org.jboss.mq.SpyXAConnectionFactory)
+- ConnectionFactory (class: org.jboss.mq.SpyConnectionFactory)
+- ebankCustomer (proxy: $Proxy67 implements interface com.sun.ebank.ejb.customer.LocalCustomerHome)
+- UserTransactionSessionFactory (proxy: $Proxy14 implements interface org.jboss.tm.usertx.interfaces.UserTransactionSessionFactory)
+- ebankCustomerController (proxy: $Proxy77 implements interface com.sun.ebank.ejb.customer.CustomerControllerHome,interface javax.ejb.Handle)
+- HTTPConnectionFactory (class: org.jboss.mq.SpyConnectionFactory)
+- XAConnectionFactory (class: org.jboss.mq.SpyXAConnectionFactory)
+- TransactionSynchronizationRegistry (class: com.arjuna.ats.internal.jta.transaction.arjunacore.TransactionSynchronizationRegistryImple)
+- ebankAccount (proxy: $Proxy68 implements interface com.sun.ebank.ejb.account.LocalAccountHome)
+- UserTransaction (class: org.jboss.tm.usertx.client.ClientUserTransaction)
+- UILXAConnectionFactory[link -> XAConnectionFactory] (class: javax.naming.LinkRef)
+- UIL2XAConnectionFactory[link -> XAConnectionFactory] (class: javax.naming.LinkRef)
+- queue (class: org.jnp.interfaces.NamingContext)
| +- A (class: org.jboss.mq.SpyQueue)
| +- testQueue (class: org.jboss.mq.SpyQueue)
| +- ex (class: org.jboss.mq.SpyQueue)
| +- DLQ (class: org.jboss.mq.SpyQueue)
| +- D (class: org.jboss.mq.SpyQueue)
| +- C (class: org.jboss.mq.SpyQueue)
| +- B (class: org.jboss.mq.SpyQueue)


Let's see what this tells us. Let's consider the Global JNDI Namespace first. It contains (among other things) the following:

+- ebankTxController (proxy: $Proxy79 implements interface com.sun.ebank.ejb.tx.TxControllerHome,interface javax.ejb.Handle)


This tells me that an object which implements com.sun.ebank.ejb.tx.TxControllerHome and javax.ejb.Handle interfaces is bound to the JNDI tree by the jndi-name "ebankTxController". So if at all i have to lookup this object, my lookup code would be something like:


Context ctx = new InitialContext();
ctx.lookup("ebankTxController");


Similarly in the same Global JDNI Namespace, we see :


+- queue (class: org.jnp.interfaces.NamingContext)
| +- A (class: org.jboss.mq.SpyQueue)



Make note of the nesting of the names here. This tells me that an object of type org.jboss.mq.SpyQueue is bound by the name "A under the path queue". So your lookup for this object should look like:


Context ctx = new InitialContext();
ctx.lookup("queue/A");


Now let's move on to the java: namespace in the JNDI tree view above. The difference between a Global JNDI namespace and the java: namespace is that, the object bound in the java: namespace can be looked-up ONLY by clients within the SAME JVM. Whereas, in case of Global JNDI namespace, the objects bound in this namespace can be looked-up by clients, even if they are not in the same JVM as the server. One would ask, how does this matter? Consider a standalone java program(client) which tries to lookup some object on the server (running in its own JVM). Whenever a standalone client is started (using the java command), a new JVM is instantiated. As a result, the server (which is started in its own JVM) and the client are running on different JVMs. Effectively, the client will NOT be able to lookup objects bound in the java: namespace of the server. However, the client can lookup the objects present in the Global JNDI namespace of the server.

So, why are we discussing these details, in a topic which was meant to explain the NameNotFoundException? Let's consider the java: namespace output above. There's a


+- DefaultDS (class: org.jboss.resource.adapter.jdbc.WrapperDataSource)


This tells me that there's an object bound to the name DefaultDS in the java: namespace. So my lookup code would be:


Context ctx = new InitialContext();
ctx.lookup("java:/DefaultDS");


As explained above, this code is going to return you the object, if this piece of code runs in the same JVM as the server. However, if this piece of code is run from a client in different JVM (maybe a standalone client), then it's going to run into NameNotFoundException. The reason i explained the java: and the Global JNDI namespace is that, sometimes people are surprised that even though the JNDI view shows that the object is bound in the java: namespace(with the same name as the one they pass to the lookup method), they still run into NameNotFoundException. The probable reason might be, the client is in a different JVM.

Wednesday, April 18, 2007

Webservice client using dynamic proxy

In one of my previous posts, i explained how to create a WebService and access it from a simple client. If you look at the client code which accesses the webservice, you will find that the client is using the stubs that were created at deploy time. Such clients which use "static stubs" for accessing webservices are known as "Static Stub Clients". In such approach, the clients will have to have the stubs in their classpath.

Another approach of accessing the webservices is through Dynamic Proxies where the proxies are generated at runtime. I will be showing a simple client which uses dynamic proxies to access an webservice. I wont be explaining the steps to deploy the webservice (which i have already done in my other post). This example assumes that the webservice is already deployed on the server.

The client code will be accessing the HelloWorld service and invoking the sayHelloTo method on that service (Refer my other post to see how the service has been deployed onto the server)

Here's the code for the client:


package org.myapp.service.client;

import java.net.MalformedURLException;
import java.net.URL;

import javax.xml.namespace.QName;
import javax.xml.rpc.Service;
import javax.xml.rpc.ServiceException;
import javax.xml.rpc.ServiceFactory;

import org.myapp.service.HelloWorldService;

/**
* Simple webservice client which uses dynamic proxy
*
* @author Jaikiran Pai
*/
public class DynamicProxyClient {

public static void main(String args[]) {

try {

// ServiceFactory instance
ServiceFactory serviceFactory = ServiceFactory.newInstance();
System.out.println("Got the service factory");
String wsdlURLString = "http://localhost:8080/axis/services/HelloWorld?wsdl";
URL wsdlURL = new URL(wsdlURLString);
String serviceName = "HelloWorldServiceService";
String nameSpaceURI = "http://jaikiran.com";
String portName = "HelloWorld";
// get hold of the service by passing the URL of the wsdl and the
// service name
Service service = serviceFactory.createService(wsdlURL, new QName(
nameSpaceURI, serviceName));
System.out.println("Got the service: " + service);
// create the service proxy
HelloWorldService serviceProxy = (HelloWorldService) service
.getPort(new QName(nameSpaceURI, portName),
HelloWorldService.class);
System.out.println("Got the service proxy: " + serviceProxy);
System.out.println("Invoking method on service proxy........");
System.out.println(serviceProxy.sayHelloTo("Dynamic user"));

} catch (ServiceException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

}



1) Our first step would be to create a ServiceFactory instance, using which we can get access to a deployed service.


ServiceFactory serviceFactory = ServiceFactory.newInstance();


2) Once the ServiceFactory has been created, we will be getting hold of the service which is deployed on the server. To do this, we require
a) The url of the wsdl of the deployed service. In this example it will be http://localhost:8080/axis/services/HelloWorld?wsdl

b) The namespace uri. In this example it will be http://jaikiran.com (You can get this uri from the wsdl file)

c) The name of the service which you want to access. In this example it will be HelloWorldServiceService



String wsdlURLString = "http://localhost:8080/axis/services/HelloWorld?wsdl";
URL wsdlURL = new URL(wsdlURLString);
String serviceName = "HelloWorldServiceService";
String nameSpaceURI = "http://jaikiran.com";

// get hold of the service by passing the URL of the wsdl and the
// service name
Service service = serviceFactory.createService(wsdlURL, new QName(
nameSpaceURI, serviceName));



3) At this point you have got access to the service. But in order to invoke an method on the service, you will have to create a proxy (dynamic proxy) from this service. The Service class, has an API getPort, which creates the proxy. In order to create the proxy, we will have to pass the portName of the service (again, this can be obtained in the wsdl file), the namespace uri and the class type of the service. In this example the service interface is org.myapp.service.HelloWorldService, so we will be passing this as the class as follows:


String portName = "HelloWorld";
// create the service proxy
HelloWorldService serviceProxy = (HelloWorldService) service
.getPort(new QName(nameSpaceURI, portName),
org.myapp.service.HelloWorldService.class);


4) At this point we have got the service proxy which is of type org.myapp.service.HelloWorldService. We can now invoke our sayHelloTo method on this proxy:


System.out.println(serviceProxy.sayHelloTo("Dynamic user"));


Simple, isnt it? You have successfully invoked the webservice through a standalone client using dynamic proxy.