Run EJB with Subject.doAs does not affect EJBContext in Wildfly 15 - java

Since we found out that it is not possible to put the SecurityIdentity into an ContextualProxy as described here we try to find some kind of workaround.
We make a programmatic login on the the security subsystem of wildfly and run a Runnable with the returning Subject. The runnable should then be able to run certain EJBs depending on the Roles of the Subject, but the EJBContext or SessionContext is always "anonymous".
Code:
The login
public class RunnableHandlerImpl implements RunnableHandler {
#Override
public void runAsPrivileged(final ContextRunnable runnable) throws LoginException {
LoginContext ctx;
ctx = new LoginContext("myDomain", new MyCallbackHandler(runnable.getAuthToken()));
ctx.login();
Subject subject = ctx.getSubject();
Subject.doAs(subject, new PrivilegedExceptionAction() {
#Override
public Object run() throws Exception {
runnable.run();
return null;
}
});
}
call from mdb:
#Inject
MySingleton bean;
public void onMessage(Message msg) {
ContextRunnable contextRunnable = (ContextRunnable) message.getObject();
contextRunnable.setSingletonBean(bean);
RunnableHandler handler = new RunnableHandlerImpl();
handler.runAsPrivileged(contextRunnable);
}
The Runnable:
public class ContextRunnable implements Serializable, Runnable {
private MySingleton bean;
public void run() {
bean.printText("hello");
}
public void setSingletonBean(MySingleton bean) {
this.bean = bean;
}
}
The Singleton:
#Singleton
#SecurityDomain("DemoApplicationDomain")
#PermitAll
public class MySingleton {
#Resource EJBContext context;
#Resource SessionContext sessionCtx;
public void printText(String text) throws EJBAccessException {
System.out.println("ejbcontext: " + context.getCallerPrincipal().getName());
System.out.println("SessionContext: " + sessionCtx.getCallerPrincipal().getName());
System.out.println("text: " + text);
}
}
the login works just fine and I always get the right name from the principal but the ejbcontext and the sessioncontext are always 'anonymous'.
My guess is that the legacy security system of wildfly is not 'connected' with the ejb-container anymore since Elytron will be used.
Is there a way to fix this?
Also I tried to use the Elytron system as described here but with the same result:
AuthenticationConfiguration config = AuthenticationConfiguration.empty().useName("admin").usePassword("admin");
AuthenticationContext.empty().with(MatchRule.ALL, config)
.run(contextRunnable);
please ask if you need more code or information

Related

Trying start RabbitListener on command after builded Application - RabbitListenerEndpointRegistry is empty

I searched all over the stackOverFlow and couldn't find an answer.
I need to start RabbitListener on command - from just REST send request to start this listener (no other option).
So I found that I need set properties in Listener like that: #RabbitListener(queues = "myQueue", id = "listener_id", autoStartup = "false") (of course is albo annotation #Component).
I made also initializer class using implementation of ApplicationContextAware and RabbitListenerEndpointRegistry like below:
#Slf4j
#Component
public class ListenerInitializer implements ApplicationContextAware {
private ApplicationContext applicationContext;
public void startListener() {
RabbitListenerEndpointRegistry listenerRegistry = applicationContext.getBean(RabbitListenerEndpointRegistry.class);
listenerRegistry.getListenerContainer("listener_id").start();
log.info("Listener started.");
}
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
And when I tried to use method startListener() mostly I received NullPointerException: null like RabbitListenerEndpointRegistry doesn't exist. I wrote mostly, because sometimes (every time I make mvn clean install) it works and everything is fine. But mostly I received NullPointerException.
With #Autowired of ApplicationContext same situation.
I tried with:
#Autowired RabbitListenerEndpointRegistry rabbitListenerEndpointRegistry
but in that case shows me that RabbitListenerEndpointRegistry bean doesn't exist (also through #RequiedArgsConstructor.
I tried also to make configuration like below:
#Configuration
#EnableRabbit
class ListenerConfig {
#Bean
#DependsOn("listener")
ListenerInitializer ListenerInitializer() {
return new ListenerInitializer();
}
#Bean(name = "listener")
Listener listener() {
return new listener();
}
}
and also doesn't work.
Anybody have any idea how I can fix it? I guess I need to initialize injection of ApplicationContext into ListenerInitializer as late as possible due to need to initialize itself RabbitListenerEndpointRegistry, but how to do it?
Many thanks for any advice
edit:
I call it to start through controller
#RequiredArgsConstructor
#RestController
class RestController {
private final ListenerInitializer listenerInitializer;
#GetMapping("/startListener")
ResponseEntity<String> startListener() {
listenerInitializer.startListener();
return ResponseEntity.ok("Listener started.");
}
}
I don't know if this is going to help you somehow, but here is a fully working Spring Boot application:
#SpringBootApplication
public class So75236736Application {
public static void main(String[] args) {
SpringApplication.run(So75236736Application.class, args);
}
#Bean
Listener listener() {
return new Listener();
}
public static class Listener {
#RabbitListener(queuesToDeclare = #Queue("myQueue"), id = "listener_id", autoStartup = "false")
void handle(Object body) {
System.out.println("Received: " + body);
}
}
#Component
public static class ListenerInitializer implements ApplicationContextAware {
private ApplicationContext applicationContext;
public void startListener() {
RabbitListenerEndpointRegistry listenerRegistry =
this.applicationContext.getBean(RabbitListenerEndpointRegistry.class);
listenerRegistry.getListenerContainer("listener_id").start();
System.out.println("Listener started");
}
#Override
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
}
#RestController
public static class StartListenerController {
private final ListenerInitializer listenerInitializer;
public StartListenerController(ListenerInitializer listenerInitializer) {
this.listenerInitializer = listenerInitializer;
}
#GetMapping("/startListener")
ResponseEntity<String> startListener() {
this.listenerInitializer.startListener();
return ResponseEntity.ok("Listener started.");
}
}
}
Only difference that I don't use an #EnableRabbit explicitly, but rather rely on the auto-configuration. I also don't declare ListenerInitializer as a #Bean since it is already marked with #Component and that is enough for Spring Boot to scan it.
Ok, I found the solution and sorry for bother you guys. Lack of knowledge. Still I don't know how it possible it works before, because I didn't changed anything, cause I was sure it should be like that, but solution was to move annotation #RabbitListener(queues = "myQueue2", id = "listener_id", autoStartup = "false") from over a class to over a method or add #RabbitHandler over a method.
Thank you #Artem Bilan for your help!

How to create a Websocket SessionManager that are reachable from other classes

I am working on an application with a WebSocket and want to save the clients id and session to a manager but have difficulties to understand how to do this correct when I also want to be able to reach this from another class with autowire.
public class Client {
private String id;
private Session session;
private MessageHandler handler;
Client(String id, Session session, MessageHandler handler) {
this.id = id;
this.session = session;
this.handler = handler;
}
}
public class ClientsManager {
private Set<Client> clientSet = new CopyOnWriteArraySet<>();
public Set<Client> getClients() {
return this.clientSet;
}
public void addClient(Client client) {
this.clientSet.add(client);
}
public void removeClient(Client client) {
clientSet.remove(client);
}
}
public class WebsocketServerEndpoint {
public static final ClientsManager manageClients = new ClientsManager();
#OnOpen
public void onOpen(Session session, #PathParam("connectId") String connectId) throws IOException, EncodeException {
MessageHandler messageHandler = new MessageHandler();
Client client = new Client(connectId, session, messageHandler);
this.client = client;
manageClients.addClient(client);
}
....
....
....
....
}
From another class:
public class DoSomething {
#Autowired
WebsocketServerEndpoint serverEndpoint;
public String doSomething() {
int numberOfClients = serverEndpoint.getClients().size()
return numberOfClients;
}
}
As I understand. This is not correct and you should not autowire static fields and so.
I can see when I debug that serverEndpoint: null in my DoSomething class but I get 1 connected client if I have one connected and so on.
When I do like this I will get the right number of clients in DoSomething class.
Have I just misunderstood this and it works as I have done?
or how should I do instead?
Is their a better way to write my Client and ClientsManager classes?
What I have read that if I would like to "Autowire" anyway there is two possible ways.
Using Constructor #Autowired For Static Field
Using #PostConstruct to set the value to Static Field
But how does this work when I would like to instantiate "public static final ClientsManager manageClients = new ClientsManager();"
Sorry for my stupid question but I feel I do not fully understand this.
If you would like to understand more about this topic search for Spring Dependency injection, but I write a short summary.
To be able to #Autowire a component you have to create a #Bean or #Service or #Component.
Creating beands first create a Configuration class, and a Beand or Beans inside.
#Configuration
public class Configuration {
#Value("${configuration.property.name}")
String username;
#Bean
public WebsocketServerEndpoint ebsocketServerEndpoint () {
return new WebsocketServerEndpoint();
}
}
#Value is not necessaty just good to mention with this annotation you can get a property name from spring application.properties file.
After this point you have created a #Bean instance of your class it is registered as a singleton class. You can get this one copy class from anywhere in your application you just have to, autowire it.
Or user construcor based dependency injection. ( #Autowired is not prefered).
Dont create beans just add #Component annotation to your class that you want to Autowire but I show a constructor injection.
#Component
public class WebsocketServerEndpoint {
public String test(){
return "test";
}
}
#RestController
public class DoSomething {
private final WebsocketServerEndpoint websocketHandler;
public DoSomething(WebsocketServerEndpoint websocketHandler) {
this.websocketHandler = websocketHandler;
}
#GetMapping(value = "/test")
public String test() {
return websocketHandler.test();
}
}
You can even test this endpoint with a curl GET request. curl http://localhost:8080/test

Using an autowired singleton bean in a non spring managed java class

Alright, this might seem pretty stupid to all the veterans out there, but bear with me here, as I'm only finding my way around Spring & Spring Boot.
I've got a Controller class here,
#RestController
public class Controller {
private static final Logger logger = LogManager.getLogger(Controller.class);
private static Controller controller = null;
#Autowired
private ApplicationParameters applicationParameters;
public static Controller getInstance() {
if (controller == null) {
synchronized (Controller.class) {
if (controller == null) {
controller = new Controller();
}
}
}
return controller;
}
public Controller() {}
public ApplicationParameters getApplicationParameters() {
return applicationParameters;
}
#RequestMapping("/")
public void init() {
try {
for (Entry<String, String> prop : applicationParameters.getProperties().entrySet())
logger.info("Loaded System Property: " + prop.getKey() + " -> " + prop.getValue());
Utils.concatenate("key1", "key2");
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
}
}
which autowires the ApplicationParameters bean with properties from a Property file.
Utils Class
public class Utils {
protected static final Logger logger = LogManager.getLogger(Utils.class);
//Need to get the value of the property keys propKey1 & propKey2 and concat them.
public static String concatenate(String propKey1, String propKey2) throws Exception {
if(StringUtils.isNoneEmpty(propKey2) && StringUtils.isNoneEmpty(propKey1)) {
return Controller.getInstance().getApplicationParameters().getProperties().get(propKey1) + Controller.getInstance().getApplicationParameters().getProperties().get(propKey2)
} else {
logger.error("System Property is undefined." );
return null;
}
}
So, I'd like use this autowired ApplicationParameters bean as a singleton instance throughout the lifecycle of my project.
For instance, I'd like to use it in the Utils class. Clearly Utils class is not spring managed, its just a regular old java class.
So I'd like to know how to use fully initialized applicationParameters in my Utils class.
This is what I've tried so far:
Autowiring the ApplicationParameters again in the Utils class, like this,
public class Utils {
#Autowired
private ApplicationParameters applicationParameters;
protected static final Logger logger = LogManager.getLogger(Utils.class);
But applicationParameters will be null here as, I'm presuming, this is because, Utils is not a spring managed bean.
Make Controller class singleton. (Not sure how to go about doing this as init() needs to get invoked when web server starts, then where to call getInstance()?)
Hence, would someone be so kind as to assist a novice here.
P.S. The Utils class is shown only as a sample to bring home the fact that, a spring managed autowired bean has to be used in a regular java class.
You could make the spring context accessible from outside with a helper class like this one:
public class SpringContextUtil implements ApplicationContextAware {
static ApplicationContext applicationContext;
public void setApplicationContext(ApplicationContext context) throws BeansException {
applicationContext = context;
}
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
}
Then, you could do something like this: SpringContextUtil.getApplicationContext.getBean("applicationParameters")
As a first rule, don't. A second don't either. Only if you really must, as there is no garantuee that this will work reliable as there is no way that everything has been properly initialized when this method is called. Instead try re-working your util to be a spring managed class as well.
If you really want, ditch most of your code as you are trying to be too smart in your code. Use this hack (yes it is a hack imho and should be avoided if necessary!).
public class SpringUtil {
private static final ApplicationContext ctx;
SpringUtil(ApplicationContext ctx) {
SpringUtil.ctx=ctx;
}
public static Controller getController() {
return this.ctx.getBean(Controller.class);
}
public static ApplicationParameters getApplicationParameters() {
return ctx.getBean(ApplicationParameters.class);
}
}
Then cleanup your controller
#RestController
public class Controller {
private static final Logger logger = LogManager.getLogger(Controller.class);
#Autowired
private ApplicationParameters applicationParameters;
#GetMapping("/")
public void init() {
try {
for (Entry<String, String> prop : applicationParameters.getProperties().entrySet())
logger.info("Loaded System Property: " + prop.getKey() + " -> " + prop.getValue());
Utils.concatenate("key1", "key2");
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
}
}
THen use the SpringUtil to obtain the ApplicationParameters instead of the controller
public class Utils {
protected static final Logger logger = LogManager.getLogger(Utils.class);
//Need to get the value of the property keys propKey1 & propKey2 and concat them.
public static String concatenate(String propKey1, String propKey2) throws Exception {
if(StringUtils.isNoneEmpty(propKey2) && StringUtils.isNoneEmpty(propKey1)) {
return SpringUtils.getApplicationParameters().getProperties().get(propKey1) + SpringUtils.getApplicationParameters().getProperties().get(propKey2)
} else {
logger.error("System Property is undefined." );
return null;
}
}
However this is quite a hack and might work in 90% of the cases. Also there is quite a design flaw/smell as you are doing a lot of getter chaining in your class. So all in all you are probably better of refactoring the Utils to make use of regular method calls and proper design techniques.

Getting Hibernate Session inside AsyncUncaughtExceptionHandler

I want to create an exception log in the database when an #Async operation fails with an exception.
You can see the implementation for AsyncExecutorConfiguration and AsyncExceptionHandler classes below.
Inside AsyncExceptionHandler class, when I call a service that tries to access the database, I am getting: org.hibernate.HibernateException: Could not obtain transaction-synchronized Session for current thread
#Configuration
#EnableAsync
public class AsyncExecutorConfiguration implements AsyncConfigurer {
#Autowired
private AsyncExceptionHandler asyncExceptionHandler;
private static final int CORE_POOL_SIZE = 3;
private static final int MAX_POOL_SIZE = 3;
private static final int QUEUE_CAPACITY = 24;
private static final String THREAD_NAME_PREFIX = "AsynchThread-";
#Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(CORE_POOL_SIZE);
executor.setMaxPoolSize(MAX_POOL_SIZE);
executor.setQueueCapacity(QUEUE_CAPACITY);
executor.setThreadNamePrefix(THREAD_NAME_PREFIX);
executor.initialize();
return executor;
}
#Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return asyncExceptionHandler;
}
}
#Component
public class AsyncExceptionHandler implements AsyncUncaughtExceptionHandler {
#Autowired
private NotificationService notificationService;
#Override
#Transactional(rollbackFor = Exception.class, readOnly = false)
public void handleUncaughtException(Throwable ex, Method method, Object... params) {
AsyncErrorLog log = new AsyncErrorLog(ex);
notificationService.saveLogAndNotify(log); // throws exception "Could not obtain transaction-synchronized Session for current thread"
}
}
#Service
public class MyServiceImpl implements MyService {
#Override
#Async
#Transactional(rollbackFor = Exception.class, readOnly = false, propagation = Propagation.REQUIRES_NEW)
public void doSomething(Long id) {
// I can execute database operations here
}
...
#Async function itself already has a valid session. What should I do to have a valid session in AsyncExceptionHandler class too?
--
UPDATE
Here is the simplified implementations for NotificationServiceImpl and LogDaoImpl.class where we get the error.
#Service
public class NotificationServiceImpl implements NotificationService {
#Autowired
private LogDao logDao;
#Override
#Transactional(rollbackFor = Exception.class, readOnly = false)
public void saveLogAndNotify(Log log) {
return logDao.createLog(log);
}
#Repository
public class LogDaoImpl{
#Autowired
protected SessionFactory sessionFactory;
#Override
public void createLog(Log log) {
sessionFactory.getCurrentSession().saveOrUpdate(log);
}
Per the Hibernate exception; if you're not using Spring Data, you'll have to make sure the notification service explicitly invokes the database calls on the Hibernate session.
On another note, in my experience, the main use cases for the UncaughtExceptionHandler (in general) are used for:
A simple last-resort to handle RuntimeExceptions that may be unknown to the programmer that for some reason cannot (or are not) caught in code
A way to catch exceptions in code that the programmer has no control over (e.g. if you're invoking Async directly on some third party library, etc.)
The commonality between the two is that this Handler is used for something unexpected. In fact, Spring itself accounts for the "unexpectedness" in your own code and Spring Async already sets a default one for you that will log to the console (code here), letting you not have to worry about rogue exceptions killing threads and not knowing why. (Note: The message in the source code says it's catching an "unexpected" exception. Of course exceptions are unexpected, but these are one's that you really didn't know could happen. Spring Async will log it for you.)
That being the case, in your example, since you're doing Spring Database operations and should know exactly what's happening inside of #doSomething, I would just go with removing the AUEH a try-catch (and/or -finally) and handle the exception inside of #doSomething:
#Service
public class MyServiceImpl implements MyService {
// Self autowired class to take advantage of proxied methods in the same class
// See https://stackoverflow.com/questions/51922604/transactional-and-stream-in-spring/51923214#51923214
private MyService myService;
private NotificationService notificationService;
#Override
#Async
public void doSomething(Long id) {
// I can execute database operations here
try {
myService.doDatabaseOperations(...);
} catch(DatabaseAccessException e) {
AsyncErrorLog log = new AsyncErrorLog(ex);
notificationService.saveLogAndNotify(log);
}
// Other exceptions (from DB operations or the notifications service) can be
// handled with other "catches" or to let the SimpleAsyncExHandler log them for you.
// You can also use standard multithreading exception handling for this
}
#Transactional(rollbackFor = Exception.class, readOnly = false, propagation = Propagation.REQUIRES_NEW)
public void doDatabaseOperations(...) {
...
}
}
You can use the applicationContext in your handler to lookup the notificationService. I had the same issue when I used #Autowired for the handler, which in turn injected my LogService. After looking at the logs I saw that the TransactionSynchronizationManager is clearing transaction synchronization after the rollback of the exception and nothing else except the no transaction for ...... error.
After using the applicationContext for looking up the logService bean and changing my handler, I saw the desired result in the logs.
begin
Initializing transaction synchronization
Getting transaction for [....AsyncService.doAsync]
Exception
rolling back
Clearing transaction synchronization
begin
Initializing transaction synchronization
Getting transaction for [.....LogService.save]
Change your config to include the interface ApplicationContextAware which will give you a convenience method to access the applicationContext. Set it as a instance variable.
See my configuration class below.
#Configuration
#EnableAsync
public class AsyncConfig implements AsyncConfigurer, ApplicationContextAware {
private static final int CORE_POOL_SIZE = 3;
private static final int MAX_POOL_SIZE = 3;
private static final int QUEUE_CAPACITY = 24;
private static final String THREAD_NAME_PREFIX = "AsynchThread-";
private ApplicationContext applicationContext;
#Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(CORE_POOL_SIZE);
executor.setMaxPoolSize(MAX_POOL_SIZE);
executor.setQueueCapacity(QUEUE_CAPACITY);
executor.setThreadNamePrefix(THREAD_NAME_PREFIX);
executor.initialize();
return executor;
}
#Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new AsyncExceptionHandler(this.applicationContext);
}
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
I have removed the #Component from the handler and use it as a POJO.
Every time getAsyncUncaughtExceptionHandler is called with an exception, a new handler instance is created with the applicationContext as a dependency.
public class AsyncExceptionHandler implements AsyncUncaughtExceptionHandler {
private final ApplicationContext applicationContext;
public AsyncExceptionHandler(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
#Override
public void handleUncaughtException(Throwable ex, Method method, Object... params) {
Log log = new Log();
log.setEntry(ex.getMessage());
LogService logService = this.applicationContext.getBean(LogService.class);
logService.save(log);
}
}
The save method on logService requires a new transaction every time it is called.
#Transactional(propagation = Propagation.REQUIRES_NEW)
public void save(Log log)
This will help you:
#Override
public void createLog(Log log) {
try {
session = sessionFactory.getCurrentSession();
} catch (HibernateException e) {
session = sessionFactory.openSession();
}
session.saveOrUpdate(log);
}

How to get bean from ServletContextListener in contextDestroyed

I want to get bean in contextDestroyed so I have this code.
public class MyContextListener implements ApplicationContextAware, ServletContextListener {
private final Logger logger = LogManager.getLogger(getClass());
private ApplicationContext appContext;
#Override
public void contextInitialized(ServletContextEvent event) {
logger.warn("Start");
}
#Override
public void contextDestroyed(ServletContextEvent event) {
logger.warn("End" + appContext);
appContext.getBean("myBean")
}
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.appContext = applicationContext;
}
}
problem is that it log null. But when I change it to
private static ApplicationContext appContext;
then I can see my appContext. Why ? What is right options to get bean
You have 2 instances of the MyContextListener. One configured by Spring and one from the web container.
The one configured by Spring will have the ApplicationContextAware callback and have the appContext property set, but it isn't known to your servlet container and as such will not participate in the lifecycle callbacks.
The second instance is the one in your servlet container, however as that isn't a spring managed one it will not receive the callback for the ApplicationContextAware and as such the appContext is going to be null.
Making it static kind of "solves" it as it is now a class variable instead of an instance variable. Now all instances share that variable.
It is better to remove the ApplicationContextAware and use the WebApplicationContextUtils.getRequiredWebApplicationContext method in your listener instead.
public class MyContextListener extends BaseTask implements ServletContextListener {
private final Logger logger = LogManager.getLogger(getClass());
#Override
public void contextInitialized(ServletContextEvent event) {
logger.warn("Start");
}
#Override
public void contextDestroyed(ServletContextEvent event) {
logger.warn("End" + appContext);
WebApplicationContextUtils.getRequiredWebApplicationContext(event.getServletContext()).getBean("myBean");
}
}
Now you only need the instance for the servlet container (web.xml or other means of configuring it) and you can remove the spring managed instance.

Categories