BaseFitNesseFixture.java
package com.reallifedeveloper.tools.test.fitnesse;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.context.support.GenericApplicationContext;
/**
* Base class for all FitNesse fixtures.
*
* @author RealLifeDeveloper
*/
public class BaseFitNesseFixture {
/**
* The date format used by {@link #parseDate(String)} ({@value #DATE_FORMAT}).
*/
public static final String DATE_FORMAT = "yyyy-MM-dd";
private static final ThreadLocal<ApplicationContext> APPLICATION_CONTEXT = ThreadLocal
.withInitial(() -> new GenericApplicationContext());
private final Logger logger = LoggerFactory.getLogger(getClass());
private @Nullable String comment;
/**
* Creates a new {@code BaseFitNesseFixture} object using the given Spring application context. The application context is associated
* with the current thread, so this fixture instance is assumed to be used by a single thread.
*
* @param applicationContext the Spring application context to use, must not be {@code null}
*/
public BaseFitNesseFixture(ApplicationContext applicationContext) {
if (applicationContext == null) {
throw new IllegalArgumentException("applicationContext must not be null");
}
APPLICATION_CONTEXT.set(applicationContext);
}
/**
* Creates a new {@code BaseFitNesseFixture} object reading the Spring application context from the given classpath resource, which
* should be a Spring XML configuration file.
*
* @param springConfigurationResourceName name of the classpath resource containing Spring XML configuration, must not be {@code null}
*/
public BaseFitNesseFixture(String springConfigurationResourceName) {
this(initApplicationContext(springConfigurationResourceName));
}
private static ApplicationContext initApplicationContext(String springConfigurationResourceName) {
if (springConfigurationResourceName == null) {
throw new IllegalArgumentException("springConfigurationResourceName must not be null");
}
return new ClassPathXmlApplicationContext(springConfigurationResourceName);
}
/**
* A comment that is useful as documentation in most FitNesse tests.
* <p>
* Any FitNesse test that uses this base class can add the column "Comment", which in most cases is not used by the test, but is useful
* as documentation of the test case.
*
* @return the comment that was written for the test case
*/
public @Nullable String getComment() {
return comment;
}
/**
* Specifies a comment for the current test case.
*
* @param comment the comment that belongs to the test case
*/
public void setComment(String comment) {
this.comment = comment;
}
/**
* Gives the Spring {@code ApplicationContext} associated with this thread.
* <p>
* This method never returns {@code null}; if it is not possible to create an {@code ApplicationContext}, an exception is thrown.
* <p>
* Note that the {@code ApplicationContext} is managed statically, so if a class already has loaded a context using this method, and
* another class is later used that wants to use a different configuration, no reload will happen automatically. In this case, you can
* use the method {@link #resetApplicationContext()} to reset the context and force a reload.
*
* @return the Spring {@code ApplicationContext}, never {@code null}
*/
protected ApplicationContext getApplicationContext() {
return APPLICATION_CONTEXT.get();
}
/**
* Sets the Spring {@code ApplicationContext} to {@code null}. This can be used to force a reload of the context the next time the
* method {@link #getApplicationContext()} is called.
*/
public static void resetApplicationContext() {
// applicationContext = null;
}
/**
* Looks up a Spring bean of a given class in the {@code ApplicationContext}. This method never returns {@code null}; if the bean does
* not exist, an exception is thrown.
*
* @param <T> the type of the Spring bean
* @param beanClass the class of the Spring bean
*
* @return the Spring bean, never {@code null}
*/
protected <T> T getBean(Class<T> beanClass) {
return getApplicationContext().getBean(beanClass);
}
/**
* Gives an {@code org.slf4j.Logger} that can be used by the concrete fixture classes for logging.
*
* @return an {@code org.slf4j.Logger}
*/
protected Logger logger() {
return logger;
}
/**
* Parses a date string on the form {@value #DATE_FORMAT} and returns the corresponding {@code java.util.Date} object.
*
* @param date the date string to parse, should be on the form {@value #DATE_FORMAT}
*
* @return the {@code java.util.Date} corresponding to {@code date}
*
* @throws IllegalArgumentException if {@code date} cannot be parsed
*/
protected Date parseDate(String date) {
DateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT);
try {
return dateFormat.parse(date);
} catch (ParseException e) {
throw new IllegalArgumentException("Unparseable date: " + date, e);
}
}
/**
* A null-safe toString method.
*
* @param o the object to be converted to string, can be {@code null}
*
* @return {@code null} if {@code o} is {@code null}, otherwise {@code o.toString()}
*/
protected @Nullable String toString(Object o) {
return o == null ? null : o.toString();
}
/**
* Make finalize method final to avoid "Finalizer attacks" and corresponding SpotBugs warning (CT_CONSTRUCTOR_THROW).
*
* @see <a href="https://wiki.sei.cmu.edu/confluence/display/java/OBJ11-J.+Be+wary+of+letting+constructors+throw+exceptions">
* Explanation of finalizer attack</a>
*/
@Override
@SuppressWarnings({ "checkstyle:NoFinalizer", "PMD.EmptyFinalizer" })
protected final void finalize() throws Throwable {
// Do nothing
}
}