All,
I'm trying to do some unit testing in some archaic java code (no interfaces, no abstraction, etc.)
This is a servlet that uses a ServletContext (which I'm assuming is set up by Tomcat) and it has database information is set up in the web.xml/context.xml file. Now, I've figured out how to make a Fake ServletContext, but the code has
InitialContext _ic = new InitialContext();
all over the place (so it isn't feasible to replace it). I need to find a way to make a default InitialContext() able to do the _ic.lookup(val) without throwing an exception.
I'm assuming there is some way that the context.xml is getting loaded, but how that magic works, I'm drawing a blank. Anyone have any ideas?
Take advantage of the fact that InitialContext uses an SPI to handle its creation. You can hook into its lifecycle by creating an implementation of javax.naming.spi.InitialContextFactory and passing that to your tests via the system property javax.naming.factory.initial (Context.INTITIAL_CONTEXT_FACTORY). It's simpler than it sounds.
Given this class:
public class UseInitialContext {
public UseInitialContext() {
try {
InitialContext ic = new InitialContext();
Object myObject = ic.lookup("myObject");
System.out.println(myObject);
} catch (NamingException e) {
e.printStackTrace();
}
}
}
And this impl of InitialContextFactory:
public class MyInitialContextFactory implements InitialContextFactory {
public Context getInitialContext(Hashtable<?, ?> arg0)
throws NamingException {
Context context = Mockito.mock(Context.class);
Mockito.when(context.lookup("myObject")).thenReturn("This is my object!!");
return context;
}
}
Creating an instance of UseInitialContext in a junit test with
-Djava.naming.initial.factory=initial.context.test.MyInitialContext
on the command line outputs This is my object!! (easy to set up in eclipse). I like Mockito for mocking and stubbing. I'd also recommend Micheal Feather's Working Effectively with Legacy Code if you deal with lots of legacy code. It's all about how to find seams in programs in order to isolate specific pieces for testing.
Here's my solution to setting up the Inintial Context for my unit tests. First I added the following test dependency to my project:
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>catalina</artifactId>
<version>6.0.33</version>
<scope>test</scope>
</dependency>
Then I created a static method with the following code:
public static void setupInitialContext() throws Exception {
System.setProperty(Context.INITIAL_CONTEXT_FACTORY, "org.apache.naming.java.javaURLContextFactory");
System.setProperty(Context.URL_PKG_PREFIXES, "org.apache.naming");
InitialContext ic = new InitialContext();
ic.createSubcontext("jdbc");
PGSimpleDataSource ds = new PGSimpleDataSource();
ds.setDatabaseName("postgres");
ds.setUser("postgres");
ds.setPassword("admin");
ic.bind("jdbc/something", ds);
}
Finally in each of my test class I add an #BeforeClass method which calls setupInitialContext.
Try setting up the system variables before:
System.setProperty(Context.INITIAL_CONTEXT_FACTORY,
"org.apache.naming.java.javaURLContextFactory");
System.setProperty(Context.URL_PKG_PREFIXES,
"org.apache.naming");
InitialContext ic = new InitialContext();
If you are using JUnit, follow this doc: https://blogs.oracle.com/randystuph/entry/injecting_jndi_datasources_for_junit
You can use PowerMock to mock construction of the InitialContext and control its behavior. Constructor Mocking is documented here.
PowerMock tests can be quite messy and complicated, refactoring is normally a better option.
Today I've faced the same problem (we can't user PowerMock) and solved it this way:
Don't lookup in the constructor so when you invoke #InitMock on the object, the constructor doesn't require the context yet.
Create a method for retrieving the service bean when needed like "getService().serviceMethod(param, param ...)":
/* Class ApplicationResourceProvider */
/* We can mock this and set it up with InjectMocks */
InitialContext ic;
/* method hiding the lookup */
protected ApplicationService getService() throws NamingException {
if(ic == null)
ic = new InitialContext();
return (ApplicationService)ic.lookup("java:global/defaultApplicationLocal");
}
On the test, set it up:
#Mock
ApplicationService applicationServiceBean;
#Mock
InitialContext ic;
#InjectMocks
ApplicationResourceProvider arp;
#Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
when(ic.lookup(anyString())).thenReturn(applicationServiceBean);
...
}
Have you considered mockito?
It's as easy as:
InitialContext ctx = mock(InitialContext.class);
By the way, should you choose to use mocks i would recommend reading this article as well: http://martinfowler.com/articles/mocksArentStubs.html
A poor man's standalone implementation using no external libraries:
public class myTestClass {
public static class TestContext extends InitialContext {
public TestContext() throws NamingException {
super(true /*prevents initialization*/);
}
static Object someExpectedValue = "the expected string or object instance";
/*override the method(s) called by the legacy program on _ic, check the parameter and return the wanted value */
public Object lookup(String name) throws NamingException {
return name != null && name.equals("theValueOfVal") ? someExpectedValue : null;
}
}
public static class TestInitialContextFactory implements InitialContextFactory {
public Context getInitialContext(Hashtable<?, ?> arg0) throws NamingException {
return new TestContext();
}
}
public static void main(String[] args) throws SQLException {
System.setProperty(Context.INITIAL_CONTEXT_FACTORY, "the.package.myTestClass$TestInitialContextFactory");
/*now call the legacy logic to be tested*/
...
You could use a switch in the override of the lookup method to return the expected value for each different val value passed to _ic.lookup(val) throughout the legacy program.
Related
We use EJB3 and JBOSS application Server in our Application. I have a Bean lookup utility method where its a generic method written to lookup stateless EJB Beans by JNDI name:
public class BeanFactory {
static Logger logger = LogManager.getLogger(BeanFactory.class.getName());
/**
*
* #param jndiName
* #return
*/
public static <T> T lookup(String jndiName){
logger.info("Inside bean BeanFactory lookup: " + jndiName);
T handle = null;
try {
InitialContext ctx = new InitialContext();
handle = (T) ctx.lookup(jndiName);
} catch (Exception e) {
logger.error(e, e.fillInStackTrace());
}
return handle;
}
So there are classes which have dependencies on Beans and they use lookup method to invoke the methods of the Bean. For Example
private AuthenticationProfileDTO getAuthenticationProfile(String credId) throws OneM2MException {
ResourceProceduresDao dao = BeanFactory.lookup(ResourceProceduresDao.JNDI_NAME);
AuthenticationProfileRemote apRemote = BeanFactory.lookup(AuthenticationProfileRemote.JNDI_NAME);
AuthenticationProfileDTO authenticationProfileDTO;
if (isKpsaId(credId))
authenticationProfileDTO = apRemote.getAuthenticationProfileDTOForSymmKeyID(credId);
else
authenticationProfileDTO = apRemote.getAuthenticationProfileDTOForCredentialID(credId);
return authenticationProfileDTO;
}
So now when we ran JProfiler on the code the lookup method is coming to be time consuming because every time lookup is called a new InitialContext is instantiated.
I was thinking of making the InitialContext static so that only once it's initialized in a static block, but I don't know what implications will it have in terms of getting Bean instances. Since this piece of code is managed by EJB Container, the run time impacts are unknown. After looking up some articles online not much clarity was there.
Any help is appreciated.
Note that javadoc for InitialContext warns that:
An InitialContext instance is not synchronized against concurrent
access by multiple threads. Multiple threads each manipulating a
different InitialContext instance need not synchronize.
Threads that need to access a single InitialContext instance
concurrently should synchronize amongst themselves and provide the
necessary locking.
So, making the field static isn't necessarily a good idea as you'll need to synchronize each lookup(jndiName) call, and this may cause other issues as per comment by James R. Perkins.
However as you have shown that getAuthenticationProfile(String credId) calls lookup twice, there is no reason why you can't make a BeanFactory hold one InitialContext to reduce the number of instances by re-using InitialContext within same calling methods.
public class BeanFactory {
private final InitialContext ctx;
private BeanFactory(InitialContext initialContext) {
this.ctx = initialContext;
}
private static final Logger logger = LogManager.getLogger(BeanFactory.class.getName());
/** JNDI lookup*/
public <T> T lookup(String jndiName){
// logger.info("Inside bean BeanFactory lookup: " + jndiName);
try {
return (T) ctx.lookup(jndiName);
} catch (Exception e) {
RuntimeException re = new RuntimeException("Could not find jndi: "+jndiName, e);
logger.error(re);
throw re;
}
}
/** Setup a new BeanFactory */
public static BeanFactory create() {
try {
return new BeanFactory(new InitialContext());
} catch (Exception e) {
throw new RuntimeException("Could not create a new context", e);
logger.error(re);
throw re;
}
}
This allows getAuthenticationProfile to use a single InitialContext for 2 lookups:
BeanFactory ctx = BeanFactory.create();
ResourceProceduresDao dao = ctx.lookup(ResourceProceduresDao.JNDI_NAME);
AuthenticationProfileRemote apRemote = ctx.lookup(AuthenticationProfileRemote.JNDI_NAME);
You might also consider whether saving BeanFactory as a thread local would help though I would be very concerned about doing this an application server because you may have little control over which and how many threads instantiate InitialContext and what from what context they run. However it might be suitable within a standalone client program accessing your EJB server logic:
private static final ThreadLocal<BeanFactory> BEANS = ThreadLocal.withInitial(BeanFactory::create);
private static BeanFactory local() {
return BEANS.get();
}
// Example lookups:
ResourceProceduresDao dao = BeanFactory.local().lookup(ResourceProceduresDao.JNDI_NAME);
AuthenticationProfileRemote apRemote = BeanFactory.local().lookup(AuthenticationProfileRemote.JNDI_NAME);
when I try to mock following method(Method is using remote EJB call for business logic) for the Junit test, it gives javax.naming.NoInitialContextException
private void someMethod(int id1, int id2, HashMap map){
......some code........
Context ctx = new InitialContext();
Object ref = ctx.lookup("com.java.ejbs.MyEJB");
EJBHome ejbHome = (EJBHome)PortableRemoteObject.narrow(ref, EJBHome.class);
EJBBean ejbBean = (EJBBean)PortableRemoteObject.narrow(ejbHome.create(), EJBBean.class);
ejbBean.someMethod(id1,name);
.......some code.......}
My unit test for above method
#Test
public void testsomeMethod() throws Exception {
.......setting initial code...
//Mock context and JNDI
InitialContext cntxMock = PowerMock.createMock(InitialContext.class);
PowerMock.expectNew(InitialContext.class).andReturn(cntxMock);
expect(cntxMock.lookup("com.java.ejbs.MyEJB")).andReturn(refMock);
..........some code..........
PowerMock.replayAll();
Whitebox.invokeMethod(ObjectOfsomeMethodClass, "someMethod", id1, id2, map);
}
when the Whitebox.invokeMethod(ObjectOfsomeMethodClass, "someMethod", id1, id2, map) method invokes it gives following exception.
javax.naming.NoInitialContextException: Need to specify class name in environment or system property, or as an applet parameter, or in an application resource file: java.naming.factory.initial
at javax.naming.spi.NamingManager.getInitialContext(NamingManager.java:645)
at javax.naming.InitialContext.getDefaultInitCtx(InitialContext.java:288)
at javax.naming.InitialContext.getURLOrDefaultInitCtx(InitialContext.java:325)
at javax.naming.InitialContext.lookup(InitialContext.java:392)
I believe, although we mock the Context in test method, it does not use the mock object when calling Whitebox.invokeMethod(ObjectOfsomeMethodClass, "someMethod", id1, id2, map) method, instead of that its trying to invoke the Context ctx = new InitialContext(); method in original method(someMethod).
Handmade
As InitialContext doc says, you can provide your own factory for InitialContext objects, using java.naming.factory.initial system property. When the code runs inside application server, the system property is set by the server. In our tests, we provide our own implementation of JNDI.
Here's my Mockito only solution: I defined a custom InitialContextFactory class, that returns a mock of InitialContext. You customize the mock as you wish, probably to return more mocks on lookup calls.
public class PlainTest {
#Mock InitialContextFactory ctx;
#InjectMocks Klasa1 klasa1;
public static class MyContextFactory implements InitialContextFactory
{
#Override
public Context getInitialContext(Hashtable<?, ?> environment) throws NamingException {
ConnectionFactory mockConnFact = mock(ConnectionFactory.class);
InitialContext mockCtx = mock(InitialContext.class);
when(mockCtx.lookup("jms1")).thenReturn(mockConnFact);
return mockCtx;
}
}
#Before
public void setupClass() throws IOException
{
MockitoAnnotations.initMocks(this);
System.setProperty("java.naming.factory.initial",
this.getClass().getCanonicalName() + "$MyContextFactory");
}
Spring (added by edit)
If you don't mind leveraging Spring Framework for testing purposes, here's their simple solution: SimpleNamingContextBuilder:
SimpleNamingContextBuilder builder = new SimpleNamingContextBuilder();
DataSource ds = new DriverManagerDataSource(...);
builder.bind("java:comp/env/jdbc/myds", ds);
builder.activate();
It's ok to put it in #Before or #BeforeClass. After activate(), jndi data will be pulled from spring dummy.
Adding to Jarekczek's answer (Thanks for it!!). Though it is an old question I would like to share my version of it in case it helps someone. I faced the same problem and one might just want to mock IntialContext only in a IntialContextFactory implementation class and it would be a better idea to use this mocked object in other tests or base test classes to avoid duplication.
public class MyContextFactory implements InitialContextFactory {
// Poor Singleton approach. Not thread-safe (but hope you get the idea)
private static InitialContext mockInitialContext;
#Override
public Context getInitialContext(Hashtable<?,?> hshtbl) throws NamingException {
if(mockInitialContext == null) {
mockInitialContext = mock(InitialContext.class);
}
return mockInitialContext;
}
}
public class TestClass {
private DataSource mockDataSource;
private Connection mockConnection;
protected void mockInitialContext() throws NamingException, SQLException {
System.setProperty("java.naming.factory.initial", "com.wrapper.MyContextFactory");
InitialContext mockInitialContext = (InitialContext) NamingManager.getInitialContext(System.getProperties());
mockDataSource = mock(DataSource.class);
mockConnection = mock(Connection.class);
when(mockInitialContext.lookup(anyString())).thenReturn(mockDataSource);
when(mockDataSource.getConnection()).thenReturn(mockConnection);
try {
when(mockDataSource.getConnection()).thenReturn(mockConnection);
} catch (SQLException ex) {
Logger.getLogger(CLASSNAME).log(Level.SEVERE, null, ex);
}
}
}
Reason behind taking this approach being if someone wants to use DataSource or any other resource provided by JNDI in a different way for different tests, you can follow this approach. There shall be just one instance created for IntialContext unless a multi-threaded test tries to access it simultaneously (don't know why would one try to do that!). That instance can be used in all places to get JNDI objects you want and use them as you want.
Hope this helps!
"Make sure you wash your hands before every meal and avoid System.out.println while debugging for healthy lifestyle"
You can refactor your code and extract the initialization of the context in new method.
private void someMethod(int id1, int id2, HashMap map){
......some code........
Context ctx = getInitialContext();
Object ref = ctx.lookup("com.java.ejbs.MyEJB");
EJBHome ejbHome = (EJBHome)PortableRemoteObject.narrow(ref, EJBHome.class);
EJBBean ejbBean = (EJBBean)PortableRemoteObject.narrow(ejbHome.create(), EJBBean.class);
ejbBean.someMethod(id1,name);
.......some code.......}
Your test code will be something like this:
Context mockContext = mock(Context.class);
doReturn(mockContext).when(yourclass).getInitalContext();
...... some code....
As of now (PowerMock 1.7.4)
Create a mock using PowerMockito.mock(InitialContext.class) rather than PowerMockito.createMock(InitialContext.class)
#Test
public void connectTest() {
String jndi = "jndi";
InitialContext initialContextMock = PowerMockito.mock(InitialContext.class);
ConnectionFactory connectionFactoryMock = PowerMockito.mock(ConnectionFactory.class);
PowerMockito.whenNew(InitialContext.class).withNoArguments().thenReturn(initialContextMock);
when(initialContextMock.lookup(jndi)).thenReturn(connectionFactoryMock);
...
// Your asserts go here ...
}
Do not create the InitialContext manually but let PowerMock do it for you. Also do not create a spy in which PowerMock expects an object. This means that you need to create the InitialContext instance.
Define the following Custom Classes
public static class CustomInitialContext extends InitialContext {
Hashtable<String, Object> ic = new Hashtable<>();
public CustomInitialContext() throws NamingException {
super(true);
}
public void bind(String name, Object object) {
ic.put(name, object);
}
public Object lookup(String name) throws NamingException {
return ic.get(name);
}
}
public static class CustomInitialContextFactory implements InitialContextFactory {
static InitialContext ic;
public CustomInitialContextFactory() {
if (ic == null) {
try {
ic = new CustomInitialContext();
} catch (NamingException e) {
e.printStackTrace();
}
}
}
public Context getInitialContext(Hashtable<?, ?> arg0) throws NamingException {
return ic;
}
}
public static class CustomInitialContextFactoryBuilder implements InitialContextFactoryBuilder {
#Override
public InitialContextFactory createInitialContextFactory(Hashtable<?, ?> environment) throws NamingException {
return new CustomInitialContextFactory();
}
}
and declare factory as
NamingManager.setInitialContextFactoryBuilder(new CustomInitialContextFactoryBuilder());
I have 2 modules containing classes:
blog.model.ArticleDAO
blog.model.CategoryDAO
users.model.UserDAO
users.model.UserGroupDAO
All these DAOs have a dependency on the same service, but I need to inject a different instance based on the package.
I mean the module blog should have a specific instance of MyService, and the module users should have another instance of MyService.
I don't want to create 2 named services because some day I may want to use the same service for all DAOs. Or I could also want to inject another specific instance for a specific class...
Is there a way to inject a service based on the package of a class?
A way to say:
inject foo (instance of MyService) into classes that are in blog.*
inject bar (instance of MyService) into classes that are in users.*
but keeping all my classes unaware of that! Their configuration should only state "Inject an instance of MyService".
First I want to say, I find this a strange requirement. I am also wondering why your DAOs need a Service. In a normal layered design, this is the opposite (the Service uses the DAO).
However I find the challenge interesting, I tried to use a FactoryBean to create a Java Proxy class which would redirect at runtime to the correct instance of MyService depending of the caller package. Here is the code:
public class CallerPackageAwareProxyFactoryBean implements
FactoryBean<MyService>, ApplicationContextAware {
private Class<?> targetServiceType;
private ApplicationContext applicationContext;
private InvocationHandler invocationHandler = new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
if (ReflectionUtils.isEqualsMethod(method)) {
// Only consider equal when proxies are identical.
return (proxy == args[0]);
} else if (ReflectionUtils.isHashCodeMethod(method)) {
// Use hashCode of service locator proxy.
return System.identityHashCode(proxy);
} else if (ReflectionUtils.isToStringMethod(method)) {
return "Service dispatcher: " + targetServiceType.getName();
} else {
String callerPackageFirstLevel = getCallerPackageFirstLevel();
Map<String, ?> beans = applicationContext
.getBeansOfType(targetServiceType);
for (Map.Entry<String, ?> beanEntry : beans.entrySet()) {
if (beanEntry.getKey().startsWith(callerPackageFirstLevel)) {
return method.invoke(beanEntry.getValue(), args);
}
}
throw new IllegalArgumentException(
String.format(
"Could not find any valid bean to forward call for method %s.",
method.getName()));
}
}
private String getCallerPackageFirstLevel() {
Throwable t = new Throwable();
StackTraceElement[] elements = t.getStackTrace();
String callerClassName = elements[3].getClassName();
return callerClassName.split("\\.")[0];
}
};
public MyService getObject() throws Exception {
return (MyService) Proxy.newProxyInstance(Thread.currentThread()
.getContextClassLoader(), new Class<?>[] { MyService.class },
invocationHandler);
}
public Class<?> getObjectType() {
return MyService.class;
}
public boolean isSingleton() {
return true;
}
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
public void setTargetServiceType(Class<?> targetServiceType) {
this.targetServiceType = targetServiceType;
}
}
I didn't had to change anything to the Dao or Service configuration. I just had to add the creation of the FactoryBean in the Spring context:
<bean id="myService" class="stackoverflow.CallerPackageAwareProxyFactoryBean">
<property name="targetServiceType" value="a.b.c.MyService" />
</bean>
Maybe a few comments:
The caller package can only be get by creating an exception and looking at the stacktrace.
The code of the InvocationHandler is inspired from ServiceLocatorFactoryBean.
I am still wondering if there is an easier way but I think there is not.
You could replace part of the InvocationHandler to use a configuration Map (package => MyService bean name)
I would not recommend using such code in a productive environment.
I have a property file and using Spring property place holder, I set values to the Spring beans. Now, this property file may be modified during the run time. Is there a way to refresh the properties of the Spring beans with this newly modified property value? Especially, I have many singleton beans? How can I refresh them with the new values? Is there already a solution to this or should it be custom coded? If it doesn't already exist, can someone please give the best approach to achieve this? Thanks!
PS: My application is a batch application. I use Spring based Quartz configuration to schedule the batches.
I'll leave this in for reference, but the updated answer is below the divider:
Well the ConfigurableApplicationContext interface contains a refresh() method, which should be what you want, but the question is: how to access that method. Whichever way you do it, you'll start with a bean that has a dependency of type ConfigurableApplicationContext:
private ConfigurableApplicationContext context;
#Autowired
public void setContext(ConfigurableApplicationContext ctx){
this.context = ctx;
}
Now the two basic options I'd suggest would be to either
use the Task Execution Framework and let your bean watch the property resources regularly, refreshing the ApplicationContext when it finds changes or
expose the bean to JMX, allowing you to manually trigger the refresh.
Referring to comments: since it seems impossible to refresh the entire context, an alternative strategy would be to create a properties factory bean and inject that into all other beans.
public class PropertiesFactoryBean implements FactoryBean<Properties>{
public void setPropertiesResource(Resource propertiesResource){
this.propertiesResource = propertiesResource;
}
private Properties value=null;
long lastChange = -1L;
private Resource propertiesResource;
#Override
public Properties getObject() throws Exception{
synchronized(this){
long resourceModification = propertiesResource.lastModified();
if(resourceModification != lastChange){
Properties newProps = new Properties();
InputStream is = propertiesResource.getInputStream();
try{
newProps.load(is);
} catch(IOException e){
throw e;
} finally{
IOUtils.closeQuietly(is);
}
value=newProps;
lastChange= resourceModification;
}
}
// you might want to return a defensive copy here
return value;
}
#Override
public Class<?> getObjectType(){
return Properties.class;
}
#Override
public boolean isSingleton(){
return false;
}
}
You could inject this properties bean into all your other beans, however, you would have to be careful to always use prototype scope. This is particularly tricky inside singleton beans, a solution can be found here.
If you don't want to inject lookup methods all over the place, you could also inject a PropertyProvider bean like this:
public class PropertiesProvider implements ApplicationContextAware{
private String propertyBeanName;
private ApplicationContext applicationContext;
public void setPropertyBeanName(final String propertyBeanName){
this.propertyBeanName = propertyBeanName;
}
#Override
public void setApplicationContext(final ApplicationContext applicationContext) throws BeansException{
this.applicationContext = applicationContext;
}
public String getProperty(final String propertyName){
return ((Properties) applicationContext.getBean(propertyBeanName)).getProperty(propertyName);
}
}
I'm trying to create an EJB factory class, which works like this: You have a method which takes as argument a class of an EJB, then it checks whether the EJB has a remote interface (if not throw an exception) and if it does, it returns the concerning EJB.
The code below does exactly this. However the object it returns is of the type of the remote interface of the concerning bean and not of the bean itself. How can I change this? Is there a way to tell Java that the generic type T is of the same type as the class passed to the methods.
import java.util.Properties;
import javax.ejb.Remote;
import javax.ejb.Stateless;
import javax.naming.*;
public class EJBFactory
{
private InitialContext ctx;
public EJBFactory() throws NamingException {
ctx = new InitialContext();
}
public EJBFactory(String host, String port) throws NamingException {
Properties props = new Properties();
props.setProperty("org.omg.CORBA.ORBInitialHost", host);
props.setProperty("org.omg.CORBA.ORBInitialPort", port);
ctx = new InitialContext(props);
}
.
// To improve: The object returned should be of the type ejbClass
// instead of the remote interface, which it implements
public <T> T createEJB(Class ejbClass) throws NamingException
{
Class remoteInterface = null;
for(Class interface_: ejbClass.getInterfaces()) {
if(interface_.isAnnotationPresent(Remote.class))
remoteInterface = interface_;
}
if(remoteInterface == null)
throw new IllegalArgumentException(
"EJB Requires a remote interface");
// Get the stateless annotation, then get the jndiName
Stateless stateless =
(Stateless)ejbClass.getAnnotation(Stateless.class);
String jndiName = stateless.mappedName();
T ejbObj = (T) ctx.lookup(jndiName);
return ejbObj;
}
}
Example of a unit test which uses the Factory.
import junit.framework.TestCase;
public class SimpleEJBTest extends TestCase
{
TestRemote testBean;
#Override
protected void setUp() throws Exception {
super.setUp();
EJBFactory ejbFactory = new EJBFactory();
testBean = ejbFactory.createEJB(TestBean.class);
}
public void testSayHello() {
assertEquals("Hello", testBean.sayHello());
}
}
Note: The example works with Glassfish, I didn't test it with any other app server.
Clients of EJBs interact with them through the local/ remote interface that the EJBs implement. Client applications never have direct access to an actual session bean class instance. This is done to make instance pooling possible, where the container can reuse EJB instances to service different requests.
I'm not sure why you need to access the actual bean's object (since obviously I dont know your requirement). But if you still need to create an instance of that you can do it as follows using reflection Class.forName(className).newInstance(); Again the instance that you create like this is not an EJB. It is just a POJO thats all.
EDIT - after your comment regarding junit testing: When you access business methods from a JavaSE as follows, you are actually calling the methods in the EJB - just that you interact thru the interface. So if you want to test any business methods you can still do it from an object got thru a JNDI lookup in a Junit test.
//MyGreatBean implements MyGreat. MyGreat has #Remote, MyGreatBean has #Stateless
ref = jndiContext.lookup("MyGreatBean/remote");
MyGreat bean = (MyGreat) ref;
String retValue = bean.businessMethod();
assertEquals("Success", retValue);
From an earlier comment, I get a feeling you want to check what kind of annotations have been added to the actual EJB class - if you want to do that kind of checking without actually running the business methods, you can create an instance using Class.forName... like I mentioned above. When you create an instance like this you can only call methods that don't do any "Java EE" stuff. For example you can call a method in the EJB class that is as follows
public String someMethod(){
return "I am a POJO but I look like an EJB";
}
I do not think that you can get the EJB object. You can only get the interface. The createEJB should be called with the interface and it returns the interface.
try replacing
public <T> T createEJB(Class ejbClass) throws NamingException
with
public <T> T createEJB(Class<T> ejbClass) throws NamingException
Can you try this?
Create a interface. Make it have #Remote. Your ejb that is annotated with #Stateless should implement the above created interface. Now try to do the same thing that you are doing I think it should give you the desired result. Typing it down here without copying from an ide so excuse any errors. But you should get the drift I guess.
#Remote
public interface Example{
String some();
}
#stateless
public class ExampleBean implements Example{
}