issue with #Aspectj logging if #EnableTransactionManagement added in sprig boot project - java

I tried to use #Aspect for logging all requests and responses the code is running fine and successful with a simple spring boot project with a common configuration and not have any init methods or DB call.
The below code is not working that includes init configuration and DB call for required data used #EnableTransactionManagement its getting Error while use #Aspect while running my application
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.transaction.annotation.EnableTransactionManagement;
#Configuration
#EnableTransactionManagement
public class MyContextClass implements ApplicationListener<ContextRefreshedEvent>{
#Autowire
private MyServiceRepository myServiceRepo;
#Override
public void onApplicationEvent(ContextRefreshedEvent event) {
// Added Db call
CommonUtils.setYears(myServiceRepo.getTotalYears());
}
}
#Aspect logging code
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.*;
#Aspect
#Component
public class LoggingAspect {
private final Logger log = LoggerFactory.getLogger(this.getClass());
#Autowired
private HttpServletRequest request;
#Autowired
private HttpServletResponse response;
#Pointcut("within(#org.springframework.stereotype.Repository *)"
+ " || within(#org.springframework.stereotype.Service *)"
+ " || within(#org.springframework.web.bind.annotation.RestController *)")
public void springBeanPointcut() {
// Method is empty as this is just a Pointcut, the implementations are in the
// advices.
log.info("springBeanPointcut");
}
/**
* Pointcut that matches all Spring beans in the application's main packages.
*/
#Pointcut("within(com.test.mypackage..*)")
public void applicationPackagePointcut() {
// Method is empty as this is just a Pointcut, the implementations are in the
// advices.
log.info("applicationPackagePointcut");
}
#Around("applicationPackagePointcut() && springBeanPointcut()")
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
String className = methodSignature.getDeclaringType().getSimpleName();
String methodName = methodSignature.getName();
String methodParameterNames = methodSignature.getMethod().toString();
if (request ! = null) {
log.info(request.getRequestURL());
log.info(request.getMethod());
}
if (response != null) {
log.info(response.getRequestURL());
log.info(response.getStatus());
}
final StopWatch stopWatch = new StopWatch();
stopWatch.start();
Object result = joinPoint.proceed();
stopWatch.stop();
}
this code build success. but when I run the application below error display the error in setYears
2021-05-07 20:14:58 [restartedMain] ERROR o.s.boot.SpringApplication-[SpringApplication.java:856]-Application run failed
java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
at org.springframework.web.context.request.RequestContextHolder.currentRequestAttributes(RequestContextHolder.java:131)
at org.springframework.web.context.support.WebApplicationContextUtils.currentRequestAttributes(WebApplicationContextUtils.java:313)
at org.springframework.web.context.support.WebApplicationContextUtils.access$400(WebApplicationContextUtils.java:66)
at org.springframework.web.context.support.WebApplicationContextUtils$RequestObjectFactory.getObject(WebApplicationContextUtils.java:329)
at org.springframework.web.context.support.WebApplicationContextUtils$RequestObjectFactory.getObject(WebApplicationContextUtils.java:324)
at org.springframework.beans.factory.support.AutowireUtils$ObjectFactoryDelegatingInvocationHandler.invoke(AutowireUtils.java:292)
at com.sun.proxy.$Proxy120.getRequestURL(Unknown Source)
at com.opl.ans.config.utils.LoggingAspect.logAround(LoggingAspect.java:137)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:634)
at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:624)
at org.springframework.aop.aspectj.AspectJAroundAdvice.invoke(AspectJAroundAdvice.java:72)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:749)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:691)
Is it the right way to do it? Are there any suggestions? Please help.

Based on the error stack the issue is that the request instance is invalid. I would guess that the request instance autowired to the Aspect could be stale or rather not assosicated with the current thread. This means that request instance is not null and the check within logAround() method gives unexepected results.
RequestContextHolder.getRequestAttributes() would return null if no request attributes are currently bound to the thread. Modifying the
if (request ! = null) { ..} check with if (RequestContextHolder.getRequestAttributes() ! = null) {..} should fix the issue.
Also spring boot provides out of the box solutions to what you are currently attempting through AOP . Do check them out as well.
HTTP Tracing
Spring MVC Metrics

Related

AttributeNotFoundException through JMX in Startup bean on Wildfly

Hello everyone!
I'm trying to load wildfly server's system properties through JMX in Startup bean's #PostConstruct method. It works fine on the already started server instance when deployment starts, but fails while starting with server instance bootstrapping.
Wildfly 11.0.0.CR1
Startup bean code:
package ru.wildfly.test.ejb.wildflyconsulregistrar.startup;
import ru.wildfly.test.ejb.wildflyconsulregistrar.api.ConsulRegistrar;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.ejb.Singleton;
import javax.ejb.Startup;
import javax.inject.Inject;
#Startup
#Singleton
public class WildflyConsulRegistrarStartupBean {
#Inject
private ConsulRegistrar consulRegistrar;
#PostConstruct
public void initialize() {
registerServices();
}
private void registerServices() {
consulRegistrar.registerService("WildflyTestCluster");
}
.............
}
ConsulRegistrar code:
package ru.wildfly.test.ejb.wildflyconsulregistrar.impl;
import com.ecwid.consul.v1.ConsulClient;
import com.ecwid.consul.v1.agent.model.NewService;
import ru.test.ejb.wildflyconsulregistrar.api.ConsulRegistrar;
import ru.wildfly.test.ejb.wildflyconsulregistrar.serversettings.api.CurrentServerNodeSettings;
import javax.annotation.PostConstruct;
import javax.enterprise.context.Dependent;
import javax.inject.Inject;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
#Dependent
public class ConsulRegistrarImpl implements ConsulRegistrar {
...............
#Inject
private CurrentServerNodeSettings currentServerNodeSettings;
.............
#Override
public void registerService(String serviceName) {
String currentNodeName = currentServerNodeSettings.getCurrentNodeName();
........................
}
.......................
}
CurrentServerNodeSettings code:
package ru.wildfly.test.ejb.wildflyconsulregistrar.serversettings.impl;
import ru.wildfly.test.ejb.wildflyconsulregistrar.serversettings.api.CurrentServerNodeSettings;
import javax.enterprise.context.Dependent;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import java.lang.management.ManagementFactory;
#Dependent
public class CurrentServerNodeSettingsWildflyImpl implements CurrentServerNodeSettings {
....................
#Override
public String getCurrentNodeName() {
String currentNodeName = getPlatformMBeanServerAttributeValue(String.class, "jboss.as:system-property=server.name", "value");
return currentNodeName;
}
private <T> T getPlatformMBeanServerAttributeValue(Class<T> valueType, String objectName, String attributeName) {
T attributeValue = null;
try {
MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();
Object attributeObject = mBeanServer.getAttribute(new ObjectName(objectName), attributeName);
attributeValue = attributeObject != null ? (valueType.cast(attributeObject)) : null;
} catch (Exception ex) {
throw new IllegalStateException(ex);
}
return attributeValue;
}
}
Error message:
Caused by: java.lang.IllegalStateException:
javax.management.AttributeNotFoundException:
"WFLYCTL0216: Management resource '[(\"system-property\" => \"server.name\")]' not found"
at ru.wildfly.test.ejb.wildflyconsulregistrar.serversettings.impl.CurrentServerNodeSettingsWildflyImpl
.getPlatformMBeanServerAttributeValue(CurrentServerNodeSettingsWildflyImpl.java:41)
I have found the same issue on jboss forum https://developer.jboss.org/message/971717#971717 , but it was unanswered.
Any suggestions?
This is a dependency problem during startup, i.e. the server name is set after your #PostConstruct method gets executed. Try to load the server name lazy when it is accessed from the application for the first time.
In Wildfly there is no generic way to enforce the sequence of deployments from the application despite the definition of module dependencies. But this won't help in your case.

How to use #Startup Annotation for Service that Pushes Data to Websocket?

This is the Java EE7 #Startup annotation for reference. I am using the latest GlassFish server and IntelliJ to run the server.
I need to instantiate this service so that it can send packets of data periodically to a websocket for processing, but the fact that I use
session.getBasicRemote().sendObject(message);
in the end forces me to throw IOException and EncodeException.
This is bad as #Startup or #Singleton disallows usage of Exceptions when instantiating:
war exploded: java.io.IOException: com.sun.enterprise.admin.remote.RemoteFailureException: Error occurred during deployment: Exception while deploying the app [keyBoard_war_exploded] : The lifecycle method [printSchedule] must not throw a checked exception.
Here is my code:
package site.xxxx.models;
import site.xxxx.message.Message;
import site.xxxx.message.TextMessage;
import site.xxxx.modules.Packet;
import site.xxxx.websocket.MainServerEndpoint;
import javax.annotation.PostConstruct;
import javax.ejb.Schedule;
import javax.ejb.Singleton;
import javax.ejb.Startup;
import javax.json.Json;
import javax.json.JsonObject;
import javax.websocket.EncodeException;
import javax.websocket.Session;
import java.io.IOException;
import java.util.Set;
#Startup
#Singleton
public class Service {
private static Service service;
Set<Session> peers = MainServerEndpoint.peers;
#PostConstruct
public void mainLoop() throws IOException, EncodeException, InterruptedException {
sendSpamPacket();
}
private void sendSpamPacket() throws IOException, EncodeException {
JsonObject ret = Json.createObjectBuilder()
.add("type", "textMessage")
.add("text", "ayyyy")
.build();
Packet packet = new Packet(new TextMessage(ret));
MainServerEndpoint.sendPacket(packet);
//results in calling session.getBasicRemote().sendObject(message);
}
}

NoSuchMethod when calling AWS Lambda using the AWS Sdk

I've created & deployed one simple GET API in API Gateway and here is the ARN and there is no authentication whatsoever on this function, I can simply call it on my browser
arn:aws:lambda:ap-southeast-1:XXXXXXXXXXXXXX:function:La
and the public url that can be browsed using the browser is:
https://xxxxxxxxx.execute-api.ap-southeast-1.amazonaws.com/v1/lambda/geta
and I'm using Spring boot project and the below code to invoke the API (Following this Doc)
The interface as the lambda service
package com.xxxxxxx.services.interfaces;
import com.amazonaws.services.lambda.invoke.LambdaFunction;
public interface ILambdaGetBalance {
#LambdaFunction(functionName="La")
String getA();
}
The service using that interface to call the lambda function
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import com.xxxxxxxx.services.interfaces.ILambdaGetBalance;
import com.amazonaws.auth.AWSCredentials;
import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.services.lambda.AWSLambda;
import com.amazonaws.services.lambda.AWSLambdaClientBuilder;
import com.amazonaws.services.lambda.invoke.LambdaInvokerFactory;
#Service
public class LambdaService {
#Value("${aws.access-key}")
private String accessKey;
#Value("${aws.secret-key}")
private String secretKey;
#Value("${aws.lambda.region-name}") // this is ap-southeast-1
private String regionName;
public void test() {
AWSCredentials credentials = new BasicAWSCredentials(accessKey,
secretKey);
AWSLambda client = AWSLambdaClientBuilder.standard()
.withCredentials(new AWSStaticCredentialsProvider(credentials))
.withRegion(regionName)
.build();
final ILambdaGetBalance getBalance = LambdaInvokerFactory.builder()
.lambdaClient(client)
.build(ILambdaGetBalance.class);
getBalance.getA();
}
}
after calling the getA function the system will through the following exception:
java.lang.NoSuchMethodError: com.amazonaws.services.lambda.AWSLambdaClient.beforeClientExecution(Lcom/amazonaws/AmazonWebServiceRequest;)Lcom/amazonaws/AmazonWebServiceRequest;
Any idea why is this happening? What am I missing?
Looks like your aws-java-sdk-lambda and aws-java-sdk-core modules may have incompatible versions. How are you resolving the dependencies for your project? The beforeClientExecution method was added to the AmazonWebServiceClient base class in version 1.11.106 of aws-java-sdk-core - see here: https://github.com/aws/aws-sdk-java/blame/master/aws-java-sdk-core/src/main/java/com/amazonaws/AmazonWebServiceClient.java#L590

dropwizard hibernate use a resource from within the server

I'm new to dropwizard and i'm trying to create an Authenticator which gets credentials from the user, then it uses the rest api getUser method which i implemented in my UserResouce class to get the user with the username that is in the credentials from the db users table. However in my autheticator class i having troubles in figuring out how to use the user resource functions to get the user.
I was trying to do something like that:
public List<com.amitbaz.tss.db.User> getUsersFromDB(String username){
SessionFactory sessionFactory = TradingSystemServerApplication.hibernateBundle.getSessionFactory();
UserDAO userDAO = new UserDAO(sessionFactory);
List<com.amitbaz.tss.db.User> user = userDAO.getUser(username);
logger.debug(user.toString());
return user;
}
inside the autheticator and call it from the authenticte function but it says there is no session bound...
EDIT:
Ok so after much thinking i got to this:
I'm dropwizard authenticator and authorizer implementions with BasicCredentials and.
Autheticator (don't mind the VALID_USER thing..):
package com.amitbaz.tss.auth;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import javax.persistence.NamedQuery;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.amitbaz.tss.TradingSystemServerApplication;
import com.amitbaz.tss.db.UserDAO;
import com.amitbaz.tss.db.UserResource;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import io.dropwizard.auth.AuthenticationException;
import io.dropwizard.auth.Authenticator;
import io.dropwizard.auth.basic.BasicCredentials;
public class TradingSystemServerAuthenticator implements Authenticator<BasicCredentials, User> {
private Logger logger = LoggerFactory.getLogger(TradingSystemServerAuthenticator.class);
private static final Map<String, Set<String>> VALID_USERS = ImmutableMap.of(
"guest", ImmutableSet.of(),
"amit", ImmutableSet.of("admin"),
"stav", ImmutableSet.of("broker")
);
private UserDAO userDAO;
public TradingSystemServerAuthenticator(UserDAO userDAO) {
// TODO Auto-generated constructor stub
this.userDAO = userDAO;
}
#Override
public Optional<User> authenticate(BasicCredentials credentials) throws AuthenticationException {
// TODO Auto-generated method stub
List<com.amitbaz.tss.db.User> user = userDAO.getUser(credentials.getUsername());
logger.debug(user.toString());
if("amit".equals(credentials.getPassword())){
return Optional.of(new User(credentials.getUsername(), VALID_USERS.get(credentials.getUsername())));
}
return Optional.empty();
}
}
Authorizer:
package com.amitbaz.tss.auth;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.amitbaz.tss.db.UserDAO;
import io.dropwizard.auth.Authorizer;
public class TradingSystemServerAuthorizer implements Authorizer<User>{
private Logger logger = LoggerFactory.getLogger(TradingSystemServerAuthorizer.class);
private UserDAO userDAO;
public TradingSystemServerAuthorizer(UserDAO userDAO) {
super();
this.userDAO = userDAO;
}
#Override
public boolean authorize(User user, String role) {
// TODO Auto-generated method stub
logger.debug(userDAO.getUser(user.getName()).toString());
return user.getName().equals("amit") && user.getRole().contains(new String("admin"));
}
}
Now, In my Application class I do this:
package com.amitbaz.tss;
import org.glassfish.jersey.server.filter.RolesAllowedDynamicFeature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.amitbaz.tss.auth.TradingSystemServerAuthenticator;
import com.amitbaz.tss.auth.TradingSystemServerAuthorizer;
import com.amitbaz.tss.auth.User;
import com.amitbaz.tss.db.Broker;
import com.amitbaz.tss.db.BrokerDAO;
import com.amitbaz.tss.db.BrokerResource;
import com.amitbaz.tss.db.Contact;
import com.amitbaz.tss.db.ContactDAO;
import com.amitbaz.tss.db.ContactResource;
import com.amitbaz.tss.db.Product;
import com.amitbaz.tss.db.ProductDAO;
import com.amitbaz.tss.db.ProductResource;
import com.amitbaz.tss.db.Test;
import com.amitbaz.tss.db.TestDAO;
import com.amitbaz.tss.db.TestResource;
import com.amitbaz.tss.db.Transaction;
import com.amitbaz.tss.db.TransactionDAO;
import com.amitbaz.tss.db.TransactionResource;
import com.amitbaz.tss.db.UserDAO;
import com.amitbaz.tss.db.UserResource;
import com.amitbaz.tss.db.UserRole;
import com.amitbaz.tss.db.UserRoleDAO;
import com.amitbaz.tss.db.UserRoleResource;
import com.amitbaz.tss.db.Website;
import com.amitbaz.tss.db.WebsiteDAO;
import com.amitbaz.tss.db.WebsiteResource;
import io.dropwizard.Application;
import io.dropwizard.auth.AuthDynamicFeature;
import io.dropwizard.auth.AuthValueFactoryProvider;
import io.dropwizard.auth.basic.BasicCredentialAuthFilter;
import io.dropwizard.db.DataSourceFactory;
import io.dropwizard.hibernate.HibernateBundle;
import io.dropwizard.setup.Bootstrap;
import io.dropwizard.setup.Environment;
import javassist.tools.web.Webserver;
public class TradingSystemServerApplication extends Application<TradingSystemServerConfiguration>{
public static void main(String[] args) throws Exception{
new TradingSystemServerApplication().run(args);
}
public final static HibernateBundle<TradingSystemServerConfiguration> hibernateBundle
= new HibernateBundle<TradingSystemServerConfiguration>(
Test.class,Broker.class, com.amitbaz.tss.db.User.class, UserRole.class
,Product.class, Transaction.class, Website.class, Contact.class
) {
#Override
public DataSourceFactory getDataSourceFactory(
TradingSystemServerConfiguration configuration
) {
return configuration.getDataSourceFactory();
}
};
final Logger logger = LoggerFactory.getLogger(TradingSystemServerApplication.class);
#Override
public void initialize(
final Bootstrap<TradingSystemServerConfiguration> bootstrap) {
bootstrap.addBundle(hibernateBundle);
}
#Override
public void run(TradingSystemServerConfiguration config, Environment env) throws Exception {
final UserDAO userDAO = new UserDAO(hibernateBundle.getSessionFactory());
final UserRoleDAO userRoleDAO = new
env.jersey().register(new UserResource(userDAO));
/...
BasicCredentialAuthFilter.Builder<User>()
.setAuthenticator(new TradingSystemServerAuthenticator(userDAO))
.setAuthorizer(new TradingSystemServerAuthorizer(userDAO))
.setRealm("Authetication Required")
.buildAuthFilter()));
env.jersey().register(RolesAllowedDynamicFeature.class);
env.jersey().register(new AuthValueFactoryProvider.Binder<>(User.class));
}
}
And I have the annotation #RolesAllowed("role_name") on one of the rest api methods which with im trying to test the auth.
Now when i try to test this and i make a request to that rest api method, I
get the error No session currently bound to execution context where i do userDAO.getUser(...) in the authanticator and in the authorizer
EDIT 2:
UserDAO implementation:
package com.amitbaz.tss.db;
import java.util.List;
import org.hibernate.SessionFactory;
import io.dropwizard.hibernate.AbstractDAO;
public class UserDAO extends AbstractDAO<User>{
public UserDAO(SessionFactory sessionFactory) {
super(sessionFactory);
// TODO Auto-generated constructor stub
}
public List<User> getUser(String username){
return list(namedQuery("com.amitbaz.tss.db.user.getUser")
.setParameter("username", username));
}
}
EDIT 3:
Added #UnitOfWork to authenticate and authorize methods.
registered them as follow ( Notice the changes in hibernateBundle and run method):
package com.amitbaz.tss;
import javax.servlet.ServletRegistration;
import org.atmosphere.cpr.ApplicationConfig;
import org.atmosphere.cpr.AtmosphereServlet;
import org.glassfish.jersey.server.filter.RolesAllowedDynamicFeature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.amitbaz.tss.auth.TradingSystemServerAuthenticator;
import com.amitbaz.tss.auth.TradingSystemServerAuthorizer;
import com.amitbaz.tss.auth.User;
import com.amitbaz.tss.db.Broker;
import com.amitbaz.tss.db.BrokerDAO;
import com.amitbaz.tss.db.BrokerResource;
import com.amitbaz.tss.db.Contact;
import com.amitbaz.tss.db.ContactDAO;
import com.amitbaz.tss.db.ContactResource;
import com.amitbaz.tss.db.Product;
import com.amitbaz.tss.db.ProductDAO;
import com.amitbaz.tss.db.ProductResource;
import com.amitbaz.tss.db.Test;
import com.amitbaz.tss.db.TestDAO;
import com.amitbaz.tss.db.TestResource;
import com.amitbaz.tss.db.Transaction;
import com.amitbaz.tss.db.TransactionDAO;
import com.amitbaz.tss.db.TransactionResource;
import com.amitbaz.tss.db.UserDAO;
import com.amitbaz.tss.db.UserResource;
import com.amitbaz.tss.db.UserRole;
import com.amitbaz.tss.db.UserRoleDAO;
import com.amitbaz.tss.db.UserRoleResource;
import com.amitbaz.tss.db.Website;
import com.amitbaz.tss.db.WebsiteDAO;
import com.amitbaz.tss.db.WebsiteResource;
import io.dropwizard.Application;
import io.dropwizard.auth.AuthDynamicFeature;
import io.dropwizard.auth.AuthValueFactoryProvider;
import io.dropwizard.auth.basic.BasicCredentialAuthFilter;
import io.dropwizard.db.DataSourceFactory;
import io.dropwizard.hibernate.HibernateBundle;
import io.dropwizard.setup.Bootstrap;
import io.dropwizard.setup.Environment;
import javassist.tools.web.Webserver;
public class TradingSystemServerApplication extends Application<TradingSystemServerConfiguration>{
public static void main(String[] args) throws Exception{
new TradingSystemServerApplication().run(args);
}
public final static HibernateBundle<TradingSystemServerConfiguration> hibernateBundle
= new HibernateBundle<TradingSystemServerConfiguration>(
Test.class,Broker.class, com.amitbaz.tss.db.User.class, UserRole.class
,Product.class, Transaction.class, Website.class, Contact.class
,TradingSystemServerAuthenticator.class, TradingSystemServerAuthorizer.class
) {
#Override
public DataSourceFactory getDataSourceFactory(
TradingSystemServerConfiguration configuration
) {
return configuration.getDataSourceFactory();
}
};
final Logger logger = LoggerFactory.getLogger(TradingSystemServerApplication.class);
#Override
public void initialize(
final Bootstrap<TradingSystemServerConfiguration> bootstrap) {
bootstrap.addBundle(hibernateBundle);
}
#Override
public void run(TradingSystemServerConfiguration config, Environment env) throws Exception {
final UserDAO userDAO = new UserDAO(hibernateBundle.getSessionFactory());
final UserRoleDAO userRoleDAO = new UserRoleDAO(hibernateBundle.getSessionFactory());
final TradingSystemServerAuthorizer authorizer = new TradingSystemServerAuthorizer(userDAO);
final TradingSystemServerAuthenticator authenticator = new TradingSystemServerAuthenticator(userDAO);
env.jersey().register(new UserResource(userDAO));
env.jersey().register(new UserRoleResource(userRoleDAO));
env.jersey().register(authorizer);
env.jersey().register(authenticator);
env.jersey().register(new AuthDynamicFeature(new BasicCredentialAuthFilter.Builder<User>()
.setAuthenticator(authenticator)
.setAuthorizer(authorizer)
.setRealm("Authetication Required")
.buildAuthFilter()));
env.jersey().register(RolesAllowedDynamicFeature.class);
env.jersey().register(new AuthValueFactoryProvider.Binder<>(User.class));
}
Your approach looks like a design issue. The issue I see is that you are trying to integrate via rest with a service that is already accessible for you within your application. That adds a lot of overhead and complicates things.
Fortunately, DW already has a fully integrated Authorization and Authentication system just waiting for you to plug in. You can read more about it here: http://www.dropwizard.io/1.0.2/docs/manual/auth.html
The essential thing to note here is that you should split the service used by your resource from your resource. In your case for example the UserDao, or you could split it into a UserService and UserResource, where the UserService provides access to your database layer. Up to you really.
Here is how you would implement this with DW integrated auth and how you would register this as well.
In my example I am skipping the Hibernate aspect of this as it isn't too relevant. you can read about it here: http://www.dropwizard.io/1.0.2/docs/manual/hibernate.html
Here's my code:
public class AuthenticatorTest extends io.dropwizard.Application<Configuration> {
#Override
public void run(Configuration configuration, Environment environment) throws Exception {
// register resource
environment.jersey().register(MyHelloResource.class);
// create the dao + dependencies
UserDao dao = new UserDao(null);
// register new authenticator
environment.jersey().register(new AuthDynamicFeature(new BasicCredentialAuthFilter.Builder<Principal>()
.setAuthenticator(new UserAuth(dao)).setRealm("SUPER SECRET STUFF").buildAuthFilter()));
// enables authentication via filter
environment.jersey().register(RolesAllowedDynamicFeature.class);
}
public static void main(String[] args) throws Exception {
new AuthenticatorTest().run("server", "/home/artur/dev/repo/sandbox/src/main/resources/config/test.yaml");
}
#Path("test")
#Produces(MediaType.APPLICATION_JSON)
public static class MyHelloResource {
#GET
#Path("asd")
#PermitAll
public String test(String x) {
return "Hello";
}
}
public static class UserAuth implements Authenticator<BasicCredentials, Principal> {
private UserDao dao;
public UserAuth(UserDao dao) {
this.dao = dao;
}
#Override
public Optional<Principal> authenticate(BasicCredentials credentials) throws AuthenticationException {
String user = dao.getUser();
return Optional.of(new Principal() {
#Override
public String getName() {
return user;
}
});
}
}
public static class UserDao {
private SessionFactory s;
public UserDao(final SessionFactory s) {
this.s = s;
}
public String getUser() {
return "pandaadb";
}
}
}
And this is the breakdown of what we are doing.
First, as per docs, you would register your HibernateBundle within the bootstrap method as shown (in docs). This gives you access to the SessionFactory you require for your authentication.
Your resource method will be annotated with a java security annotation. i am using PermitAll because I am disregarding roles.
In the run method, you then create your DAO, register your resource and use the DW builder to add the required Filter and the Authenticator. This one specifically is for BasicCredentials, however there is nothing stopping you from doing any kind of filter for this. DW already supports things like Ldap (in a different dependency), Basic Auth and so on.
Now, since you create your beans in the run method, and you added your Hibernate bundle in the bootstrap method, you have access to the SessionFactory and can instantiate the DAO accordingly. No need to have to pass it around.
You also don't have to do any rest-request to access your user (though there is nothing stopping you adding that resource anyway, in case you need external access to it.)
So, to sum up, the important parts are:
Add A security annotation to your resource (e.g. PermitAll to allow all roles)
Add an authenticator implementation (in my case UserAuth) that uses your DAO
Instantiate it in the run method provided by dropwizard and register it with the jersey environment.
Note, this requires your user to implement the javax.security.Principal interface. this is not a bad idea in general as a lot of security Frameworks make use of this.
This, also, gives you more options with regards to DW.
You can add an Authorization implementation and a filter, and you'll be able to inject the User object into any resource method by adding an #Auth annotated object (see docs).
Finally, the test of the standalone app from above:
artur#pandaadb:~$ curl "localhost:9085/api/test/asd" -v
* Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 9085 (#0)
> GET /api/test/asd HTTP/1.1
> Host: localhost:9085
> User-Agent: curl/7.47.0
> Accept: */*
>
< HTTP/1.1 401 Unauthorized
< Date: Mon, 17 Oct 2016 10:45:51 GMT
< WWW-Authenticate: Basic realm="SUPER SECRET STUFF"
< Content-Type: text/plain
< Content-Length: 49
<
* Connection #0 to host localhost left intact
Credentials are required to access this resource.
artur#pandaadb:~$ curl "localhost:9085/api/test/asd" -H "Authorization: Basic dXNlcjpwYXNz" -v
* Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 9085 (#0)
> GET /api/test/asd HTTP/1.1
> Host: localhost:9085
> User-Agent: curl/7.47.0
> Accept: */*
> Authorization: Basic dXNlcjpwYXNz
>
< HTTP/1.1 200 OK
< Date: Mon, 17 Oct 2016 10:46:11 GMT
< Content-Type: application/json
< Vary: Accept-Encoding
< Content-Length: 5
<
* Connection #0 to host localhost left intact
Helloartur
I hope this helps you with your issue.
Of course you don't need to use the DW method for Authentication. However I would recommend going down that road as you will have more support and a lot of things out of the box.
However, the one thing you should rethink (if you don't use DW) is to not do a curl request to your Filter. Instead, instantiate it in the run method, and pass that instance to your Filter.
Note also, if you register your DAO with DW (as seen in hibernate docs), you will be able to use #Inject to inject your DAO into the Filter class that needs to use it.
Right, I think that's all the info you need :)
Let me know if you have any problems,
Artur
Edit:
I am doing an edit because I wrote a lot above and don't want to go over it.
I set up Hibernate to test this. The reason you are seeing issues is because the UnitOfWork is bound to the request scope. However, the resource annotations is matched AFTER the filter is invoked (since you need to do auth before executing the method). This is why you don't have a session.
This is the solution.
In your run method, register a proxy for your auth implementation:
UnitOfWorkAwareProxyFactory fac = new UnitOfWorkAwareProxyFactory(hibernate);
UserAuth proxy = fac.create(UserAuth.class, UserDao.class, dao);
environment.jersey().register(new AuthDynamicFeature(new BasicCredentialAuthFilter.Builder<Principal>()
.setAuthenticator(proxy).setRealm("SUPER SECRET STUFF").buildAuthFilter()));
This creates a proxy around the UserAuth class so that it is aware of the UnitOfWork annotation.
In your UserAuth class (or mine rather) you do:
public static class UserAuth implements Authenticator<BasicCredentials, Principal> {
private UserDao dao;
public UserAuth(UserDao dao) {
this.dao = dao;
}
#Override
#UnitOfWork
public Optional<Principal> authenticate(BasicCredentials credentials) throws AuthenticationException {
String user = dao.getUser();
return Optional.of(new Principal() {
#Override
public String getName() {
return user;
}
});
}
}
Note the UnitOfWork annotation on the authenticate. This now opens a new session for you. Please make sure to read up on UnitOfWork as it may have tricky side effects (or not) depending on how you use it.
Finally, this allowed my dao to talk to the database on an existing session.
Regards,
Artur
I finally, after much debugginf, find the proper way to open a session and execute a query.
I added this lines in Authenticator's authenticate mehod:
Session session = userDAO.getSessionFactory().openSession();
Transaction transaction = session.getTransaction();
Query userquery = session.createQuery("select u from User u where u.username = :username").setParameter("username", credentials.getUsername());
List<com.amitbaz.tss.db.User> u = userquery.list();
session.close();
And the same in Authorizater and it now works :)

Add manually mappedName in Message Driven Bean at runtime

I need to make simple Message Driven Bean that will listen on dynamically added queue locataions. I have tried few ways to implement this, but none of them worked. I have appplication that uses esb and java message queues, and I'm trying to read queue location from config file, during the runtime, and thus tell my message driven bean what is the queue on which to listen. I am not either sure that this is possible.
I also tried to implement message listener, but because I have to use ejb module, and ejb module does not support main method, it requires his own container (like message driven bean), I don't know what to use instead of main method to achive the same goal. I am not able to use session beans because I need to achieve asynchronous communication between client and service.
I also tried to use client application (although it is not one of the options), but maven project does not support debug and run functions for this type of application in netbeans.
Does anyone know any solution for this problem, or at least have some idea?
This may not be the best solution, but it is possible to receive and process JMS messages asynchronously with a Stateful Session Bean doing something like this:
package com.example.statefuljms;
import javax.annotation.Resource;
import javax.ejb.Local;
import javax.ejb.Stateful;
import javax.jms.ConnectionFactory;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.Queue;
import javax.jms.QueueConnection;
import javax.jms.QueueReceiver;
import javax.jms.QueueSession;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
#Stateful
#Local(MessageReceiverLocal.class)
public class MessageReceiver implements MessageReceiverLocal, MessageListener {
#Resource(mappedName = "ConnectionFactory")
private ConnectionFactory connectionFactory;
private QueueConnection connection;
#Override
public void start(String queueName) throws JMSException, NamingException {
Context initialContext = new InitialContext();
connection = (QueueConnection) connectionFactory.createConnection();
QueueSession session = (QueueSession) connection.createSession(false,
Session.AUTO_ACKNOWLEDGE);
Queue queue = (Queue) initialContext.lookup(queueName);
QueueReceiver receiver = session.createReceiver(queue);
receiver.setMessageListener(this);
connection.start();
}
#Remove
#Override
public void stop() throws JMSException {
connection.stop();
connection.close();
}
#Override
public void onMessage(Message message) {
// handle message here
}
}
Use a Singleton to test:
package com.example.statefuljms;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.ejb.EJB;
import javax.ejb.Singleton;
import javax.ejb.Startup;
import javax.jms.JMSException;
import javax.naming.NamingException;
#Startup
#Singleton
public class Test {
#EJB
private MessageReceiverLocal messageReceiver;
#PostConstruct
public void run() {
messageReceiver.start("/queue/myQueue");
}
#PreDestroy
public void cleanup() {
messageReceiver.stop();
}
}

Categories

Resources