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?
Related
#Component
public abstract class CommandBase {
#Autowired
WebServiceProxy nbiService;
#Autowired
OperationCacheRepository cacheRepository;
public CommandBase(
WebServiceProxy nbiService,
OperationCacheRepository cacheRepository) {
this.nbiService = nbiService;
this.cacheRepository = cacheRepository;
}
public abstract void executeSPV(SpeedTestDTO stDTO) throws NBIException;
public abstract long executeGPV(long guid, OperationCache operationCache) throws NBIException;
#Slf4j
public class DownloadDiagnosticsCommand extends CommandBase {
public DownloadDiagnosticsCommand(WebServiceProxy nbiService, OperationCacheRepository cacheRepository) {
super(nbiService, cacheRepository);
}
#Override
public void executeSPV(SpeedTestDTO stDTO) throws NBIException {
// some executable code
}
#Override
public long executeGPV(long guid, OperationCache operationCache) throws NBIException {
// some executable code
}
}
#Slf4j
public class UploadDiagnosticsCommand extends CommandBase {
public UploadDiagnosticsCommand(WebServiceProxy nbiService, OperationCacheRepository cacheRepository) {
super(nbiService, cacheRepository);
}
#Override
public void executeSPV(SpeedTestDTO stDTO) throws NBIException {
// some executable code
}
#Override
public long executeGPV(long guid, OperationCache operationCache) throws NBIException {
//some executable code
}
}
#Component
public class RFACommandFactory {
#Autowired
WebServiceProxy nbiServiceProxy;
#Autowired
OperationCacheRepository cacheRepository;
public final CommandBase createCommand(final String measureType) {
if ("download".equalsIgnoreCase(measureType)) {
return new DownloadDiagnosticsCommand(nbiServiceProxy, cacheRepository);
} else if ("upload".equalsIgnoreCase(measureType)) {
return new UploadDiagnosticsCommand(nbiServiceProxy, cacheRepository);
}
return null;
}
}
Calling method executeSPV from abstract class
#RestController
#RequestMapping("/rfa/speedtest/v1")
#Slf4j
public class Controller {
#Autowired
CommandBase command;
#Autowired
RFACommandFactory rfaCommandFactory;
#PostMapping(value = "{id}", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
#ResponseBody
private ResponseEntity<String> post(
#PathVariable String assetId,
#RequestBody Payload payload) {
log.info("Received new payload:{}", payload);
command = rfaCommandFactory.createCommand(speedTestDTO.getType());
try {
command.executeSPV(speedTestDTO);
} catch (NBIException e) {
log.info("NBIException", e);
return new ResponseEntity(payload, HttpStatus.BAD_REQUEST);
}
return new ResponseEntity(payload, HttpStatus.CREATED);
}
}
If I remove #Componet from Upload and Download classes I receive error I need to add Bean for abstrcat class CommndBase
If I use #Compoment on Upload and Download classes I receive dual Bean is useed...
Field command in .Controller required a single bean, but 2 were found:
You should not use #Component for abstract class, because Spring context will not be able to initialize that bean. You should remove it then.
Another thing is the way you want to implement a factory pattern here - I recommend you the way described here: https://stackoverflow.com/a/39361500/14056755, refactored version https://stackoverflow.com/a/55060326/14056755.
I'm not sure whether this is possible or not but I'm trying to setup a EJB + JAX-RS (Jersey) test project and use the #RolesAllowed annotation.
I'm currently getting the following error logs:
Warning: WEB9102: Web Login Failed: com.sun.enterprise.security.auth.login.common.LoginException: Login failed: Security Exception
Severe: ejb.stateless_ejbcreate_exception
Warning: A system exception occurred during an invocation on EJB TestSB, method: public java.util.List rest.sb.TestSB.findAll()
Warning: javax.ejb.EJBException: javax.ejb.EJBException: javax.ejb.CreateException: Could not create stateless EJB
Relevant classes:
ApplicationConfig.java
#ApplicationPath("rest")
public class ApplicationConfig extends ResourceConfig {
public ApplicationConfig() {
packages("rest");
register(RolesAllowedDynamicFeature.class);
}
}
TestSBFacade.java
#Local
public interface TestSBFacade {
public List<Test> findAll();
}
TestSB.java
#Stateless
#Path("secured/test")
public class TestSB implements TestSBFacade {
#DAO #Inject
private TestDAOFacade dao;
#Context
SecurityContext securityContext;
#Secured
#RolesAllowed({"READ"})
#Path("all")
#GET
#Produces(MediaType.APPLICATION_JSON)
#Override
public List<Test> findAll() {
//this works without the #RolesAllowed so it is a possible workaroud for now.
System.out.println(securityContext.isUserInRole("READ")); //output: true
return dao.findAll();
}
}
AuthFilter.java
#Provider
#Secured //NameBinding
#Priority(Priorities.AUTHENTICATION)
public class AuthenticationFilter implements ContainerRequestFilter {
#Override
public void filter(ContainerRequestContext requestContext) throws IOException {
String token = requestContext.getHeaderString(HttpHeaders.AUTHORIZATION);
try {
verifyToken();
createSecurityContext();
} catch (Exception e) {
Logger.getLogger(AuthenticationFilter.class.getName()).log(Level.SEVERE, null, "Invalid or Expired JWT");
requestContext.abortWith(Response.status(Response.Status.UNAUTHORIZED).build());
}
}
}
My SecurityContext is set and working, the #RolesAllowed seems to be to problem since I get no errors if I remove it and a JSON is properly returned to the front-end. Keeping the #RolesAllowed results in the errors mentioned at the start.
However I'd like to use the handy annotation instead of embedding every method inside isUserInRole IF Statements. Any help and insights are much appreciated.
So apparently due to both EJB and JAX-RS implementations using#RolesAllowed they don't do well together. So I decided to create my own Annotation instead and register my own DynamicFeature in the ApplicationConfig.java.
Authorized.java
#Documented
#Retention(RUNTIME)
#Target({TYPE, METHOD})
public #interface Authorized {
public String[] value() default "";
}
AuthorizationDynamicFeature.java
public class AuthorizationDynamicFeature implements DynamicFeature {
#Override
public void configure(final ResourceInfo resourceInfo, final FeatureContext featureContext) {
Authorized auth = new AnnotatedMethod(resourceInfo.getResourceMethod()).getAnnotation(Authorized.class);
if (auth != null) {
featureContext.register(new AuthorizationRequestFilter(auth.value()));
}
}
#Priority(Priorities.AUTHORIZATION)
private static class AuthorizationRequestFilter implements ContainerRequestFilter {
private final String[] roles;
AuthorizationRequestFilter() {
this.roles = null;
}
AuthorizationRequestFilter(final String[] roles) {
this.roles = roles;
}
#Override
public void filter(final ContainerRequestContext requestContext) throws IOException {
if (!this.roles[0].isEmpty()) {
for (final String role : this.roles) {
if (requestContext.getSecurityContext().isUserInRole(role)) {
return;
}
}
throw new ForbiddenException(LocalizationMessages.USER_NOT_AUTHORIZED());
}
}
}
}
Huge thanks to #PaulSamsotha for leading me to a more suitable solution.
I am trying to inject a object provided by HK2 Factory service in a Jersey Test Class but getting unsatisfied dependencies exception.
I have a factory service as below
public class TestFactory implements Factory<TestObject>{
private final CloseableService closeService;
#Inject
public TestFactory(CloseableService closeService) {
this.closeService = closeService;
}
#Override
public TestObject provide() {
TestObject casualObject = new TestObject();
this.closeService.add(() -> dispose(casualObject));
return casualObject;
}
#Override
public void dispose(TestObject instance) {
instance.destroy();
}
}
And a Jersey Test class
public class SampleTestCass extends JerseyTestNg.ContainerPerClassTest
{
//#Inject
private TestObject myTestObject;
private ServiceLocator locator;
#Override
protected Application configure()
{
ResourceConfig resConfig = new ResourceConfig(MyApi.class);
resConfig.register(getBinder());
locator = setupHK2(getBinder());
return resConfig;
}
// setup local hk2
public setupHK2(AbstractBinder binder)
{
ServiceLocatorFactory factory = ServiceLocatorFactory.getInstance();
ServiceLocator locator = factory.create("test-locator");
DynamicConfigurationService dcs = locator.getService(DynamicConfigurationService.class);
DynamicConfiguration dc = dcs.createDynamicConfiguration();
locator.inject(binder);
binder.bind(dc);
dc.commit();
return locator;
}
// get the binder
public AbstractBinder getBinder()
{
return new AbstractBinder() {
#Override
protected void configure() {
bindFactory(TestFactory.class, Singleton.class).to(TestObject.class).in(PerLookup.class);
}
}
}
#BeforeClass
public void beforeClass()
{
myTestObject = locator.getService(TestObject.class);
// use myTestObject
}
#AfterClass
public void afterClass()
{
if (locator != null) {
locator.shutdown();
}
}
#Test()
public void someTest()
{
// some test code...
}
}
And getting below exceptions
A MultiException has 3 exceptions. They are:
org.glassfish.hk2.api.UnsatisfiedDependencyException: There was no object available for injection at SystemInjecteeImpl(requiredType=CloseableService,parent=TestFactory,qualifiers={},position=0,optional=false,self=false,unqualified=null,2053349061)
java.lang.IllegalArgumentException: While attempting to resolve the dependencies of com.test.factories.TestFactory errors were found
java.lang.IllegalStateException: Unable to perform operation: resolve on com.test.factories.TestFactory
CloseableService is a service available within a Jersey application. The ServiceLocator you created is not tied to the Jersey application. It is just a standalone locator. So trying to register the TestFactory with this locator will cause it to fail, as there is no CloseableService. The one that you registered with the ResourceConfig will work just fine.
Not sure what exactly you're trying to do, but if you want access to the service inside the test, one thing you can do is just bind the service as an instance, something like
class MyTest {
private Service service;
#Override
public ResourceConfig configure() {
service = new Service();
return new ResourceConfig()
.register(new AbstractBinder() {
#Override
public void configure() {
bind(service).to(Service.class);
}
})
}
}
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();
}
}
}
}
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;
}