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.
Related
Using Jackson, is there a way to deserialize a proprty that depends on the value of another property?
if i have this json {"foo":"a","bar":"b"} i'd like to deserialize it to the Test class below as Test [foo=a, bar=b_a], where bar is the value of the json property "bar" and the value of the property "foo".
Of course this is a trivial example, the real deal would be to deserialize a datamodel entity: {"line":"C12", "machine": {"line":"C12", "code":"A"}} machine.line and line are always the same, and i'd like to express it like this: {"line":"C12", "machine": "A"}
import java.io.IOException;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
public abstract class Main{
private static class Test {
#JsonProperty
private String foo;
#JsonProperty
#JsonDeserialize(using = CustomDeserializer.class)
private String bar;
// ...other fields to be deserialized with default behaviour
private Test() {
}
public Test(String a, String bar) {
this.foo = a;
this.bar = bar;
}
#Override
public String toString() {
return "Test [foo=" + foo + ", bar=" + bar + "]";
}
}
private static class CustomDeserializer extends StdDeserializer<String> {
protected CustomDeserializer() {
super(String.class);
}
#Override
public String deserialize(JsonParser p, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
String foo = //how to get foo property?
String value = p.getValueAsString();
if (!foo.isEmpty()) {
return value + "_" + foo;
} else {
return value;
}
}
}
public static void main(String[] args) throws IOException {
ObjectMapper mapper = new ObjectMapper();
Test foo2 = mapper.readValue("{\"foo\":\"a\",\"bar\":\"b\"}", Test.class);
System.out.println(foo2); // Test [foo=a, bar=b_a]
}
}
One way to solve your problem is specify a custom deserializer that involves your Test class instead of your string field because the deserialization of your property is based on the value of another property:
public class CustomDeserializer extends JsonDeserializer<Test> {}
#JsonDeserialize(using = CustomDeserializer.class)
public class Test {}
Then you can deserialize your object reading the JsonNode tree built from your input string:
public class CustomDeserializer extends JsonDeserializer<Test> {
#Override
public Test deserialize(JsonParser jp, DeserializationContext dc) throws IOException, JsonProcessingException {
JsonNode node = jp.getCodec().readTree(jp);
String foo = node.get("foo").asText();
String bar = node.get("bar").asText();
if (!foo.isEmpty()) {
bar = (bar + '_' + foo);
}
return new Test(foo, bar);
}
}
//your example
public class Main {
public static void main(String[] args) throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
Test foo2 = mapper.readValue("{\"foo\":\"a\",\"bar\":\"b\"}", Test.class);
System.out.println(foo2); // Test [foo=a, bar=b_a]
}
}
I got a similar problem today and I wanted to share my solution. So instead of using a #JsonDeserialize, I use a #JsonCreator on the parent object with a package private constructor to accept the "raw" properties and then I can process this data and return better objects.
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.ObjectMapper;
class Scratch {
public static void main(String[] args) throws Exception{
final var testData = "{\"foo\":\"a\",\"bar\":\"b\"}";
final var mapper = new ObjectMapper();
final var testObj = mapper.readValue(testData, Test.class);
System.out.println(testObj); // Test[foo=a, bar=a_b]
}
record Test (
String foo,
String bar
){
#JsonCreator Test(
#JsonProperty("foo") String foo,
#JsonProperty("bar") String bar,
#JsonProperty("_dummy") String _dummy // extra param for the constructor overloading
) {
this(foo, deserializeBar(foo, bar));
}
private static String deserializeBar(String foo, String bar) {
if (foo == null || foo.isEmpty()) {
return bar;
}
return "%s_%s".formatted(foo, bar);
}
}
}
In the end, I've resorted using BeanDeserializerModifier
Please notice that the following code is not fully functioning because it relies on code I'm not allowed to share, but it should suffice to get the idea.
package com.example;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.reflections.Reflections;
import org.reflections.scanners.SubTypesScanner;
import org.reflections.scanners.TypeAnnotationsScanner;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.DeserializationConfig;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.deser.BeanDeserializer;
import com.fasterxml.jackson.databind.deser.BeanDeserializerModifier;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
import com.google.inject.assistedinject.AssistedInject;
public class JsonDelegateDeserializerModule extends SimpleModule {
// !! must be registered as guice factory
public interface JsonDelegateDeserializerFactory {
JsonDelegateDeserializerModule create(String packagePath);
}
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
public #interface JsonDelegateDeserializer {
public Class<? extends StdDeserializer<?>> deserializer();
public Class<?> forType();
}
protected interface JsonDeserializerFactory {
// non metto nessun generic in TagHandler o guice non riesce piu a creare la
// factory!
#SuppressWarnings("rawtypes")
public JsonDeserializer create(JsonDeserializer baseDeserializer);
}
private static final Logger LOGGER = LoggerFactory.getLogger(JsonDelegateDeserializerModule.class);
#Inject
private FactoryInjector injector;
private final String packagePath;
#AssistedInject
protected JsonDelegateDeserializerModule(#Assisted String packagePath) {
super();
this.packagePath = packagePath;
}
#Override
public String getModuleName() {
return JsonDelegateDeserializerModule.class.getSimpleName() + "[" + packagePath + "]";
}
#Override
public Object getTypeId() {
return JsonDelegateDeserializerModule.class.getSimpleName() + "[" + packagePath + "]";
}
#Override
public void setupModule(SetupContext context) {
Reflections reflectios = new Reflections(packagePath, new SubTypesScanner(), new TypeAnnotationsScanner());
Map<Class<?>, JsonDeserializerFactory> classToDeserializerFactory = new HashMap<>();
Set<Class<?>> classesWithModifier = reflectios.getTypesAnnotatedWith(JsonDelegateDeserializer.class);
for (Class<?> classWithModifier : classesWithModifier) {
JsonDelegateDeserializer annotation = classWithModifier.getAnnotation(JsonDelegateDeserializer.class);
if (annotation != null) {
Class<? extends StdDeserializer<?>> deserializerType = annotation.deserializer();
Class<?> forType = annotation.forType();
try {
JsonDeserializerFactory factory = injector.getFactory(JsonDeserializerFactory.class,
deserializerType);
classToDeserializerFactory.put(forType, factory);
} catch (Exception e) {
LOGGER.error("Exception was thown while creating deserializer {} for type {}:", deserializerType,
forType, e);
throw new RuntimeException(e);
}
}
}
if (!classToDeserializerFactory.isEmpty()) {
setDeserializerModifier(new BeanDeserializerModifier() {
#Override
public JsonDeserializer<?> modifyDeserializer(DeserializationConfig config, BeanDescription beanDesc,
JsonDeserializer<?> deserializer) {
List<Class<?>> possibleTypesList = new LinkedList<>();
if (deserializer instanceof BeanDeserializer) {
for (Entry<Class<?>, JsonDeserializerFactory> entry : classToDeserializerFactory.entrySet()) {
Class<?> type = entry.getKey();
if (type.isAssignableFrom(deserializer.handledType())) {
possibleTypesList.add(type);
}
}
if (possibleTypesList.size() > 1) {
possibleTypesList.sort(new Comparator<Class<?>>() {
#Override
public int compare(Class<?> o1, Class<?> o2) {
if (o1.isAssignableFrom(o2)) {
return 1;
} else {
return -1;
}
}
});
}
Class<?> type = Utils.first(possibleTypesList);
if (type == null) {
return super.modifyDeserializer(config, beanDesc, deserializer);
} else {
JsonDeserializerFactory factory = classToDeserializerFactory.get(type);
JsonDeserializer<?> modifiedDeserializer = factory.create(deserializer);
return super.modifyDeserializer(config, beanDesc, modifiedDeserializer);
}
} else {
// รจ gia stato impostato un deserializzatore piu specifico, non imposato questo
return super.modifyDeserializer(config, beanDesc, deserializer);
}
}
});
}
super.setupModule(context);
}
}
then you can simply annotate the Mixin to add the custom deserializer
#JsonDelegateDeserializer(deserializer = LoadLineDeserializer.class, forType = Line.class)
public interface LineMixIn {
public static class LoadLineDeserializer extends DelegatingDeserializer {
#AssistedInject
public LoadLineDeserializer(#Assisted JsonDeserializer baseDeserializer, LineService lineService) {
super(baseDeserializer);
}
// ...
}
// ...
}
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:
Hi community: Trying to integrate Cucumber with JUnit, I have the next class.
import cl.cukes.ParameterizedRunnerFactory;
import cl.test.AutomatedWebTest;
import cl.util.report.Report;
import cucumber.annotation.en.Given;
import cucumber.annotation.en.Then;
import cucumber.annotation.en.When;
import cucumber.api.junit.Cucumber;
import org.junit.runner.RunWith;
import org.junit.runner.Runner;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.UseParametersRunnerFactory;
import org.junit.runners.model.InitializationError;
import org.junit.runners.parameterized.TestWithParameters;
import org.openqa.selenium.By;
import org.openqa.selenium.ie.InternetExplorerDriver;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
//JAVA
#RunWith(Parameterized.class)
#UseParametersRunnerFactory(ParameterizedRunnerFactory.class)
public class CucumberStepDefs extends AutomatedWebTest {
#Parameterized.Parameter(value = 0)
public String user;
#Parameterized.Parameter(value = 1)
public String pass;
public static class CucumberRunnerFactor extends ParameterizedRunnerFactory {
public Runner createRunnerForTestWithParameters(TestWithParameters test)
throws InitializationError {
try {
return new Cucumber(test.getTestClass().getJavaClass());
} catch (IOException e) {
throw new InitializationError(e);
}
}
}
#Given("^Enter to the QA URL environment")
public void Login() throws Exception {
baseUrl = "http://www.miprivado.cl/";
driver = new InternetExplorerDriver();
driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);
driver.get(baseUrl + "ucachaca/test.htm");
driver.switchTo().defaultContent();
driver.switchTo().frame("frmbody");
}
#When("^Fields Username y Passwordare displayed so enter parameters$")
public void UsuarioPass() throws Exception {
driver.findElement(By.id("TxbTELLUSERID")).clear();
driver.findElement(By.id("TxbTELLUSERID")).sendKeys(user);
driver.findElement(By.id("TxbUSERPASSW")).clear();
driver.findElement(By.id("TxbUSERPASSW")).sendKeys(pass);
screenshot.take(this, driver, "LoginR C01");
driver.findElement(By.id("BtnSubmit")).click();
driver.switchTo().defaultContent();
driver.switchTo().frame("frmwebteller");
driver.manage().window().maximize();
}
#Then("^User gets into the system and do Logout Logout$")
public void Logout() throws Exception {
screenshot.take (this, driver, "LoginR C02");
driver.switchTo ().defaultContent ();
driver.switchTo ().frame ("frmbody").switchTo ().frame ("menu");
driver.findElement(By.cssSelector("b")).click();
screenshot.take (this, driver, "LoginR C03");
driver.findElement(By.linkText("Log Off")).click();
screenshot.take (this, driver, "LoginR C04");
driver.quit();
}
}
The ParameterizedRunnerFactory is the next:
import org.junit.runner.Runner;
import org.junit.runners.model.InitializationError;
import org.junit.runners.parameterized.ParametersRunnerFactory;
import org.junit.runners.parameterized.TestWithParameters;
/**
*
*/
public class ParameterizedRunnerFactory implements ParametersRunnerFactory {
#Override
public Runner createRunnerForTestWithParameters(TestWithParameters test) throws InitializationError {
return new ParameterizedRunner(test);
}
}
The parameterized Runner class is the next:
import org.junit.After;
import org.junit.Before;
import org.junit.runners.model.*;
import org.junit.runners.parameterized.BlockJUnit4ClassRunnerWithParameters;
import org.junit.runners.parameterized.TestWithParameters;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
public class ParameterizedRunner extends BlockJUnit4ClassRunnerWithParameters {
public ParameterizedRunner(TestWithParameters test) throws InitializationError {
super(test);
}
// workaround for: https://github.com/junit-team/junit/issues/1046
private static final ConcurrentHashMap<Class<?>, TestClass> testClasses = new ConcurrentHashMap<>();
#Override
protected TestClass createTestClass(Class<?> clazz) {
TestClass testClass = testClasses.get(clazz);
if (testClass == null) {
testClasses.put(clazz, testClass = new TestClass(clazz));
}
return testClass;
}
// playing whack-a-mole with new TLAB allocations by re-defining with{Befores,Afters}...
#Override
protected Statement withBefores(FrameworkMethod method, Object target, Statement statement) {
List<FrameworkMethod> list = getTestClass().getAnnotatedMethods(Before.class);
if (list.isEmpty()) {
return statement;
}
return new BeforesStatement(target, statement, list);
}
#Override
protected Statement withAfters(FrameworkMethod method, Object target, Statement statement) {
List<FrameworkMethod> list = getTestClass().getAnnotatedMethods(After.class);
if (list.isEmpty()) {
return statement;
}
return new AftersStatement(target, statement, list);
}
private static final class BeforesStatement extends Statement {
private static final Object[] EMPTY_ARGS = new Object[0];
private final Object target;
private final Statement statement;
private final List<FrameworkMethod> list;
BeforesStatement(Object target, Statement statement, List<FrameworkMethod> list) {
this.target = target;
this.statement = statement;
this.list = list;
}
#Override
public void evaluate() throws Throwable {
// (1) Avoid ImmutableCollections#iterator()
for (int i = 0, size = list.size(); i < size; ++i) {
list.get(i).invokeExplosively(target, EMPTY_ARGS);
}
statement.evaluate();
}
}
private static final class AftersStatement extends Statement {
private static final Object[] EMPTY_ARGS = new Object[0];
private final Object target;
private final Statement statement;
private final List<FrameworkMethod> list;
AftersStatement(Object target, Statement statement, List<FrameworkMethod> list) {
this.target = target;
this.statement = statement;
this.list = list;
}
#Override
public void evaluate() throws Throwable {
// (2) Lazily create ArrayList
ArrayList<Throwable> throwables = null;
try {
statement.evaluate();
} catch (Throwable e) {
throwables = new ArrayList<Throwable>();
throwables.add(e);
} finally {
for (int i = 0, size = list.size(); i < size; ++i) {
try {
list.get(i).invokeExplosively(target, EMPTY_ARGS);
} catch (Throwable e) {
if (throwables == null) {
throwables = new ArrayList<Throwable>();
}
throwables.add(e);
}
}
}
if (throwables != null) {
MultipleFailureException.assertEmpty(throwables);
}
}
}
}
The Cucumber Runner Test class is the next:
import cucumber.api.CucumberOptions;
import cucumber.api.junit.Cucumber;
import org.junit.runner.RunWith;
#RunWith(Cucumber.class)
#CucumberOptions(features={"//src/features/Login.feature"}
,format = {"pretty", "html:target/cucumber"}
,glue = {"cucumber.CucumberStepDefs"}
)
/*#Suite.SuiteClasses({
CucumberStepDefs.class,
})*/
public class CucumberRunnerTest {
}
When I run my CucumberStepDefs, it displays the next issue on IntelliJ:
java.lang.Exception: No public static parameters method on class cl.cucumber.CucumberStepDefs
All the classes don't show error, but I'm not able to run this class.
Could anybody help me with this?
Don't try to mix JUnit with Maven:
The best answer were done by Grasshopper:
1.- Create the Feature file.
2.- Use Scenario Outline and put the data in Examples section.
3.- Use a runner like next:
#CucumberOptions(features = {"src/test/resource/features"},
glue={"stepdefs"},
monochrome = true,
tags = {"~#Ignore"},
plugin = {"pretty","html:target/cucumber-reports/cucumber-pretty",
"json:target/cucumber-reports/CucumberTestReport.json",
"rerun:target/cucumber-reports/rerun.txt",
"usage:target/cucumber-usage.json"}
)
public class TestRunner extends ExtendedTestNGRunner{
private TestNGCucumberRunner testNGCucumberRunner;
#BeforeClass(alwaysRun = true)
public void setUpClass() throws Exception {
testNGCucumberRunner = new TestNGCucumberRunner(this.getClass());
}
#Test(groups = "cucumber", description = "Runs Cucumber Feature", dataProvider = "features")
public void feature(CucumberFeatureWrapper cucumberFeature) {
testNGCucumberRunner.runCucumber(cucumberFeature.getCucumberFeature());
}
#DataProvider
public Object[][] features() {
return testNGCucumberRunner.provideFeatures();
}
#AfterClass(alwaysRun = true)
public void tearDownClass() throws Exception {
testNGCucumberRunner.finish();
}
}
And run the Feature.
Thanks a lot, Grasshopper!!!
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();
}
}
everytime when i am running my spring with hibernate socket program it saves for the first time but i am sending infinitely from the client side but in server end only first time it is saving..What type of problem i am facing?? how to resolve
package utilclass;
import org.hibernate.*;
import org.springframework.orm.hibernate3.HibernateTemplate;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
public class HibernateUtemplate {
private static HibernateTemplate template;
public void setTemplate(HibernateTemplate template){
HibernateUtemplate.template=template;
}
public static SessionFactory getSessionFactory(){
return template.getSessionFactory();
}
public static Session getSession(){
getSessionFactory().getCurrentSession().beginTransaction();
return getSessionFactory().getCurrentSession();
}
public static void commitTransaction() {
getSession().getTransaction().commit();
}
public static void rollbackTransaction() {
getSession().getTransaction().rollback();
}
public static void closeSession() {
getSession().close();
}
}
this is my generic file
package daoimplclasses;
import java.io.Serializable;
import java.util.List;
import org.hibernate.Query;
import org.hibernate.Session;
import org.springframework.orm.hibernate3.HibernateTemplate;
import utilclass.HibernateUtemplate;
import daointerfaces.GenericDao;
public abstract class GenericDaoimpl<T, ID extends Serializable> implements GenericDao<T, ID> {
//HibernateTemplate template;
protected Session getSession() {
return HibernateUtemplate.getSession();
}
public void save(T entity) {
//template.saveOrUpdate(entity);
HibernateUtemplate.getSession().saveOrUpdate(entity);
// HibernateUtemplate.beginTransaction().saveOrUpdate(entity);
}
public void merge(T entity) {
// template.merge(entity);
HibernateUtemplate.getSession().merge(entity);
// HibernateUtemplate.beginTransaction().merge(entity);
}
public void update(T entity) {
// template.update(entity);
HibernateUtemplate.getSession().update(entity);
// HibernateUtemplate.beginTransaction().update(entity);
}
public void delete(T entity) {
// template.delete(entity);
HibernateUtemplate.getSession().delete(entity);
//HibernateUtemplate.beginTransaction().delete(entity);
}
public List<T> findMany(Query query) {
List<T> t;
t = (List<T>) query.list();
return t;
}
public T findOne(Query query) {
T t;
t = (T) query.uniqueResult();
return t;
}
public T findByID(Class claz,String siteid){
T t=null;
//t=(T)template.get(claz, siteid);
t=(T) HibernateUtemplate.getSession().get(claz, siteid);
//t=(T) HibernateUtemplate.beginTransaction().get(claz, siteid);
return t;
}
public List<T> findAll(Class clazz) {
List<T> T = null;
Query query= HibernateUtemplate.getSession().createQuery("from "+clazz.getName());
// Query query= HibernateUtemplate.beginTransaction().createQuery("from "+clazz.getName());
T = query.list();
return T;
}
}