We are working with JAX-RS and JPA. We use methods that have the following structure (details omitted):
#PUT
#Path("{id}")
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public ResultObject saveById( #PathParam("id") BigInteger id,
SomeObject someObject) {
entityManager = EMF.obtainEntityManager();
try {
.. start transaction
.. write all information to the database
.. commit transaction
.. return ResultObject
} catch ( Exception exception) {
.. rollback transaction
.. return ResultObject together with an appropriate error
} finally {
entityManager.close();
}
}
Is there a ‘best’ way to avoid repeating the catch and finally on every JAX-RS method that we create? Using filters?
Our service provicer only supports Tomcat. No Glassfish or other containers.
Thanks for any help.
I would move implementation to different service that would be called by REST service. REST should be a type of your API. And logic should be in different service.
#PUT
#Path("{id}")
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public ResultObject saveById( #PathParam("id") BigInteger id, SomeObject someObject) {
ResultObject resultObject = new ResultObject();
ResultType res = someService.saveById(id, someObject)
// do something with res
return resultObject ;
}
And then in SomeService would implement some abstract class that could implement a logic of transaction.
public abstract class AbstractService {
protected void startTransaction() {
//...
}
protected void endTransaction() {
//...
}
}
public class SomeService extends AbstractService {
public ResultType saveById(BigInteger id, SomeObject someObject) {
startTransaction();
// your logic
endTransaction();
}
}
There is also better way. You could use Spring Framework if you know it.
In that solution you annotate SomeService (or method in that class) with #Transactional.
#Transactional
public class SomeService {
public ResultType saveById(BigInteger id, SomeObject someObject) {
// your logic
}
}
Create EntityManger in a filter
Eg:-
public class EntityManagerFilter implements Filter {
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
EntityManager em = null;
try {
/
em = EntityManagerFactoryUtil.entityManagerFactory.createEntityManager();
//
EntityManagerUtil.ENTITY_MANAGERS.set(em);
chain.doFilter(request, response);
EntityManagerUtil.ENTITY_MANAGERS.remove();
//
} catch (Exception ex) {
//
} finally {
try {
if (em != null) {
em.close();
//
};
} catch (Throwable t) {
//
}
}
}
public void init(FilterConfig config) {
destroy();
initEntityManagerFactory();
}
private void initEntityManagerFactory() {
EntityManagerFactoryUtil.entityManagerFactory =
Persistence.createEntityManagerFactory("PersistanceUnitName");
//
}
public void destroy() {
//
try {
if (EntityManagerFactoryUtil.entityManagerFactory != null) {
EntityManagerFactoryUtil.entityManagerFactory.close();
};
} catch (Exception t) {
/
}
}
public class EntityManagerUtil {
public static final ThreadLocal<EntityManager> ENTITY_MANAGERS = new ThreadLocal<EntityManager>();
/** Returns a fresh EntityManager */
public static EntityManager getEntityManager() {
return ENTITY_MANAGERS.get();
}
}
public class EntityManagerFactoryUtil {
public static EntityManagerFactory entityManagerFactory;
}
Related
Environment:
JBoss 7.2
Java 11
I am trying to insert data to a DB from a JSF page through TestBean controller, but I am getting an error saying javax.persistence.TransactionRequiredException that I need a transaction.
The insert method() is in a #Stateless ConsultaService, so I thought that it was by default transactional but it looks like something is wront.
This code is being migrated from JBoss 5.2 but I had no problem, when I invoked insert method() a transaction was created without any problem. Has that changed in JBoss 7.2?
Should I add #Transactional to every method? or there is something else that I am missing?
**TestBean.java **
#Named
#ViewScoped
public class TestBean implements Serializable {
...
#Inject
ConsultaServiceable consultaSvc;
...
public void insert() {
try {
Consulta consulta1 = new Consulta();
consulta1.setConsulta("hola");
consulta1.setCognom1("cognom");
consulta1.setCp("07000");
consulta1.trace();
consultaSvc.insert(consulta);//ERROR TransactionRequiredException
} catch (AppException e) {
log.error("error inserting consulta", e);
}
}
...
}
BusinessServiceable.java
public interface BusinessServiceable<T> extends QueryServiceable<T> {
T insert(T entity) throws AppException;
T update(T entity) throws AppException;
void remove(T entity) throws AppException;
}
QueryService.java
public abstract class QueryService<T> extends Queryable implements QueryServiceable<T> {
#PersistenceContext(unitName = "bdPU")
protected EntityManager eManager;
protected Class<T> entityClass;
...
public QueryService(Class<T> entityClass) {
this.entityClass = entityClass;
}
...
}
BusinessService.java
public abstract class BusinessService<T> extends QueryService<T> implements BusinessServiceable<T> {
...
#PermitAll
public T insert(T entity) throws AppException {
try {
eManager.persist(entity);
} catch (Exception e) {
log.error("Error on BusinessService.insert ", e);
Throwable t = e.getCause();
if (t instanceof org.hibernate.exception.ConstraintViolationException) {
throw new AppException("Error on BusinessService.insert " + entity.getClass().getSimpleName() + " id " + entity.toString(), e);
} else {
throw new AppException("Error on insert", e);
}
}
return entity;
}
...
}
ConsultaService.java
#Stateless
#Local
#PermitAll
public class ConsultaService extends BusinessService<Consulta> implements ConsultaServiceable {
public ConsultaService() {
super(Consulta.class);
}
#PermitAll
#Override
public Consulta insert(Consulta entity) throws AppException {
return super.insert(entity);
}
...
}
Error server log
10:46:52,329 ERROR [es.caib.accfor.business.QueryService] (default task-1) Error on BusinessService.insert : javax.persistence.TransactionRequiredException: WFLYJPA0060: Transaction is required to perform this operation (either use a transaction or extended persistence context)
at org.jboss.as.jpa#7.2.0.GA-redhat-00005//org.jboss.as.jpa.container.AbstractEntityManager.transactionIsRequired(AbstractEntityManager.java:877)
at org.jboss.as.jpa#7.2.0.GA-redhat-00005//org.jboss.as.jpa.container.AbstractEntityManager.persist(AbstractEntityManager.java:579)
at deployment.accfor2.ear//es.caib.accfor.business.BusinessService.insert(BusinessService.java:43)
at deployment.accfor2.ear//es.caib.accfor.business.consulta.boundary.ConsultaService.insert(ConsultaService.java:46)
at deployment.accfor2.ear//es.caib.accfor.business.consulta.boundary.ConsultaService$Proxy$_$$_WeldSubclass.insert(Unknown Source)
at deployment.accfor2.ear//es.caib.accfor.business.consulta.boundary.ConsultaService.insert(ConsultaService.java:14)
at deployment.accfor2.ear.accfor-front.war//es.caib.accfor.presentation.front.TestBean.insert(TestBean.java:57)
It was a problem wih pom.xml dependencies and ejb module.
I am writing unit test for my Jersey rest API which uses MyBatis at the background.
This is the structure of my classes:
rest service:
#Path("/api")
public class HelloRestService {
#Inject
HelloBean helloBean;
#GET
#Path("/echo/{name}")
public Response echo(#PathParam("name") String name) {
return Response.status(200).entity(helloBean.sayHello(name)).build();
}
}
Stateless EJB:
#Stateless
public class HelloStatelessBean implements HelloBean {
// Injected MyBatis mapper (dao)
#Inject
private EmployeeMapper employeeMapper;
#Override
public Employee getEmployeeById(final Long id) {
return employeeMapper.getEmployeeById(id);
}
#Override
public ArrayList<Employee> getEmployee() {
return employeeMapper.getAllEmployee();
}
/**
* Echo.
*/
#Override
public String sayHello(String name) {
return String.format("Hello %s! This is Ejb :)", name);
}
}
MyBatis mapper interface:
#Dependent
#Mapper
public interface EmployeeMapper {
#Select("select * from EMPLOYEE where id = #{id}")
Employee getEmployeeById(Long id);
#Select("select * from EMPLOYEE")
ArrayList<Employee> getAllEmployee();
}
I have a nice junit test for testing my MyBatis mapper. It works fine. The next step is that I would like to write a test for my jersey rest class. This is what I have:
public class HelloRestServiceTest extends JerseyTest {
#Override
public Application configure() {
enable(TestProperties.LOG_TRAFFIC);
enable(TestProperties.DUMP_ENTITY);
return new ResourceConfig(HelloRestService.class) {
{
register(new HelloStatelessBean());
register(Mockito.mock(EmployeeMapper.class));
}
};
}
#Test
public void echo() throws Exception {
Response response = target("/api/echo/John").request().get();
Assert.assertEquals(200, response.getStatus());
Assert.assertNotNull(response.getEntity());
}
}
But this test throws an exception:
org.glassfish.jersey.internal.inject.Providers checkProviderRuntime
WARNING: A provider a.b.HelloStatelessBean registered in SERVER runtime does not implement any provider interfaces applicable in the SERVER runtime. Due to constraint configuration problems the provider a.b.HelloStatelessBean will be ignored.
org.glassfish.jersey.internal.inject.Providers checkProviderRuntime
WARNING: A provider a.b.EmployeeMapper$$EnhancerByMockitoWithCGLIB$$ee86c913 registered in SERVER runtime does not implement any provider interfaces applicable in the SERVER runtime. Due to constraint configuration problems the provider a.b.EmployeeMapper$$EnhancerByMockitoWithCGLIB$$ee86c913 will be ignored.
A MultiException has 1 exceptions. They are:
1. org.glassfish.hk2.api.UnsatisfiedDependencyException: There was no object available for injection at SystemInjecteeImpl(requiredType=EmployeeMapper,parent=HelloStatelessBean,qualifiers={},position=-1,optional=false,self=false,unqualified=null,52451302)
MultiException stack 1 of 1
org.glassfish.hk2.api.UnsatisfiedDependencyException: There was no object available for injection at SystemInjecteeImpl(requiredType=EmployeeMapper,parent=HelloStatelessBean,qualifiers={},position=-1,optional=false,self=false,unqualified=null,52451302)
What is the proper way to mock MyBatis mapper with mockito?
UPDATE 1
EJB test works like a charm:
#RunWith(MockitoJUnitRunner.class)
public class HelloStatelessBeanTest {
private static SqlSessionFactory sqlSessionFactory;
#Spy
private HelloBean helloBean = new HelloStatelessBean();
#Test
public void sayHello() throws Exception {
String actual = helloBean.sayHello("Pear");
System.out.println(actual);
}
#Test
public void testGetEmployeeById() {
// create an SqlSessionFactory
try (Reader reader = Resources.getResourceAsReader("mybatis-configuration.xml")) {
sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
SqlSession sqlSession = sqlSessionFactory.openSession();
EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
Employee employee = mapper.getEmployeeById(1l);
Assert.assertNotNull(employee);
Assert.assertNotNull(employee.getId());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
But it does not work with jersey. I have tried it this way, but I get same exception then before:
public class HelloRestServiceTest extends JerseyTest {
#Override
public Application configure() {
enable(TestProperties.LOG_TRAFFIC);
enable(TestProperties.DUMP_ENTITY);
return new ResourceConfig(HelloRestService.class) {
{
try (Reader reader = Resources.getResourceAsReader("configuration.xml")) {
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
SqlSession sqlSession = sqlSessionFactory.openSession();
EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
register(new HelloStatelessBean());
register(mapper, EmployeeMapper.class);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
};
}
#Test
public void echo() throws Exception {
Response response = target("/api/echo/Arnold").request().get();
Assert.assertEquals(200, response.getStatus());
Assert.assertNotNull(response.getEntity());
}
}
Any idea?
I have the following classes:
public FooDAO extends AbstractDAO<Foo> { // Dropwizard DAO
#Inject FooDAO(SessionFactory sf) { super(sf); }
public void foo() { /* use SessionFactory */ }
}
public class FooService {
private final FooDAO fooDAO; // Constructor-injected dependency
#Inject FooService (FooDAO fooDAO) { this.fooDAO = fooDAO; }
#UnitOfWork
public void foo() {
this.fooDAO.foo();
System.out.println("I went through FooService.foo()");
}
}
Now, FooService is not a resource, so Dropwizard doesn't know about it and doesn't automagically proxy it. However the smart guys at Dropwizard made it so I can get a proxy through UnitOfWorkAwareProxyFactory.
I tried doing feeding these proxies to Guice with an interceptor, but I faced an issue because UnitOfWorkAwareProxyFactory only ever creates new instances and never lets me pass existing objects. The thing with new instances is that I don't know the parameters to give it since they're injected by Guice.
How do I create #UnitOfWork-aware proxies of existing objects?
Here's the interceptor I've made so far:
public class UnitOfWorkModule extends AbstractModule {
#Override protected void configure() {
UnitOfWorkInterceptor interceptor = new UnitOfWorkInterceptor();
bindInterceptor(Matchers.any(), Matchers.annotatedWith(UnitOfWork.class), interceptor);
requestInjection(interceptor);
}
private static class UnitOfWorkInterceptor implements MethodInterceptor {
#Inject UnitOfWorkAwareProxyFactory proxyFactory;
Map<Object, Object> proxies = new IdentityHashMap<>();
#Override public Object invoke(MethodInvocation mi) throws Throwable {
Object target = proxies.computeIfAbsent(mi.getThis(), x -> createProxy(mi));
Method method = mi.getMethod();
Object[] arguments = mi.getArguments();
return method.invoke(target, arguments);
}
Object createProxy(MethodInvocation mi) {
// here, what to do? proxyFactory will only provide objects where I pass constructor arguments, but... I don't have those!
}
}
}
Of course, if Dropwizard (or Guice) offers me a simpler way to do so, which is it?
As from Dropwizard 1.1: (not yet released, as of August 10, 2016)
public class UnitOfWorkModule extends AbstractModule {
#Override
protected void configure() {
UnitOfWorkInterceptor interceptor = new UnitOfWorkInterceptor();
bindInterceptor(Matchers.any(), Matchers.annotatedWith(UnitOfWork.class), interceptor);
requestInjection(interceptor);
}
#Provides
#Singleton
UnitOfWorkAwareProxyFactory provideUnitOfWorkAwareProxyFactory(HibernateBundle<AlexandriaConfiguration> hibernateBundle) {
return new UnitOfWorkAwareProxyFactory(hibernateBundle);
}
private static class UnitOfWorkInterceptor implements MethodInterceptor {
#Inject
UnitOfWorkAwareProxyFactory proxyFactory;
#Override
public Object invoke(MethodInvocation mi) throws Throwable {
UnitOfWorkAspect aspect = proxyFactory.newAspect();
try {
aspect.beforeStart(mi.getMethod().getAnnotation(UnitOfWork.class));
Object result = mi.proceed();
aspect.afterEnd();
return result;
} catch (InvocationTargetException e) {
aspect.onError();
throw e.getCause();
} catch (Exception e) {
aspect.onError();
throw e;
} finally {
aspect.onFinish();
}
}
}
}
Using Dropwizard 0.9.1 I have created a custom AuthFilter to check session cookie as below:
Priority(Priorities.AUTHENTICATION)
public class SessionAuthFilter extends AuthFilter<String /*session key*/, SessionUser /*principal*/> {
private SessionAuthFilter() {
}
#Override
public void filter(ContainerRequestContext requestContext) throws IOException {
Cookie sessionKey = requestContext.getCookies().get("sessionKey");
if (sessionKey != null) {
try {
Optional<SessionUser> principal = new SessionAuthenticator().authenticate(sessionKey.getValue());
requestContext.setSecurityContext(new SecurityContext() {
#Override
public Principal getUserPrincipal() {
return principal.get();
}
#Override
public boolean isUserInRole(String role) {
return false;
}
#Override
public boolean isSecure() {
return requestContext.getSecurityContext().isSecure();
}
#Override
public String getAuthenticationScheme() {
return SecurityContext.FORM_AUTH;
}
});
return;
} catch (AuthenticationException e) {
throw new InternalServerErrorException(e.getMessage(), e);
}
}
throw new NotAuthorizedException("Please log in!", "realm="+realm);
}
And registered it as below:
environment.jersey().register(new AuthDynamicFeature(new SessionAuthFilter.Builder().setAuthenticator(new
SessionAuthenticator()).setRealm("Login").buildAuthFilter()));
environment.jersey().register(RolesAllowedDynamicFeature.class);
The problem is I can not use #Permitall annotation on class level in Resource classes. It works fine If I use on method, but not filtering on class.
Resource class:
#Path("/")
#PermitAll //Doesn't work here
#Produces(MediaType.APPLICATION_JSON)
public class HomeResource {
#GET
#PermitAll //Works fine if here
#Path("/about")
public Response get() {
}
}
Any idea anyone?
Authz annotations at the class level is not supported in DW 9.x. You can see in the source code of AuthDynamicFeature, only method level annotations are checked, ultimately only registering the auth filter to methods with the Authz annotations.
This limitiation has been fixed in this pull request (to 1.0.0), where #RolesAllowed and #PermitAll at the class level will be supported.
Using Jersey 1.14 and Spring 3.1.2
I want to create a filter like this: https://gist.github.com/3031495
but in that filter I want access to a provider I created.
I'm getting an IllegalStateException. I suspect something in my lifecycle is hosed up. I can access #Context private HttpServletRequest and pull the session info I need from there, but then two classes have to know about where/how to get my "AuthUser" object.
Any help is appreciated!
My Provider:
#Component
#Provider
public class AuthUserProvider extends AbstractHttpContextInjectable<AuthUser> implements
InjectableProvider<Context, Type> {
private static final Logger LOG = LoggerFactory.getLogger(AuthUserProvider.class);
#Context
HttpServletRequest req;
public void init() {
LOG.debug("created");
}
#Override
// this may return a null AuthUser, which is what we want....remember, a
// null AuthUser means the user hasn't authenticated yet
public AuthUser getValue(HttpContext ctx) {
return (AuthUser) req.getSession().getAttribute(AuthUser.KEY);
}
// InjectableProvider implementation:
public ComponentScope getScope() {
return ComponentScope.Singleton;
}
public Injectable<AuthUser> getInjectable(ComponentContext ic, Context ctx, Type c) {
if (AuthUser.class.equals(c)) {
return this;
}
return null;
}
}
My Filter:
#Component
public class TodoFilter implements ResourceFilter {
private static final Logger LOG = LoggerFactory.getLogger(TodoFilter.class);
#Autowired
private JdbcTemplate todoTemplate;
// this works
#Context
private HttpServletRequest servletRequest;
// this throws a java.lang.IllegalStateException
// #Context
// private AuthUser authUser;
public void init() throws Exception {
LOG.debug("created");
LOG.debug(todoTemplate.getDataSource().getConnection().getMetaData()
.getDatabaseProductName());
}
#Override
public ContainerRequestFilter getRequestFilter() {
return new ContainerRequestFilter() {
#Override
public ContainerRequest filter(ContainerRequest request) {
LOG.debug("checking if {} is authorized to use {}", "my authenticated user",
request.getPath());
// String name = request.getUserPrincipal().getName();
// String[] admins = settings.getAdminUsers();
// for (String adminName : admins) {
// if (adminName.equals(name))
// return request;
// }
// if (authUser.getUsername().equals("jberk")) {
// return request;
// }
// return HTTP 403 if name is not found in admin users
throw new WebApplicationException(Response.status(Response.Status.FORBIDDEN)
.entity("You are not authorized!").build());
}
};
}
#Override
public ContainerResponseFilter getResponseFilter() {
return new ContainerResponseFilter() {
#Override
public ContainerResponse filter(ContainerRequest request,
ContainerResponse response) {
// do nothing
return response;
}
};
}
}
My Service (aka Resource):
#Component
#Path("/rs/todo")
#Produces(MediaType.APPLICATION_JSON)
#ResourceFilters(TodoFilter.class)
public class TodoService {
#GET / #POST methods
}
so I think I figured this out....
I added this to my ResourceFilter:
#Context
private HttpContext ctx;
#Autowired
private AuthUserProvider provider;
then I can do this in the filter method:
public ContainerRequest filter(ContainerRequest request) {
AuthUser authUser = provider.getValue(ctx);
// use authuser in some way
}
this might not be "correct"...but it's working and I don't have code duplication
public ComponentScope getScope() {
return ComponentScope.Singleton;
}
It should be
public ComponentScope getScope() {
return ComponentScope.PerRequest;
}