I have a web service(jboss seam) which consume requests, it further delegates its work to different seam components. One of the seam component has event generation capability as Events.instance().raiseTransactionSuccessEvent, the method which is listening to this event does not get invoked.
Am i missing any configurations?
Please suggest
Make sure a transaction is started, when invoking a Seam component from you web-service. If not, start it manually.
Make sure that transaction actually commits.
#Tair
I have an ejb component which consumes web service requests as below
#Name("plugin")
#Stateless
#WebService(name = "PluginService", serviceName = "PluginService")
public class PluginService implements IPlugin {
#WebMethod
public boolean createUser(String username, String password) {
System.out.println("in login ");
WebAuthenticator authenticator = (WebAuthenticator) Component
.getInstance("webauthenticator");
usreCreated = authenticator.create(username, password);
System.out.println("valid user "+validUser);
return userCreated;
}
}
WebAuthenticator is a seam component as follows
#Name("webauthenticator")
#Scope(ScopeType.CONVERSATION)
public class WebAuthenticator {
#In
EntityManager entityManager;
#Observer("test")
public void test()
{
System.out.println("A transaction success event was fired");
}
private static Log log = (Log) Logging.getLog(WebAuthenticator.class);
private static byte[] accountPassword = null;
private static Account currentAccount = null;
public boolean createUser(String username, String password) {
System.out.println("In web authenticator");
User user = new User();
user.username = username;
user.password = password;
entityManager.persist(user);
entityManager.flush();
Events.instance().raisTransactionSuccessEvent("test");
}
}
in components.xml file, i have <transaction:ejb-transaction /> , so that seam can get updates about container transaction events and I have seam managed persistence context
<persistence:managed-persistence-context
auto-create="true" name="entityManager" persistence-unit-jndi- name="java:/pilotEntityManagerFactory" />
I figured it out.
The conclusion is never to mix ejb transaction with seam transactions.
I explicitly disable the ejb trasaction management in the ejb.
It worked!!.
Related
I have a strage(for me) question to ask. I have created synchronized Service which is called by Controller:
#Controller
public class WebAppApiController {
private final WebAppService webApService;
#Autowired
WebAppApiController(WebAppService webApService){
this.webApService= webApService;
}
#Transactional
#PreAuthorize("hasAuthority('ROLE_API')")
#PostMapping(value = "/api/webapp/{projectId}")
public ResponseEntity<Status> getWebApp(#PathVariable(value = "projectId") Long id, #RequestBody WebAppRequestModel req) {
return webApService.processWebAppRequest(id, req);
}
}
Service layer is just checking if there is no duplicate in request and store it in database. Because client which is using this endpoint is making MANY requests continously it happened that before one request was validated agnist duplicate other the same was put in database - that is why I am trying to do synchronized block.
#Service
public class WebAppService {
private final static String UUID_PATTERN_TO = "[a-zA-Z0-9]{8}-[a-zA-Z0-9]{4}-[a-zA-Z0-9]{4}-[a-zA-Z0-9]{4}-[a-zA-Z0-9]{12}";
private final WebAppRepository waRepository;
#Autowired
public WebAppService(WebAppRepository waRepository){
this.waRepository= waRepository;
}
#Transactional(rollbackOn = Exception.class)
public ResponseEntity<Status> processScanWebAppRequest(Long id, WebAppScanModel webAppScanModel){
try{
synchronized (this){
Optional<WebApp> webApp=verifyForDuplicates(webAppScanModel);
if(!webApp.isPresent()){
WebApp webApp=new WebApp(webAppScanModel.getUrl())
webApp=waRepository.save(webApp);
processpropertiesOfWebApp(webApp);
return new ResonseEntity<>(HttpStatus.CREATED);
}
return new ResonseEntity<>(HttpStatus.CONFLICT);
}
} catch (NonUniqueResultException ex){
return new ResponseEntity<>(HttpStatus.PRECONDITION_FAILED);
} catch (IncorrectResultSizeDataAccessException ex){
return new ResponseEntity<>(HttpStatus.PRECONDITION_FAILED);
}
}
}
Optional<WebApp> verifyForDuplicates(WebAppScanModel webAppScanModel){
return waRepository.getWebAppByRegex(webAppScanModel.getUrl().replaceAll(UUID_PATTERN_TO,UUID_PATTERN_TO)+"$");
}
And JPA method:
#Query(value="select * from webapp wa where wa.url ~ :url", nativeQuery = true)
Optional<WebApp> getWebAppByRegex(#Param("url") String url);
processpropertiesOfWebApp method is doing further processing for given webapp which at this point should be unique.
Intended behaviour is:
when client post request contains multiple urls like:
https://testdomain.com/user/7e1c44e4-821b-4d05-bdc3-ebd43dfeae5f
https://testdomain.com/user/d398316e-fd60-45a3-b036-6d55049b44d8
https://testdomain.com/user/c604b551-101f-44c4-9eeb-d9adca2b2fe9
Only first one will be stored within database but at this moment this is not what is happening. Select from my database:
select inserted,url from webapp where url ~ 'https://testdomain.com/users/[a-zA-Z0-9]{8}-[a-zA-Z0-9]{4}-[a-zA-Z0-9]{4}-[a-zA-Z0-9]{4}-[a-zA-Z0-9]{12}$';
2019-11-07 08:53:05 | https://testdomain.com/users/d398316e-fd60-45a3-b036-6d55049b44d8
2019-11-07 08:53:05 | https://testdomain.com/users/d398316e-fd60-45a3-b036-6d55049b44d8
2019-11-07 08:53:05 | https://testdomain.com/users/d398316e-fd60-45a3-b036-6d55049b44d8
(3 rows)
I will try to add unique constraint on url column but I can't imagine this will solve the problem while when UUID changes new url will be unique
Could anyone give me a hint what I am doing wrong?
Question is related with the one I asked before but not found proper solution, so I simplified my method but still no success
I am trying to implement a database read/write seperation using the spring class ( I used this tutorial)
AbstractRoutingDataSource
The application I am working on is using multinenacy and connection pooling (hikari). So I created a ( master/replica datasources for each tenant)
This is how I create the datasources
public DataSource RoutingDatasource(String tenantId,
String databaseMasterUrl,
String databaseReplicaUrl,
String user,
String password) {
final RoutingDataSource routingDataSource = new RoutingDataSource();
final DataSource masterDataSource = buildTargetDataSource(tenantId,
cachePrepStmtsValue,
prepStmtCacheSize,
prepStmtCacheSqlLimit,
databaseMasterUrl,
driverClass,
user,
password,
MASTER_DATASOURCE_PREFIX);
final DataSource replicaDataSource = buildTargetDataSource(poolName + tenantId,
cachePrepStmtsValue,
prepStmtCacheSize,
prepStmtCacheSqlLimit,
databaseReplicaUrl,
driverClass,
user,
password,
REPLICA_DATASOURCE_PREFIX);
final Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put(DbContext.DbType.MASTER, masterDataSource);
targetDataSources.put(DbContext.DbType.REPLICA, replicaDataSource);
routingDataSource.setTargetDataSources(targetDataSources);
routingDataSource.setDefaultTargetDataSource(masterDataSource);
routingDataSource.afterPropertiesSet();
return routingDataSource;
}
This is how the context is determined
public class RoutingDataSource extends AbstractRoutingDataSource {
#Override
protected Object determineCurrentLookupKey() {
return DbContext.getDbType();
}
and this is how I tell to transaction to switch context
#Transactional(readOnly = true)
public Opportunite consulter(UUID personUuid) {
DbContext.setDbType(DbContext.DbType.REPLICA);
//some work
DbContext.reset();
return some_result ;
}
The code is compiling fine but is not really switching context. In fact after debugging the problem was that the datasource was requested before the transaction is setup. When the transaction is finally setup, it is too late, the datasource has already been loaded.
How can I fix this behavior ? thank you.
To solve the problem I got to define which database to use before entring to the transaction to do so I have defined a filter ( I am using spring)
public class DbFilter extends {
//les urls that do not only GET method
private final static String NOT_ONLY_GET_URL= "/api/parametre";
#Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain) throws IOException, ServletException, RestException {
DbContext.setDbType(DbContext.DbType.MASTER);
if (!NOT_ONLY_GET_URL.equals(request.getRequestURI()) && request.getMethod().equals("GET")) {
DbContext.setDbType(DbContext.DbType.REPLICA);
}
filterChain.doFilter(request, response);
}
This method is runned before calling any #transactional method and is switching the database if it is a GET method.
I have deleted
DbContext.setDbType(DbContext.DbType.REPLICA); and Dbcontext.reset() in the #transactional methods.
the "not only get urls parts" is only defined because in my application there are some get methods that do an update inside so I detected those urls and I affected them to the master databse .
Set spring.jpa.open-in-view to false
I had written rest services in spring that is running perfectly fine.
Now, I need to add perform some db transactions before returning response to user.
This db transaction is independent to response retrieved.
For example,
#PostMapping("login")
public TransactionResponse loginAuthentication(#Valid #RequestBody LoginRequestBody loginRequest) {
TransactionResponse transactionResponse = new TransactionResponse();
try {
transactionResponse = loginService.validateUser(loginRequest);
//independent transaction needs to be executed in a separate thread
loginSerice.addLoginLog(transactionResponse);
//return below response without waiting to compelete above log transaction
return transactionResponse;
}
catch (Exception e) {
return CommonUtils.setErrorResponse(transactionResponse, e);
}
}
I read upon async controller in spring mvc link. Although controller
executes respective functionality in a separate thread but I don't want to wait for db transaction to be completed. After getting response from service layer, it should be forwarded to user without any delay.
Any Suggestions !!
Spring version is 4.3
I posted this answer to help the fellow developers with same kind of requirement (to execute a void function in a separate thread).
Since I am not experienced in multithreading/asynchronous environment, I want to keep it simple by using spring asynchronous methods.
So, First I created the Thread Pool
#Configuration
#EnableAsync
public class ThreadConfig {
#Bean
public TaskExecutor threadPoolTaskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(4);
executor.setMaxPoolSize(4);
executor.setThreadNamePrefix("WEBAPP");
executor.initialize();
return executor;
}
}
Then I created a service that will execute my code in a separate thread.
#Async
#Service
#Transactional
public class LoggingService {
public void logintransaction() throws Exception{
System.out.println("start login loggin");
Thread.sleep(5000);
System.out.println("exit");
}
}
Lastly, I called the above service on my controller. As I can see Total Time Taken is printed first, then "start login loggin" is printed. This means my new method is executed in a new thread.
#Autowired
private LoggingService loggingService;
#PostMapping("login")
public TransactionResponse loginAuthentication(#Valid #RequestBody LoginRequestBody loginRequest) {
long startTime = System.currentTimeMillis();
TransactionResponse transactionResponse = new TransactionResponse();
try {
transactionResponse = loginService.validateUser(loginRequest);
//independent transaction needs to be executed in a separate thread
//loginSerice.addLoginLog(transactionResponse);
loggingService.logintransaction();
//return below response without waiting to compelete above log transaction
System.err.println("Total Time Taken=>"+(System.currentTimeMillis() - startTime));
return transactionResponse;
}
catch (Exception e) {
return CommonUtils.setErrorResponse(transactionResponse, e);
}
}
Thanks
I'm currently having a problem using a JPA Listener to update/persist the current user updating/creating an object. Here is the JPAListener's code
private static UserSession userSession = null;//Scoped-session bean
// yes i know i'm accessing a session stored in HTTP in persistence layer
#PreUpdate
public void preUpdate(AbstractDAOAuditedEntity abstractEntity) {
abstractEntity.setModificationDate(new Date());
// use userSession here to set currentUser or system
}
#PrePersist
public void prePersist(AbstractDAOAuditedEntity abstractEntity) {
// same
}
public static void setUserSession(UserSession userSession) {
DAOEntityListener.userSession = userSession;
}
If i do it while processing an HttpRequest it works, because userSession is bound to an Http Session managed by spring.
But now i have a new usage, i'm receiving data from a JmsMessage, this mean i'm running in a thread without HttpContext, and so the listener crash when trying to use userSession.
As a really quick and really dirty fix i did the following :
boolean haveUser = true;
try {
userSession.getUser();
} catch (Exception e) {
haveUser = false;
}
if (!haveUser) {}
My question is not so about how to make it works but how i should have handle this properly, whether i'm in HttpContext or not ?
I have implemented Spring security in a Spring MVC web application.
For the authentication purpose I am using LDAP and for authorization I am calling a third party Web Service that provides me All the authorizations and also a Session Id.
Once user log out or session timeout, I need to call the third party web service again with the same session Id for invalidation of session.
I have created a Log out Listener that listen to SessionDestroyedEvent like this
public class LogoutListener implements ApplicationListener<SessionDestroyedEvent>{
private SecurityServiceHandler securityServiceHandler;
#Override
public void onApplicationEvent(SessionDestroyedEvent event) {
SecurityContext securityContext = event.getSecurityContext();
UserDetails ud=null;
if(securityContext!=null){
ud = (UserDetails) securityContext.getAuthentication().getPrincipal();
if(securityServiceHandler==null){
securityServiceHandler = new SecurityServiceHandler();
}
//String sessionId = securityServiceHandler.getSessionId();
String sessionId = VirgoSessionManager.getSessionId();
System.out.println(ud.getUsername());
System.out.println(VirgoSessionManager.getSessionId());
securityServiceHandler.invalidateSession(ud.getUsername(),sessionId);
//reset the sessionId
securityServiceHandler.setSessionId(null);
}
}
I have used ThreadLocal in the VirgoSessionManager Class like follow
public class VirgoSessionManager {
private static ThreadLocal<String> sessionId = new ThreadLocal<String>();
public static String getSessionId(){
return sessionId.get();
}
public static void setSessionId(String sId) {
sessionId.set(sId);
}
public static void remove() {
sessionId.remove();
}
}
My problem is the that The VirgoSessionManager is not returning the session I have set during the Third party Session creation call after successful session cration even though I have implemented thread Local.
Any help will be appreciated.
Thank you!
you can have completely different thread serving your log out functionality which results in not having any value in ThreadLocal variable. For example tomcat uses thread pools so you need to be careful here. Try to log it/debug using Thread.currentThread().getName() and Thread.currentThread().getId() in getSessionId() and a place you set this value
I fixed the issue with the separate thread calling the Logout/Session time out.
I created a new customized User class and extended the Original spring "org.springframework.security.core.userdetails.User" class. I added new field "sessionId" to my customized user class.
So whenever I get the logged user details from Spring SecurityContext during logout/timeout, I will always have that sessionId and use to call invalidateSession method.
My customized user class looks like this.
package com.wvo.custom.security;
import java.util.Collection;
import java.util.List;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.User;
public class CustomUser extends User {
private String virgoSessionId ;
/**
*
*/
private static final long serialVersionUID = 1L;
public CustomUser(String username, String password,boolean enabled, boolean accountNonExpired, boolean accountNonLocked, boolean credentialsNonExpired,
Collection<? extends GrantedAuthority> authorities) {
super(username, password,enabled, accountNonExpired, accountNonLocked, credentialsNonExpired, authorities);
}
public String getVirgoSessionId() {
return virgoSessionId;
}
public void setVirgoSessionId(String virgoSessionId) {
this.virgoSessionId = virgoSessionId;
}
}
Thank you !