JSF update managed bean with ServletContext listener for testing - jsf

In a JSF 2.2 application, I want to build a war file for testing with Selenium. In that webtest.war, I want to replace a central class, called the NodeCache, with a mock version, called the WebtestNodeCache, to keep the database and other external dependencies out of the tests.
NodeCache is a managed bean:
#javax.faces.bean.ManagedBean(name = NodeCache.INSTANCE)
#javax.faces.bean.ApplicationScoped
public class NodeCache {
public static final String INSTANE = "nodecache";
// ...
}
To sneak in WebtestNodeCache, I use a ServletContextListener like this:
public class WebtestContextListener implements ServletContextListener {
#Override
public void contextInitialized(ServletContextEvent event) {
WebtestNodeCache nodeCache = new WebtestNodeCache();
ServletContext context = event.getServletContext();
context.setAttribute(NodeCache.INSTANCE, nodeCache);
}
#Override
public void contextDestroyed(ServletContextEvent sce) {}
}
In normal builds, WebtestContextListener and WebtestNodeCache are excluded from the war file, in test builds, they are included.
This seems to work: when I log in, I get dummy nodes from the WebtestNodeCache.
Is this a reliable way to replace a bean in application context or did I just get lucky?
Is there a better way to sneak in test dummies?

Using both an #ManagedBean annotation and a Listener to replace the object did not work. The code was always using the unmocked production code managed bean.
Defining a new #ManagedBean with the same name is an error and prevents deployment.
I ended up with this:
Put the #ManagedBean annotation with the same name on both the real bean and its mock.
When building, only include the mocks when building the webtest.war, but not in the regular build.
When building, have the build script (Gradle in my case) copy and filter the sources, looking for a special comment behind the #ManagedBean declaration in the production code and taking out these lines to remove the #ManagedBean declaration on the production code so that only the ones in the mock remains.
So the original NodeCache looks like this now:
#javax.faces.bean.ManagedBean(name = NodeCache.INSTANCE) // webtest:remove
#javax.faces.bean.ApplicationScoped // webtest:remove
public class NodeCache {
public static final String INSTANE = "nodecache";
// ...
}
and the mocked version has the same annotations, just without the comment:
#javax.faces.bean.ManagedBean(name = NodeCache.INSTANCE)
#javax.faces.bean.ApplicationScoped
public class WebtestNodeCache extends NodeCache {
// ...
}
Here is the relevant part of the Gradle build script:
boolean isWebtest = false
gradle.taskGraph.whenReady { taskGraph ->
isWebtest = taskGraph.hasTask(compileWebtestWarJava);
}
task copySrc(type: Copy) {
from "src"
into "${buildDir}/src"
outputs.upToDateWhen {
// Always execute this task so that resources do or don't get filtered
// when switching between normal war file and webtests.
false
}
filter { String line ->
isWebtest && line.contains("webtest:remove") ? null : line;
}
}
This solves the problem for me. Hope someone else finds it useful.

Related

How to run the SpringBootTest with only a single bean and with included resilience4j annotations

I would like to run an integration test of a single bean with resilience4j annotated method in a spring boot app. My intent is to test resiliency of bean method calls while not loading the full spring context.
The setup is as follows:
Dependencies include the following:
io.github.resilience4j:resilience4j-spring-boot2
io.github.resilience4j:resilience4j-reactor
org.springframework.boot:spring-boot-starter-aop
The resilience4j time limited spring bean with method to test:
#Service
public class FooService {
#TimeLimiter(name = "fooTimeLimiter")
public FooResponse foo() {
//entertain operation that might timeout
}
}
Configuration:
resilience4j.timelimiter.instances.fooTimeLimiter.timeoutDuration=1s
And the test:
#SpringBootTest
#ContextConfiguration(classes = FooService.class)
public class FooServiceIT {
#Autowired
private FooService service;
#MockBean
private Bar bar;
#Test
void foo_timeout() {
//setup mocks so the operation delays the output and shall end up with timeout
Assertions.assertThrows(TimeoutException.class, () -> service.foo());
}
}
However, the TimeLimiterAdvice.proceed() is not entertained, no timeout exception is thrown and the test fails.
Same question has been asked here: Testing SpringBoot with annotation-style Resilience4j but there is no solution.
I tried both approaches - implement FooService interface and program directly using the concrete class. With the same result.
How can I achieve the time limiter annotation is taken into account in my test?
Edit: I even tried plain spring test (no spring boot) with the following setup:
#ExtendWith(SpringExtension.class)
#ContextConfiguration(loader = AnnotationConfigContextLoader.class)
public class FooServiceIT {
#Configuration
#Import({TimeLimiterConfiguration.class, FallbackConfiguration.class, SpelResolverConfiguration.class})
static class ContextConfiguration {
#Bean
public FooService fooService() {
//prepare bean;
}
#Bean
public TimeLimiterConfigurationProperties timeLimiterConfigurationProperties() {
return new TimeLimiterConfigurationProperties();
}
}
#Autowired
private FooService service;
//tests...
}
Same result (i.e. no timeout exception).
When dealing with SpringBootTest and #CircuitBreaker, it was sufficient to add #EnableAspectJAutoProxy annotation to the test. After this change, the CircuitBreakerAspect was entertained and the test behaves as expected.
In order to make #TimeLimiter working as expected, one need to add #Bulkhead annotation to the method as well.
The updated method looks as follows:
#Bulkhead(name = "fooBulkhead", type = Type.THREADPOOL)
#CircuitBreaker(
name = "fooCircuitBreaker",
fallbackMethod = "fooFallback"
)
#TimeLimiter(
name = "fooTimeLimiter"
)
public CompletableFuture<FooResponse> foo() {
//...
}
and the test:
#SpringBootTest(classes = FooService.class)
#EnableAspectJAutoProxy
#Import(value = {CircuitBreakerAutoConfiguration.class, TimeLimiterAutoConfiguration.class, BulkheadAutoConfiguration.class})
public class FooServiceIT {
//...
}

Inject different implementations based on application property in Quarkus

I have a Repository interface that has two implementations. One reads data from a locally stored CSV file while the other reads from an Amazon Dynamo DB. I would like to be able to switch between which implementation I'm using based on an application property or custom build profile. I would normally use a Factory to retrieve the correct class at runtime, but I would like to do this with injection if possible.
I found a similar question using Spring boot but couldn't find an equivalent that would work in Quarkus Spring choose bean implementation at runtime
I also tried implementing a Configuration class similar to what is found in the docs here but again didn't have much luck. https://quarkus.io/guides/cdi-reference#default_beans
It feels like I'm missing something obvious so any pointers would be much appreciated.
Here is a simple example of my classes:
#ApplicationScoped
public class ExampleService {
#Inject
ExampleRepository repository;
public List<Data> retrieveData() {
return repository.retrieveData();
}
}
public interface ExampleRepository {
List<Data> retrieveData();
}
#ApplicationScoped
public class DynamoRepository implements ExampleRepository {
#Override
public List<Data> retrieveData() {
//Get Data from DynamoDb
}
}
#ApplicationScoped
public class CsvRepository implements ExampleRepository {
#Inject
CsvBeanHandler csvBeanHandler;
#Inject
LocalFileReader fileReader;
#Override
public List<Data> retrieveData() {
// Get data from CSV
}
}
I currently also have the following in my application.yml:
com:
example:
application:
storage-type: 'CSV' # OR AMAZON_DYNAMO_DB
It looks like they've added this directly to the documentation:
https://quarkus.io/guides/cdi-reference#declaratively-choose-beans-that-can-be-obtained-by-programmatic-lookup
I feel a bit guilty pasting this much, but it's the SO way.
I can add that it is NOT like a Guice 'binding'; BOTH classes will be instantiated, but only one will be injected. Also unlike Guice, you cannot inject the interface (or I did it wrong) - you have to do what's shown below, with Instance.
Personally I just use constructor injection and then drop the value of the Instance wrapper into a final field, so I'm not crying about the extra step. I do miss the power and explicit bindings possible with Modules ala Guice, but the simplicity here has its own value.
5.16. Declaratively Choose Beans That Can Be Obtained by Programmatic Lookup
It is sometimes useful to narrow down the set of beans that can be
obtained by programmatic lookup via javax.enterprise.inject.Instance.
Typically, a user needs to choose the appropriate implementation of an
interface based on a runtime configuration property.
Imagine that we have two beans implementing the interface
org.acme.Service. You can’t inject the org.acme.Service directly
unless your implementations declare a CDI qualifier. However, you can
inject the Instance instead, then iterate over all
implementations and choose the correct one manually. Alternatively,
you can use the #LookupIfProperty and #LookupUnlessProperty
annotations. #LookupIfProperty indicates that a bean should only be
obtained if a runtime configuration property matches the provided
value. #LookupUnlessProperty, on the other hand, indicates that a bean
should only be obtained if a runtime configuration property does not
match the provided value.
#LookupIfProperty Example
interface Service {
String name();
}
#LookupIfProperty(name = "service.foo.enabled", stringValue = "true")
#ApplicationScoped
class ServiceFoo implements Service {
public String name() {
return "foo";
}
}
#ApplicationScoped
class ServiceBar implements Service {
public String name() {
return "bar";
}
}
#ApplicationScoped
class Client {
#Inject
Instance<Service> service;
void printServiceName() {
// This will print "bar" if the property "service.foo.enabled" is NOT set to "true"
// If "service.foo.enabled" is set to "true" then service.get() would result in an AmbiguousResolutionException
System.out.println(service.get().name());
}
}
If your request is to bind at startup time the right implementation based on a configuration property, I suppose your problem may be resolved used #Produces annotation:
public class ExampleRepositoryFactory {
#Config("storage-type")
String storageType;
#Produces
public ExampleRepository dynamoInstance() {
return storageType == "CSV" ? new CsvRepository() : new DynamoRepository();
}
}

How can I run code in JUnit before Spring starts?

How can I run code in my #RunWith(SpringRunner.class) #SpringBootTest(classes = {...}) JUnit test before Spring starts?
This question has been asked several times (e.g. 1, 2) but was always "solved" by some configuration recommendation or other, never with a universal answer. Kindly don't question what I am about to do in that code but simply suggest a clean way to do it.
Tried so far and failed:
Extend SpringJUnit4ClassRunner to get a class whose constructor can run custom code before initializing Spring. Failed because super(testClass) must be called first thing and already does a whole lot of things that get in the way.
Extend Runner to get a class that delegates to SpringRunner instead of inheriting it. This class could run custom code in its constructor before actually instantiating the SpringRunner. However, this setup fails with obscure error messages like java.lang.NoClassDefFoundError: javax/servlet/SessionCookieConfig. "Obscure" because my test has no web config and thus shouldn't meddle with sessions and cookies.
Adding an ApplicationContextInitializer that is triggered before Spring loads its context. These things are easy to add to the actual #SpringApplication, but hard to add in Junit. They are also quite late in the process, and a lot of Spring has already started.
One way to do it is to leave out SpringRunner and use the equivalent combination of SpringClassRule and SpringMethodRule instead. Then you can wrap the SpringClassRule and do your stuff before it kicks in:
public class SomeSpringTest {
#ClassRule
public static final TestRule TestRule = new TestRule() {
private final SpringClassRule springClassRule =
new SpringClassRule();
#Override
public Statement apply(Statement statement, Description description) {
System.out.println("Before everything Spring does");
return springClassRule.apply(statement, description);
}
};
#Rule
public final SpringMethodRule springMethodRule = new SpringMethodRule();
#Test
public void test() {
// ...
}
}
(Tested with 5.1.4.RELEASE Spring verison)
I don't think you can get more "before" than that. As for other options you could also check out #BootstrapWith and #TestExecutionListeners annotations.
Complementing jannis' comment on the question, the option to create an alternative JUnit runner and let it delegate to the SpringRunner does work:
public class AlternativeSpringRunner extends Runner {
private SpringRunner springRunner;
public AlternativeSpringRunner(Class testClass) {
doSomethingBeforeSpringStarts();
springRunner = new SpringRunner(testClass);
}
private doSomethingBeforeSpringStarts() {
// whatever
}
public Description getDescription() {
return springRunner.getDescription();
}
public void run(RunNotifier notifier) {
springRunner.run(notifier);
}
}
Being based on spring-test 4.3.9.RELEASE, I had to override spring-core and spring-tx, plus javax.servlet's servlet-api with higher versions to make this work.

spring: share value between annotated beans like jsf applicationscope

I am working on a project with jsf 2.2 on the web side and spring 4 on the business side. I have a web filter which receives a parameter from the request url. From this parameter I have to connect to a database. There are cases where there are different databases possible, so depending on the parameter I have to initiate a different database connection. The web filter looks like this:
#Component
public final class SecurityFilter implements Filter
{
#Autowired
private CommonEao commonEao;
#Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException
{
HttpServletRequest req = (HttpServletRequest)request;
String instance = req.getParameter("instance");
//I would like to have something here like:
// springContext.addParameter("instance", instance);
String company = req.getParameter("company");
commonEao.getConfiguration(company);
... Do stuff
}
}
How does it works? The commonEao contains methods to make queries to the database (JPA/Eclipselink...). At initialization, no entityManager is present in commonEao since it is injected in SecurityFilter before the doFilter is executed when an url is requested. After the url is requested, the instance of the database to use is known through the 'instance' request parameter.
When the method commonEao.getConfiguration(company) is invoked, the first thing that should happen is to create an entity Manager:
#Repository
public final class CommonEao extends AbstractEao
{
public final void getConfiguration(final String company)
{
if (entityManager == null)
{
//I would like to have something here like:
// String instance = springContext.getParameter("instance");
createEntityManager(instance);
}
else ...
}
}
As you can see, when the first time the url is requested, no entityManager exists and it needs to be created based on the instance name provided by the request. Based on the instance name the properties files containing database connection parameters will be used the call the Persistence.createEntityManagerFactory functionality... etc etc... :)
What is the idea? The idea that I had, as you can see in my comments, is to put a parameter in some global context/container that is available for all Spring beans. This idea comes from the JSF world, where you can create a managed bean, annotate it with applicationscope, define a variable in it, and access this variable from any jsf managed bean through injecting the application scoped bean with the managedproperty annotation:
#ManagedBean
public final class SomeJsfBean
{
#ManagedProperty(value = "#{applicationBean}")
private ApplicationBean applicationBean;
private void method()
{
applicationBean.setInstanceName("instance");
}
}
#ManagedBean
public final class AnotherJsfBean
{
#ManagedProperty(value = "#{applicationBean}")
private ApplicationBean applicationBean;
private void method()
{
String instance = applicationBean.getInstanceName();
}
}
I have some restrictions though. I want to use a global object of Spring. I don't have any XML config in my project. Spring is configured like this and nothing more:
#Configuration
#ComponentScan(value = { "megan.fox.is.hot", "as.is.lindsay.lohan" })
public class SpringConfiguration
{
}
I have looked in many places, something I found was fetching a property from PropertyPlaceholderConfigurer and stuff like that, but I didn't understand how it works and mainly it looks way too complex for what i need: just sharing one variable.
There must be an easy solution like in the JSF world, but i suspect i am looking for the wrong name in the Spring world! :)
Any help is greatly appreciated, this is the last thing I need to fix in my project!

Tapestry 4: Asset Cache Control?

I use Tapestry 4, and whenever we push a release that changes any assets (image, style sheet, JS library), we get problems because users still have the old version of the asset in their browser cache. I'd like to set up some easy way to allow caching, but force a new asset download when we update the application. Simply disallowing caching entirely for assets is not an acceptable solution.
I couldn't see any existing mechanism for doing this, but I was figuring that there might be some way to tell Tapestry to add the build number to the URL, something like this:
http://www.test.com/path/to/the/asset/asset.jpg?12345
That way, every new build would make it look like a different asset to the end user.
Does Tapestry provide an easy way to solve the cache problem that I'm not aware of? If not, how would one go about modifying the URL generated by Tapestry? And how would the code responsible for doing that get the build number? (I could get the build number into a Spring bean, for example, but how would the new URL building mechanism get at it?)
After stewing about this problem for a long time, I eventually solved it myself. This solution assumes you have the tapestry-spring library in your project.
In my case, I have a Spring bean that contains some of my application's global properties:
package myapp;
public class AppProperties {
private String build;
public String getBuild() {
return build;
}
public void setBuild(String build) {
this.build = build;
}
// other properties
}
Declare this bean in your Spring configuration:
<bean id="appProperties" class="myapp.AppProperties">
<property name="build" value="#BUILD_NUMBER#"/>
</bean>
You can set up your Ant build script to replace #BUILD_NUMBER# with the actual number (see the Copy task in the Ant manual for details).
Now create a class that will wrap IAssets and tack the build number onto the URL:
package myapp;
import java.io.InputStream;
import org.apache.hivemind.Location;
import org.apache.hivemind.Resource;
import org.apache.tapestry.IAsset;
public class BuildAwareAssetWrapper implements IAsset {
private IAsset wrapped;
private String build;
public BuildAwareAssetWrapper(IAsset wrapped, String build) {
this.wrapped = wrapped;
this.build = build;
}
public String buildURL() {
return addParam(wrapped.buildURL(), "build", build);
}
public InputStream getResourceAsStream() {
return wrapped.getResourceAsStream();
}
public Resource getResourceLocation() {
return wrapped.getResourceLocation();
}
public Location getLocation() {
return wrapped.getLocation();
}
private static String addParam(String url, String name, String value) {
if (url == null) url = "";
char sep = url.contains("?") ? '&' : '?';
return url + sep + name + '=' + value;
}
}
Next, we need to make Tapestry wrap all assets with our wrapper. The AssetSourceImpl class is responsible for providing IAsset instances to Tapestry. We'll extend this class and override the findAsset() method so that we can wrap the created assets with the wrapper class:
package myapp;
import java.util.Locale;
import org.apache.hivemind.Location;
import org.apache.hivemind.Resource;
import org.apache.tapestry.IAsset;
import org.apache.tapestry.asset.AssetSourceImpl;
public class BuildAwareAssetSourceImpl extends AssetSourceImpl {
private AppProperties props;
#Override
public IAsset findAsset(Resource base, String path, Locale locale, Location location) {
IAsset asset = super.findAsset(base, path, locale, location);
return new BuildAwareAssetWrapper(asset, props.getBuild());
}
public void setAppProperties(AppProperties props) {
this.props = props;
}
}
Notice that the implementation has a setter which can accept our Spring bean. The last step is to get Tapestry to use BuildAwareAssetSourceImpl to create assets instead of AssetSourceImpl. We do this by overriding the corresponding service point in hivemodule.xml:
<!-- Custom asset source -->
<implementation service-id="tapestry.asset.AssetSource">
<invoke-factory service-id="hivemind.BuilderFactory" model="singleton">
<construct class="myapp.BuildAwareAssetSourceImpl">
<set-object property="appProperties" value="spring:appProperties"/>
<set-configuration property="contributions" configuration-id="tapestry.asset.AssetFactories"/>
<set-service property="lookupAssetFactory" service-id="tapestry.asset.LookupAssetFactory"/>
<set-service property="defaultAssetFactory" service-id="tapestry.asset.DefaultAssetFactory"/>
</construct>
</invoke-factory>
</implementation>
That's it. If you run your application and view the source for any page that uses an asset, you will see that the URL will have the new build parameter on it.

Resources