Is there a way to use #RepeatedTest annotation alongside with #TestTemplate?
The objective is to run test multiple times for each type of Dependency, which is injected by an Extension class.
#TestTemplate
#RepeatedTest(100)
#Timeout(1)
void test(final Dependency dep) throws Exception {
....
}
Note: Example below provides implementation of custom #TestTemplate using custom Dependency class implementation
Consider this:
import org.junit.jupiter.api.TestTemplate;
import org.junit.jupiter.api.Timeout;
import org.junit.jupiter.api.extension.*;
import java.util.Collections;
import java.util.List;
import java.util.stream.IntStream;
import java.util.stream.Stream;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class RepeatedParametrizedTest {
#TestTemplate
#ExtendWith(MyTemplate.class)
#Timeout(1)
void test(final Dependency dep) {
assertEquals(true, true);
}
}
class Dependency {
private final String name;
public Dependency(String name) {
this.name = name;
}
public String getName() {
return name;
}
#Override
public String toString() {
return name;
}
}
class MyTemplate implements TestTemplateInvocationContextProvider {
#Override
public boolean supportsTestTemplate(ExtensionContext context) {
return true;
}
#Override
public Stream<TestTemplateInvocationContext> provideTestTemplateInvocationContexts(ExtensionContext context) {
// here goes your implementation of all possible Dependency objects wrapped in invocationContext()
return IntStream.range(0, 100)
.flatMap(n -> IntStream.range(1, 10))
.mapToObj(n -> invocationContext(new Dependency("dependency" + n)));
}
private TestTemplateInvocationContext invocationContext(Dependency dependency) {
return new TestTemplateInvocationContext() {
#Override
public String getDisplayName(int invocationIndex) {
return dependency.getName();
}
#Override
public List<Extension> getAdditionalExtensions() {
return Collections.singletonList(new ParameterResolver() {
#Override
public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {
return parameterContext.getParameter().getType().equals(Dependency.class);
}
#Override
public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {
return dependency;
}
});
}
};
}
}
For 10 * 10 instances that would produce:
Related
Help me in the following code and how to used the backup on the Hazelcast
migration of the hazelcast 3.x.x to 5.x.x
package com.hazelcast.map;
import com.hazelcast.core.HazelcastInstance;
import com.hazelcast.core.HazelcastInstanceAware;
import com.hazelcast.nio.serialization.impl.BinaryInterface;
import java.util.Map;
// Interface AbstractEntryProcessor
#BinaryInterface
public abstract class AbstractEntryProcessor<K,V> implements EntryProcessor<K,V> {
private final EntryBackupProcessor<K,V> entryBackupProcessor;
// Non Parameterize Constructor
public AbstractEntryProcessor() {
this(true);
}
// Parameterize Constructor AbstractEntryProcessor
public AbstractEntryProcessor(boolean applyOnBackup) {
if (applyOnBackup) {
entryBackupProcessor = new EntryBackupProcessorImpl();
} else {
entryBackupProcessor = null;
}
}
//EntryBackupProcessor
#Override
public final EntryBackupProcessor getBackupProcessor() {
return entryBackupProcessor;
}
// class EntryBackupProcessorImpl
private class EntryBackupProcessorImpl implements EntryBackupProcessor<k,V>, HazelcastInstanceAware {
// generated for EntryBackupProcessorImpl which doesn't implement HazelcastInstanceAware
static final long serialVersionUID = -5081502753526394129L;
#Override
public void processBackup(Map.Entry<K,V> entry) {
process(entry);
}
#Override
public void setHazelcastInstance(HazelcastInstance hazelcastInstance) {
final AbstractEntryProcessor<k,V> outer = AbstractEntryProcessor.this;
if (outer instanceof HazelcastInstanceAware) {
((HazelcastInstanceAware) outer).setHazelcastInstance(hazelcastInstance);
}
}
}
}
How to used the backup methods in 5.x.x versons of series
how to used the backup in the above question ?
This should work:
public abstract class AbstractEntryProcessor implements EntryProcessor, HazelcastInstanceAware {
protected transient HazelcastInstance hazelcastInstance;
private final boolean applyOnBackup;
// Non Parameterize Constructor
public AbstractEntryProcessor() {
this(true);
}
// Parameterize Constructor AbstractEntryProcessor
public AbstractEntryProcessor(boolean applyOnBackup) {
this.applyOnBackup = applyOnBackup;
}
//EntryBackupProcessor
#Override
public final EntryProcessor getBackupProcessor() {
if (!applyOnBackup || this instanceof ReadOnly) {
return null;
}
return this;
}
#Override
public void setHazelcastInstance(HazelcastInstance hazelcastInstance) {
this.hazelcastInstance = hazelcastInstance;
}
}
I want to check if all the ingredients(toppings and fillings) inside a wrap are both vegan and nut free. This is the solution that I came up with, howver I think its a bit inefficient as there is duplication of code. Is there a more efficient way to do it?
(I have a map for all the toppings and fillings which every one contains boolean to know if the topping/filling is vegan and if it is nut free.
public boolean isVegan() {
for (Topping t : toppings) {
if (!t.isVegan()) {
return false;
}
}
for (Filling f : fillings) {
if (!f.isVegan()) {
return false;
}
}
return bread.isVegan();
}
public boolean isNutFree() {
for (Topping t : toppings) {
if (!t.isNutFree()) {
return false;
}
}
for (Filling f : fillings) {
if (!f.isNutFree()) {
return false;
}
}
return bread.isNutFree();
}
Supposing that Ingredient is the base class of these different classes and that this class defines the isVegan() method, you could create a Stream from all these objects and computing whether all are vegan :
public boolean isVegan() {
return
Stream.concat(toppings.stream(), fillings.stream(), Stream.of(bread))
.allMatch(Ingredient::isVegan);
}
For isNutFree() the idea is the same :
public boolean isNutFree() {
return
Stream.concat(toppings.stream(), fillings.stream(), Stream.of(bread))
.allMatch(Ingredient::isNutFree);
}
Note that you could also generalize a matching method to reduce further the duplication :
public boolean allMatch(Predicate<Ingredient> predicate) {
return
Stream.concat(toppings.stream(), fillings.stream(), Stream.of(bread))
.allMatch( i -> predicate.test(i));
}
And use it such as :
boolean isNutFree = allMatch(Ingredient::isNutFree);
boolean isVegan = allMatch(Ingredient::isVegan);
Here is a food type replacing either Topping or Filling or anything:
public interface FoodPart {
boolean isVegan();
boolean isNutFree();
}
Here we have an abstract Food class containing all common codes:
public abstract class Food {
private List<? extends FoodPart> foodParts;
public boolean isVegan() {
return foodParts.stream().noneMatch(foodPart -> foodPart.isVegan());
}
public boolean isNutFree() {
return foodParts.stream().noneMatch(foodPart -> foodPart.isNutFree());
}
}
And here is a concrete and not abstract food:
public class Lasagne extends Food {}
Edit:
If you don't want to inherit from FoodPart then you can change List<? extends FoodPart> simply to List<FoodPart>.
You can also make Food to not abstract so you can easily use it, and don't forget to add getters/setters to provide the foodParts.
Yeez, you guys are fast :)
What I wrote is pretty much already covered in the other answers here but just posting since mine does have some subtle differences (not necessarily better). And since I already went through the motions of writing the code I might as well post it :)
First an interface for your fillings and toppings:
public interface FoodInformation {
boolean isVegan();
boolean isNutFree();
boolean isGlutenFree();
}
Then an abstract class which your toppings and fillings can extend:
public abstract class Ingredient implements FoodInformation {
private boolean vegan;
private boolean nutFree;
private boolean glutenFree;
public Ingredient(boolean vegan, boolean nutFree, boolean glutenFree) {
this.vegan = vegan;
this.nutFree = nutFree;
this.glutenFree = glutenFree;
}
#Override
public boolean isVegan() {
return vegan;
}
#Override
public boolean isNutFree() {
return nutFree;
}
#Override
public boolean isGlutenFree() {
return glutenFree;
}
}
Your Filling:
public class Filling extends Ingredient {
public Filling(boolean vegan, boolean nutFree, boolean glutenFree) {
super(vegan, nutFree, glutenFree);
}
}
Your Topping:
public class Topping extends Ingredient {
public Topping(boolean vegan, boolean nutFree, boolean glutenFree) {
super(vegan, nutFree, glutenFree);
}
}
And your Wrap:
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Stream;
public class Wrap {
private List<Filling> fillings;
private List<Topping> toppings;
public Wrap(List<Filling> fillings, List<Topping> toppings) {
this.fillings = fillings;
this.toppings = toppings;
}
public boolean isNutFree() {
return testIngredient(FoodInformation::isNutFree);
}
public boolean isVegan() {
return testIngredient(FoodInformation::isVegan);
}
public boolean isGlutenFree() {
return testIngredient(FoodInformation::isGlutenFree);
}
private boolean testIngredient(Predicate<FoodInformation> predicate) {
// edited thanks to davidxxx for the Stream.concat notation!
return Stream
.concat(fillings.stream(), toppings.stream())
.allMatch(predicate);
}
}
And a test to show the implementation works:
import org.junit.Before;
import org.junit.Test;
import java.util.Arrays;
import java.util.Collections;
import static junit.framework.TestCase.assertTrue;
import static org.junit.Assert.assertFalse;
public class WrapTest {
private Wrap wrap;
#Before
public void setup() {
Filling filling1 = new Filling(true, true, false);
Filling filling2 = new Filling(true, false, true);
Filling filling3 = new Filling(true, true, true);
Topping topping1 = new Topping(true, true, true);
wrap = new Wrap(Arrays.asList(filling1, filling2, filling3), Collections.singletonList(topping1));
}
#Test
public void testIsGlutenFree() {
assertFalse(wrap.isGlutenFree());
}
#Test
public void testIsNutFree() {
assertFalse(wrap.isNutFree());
}
#Test
public void testIsVegan() {
assertTrue(wrap.isVegan());
}
}
Have fun with your project!
create an interface that has isVegan and isNutFree
public interface MyInterface {
boolean isVegan();
boolean isNutFree();
}
Then each of your classes with implement your interface
public class Topping implements MyInterface {
#Override
public boolean isVegan() {
return isVegan;
}
#Override boolean isNutFree() {
return isNutFree;
}
}
public class Filling implements MyInterface {
#Override
public boolean isVegan() {
return isVegan;
}
#Override boolean isNutFree() {
return isNutFree;
}
}
Next create a method that can test the lists
public boolean isVegan(List<? extends MyInterface> list) {
for(MyInterface myObj : list) {
if (myObj.isVegan()) return true;
}
return false;
}
public boolean isNutFree(List<? extends MyInterface> list) {
for(MyInterface myObj: list) {
if (myObj.isNutFree()) return true;
}
return false;
}
then each list you can pass into the methods to get the results
in my use case i need to develop a custom annotation by wich i can instanziate the implementation of a DAO.
So i have the interface:
public interface IDAO{
public void method1();
public void method2();
}
and the resource config implementation:
public class JAXRSConfig extends ResourceConfig {
public JAXRSConfig() {
register(new AbstractBinder() {
#Override
protected void configure() {
/*Factory Classes Binding*/
bindFactory(DaoFactory.class).to(IDAO.class).in(RequestScoped.class);
/*Injection Resolver Binding*/
bind(CustomContextInjectionResolver.class).to(new TypeLiteral<InjectionResolver<CustomContext>>(){}).in(Singleton.class);
}
});
}
I'm stucking with the factory implementation:
public class DaoFactory implements Factory<IDAO>{
private final HttpServletRequest request;
#Inject
public DaoFactory(HttpServletRequest request) {
this.request = request;
}
#Override
public IDAO provide() {
IDAO dao = null;
try {
???????????
} catch (InstantiationException | IllegalAccessException e) {
e.printStackTrace();
}
return dao;
}
#Override
public void dispose( IDAO mud) {
}
}
And here of course i have my IDAO implementation:
public class DAOImplementation implements IDAO {
public void method1(){
//do some stuff
}
public void method2(){
//do some stuff
}
public MyEntity getEntity(){
//get my entity
}
}
Result i want to get is:
#Path("/myResource")
public class myService(){
#CustomContext
DAOImplementation myDao;
public String myService(){
MyEntity entity = myDao.getEntity();
}
}
Is there a way to connect the factory to the injection resolver the way i can get the real implementation to provide? Does hk2 provide any means to do this?
EDITED
I can have multiple implementations of the IDAO interface... for example if i have:
public class DAOImplementation2 implements IDAO {
public void method1(){
//do some stuff
}
public void method2(){
//do some stuff
}
public MyEntity2 getEntity2(){
//get my entity
}
}
i should be able to get second implementation like this:
#Path("/myResource")
public class myService(){
#CustomContext
DAOImplementation myDao;
#CustomContext
DAOImplementation2 mySecondDao;
public String myService(){
MyEntity entity = myDao.getEntity();
MyEntity2 entity = mySecondDao.getEntity2();
}
}
So based on our previous chat, below is the idea I was trying to get across. Basically you would add a Feature where the user can pass the IDao implementation classes to. In the Feature you can bind them by name
public static class DaoFeature implements Feature {
private final Class<? extends IDao>[] daoClasses;
public DaoFeature(Class<? extends IDao> ... daoClasses) {
this.daoClasses = daoClasses;
}
#Override
public boolean configure(FeatureContext context) {
context.register(new Binder());
return true;
}
private class Binder extends AbstractBinder {
#Override
protected void configure() {
...
for (Class<? extends IDao> daoClass: daoClasses) {
bind(daoClass).to(IDao.class)
.named(daoClass.getCanonicalName()).in(RequestScoped.class);
}
}
}
}
Then in the InjectionResolver you can look then up by name and also add it to the CloseableService. All without the need for any ugly reflection.
public static class CustomContextResolver
implements InjectionResolver<CustomContext> {
#Inject
private ServiceLocator locator;
#Inject
private IDaoProviders daoClasses;
#Inject
private CloseableService closeableService;
#Override
public Object resolve(Injectee injectee, ServiceHandle<?> root) {
Type requiredType = injectee.getRequiredType();
for (Class type: daoClasses.getDaoClasses()) {
if (requiredType == type) {
IDao dao = locator.getService(IDao.class, type.getCanonicalName());
addToCloseableService(dao);
return type.cast(dao);
}
}
return null;
}
...
}
The EntityManager would be handled with a Factory. The Factory I used is a Jersey class that lets you get the ContainerRequest which you can pretty much get anything you would be able to get from a HttpServletRequest
public static class DummyEntityManagerFactory
extends AbstractContainerRequestValueFactory<DummyEntityManager> {
#Override
public DummyEntityManager provide() {
ContainerRequest request = getContainerRequest();
// get some condition for EntityManager
return new DummyEntityManager();
}
}
In the abstract IDao class, you can inject the EntityManager, and handle and disposing of resource yourself.
public static abstract class IDao {
#Inject
private DummyEntityManager em;
protected abstract String getData();
public void close() {
em.close();
}
protected DummyEntityManager getEntityManager() {
return em;
}
}
Here's the complete test case
import java.io.Closeable;
import java.io.IOException;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.inject.Inject;
import javax.inject.Singleton;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.core.Feature;
import javax.ws.rs.core.FeatureContext;
import javax.ws.rs.core.Response;
import org.glassfish.hk2.api.Injectee;
import org.glassfish.hk2.api.InjectionResolver;
import org.glassfish.hk2.api.ServiceHandle;
import org.glassfish.hk2.api.ServiceLocator;
import org.glassfish.hk2.api.TypeLiteral;
import org.glassfish.hk2.utilities.binding.AbstractBinder;
import org.glassfish.jersey.process.internal.RequestScoped;
import org.glassfish.jersey.server.CloseableService;
import org.glassfish.jersey.server.ContainerRequest;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.server.internal.inject.AbstractContainerRequestValueFactory;
import org.glassfish.jersey.test.JerseyTest;
import static org.junit.Assert.assertEquals;
import org.junit.Test;
public class CustomDaoTest extends JerseyTest {
public static class DummyEntityManager {
String findByDaoClass(Class cls) {
return "Data from " + cls.getSimpleName();
}
public void close() { /* noop */ }
}
public static abstract class IDao {
private static final Logger LOG = Logger.getLogger(IDao.class.getName());
#Inject
private DummyEntityManager em;
protected abstract String getData();
public void close() {
LOG.log(Level.INFO, "Closing IDAO: {0}", this.getClass().getName());
em.close();
}
protected DummyEntityManager getEntityManager() {
return em;
}
}
public static class DaoImplOne extends IDao {
#Override
public String getData() {
return getEntityManager().findByDaoClass(this.getClass());
}
}
public static class DaoImplTwo extends IDao {
#Override
protected String getData() {
return getEntityManager().findByDaoClass(this.getClass());
}
}
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.FIELD, ElementType.CONSTRUCTOR})
public static #interface CustomContext{}
public static class CustomContextResolver
implements InjectionResolver<CustomContext> {
#Inject
private ServiceLocator locator;
#Inject
private IDaoProviders daoClasses;
#Inject
private CloseableService closeableService;
#Override
public Object resolve(Injectee injectee, ServiceHandle<?> root) {
Type requiredType = injectee.getRequiredType();
for (Class type: daoClasses.getDaoClasses()) {
if (requiredType == type) {
IDao dao = locator.getService(IDao.class, type.getCanonicalName());
addToCloseableService(dao);
return type.cast(dao);
}
}
return null;
}
private void addToCloseableService(final IDao idao) {
closeableService.add(new Closeable(){
#Override
public void close() throws IOException {
idao.close();
}
});
}
#Override
public boolean isConstructorParameterIndicator() {
return false;
}
#Override
public boolean isMethodParameterIndicator() {
return false;
}
}
public static class DummyEntityManagerFactory
extends AbstractContainerRequestValueFactory<DummyEntityManager> {
#Override
public DummyEntityManager provide() {
ContainerRequest request = getContainerRequest();
// get some condition for EntityManager
return new DummyEntityManager();
}
}
public static class IDaoProviders {
private final List<Class<? extends IDao>> daoClasses;
public IDaoProviders(Class<? extends IDao> ... daoClasses) {
this.daoClasses = new ArrayList<>(Arrays.asList(daoClasses));
}
public List<Class<? extends IDao>> getDaoClasses() {
return daoClasses;
}
}
public static class DaoFeature implements Feature {
private final Class<? extends IDao>[] daoClasses;
public DaoFeature(Class<? extends IDao> ... daoClasses) {
this.daoClasses = daoClasses;
}
#Override
public boolean configure(FeatureContext context) {
context.register(new Binder());
return true;
}
private class Binder extends AbstractBinder {
#Override
protected void configure() {
bind(CustomContextResolver.class)
.to(new TypeLiteral<InjectionResolver<CustomContext>>(){})
.in(Singleton.class);
bindFactory(DummyEntityManagerFactory.class)
.to(DummyEntityManager.class)
.in(RequestScoped.class);
for (Class<? extends IDao> daoClass: daoClasses) {
bind(daoClass).to(IDao.class)
.named(daoClass.getCanonicalName()).in(RequestScoped.class);
}
IDaoProviders daoProviders = new IDaoProviders(daoClasses);
bind(daoProviders).to(IDaoProviders.class);
}
}
}
#Path("dao")
public static class DaoResource {
#CustomContext
private DaoImplOne daoOne;
#CustomContext
private DaoImplTwo daoTwo;
#GET
#Path("one")
public String getOne() {
return daoOne.getData();
}
#GET
#Path("two")
public String getTwo() {
return daoTwo.getData();
}
}
#Override
public ResourceConfig configure() {
return new ResourceConfig(DaoResource.class)
.register(new DaoFeature(DaoImplOne.class, DaoImplTwo.class));
}
#Test
public void should_return_dao_one_data() {
Response response = target("dao/one").request().get();
assertEquals(200, response.getStatus());
assertEquals("Data from DaoImplOne", response.readEntity(String.class));
response.close();
}
#Test
public void should_return_dao_two_data() {
Response response = target("dao/two").request().get();
assertEquals(200, response.getStatus());
assertEquals("Data from DaoImplTwo", response.readEntity(String.class));
response.close();
}
}
UPDATE
Using web.xml (no ResourceConfig)
import java.io.Closeable;
import java.io.IOException;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.inject.Inject;
import javax.inject.Singleton;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.core.Feature;
import javax.ws.rs.core.FeatureContext;
import javax.ws.rs.core.Response;
import org.glassfish.hk2.api.Injectee;
import org.glassfish.hk2.api.InjectionResolver;
import org.glassfish.hk2.api.ServiceHandle;
import org.glassfish.hk2.api.ServiceLocator;
import org.glassfish.hk2.api.TypeLiteral;
import org.glassfish.hk2.utilities.binding.AbstractBinder;
import org.glassfish.jersey.internal.util.PropertiesHelper;
import org.glassfish.jersey.process.internal.RequestScoped;
import org.glassfish.jersey.server.CloseableService;
import org.glassfish.jersey.server.ContainerRequest;
import org.glassfish.jersey.server.internal.inject.AbstractContainerRequestValueFactory;
import org.glassfish.jersey.servlet.ServletContainer;
import org.glassfish.jersey.test.DeploymentContext;
import org.glassfish.jersey.test.JerseyTest;
import org.glassfish.jersey.test.ServletDeploymentContext;
import org.glassfish.jersey.test.grizzly.GrizzlyWebTestContainerFactory;
import org.glassfish.jersey.test.spi.TestContainerFactory;
import static org.junit.Assert.assertEquals;
import org.junit.Test;
public class WebXmlCustomDaoTest extends JerseyTest {
public static class DummyEntityManager {
String findByDaoClass(Class cls) {
return "Data from " + cls.getSimpleName();
}
public void close() { /* noop */ }
}
public static abstract class IDao {
private static final Logger LOG = Logger.getLogger(IDao.class.getName());
#Inject
private DummyEntityManager em;
protected abstract String getData();
public void close() {
LOG.log(Level.INFO, "Closing IDAO: {0}", this.getClass().getName());
em.close();
}
protected DummyEntityManager getEntityManager() {
return em;
}
}
public static class DaoImplOne extends IDao {
#Override
public String getData() {
return getEntityManager().findByDaoClass(this.getClass());
}
}
public static class DaoImplTwo extends IDao {
#Override
protected String getData() {
return getEntityManager().findByDaoClass(this.getClass());
}
}
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.FIELD, ElementType.CONSTRUCTOR})
public static #interface CustomContext {
}
public static class CustomContextResolver
implements InjectionResolver<CustomContext> {
#Inject
private ServiceLocator locator;
#Inject
private IDaoProviders daoClasses;
#Inject
private CloseableService closeableService;
#Override
public Object resolve(Injectee injectee, ServiceHandle<?> root) {
Type requiredType = injectee.getRequiredType();
for (Class type : daoClasses.getDaoClasses()) {
if (requiredType == type) {
IDao dao = locator.getService(IDao.class, type.getCanonicalName());
addToCloseableService(dao);
return type.cast(dao);
}
}
return null;
}
private void addToCloseableService(final IDao idao) {
closeableService.add(new Closeable() {
#Override
public void close() throws IOException {
idao.close();
}
});
}
#Override
public boolean isConstructorParameterIndicator() {
return false;
}
#Override
public boolean isMethodParameterIndicator() {
return false;
}
}
public static class DummyEntityManagerFactory
extends AbstractContainerRequestValueFactory<DummyEntityManager> {
#Override
public DummyEntityManager provide() {
ContainerRequest request = getContainerRequest();
// get some condition for EntityManager
return new DummyEntityManager();
}
}
public static class IDaoProviders {
private final List<Class<? extends IDao>> daoClasses;
public IDaoProviders(Class<? extends IDao>... daoClasses) {
this.daoClasses = new ArrayList<>(Arrays.asList(daoClasses));
}
public List<Class<? extends IDao>> getDaoClasses() {
return daoClasses;
}
}
public static class DaoFeature implements Feature {
public static final String DAO_IMPLEMENTATIONS = "dao.implementations";
#Override
public boolean configure(FeatureContext context) {
Map<String, Object> props = context.getConfiguration().getProperties();
String initParam = getValue(props, DAO_IMPLEMENTATIONS, String.class);
context.register(new Binder(getFromStringParam(initParam)));
return true;
}
private List<Class<? extends IDao>> getFromStringParam(String initParam) {
String[] daoClassNames = initParam.split(",");
List<Class<? extends IDao>> daoClasses = new ArrayList<>();
for (int i = 0; i < daoClassNames.length; i++) {
try {
String classname = daoClassNames[i].trim();
Class<?> cls = Class.forName(daoClassNames[i].trim());
if (IDao.class.isAssignableFrom(cls)) {
Class<? extends IDao> c = (Class<? extends IDao>)cls;
daoClasses.add(c);
}
} catch (ClassNotFoundException ex) {
// noop - ignore non IDao classes.
System.out.println(ex.getMessage());
}
}
return daoClasses;
}
public static <T> T getValue(Map<String, ?> properties, String key, Class<T> type) {
return PropertiesHelper.getValue(properties, key, type, null);
}
private class Binder extends AbstractBinder {
List<Class<? extends IDao>> daoClasses;
public Binder(List<Class<? extends IDao>> daoClasses) {
this.daoClasses = daoClasses;
}
#Override
protected void configure() {
bind(CustomContextResolver.class)
.to(new TypeLiteral<InjectionResolver<CustomContext>>() {
})
.in(Singleton.class);
bindFactory(DummyEntityManagerFactory.class)
.to(DummyEntityManager.class)
.in(RequestScoped.class);
for (Class<? extends IDao> daoClass : daoClasses) {
bind(daoClass).to(IDao.class)
.named(daoClass.getCanonicalName()).in(RequestScoped.class);
}
Class<? extends IDao>[] array = daoClasses.toArray(new Class[]{});
IDaoProviders daoProviders = new IDaoProviders(array);
bind(daoProviders).to(IDaoProviders.class);
}
}
}
#Path("dao")
public static class DaoResource {
#CustomContext
private DaoImplOne daoOne;
#CustomContext
private DaoImplTwo daoTwo;
#GET
#Path("one")
public String getOne() {
return daoOne.getData();
}
#GET
#Path("two")
public String getTwo() {
return daoTwo.getData();
}
}
#Override
protected TestContainerFactory getTestContainerFactory() {
return new GrizzlyWebTestContainerFactory();
}
/**
*
* This method is to configure a web deployment using a "web.xml".
*
* The "dao.implementations" is a property from the `DaoFeature`
* The user should list the `IDao` implementation classes separated
* by a comma.
*
* The `DaoFeature` is register with the Jersey init-param
* `jersey.config.server.provider.classnames`
*
* The class names I listed use a `$` only because they are inner classes.
* Normally you would not need that.
*
* See http://stackoverflow.com/a/7007859/2587435
*/
#Override
protected DeploymentContext configureDeployment() {
return ServletDeploymentContext
.forServlet(ServletContainer.class)
.initParam("jersey.config.server.provider.packages",
this.getClass().getPackage().getName())
.initParam("jersey.config.server.provider.classnames",
"com.stackoverflow.dao.WebXmlCustomDaoTest$DaoFeature")
.initParam("dao.implementations",
"com.stackoverflow.dao.WebXmlCustomDaoTest$DaoImplOne,"
+ "com.stackoverflow.dao.WebXmlCustomDaoTest$DaoImplTwo")
.build();
}
#Test
public void should_return_dao_one_data() {
Response response = target("dao/one").request().get();
assertEquals(200, response.getStatus());
assertEquals("Data from DaoImplOne", response.readEntity(String.class));
response.close();
}
#Test
public void should_return_dao_two_data() {
Response response = target("dao/two").request().get();
assertEquals(200, response.getStatus());
assertEquals("Data from DaoImplTwo", response.readEntity(String.class));
response.close();
}
}
I have two classes like this:
public class A {
String aProp = "aProp";
public String getAProp() {
return aProp;
}
}
public class B {
String bProp = "bProp";
A a = new A();
#JsonProperty("bProp")
public String getBProp() {
return bProp;
}
#JsonSerialize(using = CustomSerializer.class)
public A getA() {
return a;
}
}
I'm expecting to get JSON like this:
{
"bProp": "bProp", // just serizlised bProp
"sProp1": "sProp1_aProp", // computed using aProp
"sProp2": "sProp2_aProp" // computed another way
}
So I wrote custom JsonSerializer like this:
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
public class CustomSerializer extends JsonSerializer<A> {
#Override
public void serialize(A a, JsonGenerator json, SerializerProvider provider) throws IOException {
json.writeStringField("sProp1", "sProp1_" + a.getAProp());
json.writeStringField("sProp2", "sProp2_" + a.getAProp());
}
}
But I keep getting an error:
com.fasterxml.jackson.core.JsonGenerationException: Can not write a field name, expecting a value
Unless I put json.writeStartObject(); and json.writeEndObject(); in serialize method (so it produces wrong JSON).
So I'm looking for a solution like #JsonUnwrapped to use with custom JsonSerializer.
I understand your problem and the thing that you need is UnwrappingBeanSerializer. You can see another related SO post:
Different JSON output when using custom json serializer in Spring Data Rest
The problem is that you cannot have both annotations #JacksonUnwrapped and #JsonSerialize in one field because when you have #JsonSerializer Jackson will always write field name.
Here is the complete solution:
public class CustomSerializer extends UnwrappingBeanSerializer {
public CustomSerializer(BeanSerializerBase src, NameTransformer transformer) {
super(src, transformer);
}
#Override
public JsonSerializer<Object> unwrappingSerializer(NameTransformer transformer) {
return new CustomSerializer(this, transformer);
}
#Override
protected void serializeFields(Object bean, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonGenerationException {
A a = (A) bean;
jgen.writeStringField("custom", a.getAProp());
jgen.writeStringField("custom3", a.getAProp());
}
#Override
public boolean isUnwrappingSerializer() {
return true;
}
}
Test case, you should redefine your object mapper with custom configuration or research for other method .
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
#SpringApplicationConfiguration(classes = Application.class)
public class ColorsTest {
ObjectMapper mapper = new ObjectMapper();
#Before
public void setUp(){
mapper.registerModule(new Module() {
#Override
public String getModuleName() {
return "my.module";
}
#Override
public Version version() {
return Version.unknownVersion();
}
#Override
public void setupModule(SetupContext context) {
context.addBeanSerializerModifier(new BeanSerializerModifier() {
#Override
public JsonSerializer<?> modifySerializer(SerializationConfig config, BeanDescription beanDesc, JsonSerializer<?> serializer) {
if(beanDesc.getBeanClass().equals(A.class)) {
return new CustomSerializer((BeanSerializerBase) serializer, NameTransformer.NOP);
}
return serializer;
}
});
}
});
}
#Test
public void testSerializer() throws JsonProcessingException {
System.out.println(mapper.writeValueAsString(new B()));
}
}
Class B:
public class B {
#JsonProperty("bProp")
public String getBProp() {
return "bProp";
}
#JsonUnwrapped
public A getA() {
return new A();
}
}
I like to add this post and solution to the question asked here: Using custom Serializers with JsonUnwrapperd as the original poster is using JsonSerializer as I am. The suggest approach with the UnwrappingBeanSerializer won't work in this case. My post has a slightly different goal, but the idea from the post should be applicable to your use case easily, as it is just overwriting one more method and not having to add bunch of stuff apart from JsonUnwrapped on the property.
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonUnwrapped;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import java.io.IOException;
public class Test {
static class A {
String aProp = "aProp";
public String getAProp() {
return aProp;
}
}
static class B {
String bProp = "bProp";
A a = new A();
#JsonProperty("bProp")
public String getBProp() {
return bProp;
}
#JsonSerialize(using = CustomSerializer.class)
#JsonUnwrapped
public A getA() {
return a;
}
}
static class CustomSerializer extends JsonSerializer<A> {
#Override
public boolean isUnwrappingSerializer() {
return true;
}
#Override
public void serialize(A a, JsonGenerator json, SerializerProvider provider) throws IOException {
json.writeStringField("sProp1", "sProp1_" + a.getAProp());
json.writeStringField("sProp2", "sProp2_" + a.getAProp());
}
}
public static void main(String... a) throws Exception {
final ObjectMapper o = new ObjectMapper();
o.enable(SerializationFeature.INDENT_OUTPUT);
System.out.println(o.writeValueAsString(new B()));
}
}
I'm having problems with Spring transactions. I really need help as I can't figure out why personsDao2 is not rolled back as should (see assert below commented with "FAILS!"). Any input?
My Eclipse project is available for download at http://www52.zippyshare.com/v/4142091/file.html. All dependencies are there so it's easy to get going.
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import com.google.common.collect.Lists;
public class MyInnerClass {
private PersonsDao personsDao;
public MyInnerClass() {
}
public PersonsDao getPersonsDao() {
return personsDao;
}
public void setPersonsDao(PersonsDao personsDao) {
this.personsDao = personsDao;
}
#Transactional(propagation = Propagation.REQUIRES_NEW, noRollbackFor = Exception.class)
public void method() {
personsDao.createPersons(Lists.newArrayList(new Person("Eva")));
}
}
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import com.google.common.collect.Lists;
public class MyOuterClass {
private MyInnerClass myInnerClass;
private PersonsDao personsDao;
public MyInnerClass getMyInnerClass() {
return myInnerClass;
}
public void setMyInnerClass(MyInnerClass myInnerClass) {
this.myInnerClass = myInnerClass;
}
public void setMyInnerClass() {
}
public PersonsDao getPersonsDao() {
return personsDao;
}
public void setPersonsDao(PersonsDao personsDao) {
this.personsDao = personsDao;
}
public MyOuterClass() {
}
#Transactional(propagation = Propagation.REQUIRED, rollbackFor=Exception.class)
public void method() {
try {
personsDao.createPersons(Lists.newArrayList(new Person("Adam")));
throw new RuntimeException("Forced rollback");
} finally {
myInnerClass.method();
}
}
}
public class Person {
public Person(String name) {
this.name = name;
}
public String getName() {
return name;
}
#Override
public String toString() {
return "Customer [name=" + name + "]";
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Person other = (Person) obj;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
private String name;
}
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.sql.DataSource;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
import org.springframework.jdbc.core.namedparam.SqlParameterSourceUtils;
public class PersonsDao {
public PersonsDao(DataSource dataSource, String tableName) {
namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(dataSource);
this.tableName = tableName;
}
public List<Person> getPersons() {
Map<String, Object> namedParameters = new HashMap<String, Object>();
String getCustomers = "SELECT name FROM " + tableName + " ORDER BY name ASC";
return namedParameterJdbcTemplate.query(getCustomers, namedParameters, getRowMapper());
}
public void createPersons(List<Person> customers) {
SqlParameterSource[] params = SqlParameterSourceUtils.createBatch(customers.toArray());
String createCustomer = "INSERT INTO " + tableName + " VALUES(:name)";
namedParameterJdbcTemplate.batchUpdate(createCustomer, params);
}
public void deleteCustomers() {
Map<String, Object> namedParameters = new HashMap<String, Object>();
String deleteCustomers = "DELETE FROM " + tableName;
namedParameterJdbcTemplate.update(deleteCustomers, namedParameters);
}
private static RowMapper<Person> getRowMapper() {
return new RowMapper<Person>() {
#Override
public Person mapRow(ResultSet arg0, int arg1) throws SQLException {
return new Person(arg0.getString("name"));
}
};
}
private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
private String tableName;
}
import static org.junit.Assert.*;
import javax.annotation.Resource;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional;
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = "/beans.xml")
#Transactional(rollbackFor = Exception.class)
public class PersonsDaoTest {
#Resource
private MyInnerClass myInnerClass;
#Resource
private MyOuterClass myOuterClass;
#Test(expected = Exception.class)
public void test() {
myOuterClass.method();
fail();
}
#After
public void after() {
assertEquals(1, myInnerClass.getPersonsDao().getPersons().size());
assertEquals(0, myOuterClass.getPersonsDao().getPersons().size());
}
#Before
public void before() {
myInnerClass.getPersonsDao().deleteCustomers();
myOuterClass.getPersonsDao().deleteCustomers();
assertEquals(0, myInnerClass.getPersonsDao().getPersons().size());
assertEquals(0, myOuterClass.getPersonsDao().getPersons().size());
}
}
First of all, the #Transactional annotations on your two classes are ignored, since you instantiates these classes directly (using new) rather than getting an instance from the spring context.
So in fact, it boild down to this code:
try {
personDao2.createPerson(); // creates a person in persons2
throw new RuntimeException();
}
finally {
personDao1.createPerson(); // creates a person in person1
}
A finally block is always executed, even if an exception is thrown in the try block. So the test creates a person in person1 and in person2.
#Anders Your InnerClass with the #Transactional annotation does not derive from an interface, if you are not using AspectJ weaving or CG-LIB based proxies, the #Transactional aspect won't take effect, as the dynamic proxies require an interface to be present. A quick fix will be to derive your inner class from an interface, define the bean in the spring config and consistently use the interface for referring to the bean.