Testing ServiceLocator using JUnit - java

This is a follow up question to my previous question.
I am trying to write test case for my ServiceLocator class but it gives me the following error:
com/iplanet/ias/admin/common/ASException
java.lang.NoClassDefFoundError: com/iplanet/ias/admin/common/ASException
at java.lang.ClassLoader.defineClass1(Native Method)
My test case:
public void testServiceLocator () throws UivException, NamingException
{
DataSource ds = ServiceLocator.getInstance().getDataSource("jdbc/RSRC/my/mydb");
//I have not put any assert statements because i know above line is failing
}
The above code fails on getInstance() method which looks like this:
static public ServiceLocator getInstance() throws UivException {
try {
if (me == null) {
synchronized(ServiceLocator.class) {
me = new ServiceLocator();
}
}
return me;
}
catch (UivException e) {
throw new UivException(ErrorCode.SERVICE_LOCATOR_ERROR,
ErrorCode.SERVICE_LOCATOR_LOOKUP_ERROR,
e.getMessage());
}
}
I know this ServiceLocator works fine because when I test my application from the front end there are no issues. Only reason I am writing this test case is because I want to test my DAO. And for me to test my DAO ServiceLocator has to work (from JUnit).
I don't have a clue what to make of the error message. Does anyone want to suggest something I can try that will hopefully work?
EDIT: ServiceLocator constructor
private ServiceLocator() throws UivException {
try {
ic = new InitialContext();
// System.out.println("Created the Initial Context");
cache = Collections.synchronizedMap(new HashMap());
}
catch (NamingException ne) {
throw new UivException(ErrorCode.SERVICE_LOCATOR_ERROR,
0, ne.getMessage());
}
catch (NullPointerException e) {
throw new UivException(ErrorCode.SERVICE_LOCATOR_ERROR,
0, e.getMessage());
}
}

Actually, the error is pretty clear: java.lang.NoClassDefFoundError: com/iplanet/ias/admin/common/ASException indicates that the definition of ASException cannot be found at runtime.
When running in the application server, this class is provided by iPlanet Application Server and the code just runs fine. To run this code outside the application server context, you'll have to provide it "manually" by putting it to the classpath. So you have to add the right iPlanet JAR to the classpath (this is the tricky part, you'll have to find which one).
Additionally, I can see taht you're using the InitialContext non-arg constructor so you're using iPlanet's environment settings when running inside the application server. Outside iPlanet (e.g. for a unit test), you'll have to provide the right JNDI properties for iPlanet yourself. To do so, you'll have to put a jndi.properties file on the classpath with (at least) the initial context factory and the provider URL. Something like this:
java.naming.factory.initial=...
java.naming.provider.url=...
Check your iPlanet documentation for the values.

Well, is com.iplanet.ias.admin.common.ASException on the classpath when you invoke your tests?
Are you relying on the app server to have the JDBC driver's libraries in it's classpath, or are you deploying it yourself?

I'm curious why you're writing a service locator. Is it part of an assignment? In the real world, Dependency Injection is the clearly superior choice, using either Guice or Spring.

Related

Spring do on startup ingnoring failures

I need to perform some work when the spring application is ready, something similar to #Scheduled but I want it to perform only once.
I found some ways to do it, such as using #PostConstruct on a bean, using #EventListener or InitializingBean, however, all of these ways does not match my need. If during the execution of this logic something goes wrong, I want to ignore it so the application starts anyway. But using these methods the application crashes.
Of course, I can surround the logic with try-catch and it will work. But, is there any more elegant way?
We faced a similar issue with our microservices , in order to run code just after startup we added a Component.
ApplicationStartup implements ApplicationListener<ApplicationReadyEvent>
Within the application to make a call to the services just after application startup, this worked for us.
#Component
public class ApplicationStartup implements ApplicationListener<ApplicationReadyEvent> {
#Autowired
YourService yourService;
#Override
public void onApplicationEvent(final ApplicationReadyEvent event) {
System.out.println("ApplicationReadyEvent: application is up");
try {
// some code to call yourservice with property driven or constant inputs
} catch (Exception e) {
e.printStackTrace();
}
}
}
When you use #PostConstruct for implementing a logic, the application is not ready yet, so it kind of contradicts your requirement. spring initializes the beans one by one (with respect to the dependencies between them.
After all it builds up the application context.
When the application context is fully initialized, spring indeed allows listeners to be run. So The listeners is a way to go - when the listener is invoked the application is ready.
In both cases (PostConstruct, EventListener) as long as you're not using try/catch block the application context will fail, because it waits till all the listeners will be done.
You can use #Async if you don't want the application context to wait for listeners execution. In this case the exception handling will be done by the task executor. See here
Personally I don't see any issue with try/catch approach
You can use #PostConstruct (as you said) but you must wrap your business in try catch and ignore it when it throws an exception.
Sample Code
#PostConstruct
void init() {
try {
//Your business
}
catch (Exception e) {
//Do nothing Or you can just log
}

How to connect junit and tomcat in java?

I have configured MysqlDataSource in tomcat using this link.I have written junit test cases.when am i calling below connection from junit it throws following errors.
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
I have used following code
class DataConnection {
private static DataSource dataSource;
public DataConnection() {
try {
Context ctx = new InitialContext();
dataSource = (DataSource)ctx.lookup("java:comp/env/jdbc/test");
} catch (NamingException e) {
e.printStackTrace();
}
}
public static Connection getConnection() throws SQLException {
new DataConnection();
Connection con=dataSource.getConnection();
return con;
}
}
How to call tomcat from junit? How to achieve this?
The code you give gets the database connection from JNDI, e.g. when running in tomcat from the container. However, for Unit Tests (assuming that's what you use JUnit for) I'd rather suggest to use "dependency injection" - e.g. explicitly pass a database connection to the code under test or manually set it up before the test runs.
There's no need to rely on JNDI for executing your tests: That's not what you want to test, instead, you want to just verify that your actual code is running correctly.
You don't need any fancy library (e.g. spring) for dependency injection, just a slightly adjusted architecture. This will greatly enhance the testability of your application and lower the execution time of your tests.
(This is based on my assumptions of your situation based on the little bit of information that you give in your question)
Give TomcatJNDI a try. It is based on embedded Tomcat but initializes only Tomcat's JNDI environment without starting a server. So you can access all your resources as configured in Tomcat's configuration files in tests or from within any Java SE application. The API is simple. For instance to get a DataSource declared in context.xml:
TomcatJNDI tomcatJNDI = new TomcatJNDI();
tomcatJNDI.processContextXml(contextXmlFile);
tomcatJNDI.start();
Then you can lookup the DataSource as usual
DataSource ds = (DataSource) InitialContext.doLookup("java:comp/env/path/to/datasource")
More information about TomcatJNDI can be found here.

Simple Arquillian test to start and stop deployed app

I'm trying to wrap my head around Arquillian and perhaps even start using it in my project. I have a simple Java web app that deploys as a WAR to Tomcat.
In my project, I define a ServletContextListener impl so that I can execute code when Tomcat starts and stops the application.
I'm trying to write a super-simple Arquillian test class that uses ShrinkWrap and:
Confirms that my bundled WAR can be deployed to Tomcat and started without throwing exceptions; and
Can access a simple system property once the app is running (that the ServletContextListener checks for); and
Confirms that when Tomcat shuts down, no exceptions are thrown (clean shutdown)
Also, my class that implements ServletContextListener is called AppLifecycleManager:
public class AppLifeCycleManager implements ServletContextListener {
private String logLevel;
// Injected by Guice, but that's not really relevant for this question.
#Inject
private Logger logger;
// Getter and setter for logLevel and logger
#Override
public void contextInitialized(ServletContextEvent event) {
logLevel = System.getProperty("log.level");
}
#Override
public void contextDestroyed(ServletContextEvent event) {
logger.info("Peacefully shutting down the application.");
}
}
So far, here's my best attempt:
#RunWith(Arquillian.class)
public class MyFirstRealIntegrationTest {
#Deployment
public static Archive<?> createDeployment() {
// Haven't figured this part out yet, but for the sake of
// this question lets pretend this returns a properly-packaged
// WAR of my web app, the same that my Ant build currently produces.
}
#Test
public void shouldBeAbleToStartTomcatWithoutExceptions() {
// Given
Archive war = createDeployment();
// When - deploy war to Tomcat container
try {
// ??? how to access/init a Tomcat container?
TomcatContainer tomcat = new TomcatContainer(); // this is wrong
tomcat.start();
} catch(Throwable throwable) {
// Starting the container should not throw exceptions
Assert.fail();
}
}
#Test
public void shouldBeAbleToStopTomcatWithoutExceptions {
// Same setup as above test but stops tomcat and checks for
// thrown exceptions. Omitted for brevity.
}
#Test
public void shouldHaveAccessToSysPropsOnceRunning() {
// Here, deploy to the container and start it.
// Then, confirm that AppLifecycleManager correctly read
// the log.level system property.
// Given
Archive war = createDeployment();
TomcatContainer tomcat = new TomcatContainer();
// When - AppLifeycleManager should now read the system property
tomcat.start();
// Then - make sure log.level was set to "DEBUG" and that it was
// correctly read by AppLifeCycleManager.
Assert.assertTrue(war.getClass(AppLifeCycleManager.class)
.getLogLevel().equals("DEBUG"));
}
}
So, given my approach here, I immediately have several problems:
I'm not sure how to access/instantiate my Tomcat container so that it can even be started/stopped
I'm not sure how to actually execute tests from inside my running/deployed web app. In the 3rd test above I used war.getClass(AppLifeCycleManager.class).getLogLevel() to try and get access to a "live" class instance and check its logLevel property's runtime value, but I know this is wrong.
So I ask: how would a battle-worn Arquillian veteran write these 3 simple tests, and how do I actually go about performing tests on my "running" web app from inside the JUnit test? Thanks in advance.
I dont't think you should handle the startup/shutdown of tomcat in your test. I would by much easier if you used the embedded tomcat container: https://docs.jboss.org/author/display/ARQ/Tomcat+7.0+-+Embedded. In this case, arquillian will handle the startup and shutdown of tomcat.
You should create the deployment in the method annotated with #Deployment. Just read the following guide: http://arquillian.org/guides/getting_started/
You're almost there. createDeployment manages the lifecycle of your embedded container for you (starts and stops the virtual container automatically). That way, you're just focussing on the tests themselves. To write your integration tests, there's no "framework" or "Arquillian API" to code against. You just call classes and methods the way your main code does.
The key thing here is: your tests are actually running inside the container. If an exception occurs, or if your assert fails, the Arquillian runner throws an exception and stops the container. For your code example, where you're testing to see if you can read a system property:
#RunsWith(Arquillian.class)
public class MyArquillianTest {
#Deployment
public Archive<?> createDeployment() { ... }
#Test
public void shouldBeAbleToReadSysPropAtStartup() {
Assert.assertTrue(System.getProperty("log.level") != null);
}
}
Remember, Assert.assertTrue(System.getProperty("log.level") != null) Arquillian is "transporting" your code inside the container you configure it for. So that assert is actually running inside your deployed container.

Can I ignore BeanCreationException and inject null instead?

We have a situation where our Spring wires up some beans that include ActiveMQ classes built with Java 6. Our application runs on customer's servers, so we can't guarantee that they have Java 6 or later installed. If they happen to have Java 5, the application can't start because of BeanCreationExceptions with the classes that depend on ActiveMQ (root cause being a UnsupportedClassVersionError).
So my question is, is there any way to ignore a BeanCreationException and still start the application? I want to be able to display an error message saying that they need to install Java 6 or later, but since the application can't even start, I never have a chance to do this.
My hunch is that there is no way to do this, because Spring needs to be able to guarantee that my application is built properly after initialization, but I thought I would ask anyway. Any other suggestions for how to accomplish my end goal would also be helpful and appreciated.
We are using Spring 3.0.6
Thanks!
If you can upgrade to Spring 3.1 (stable), take advantage of Java configuration:
#Bean
public SomeBean public someBean() {
if(isEverythingOkWithJavaVersion()) {
return new CorrectBean();
} else {
return null;
}
}
or:
#Bean
public SomeBean public someBean() {
try {
return new CorrectBean();
} catch(UnsupportedClassVersionError e) {
log.warn("", e);
return null;
}
}
In older versions of Spring FactoryBean might be used to implement the same logic. Instead of returning null you might also return some fake implementation that you can discover later and warn the user when the application tries to use it.
First off, any throwables that are a subclass of java.lang.Error are generally considered to be non-recoverable. So while it's possible to catch them, it's strongly discouraged:
An Error is a subclass of Throwable that indicates serious problems that a reasonable application should not try to catch. Most such errors are abnormal conditions.
However, if all you're going to do is display an error message, then you should be able to get away with it.
SO to get back to your question, I suggest creating an implementation of Spring's FactoryBean interface, which would try to load the ActiveMQ classes. If it works, then it can return the appropriate object from FactoryBean.getObject. If it fails (via a caught UnsupportedClassVersionError), then it can return either a null, or some other object representing that condition.
You could create a factory bean and let it create the actual ActiveMQ bean. If it can't be initialized the factory could return a dummy/mock implementation so things don't break. Later you could ask the factory if everything went alright.
http://static.springsource.org/spring/docs/2.5.x/reference/beans.html#beans-factory-extension-factorybean

OpenEJB Local Client Injection Fails

Note that I'm mirroring the example given here very closely.
In fact, my situation is somewhat simpler as I'm not even testing with a persistence unit at this point. My test project provides a simple MDB and a session bean; both the MDB and the session bean are getting loaded as normal, and can be successfully tested (in a constrained fashion) without injection.
The suggested injection with the #LocalClient annotation on my unit tests is failing with the known error:
javax.naming.NamingException: Unable to find injection meta-data for [your-class]. Ensure that class was annotated with #org.apache.openejb.api.LocalClient and was successfully discovered and deployed. See http://openejb.apache.org/3.0/local-client-injection.html
When I visit this page it informs me that I may need to add an extra property to my test case context setup. So that now looks like:
#Override
public void setUp() throws Exception {
initializeContext();
}
public void initializeContext() {
Properties p = new Properties();
p.put(Context.INITIAL_CONTEXT_FACTORY, "org.apache.openejb.client.LocalInitialContextFactory");
// the property i've added
p.put("openejb.tempclassloader.skip", "annotations");
try {
InitialContext initialContext = new InitialContext(p);
initialContext.bind("inject", this);
} catch (Throwable throwable) {
throwable.printStackTrace();
throw new RuntimeException(throwable);
}
}
But it's still failing. I really like this idiom and would be very excited if I could successfully use it in my projects.
A few other notes:
I am providing an 'empty' ejb-jar.xml (in src/main/resources) and an application-client.xml (in src/test/resources) as suggested by Apache to tell OpenEJB to scan the classpath [UPDATE: as it turns out, I was doing this wrong. See my answer below for the suggestion that worked for me.]
The test cases annotated with #LocalClient aren't identified by the OpenEJB engine as actually getting picked up and processed properly (as my MDBs are, for example)
Thanks in advance for any help or guidance.
This issue is likely caused by improper location of the descriptors which hint OpenEJB which sorts of modules are available.
To ensure the test-classes get picked up properly, make sure you're placing a file named application-client.xml at src/test/resources/META-INF with the following content:
<application-client/>
This should force OpenEJB to scan and react to the presence of #LocalClient annotations.
I had a similar issue when I tried to test stuff in a test project called tomee-embedded-trial and it turned out that openejb ignores stuff called tomee-.* .
I fixed it for me by specifying the following system properties:
openejb.deployments.classpath.include=".*-trial.*" openejb.deployments.package.include=".*-trial.*"

Categories

Resources