Cleanup JSF session scope bean when application shutdown - java

I need to invalidate and cleanup active session objects when the JSF application shutdown or tomact shutdown. The following is the code I have written in application scoped bean
#PreDestroy
public void shutdown(){
Map appMap = FacesContext.getCurrentInstance().getExternalContext().getApplicationMap();
Map<String,HttpSession> userSessionMap=(Map<String,HttpSession>)appMap.get("USERS");
log.info("userSessionMap " + userSessionMap);
Set<Entry<String, HttpSession>> entrySet = userSessionMap.entrySet();
for(Entry<String, HttpSession> entry:entrySet){
HttpSession session = entry.getValue();
log.info("HttpSession " + session.getId() + " calling invalidate");
session.invalidate();
}
}
and the following is overwritten HttpSessionListener
#Override
public void sessionDestroyed(HttpSessionEvent se){
HttpSession session = se.getSession();
String id = session.getId();
LoginActionController controller = (LoginActionController) session.getAttribute("userInfo");
log.info("HttpSession " + id + ", LoginActionController " + controller + " is being destroyed...");
if(controller != null){
log.info("User " + controller.getUserName() + " is logged out");
String userName = controller.getUserName();
ServletContext context = session.getServletContext();
Object obj = context.getAttribute("USERS");
if(obj instanceof Map){
Map map = (Map) obj;
map.remove(userName);
RWFacade rWFacade =(RWFacade)session.getAttribute("rWFacade");
rWFacade.closeFacade();
}
}
}
when run this code
session.invalidate();
is not getting executed. I missed anything ? Thanks.

To disable session persitence across restart in tomcat you can uncomment a config in
$TOMCAT_HOME/conf/context.xml
<!-- Uncomment this to disable session persistence across Tomcat restarts
<Manager pathname="" /> -->
May be it will help you.

Related

Jetty Websocket API Session

Hello I try many hours to get the Websocket API from Jetty with Sessions running.
My Problem is that, i can't interact externally with the Session, to send Data.
I can only interact to all Sessions and not only to one.
I tried to use the "Sec-WebSocket-Key" as Session Key, but it always returns java.lang.NullPointerException as the onWebSocketError.
I already tried to save the Session in a HashMap but every time, when i like to execute a sendString the Session in the HashMap is NULL.
My Code:
Connect Method
#Override
public void onWebSocketConnect(Session sessionIO) {
sessionIO.setIdleTimeout(Integer.MAX_VALUE);
System.out.print(gsonIO.toJson(this.getSession().getUpgradeResponse().getHeaders()));
sessionsIO.add(sessionIO);
loggerIO.log(LogType.SOCKET, "New Socket Connection from Client " + sessionIO.getRemoteAddress() + " with Sesssion ID: " + sessionIO.getPolicy().getBehavior().name() + ".");
}
My Function where i try to fetch my Session out of the Array
public static Session getSession(String handshakeIO) {
//String handshakeIO = sessionIO.getUpgradeRequest().getHeader("Sec-WebSocket-Key");
Session cacheIO = null;
for (Session rowIO : sessionsIO) {
if (handshakeIO.equalsIgnoreCase(rowIO.getUpgradeRequest().getHeader("Sec-WebSocket-Key"))) {
loggerIO.info("FOUND SESSION WITH Sec-WebSocket-Key " + handshakeIO + ".");
cacheIO = rowIO;
}
}
return cacheIO;
}
Fetching Session
#SneakyThrows
#EventHandler
public static void onJoin(UserLoginEvent eventIO) {
Habbo playerIO = eventIO.habbo;
int idIO = playerIO.getHabboInfo().getId();
for (Session rowIO : sessionsIO) {
if (rowIO.isOpen()) {
rowIO.getRemote().sendString(gsonIO.toJson(new ActionPaket("broadcast", "joinedplayer")));
}
}
if (ViceCache.playerIO.containsKey(idIO)) {
Session sessionIO = ViceCache.getSession(ViceCache.playerIO.get(idIO).getSessionIO());
if (sessionIO.isOpen()) {
sessionIO.getRemote().sendString(gsonIO.toJson(new ActionPaket("session", "granted")));
}
playerIO.whisper("Voicechat was successfully connected to Room.");
loggerIO.info("Player " + playerIO.getHabboInfo().getUsername() + " in Cache joined the Server, sending Callback.");
} else {
playerIO.whisper("To use Voice Chat, please download the Offical Vice Client.");
loggerIO.warning("Can't find Player " + playerIO.getHabboInfo().getUsername() + " with ID " + idIO + " in Cache, cannot send Callback for ViceChat.");
}
}

EJB, exception during persist: "EntityManager must be access within a transaction"

I received this error: "EntityManager must be access within a transaction" what is strange for me because my bean is mark with annotation:
#Stateless(name = "UserControllerSession")
I have injected EntityManager like this:
#PersistenceContext(unitName = "app")
private EntityManager em;
and this is my method:
#Override
#PermitAll
public String updateBlockedAndDeactivationReasonForIds(String userPk, String iban, List<String> associatedIds) {
UserLocal user = em.find(UserLocal.class, Long.valueOf(userPk));
if (user == null) {
return ("User with id: " + userPk + " does not exist ! \n");
}
user.setBlocked(true);
user.setActive(false);
user.setDeactivationReason(DeactivationReason.DEACTIVATION_MULTIPLY_IBAN_REASON);
user.setDeactivationDate(new Date());
StringBuilder message = new StringBuilder();
message.append(" IBAN " + iban + " is linked to multiple accounts: ");
for (String id : associatedIds) {
message.append(id + " ");
}
ContactJournalEntryBean cjb = new ContactJournalEntryBean("USER", user.getLogin(), new Date(), "Internet",
message.toString());
em.persist(user);
em.persist(cjb);
return "SUCCESS operation for user with id: " + userPk;
}
As far I know default value for EJB method is Transactioanl.REQUIRED so by defualt transaction should be created. Another question is can I have 2 persists in one transaction ? Thanks for any help
I think the default REQUIRED only applies if you actually use the #TransactionAttribute annotation. Try adding it to your method (or class if you want it as default on all methods).

What is the right way to use Cassandra driver from a web application

I want to build a RESTful API with Java and Cassandra 2.x (on Jersey framework). I'm new to both technologies so I would like to ask you is that the correct way to integrate and share Cassandra driver.
0. Get the driver though Maven
<dependency>
<groupId>com.datastax.cassandra</groupId>
<artifactId>cassandra-driver-core</artifactId>
<version>2.0.3</version>
</dependency>
1. Wrap driver's functionality with a Client class:
package com.example.cassandra;
import com.datastax.driver.core.*;
public class Client {
private Cluster cluster;
private Session session;
public Client(String node) {
connect( node );
}
private void connect(String node) {
cluster = Cluster.builder()
.addContactPoint(node)
.build();
session = cluster.connect();
}
public ResultSet execute( String cql3 ) {
return session.execute( cql3 );
}
public void close() {
cluster.close();
}
}
2. I insatiate the client in ContextListener and share it though context attribute
package com.example.listener;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import com.example.cassandra.Client;
public class ExampleContextListener implements ServletContextListener {
Client cassandraClient;
public void contextInitialized(ServletContextEvent servletContextEvent) {
ServletContext ctx = servletContextEvent.getServletContext();
cassandraClient = new Client( ctx.getInitParameter( "DBHost" ) );
ctx.setAttribute( "DB", cassandraClient );
}
public void contextDestroyed(ServletContextEvent servletContextEvent) {
cassandraClient.close();
}
}
3. Now I get the client from servlet's context and use it
Client client = (Client) context.getAttribute("DB");
client.execute("USE testspace;");
ResultSet rs = client.execute("SELECT * from users;");
for (Row row : rs ) {
output += row.getString("lname") + "|";
}
Is that the correct way to do it (both from performance and architectural point of view)?
Full example available on: https://github.com/lukaszkujawa/jersey-cassandra
I just developed what you are going to develop. What you wrote works but it's not my favourite approach. I'd rather create a Singleton (since 1 session is enough for an application). Following Joshua Bloch enum's singleton's pattern here is what I did
public enum Cassandra {
DB;
private Session session;
private Cluster cluster;
private static final Logger LOGGER = LoggerFactory.getLogger(Cassandra.class);
/**
* Connect to the cassandra database based on the connection configuration provided.
* Multiple call to this method will have no effects if a connection is already established
* #param conf the configuration for the connection
*/
public void connect(ConnectionCfg conf) {
if (cluster == null && session == null) {
cluster = Cluster.builder().withPort(conf.getPort()).withCredentials(conf.getUsername(), conf.getPassword()).addContactPoints(conf.getSeeds()).build();
session = cluster.connect(conf.getKeyspace());
}
Metadata metadata = cluster.getMetadata();
LOGGER.info("Connected to cluster: " + metadata.getClusterName() + " with partitioner: " + metadata.getPartitioner());
metadata.getAllHosts().stream().forEach((host) -> {
LOGGER.info("Cassandra datacenter: " + host.getDatacenter() + " | address: " + host.getAddress() + " | rack: " + host.getRack());
});
}
/**
* Invalidate and close the session and connection to the cassandra database
*/
public void shutdown() {
LOGGER.info("Shutting down the whole cassandra cluster");
if (null != session) {
session.close();
}
if (null != cluster) {
cluster.close();
}
}
public Session getSession() {
if (session == null) {
throw new IllegalStateException("No connection initialized");
}
return session;
}
}
And in the context listener I call connect or shutdown.
Since all exceptions in new driver are unchecked my tip for you is to create your own implementation of the Jersey ExceptionMapper mapping DriverException. One more thing, think about working with PreparedStatements rather than Strings so that Cassandra parse the query only once. In my application I followed the above patterns also for the queries (an enum singleton that prepare statements when loaded first time and then expose methods to use these statements).
HTH,
Carlo

Session-scoped bean not instantiated

I want to create a session-scoped bean to monitor activations and passivations of HTTP sessions. The bean is very simple:
package my.log;
import javax.servlet.http.HttpSessionActivationListener;
import javax.servlet.http.HttpSessionEvent;
import org.apache.log4j.Logger;
public class SessionLoggingListenerBean implements HttpSessionActivationListener {
private final Logger LOG = Logger.getLogger(this.getClass());
public SessionLoggingListenerBean() {
LOG.info("SessionLoggingListenerBean starting");
}
public void init() {
LOG.info("SessionLoggingListenerBean init");
}
public void sessionDidActivate(HttpSessionEvent event) {
LOG.info("Session " + event.getSession().getId() + " activated");
}
public void sessionWillPassivate(HttpSessionEvent event) {
LOG.info("Session " + event.getSession().getId() + " will passivate");
}
}
Bean definition in application context:
<bean id="sessionLoggingListenerBean" class="my.log.SessionLoggingListenerBean" scope="session" init-method="init" lazy-init="false"/>
With this configuration there is no logs from this class, even from the constructor or init() method. Apparently, Spring does not create this bean.
By trial and error I checked that Spring instantiates such a bean when it is needed by another bean, e.g. used by UI. Is there any other (better) way? Is it a bug in Spring?
Spring version used: 2.0.8.
HttpSessionActivationListener is part of the javax.servlet.http package. That should give you a hint that it should be managed by the Servlet container. In your case, you aren't registering the Listener with your ServletContext, neither through the web.xml or a SerlvetContainerInitializer.
Through web.xml you wouldn't be able to make it both a Spring and Servlet container managed object so instead these workarounds exist, first, second.
If you are using a WebApplicationInitializer, you can instantiate your AnnotationConfigWebApplicationContext, have the SessionLoggingListenerBean bean created, retrieve it and use it with
SessionLoggingListenerBean yourBean = context.getBean(SessionLoggingListenerBean.class);
servletContext.addListener(yourBean);
After some experimenting I think it is better not to use Spring for this purpose. I've modified the class to implement also HttpSessionListener and Serializable:
public class SessionLoggingListener implements HttpSessionListener,
HttpSessionActivationListener, Serializable {
private static final long serialVersionUID = -763785365219658010L;
private static final Logger LOG = Logger.getLogger(SessionLoggingListener.class);
public SessionLoggingListener() {
LOG.info("SessionLoggingListener created");
}
public void sessionDidActivate(HttpSessionEvent event) {
LOG.info("Session " + event.getSession().getId() + " activated");
}
public void sessionWillPassivate(HttpSessionEvent event) {
LOG.info("Session " + event.getSession().getId() + " will passivate");
}
public void sessionCreated(HttpSessionEvent event) {
final HttpSession session = event.getSession();
LOG.info("Session " + session.getId() + " created. MaxInactiveInterval: " + session.getMaxInactiveInterval() + " s");
session.setAttribute(this.getClass().getName(), this);
}
public void sessionDestroyed(HttpSessionEvent event) {
LOG.info("Session " + event.getSession().getId() + " destroyed");
event.getSession().removeAttribute(this.getClass().getName());
}
}
and added it to web.xml:
<listener>
<listener-class>
evo.log.SessionLoggingListener
</listener-class>
</listener>
From now on, whenever a new session is created, the listener binds to it (session.setAttribute(...)). This is necessary to make the container notice the listener about session activation or passivation.
In case of Spring and sessions - according to this forum thread Spring does not load session beans until they are requested:
Session bean is treated as a special form of "prototype". That means it will follow prototype symantics in creating an instance.
For me this is an unintuitive and not well documented behavior.

Seam Hibernate Serves same EntityManger instance to two separate threads

I am new to the Java / Hibernate / Seam way of development but I appear to have a strange issue with Hibernate and concurrent threads.
I have a application scoped Seam component which is executed via EJB timers at a set interval (Orchestrator.java) calling the method startProcessingWorkloads.
This method has a injected EntityManager which it uses to check the database for a collection of data, and if it finds a work collection it creates a new Asynchronous Seam component (LoadContoller.java) and executes the start() method on the Controller
LoadController has EntityManager injected and use it to perform a very large transaction (About one hour)
Once the LoadController is running as a separate thread, the Orchestrator is still being executed as a thread at a set interval, so for example
1min
Orchestrator - Looks for work collection (None found) (thread 1)
2min
Orchestrator - Looks for work collection (finds one, Starts LoadController) (thread 1)
LoadController - Starts updating database records (thread 2)
3min
Orchestrator - Looks for work collection (None found) (thread 1)
LoadController - Still updating database records (thread 2)
4min
Orchestrator - Looks for work collection (None found) (thread 1)
LoadController - Still updating database records (thread 2)
5min
Orchestrator - Looks for work collection (None found) (thread 1)
LoadController - Done updating database records (thread 2)
6min
Orchestrator - Looks for work collection (None found) (thread 1)
7min
Orchestrator - Looks for work collection (None found) (thread 1)
However, I am receiving a intermittent error (See below) when the Orchestrator runs concurrently with the LoadController.
5:10:40,852 WARN [AbstractBatcher]
exception clearing
maxRows/queryTimeout
java.sql.SQLException: Connection is
not associated with a managed
connection.org.jboss.resource.adapter.jdbc.jdk6.WrappedConnectionJDK6#1fcdb21
This error is thrown after the Orchestrator has completed its SQl query and as the LoadController attempts to execute its next SQl query.
I did some research I came to the conclusion that the EntityManager was being closed hence the LoadController was unable to use it.
Now confused as to what exactly closed the connection I did some basic object dumps of the entity manager objects used by the Orchestrator and the LoadController when each of the components are called and I found that just before I receive the above error this happens.
2010-07-30 15:06:40,804 INFO
[processManagement.LoadController]
(pool-15-thread-2)
org.jboss.seam.persistence.EntityManagerProxy#7e3da1
2010-07-30 15:10:40,758 INFO
[processManagement.Orchestrator]
(pool-15-thread-1)
org.jboss.seam.persistence.EntityManagerProxy#7e3da1
It appears that during one of the Orchestrator execution intervals it obtains a reference to the same EntityManager that the LoadController is currently using. When the Orchestrator completes its SQL execution it closes the connection and than LoadController can no longer execute its updates.
So my question is, does any one know of this happening or having I got my threading all mucked up in this code?
From my understanding when injecting a EntityManager a new instance is injected from the EntityManagerFactory which remains with that particualr object until object leaves scope (in this case they are stateless so when the start() methods ends), how could the same instance of a entity manager be injected into two separate threads?
Orchestrator.java
#Name("processOrchestrator")
#Scope(ScopeType.APPLICATION)
#AutoCreate
public class Orchestrator {
//___________________________________________________________
#Logger Log log;
#In EntityManager entityManager;
#In LoadController loadController;
#In WorkloadManager workloadManager;
//___________________________________________________________
private int fProcessInstanceCount = 0;
//___________________________________________________________
public Orchestrator() {}
//___________________________________________________________
synchronized private void incrementProcessInstanceCount() {
fProcessInstanceCount++;
}
//___________________________________________________________
synchronized private void decreaseProcessInstanceCount() {
fProcessInstanceCount--;
}
//___________________________________________________________
#Observer("controllerExceptionEvent")
synchronized public void controllerExceptionListiner(Process aProcess, Exception aException) {
decreaseProcessInstanceCount();
log.info(
"Controller " + String.valueOf(aProcess) +
" failed with the error [" + aException.getMessage() + "]"
);
Events.instance().raiseEvent(
Application.ApplicationEvent.applicationExceptionEvent.name(),
aException,
Orchestrator.class
);
}
//___________________________________________________________
#Observer("controllerCompleteEvent")
synchronized public void successfulControllerCompleteListiner(Process aProcess, long aWorkloadId) {
try {
MisWorkload completedWorklaod = entityManager.find(MisWorkload.class, aWorkloadId);
workloadManager.completeWorkload(completedWorklaod);
} catch (Exception ex) {
log.error(ex.getMessage(), ex);
}
decreaseProcessInstanceCount();
log.info("Controller " + String.valueOf(aProcess) + " completed successfuly");
}
//___________________________________________________________
#Asynchronous
public void startProcessingWorkloads(#IntervalDuration long interval) {
log.info("Polling for workloads.");
log.info(entityManager.toString());
try {
MisWorkload pendingWorkload = workloadManager.getNextPendingWorkload();
if (pendingWorkload != null) {
log.info(
"Pending Workload found (Workload_Id = " +
String.valueOf(pendingWorkload.getWorkloadId()) +
"), starting process controller."
);
Process aProcess = pendingWorkload.retriveProcessIdAsProcess();
ControllerIntf controller = createWorkloadController(aProcess);
if (controller != null) {
controller.start(aProcess, pendingWorkload.getWorkloadId());
workloadManager.setWorkloadProcessing(pendingWorkload);
}
}
} catch (Exception ex) {
Events.instance().raiseEvent(
Application.ApplicationEvent.applicationExceptionEvent.name(),
ex,
Orchestrator.class
);
}
log.info("Polling complete.");
}
//___________________________________________________________
private ControllerIntf createWorkloadController(Process aProcess) {
ControllerIntf newController = null;
switch(aProcess) {
case LOAD:
newController = loadController;
break;
default:
log.info(
"createWorkloadController() does not know the value (" +
aProcess.name() +
") no controller will be started."
);
}
// If a new controller is created than increase the
// count of started controllers so that we know how
// many are running.
if (newController != null) {
incrementProcessInstanceCount();
}
return newController;
}
//___________________________________________________________
}
LoadController.java
#Name("loadController")
#Scope(ScopeType.STATELESS)
#AutoCreate
public class LoadController implements ControllerIntf {
//__________________________________________________
#Logger private Log log;
#In private EntityManager entityManager;
//__________________________________________________
private String fFileName = "";
private String fNMDSFileName = "";
private String fAddtFileName = "";
//__________________________________________________
public LoadController(){ }
//__________________________________________________
#Asynchronous
synchronized public void start(Process aProcess, long aWorkloadId) {
log.info(
LoadController.class.getName() +
" process thread was started for WorkloadId [" +
String.valueOf(aWorkloadId) + "]."
);
log.info(entityManager.toString());
try {
Query aQuery = entityManager.createQuery(
"from MisLoad MIS_Load where Workload_Id = " + String.valueOf(aWorkloadId)
);
MisLoad misLoadRecord = (MisLoad)aQuery.getSingleResult();
fFileName =
misLoadRecord.getInitiatedBy().toUpperCase() + "_" +
misLoadRecord.getMdSourceSystem().getMdState().getShortName() + "_" +
DateUtils.now(DateUtils.FORMAT_FILE) + ".csv"
;
fNMDSFileName = "NMDS_" + fFileName;
fAddtFileName = "Addt_" + fFileName;
createDataFile(misLoadRecord.getFileContents());
ArrayList<String>sasCode = generateSASCode(
misLoadRecord.getLoadId(),
misLoadRecord.getMdSourceSystem().getPreloadFile()
);
//TODO: As the sas password will be encrypted in the database, we will
// need to decrypt it before passing to the below function
executeLoadSASCode(
sasCode,
misLoadRecord.getInitiatedBy(),
misLoadRecord.getSasPassword()
);
createWorkloadContentRecords(aWorkloadId, misLoadRecord.getLoadId());
//TODO: Needs to remove password from DB when complete
removeTempCSVFiles();
Events.instance().raiseEvent(
Application.ApplicationEvent.controllerCompleteEvent.name(),
aProcess,
aWorkloadId
);
log.info(LoadController.class.getName() + " process thread completed.");
} catch (Exception ex) {
Events.instance().raiseEvent(
Application.ApplicationEvent.controllerExceptionEvent.name(),
aProcess,
ex
);
}
}
//__________________________________________________
private void createDataFile(byte[] aFileContent) throws Exception {
File dataFile =
new File(ECEConfig.getConfiguration().sas_tempFileDir() + "\\" + fFileName);
FileUtils.writeBytesToFile(dataFile, aFileContent, true);
}
//__________________________________________________
private ArrayList<String> generateSASCode(long aLoadId, String aSourceSystemPreloadSasFile) {
String sasTempDir = ECEConfig.getConfiguration().sas_tempFileDir();
ArrayList<String> sasCode = new ArrayList<String>();
sasCode.add("%let sOracleUserId = " + ECEConfig.getConfiguration().oracle_username() + ";");
sasCode.add("%let sOraclePassword = " + ECEConfig.getConfiguration().oracle_password() + ";");
sasCode.add("%let sOracleSID = " + ECEConfig.getConfiguration().oracle_sid() + ";");
sasCode.add("%let sSchema = " + ECEConfig.getConfiguration().oracle_username() + ";");
sasCode.add("%let sECESASSourceDir = " + ECEConfig.getConfiguration().sas_sourceDir() + ";");
sasCode.add("libname lOracle ORACLE user=&sOracleUserId pw=&sOraclePassword path=&sOracleSID schema=&sSchema;");
sasCode.add("%let sCommaDelimiter = %str(" + ECEConfig.getConfiguration().dataload_csvRawDataFileDelimiter() + ");");
sasCode.add("%let sPipeDelimiter = %nrquote(" + ECEConfig.getConfiguration().dataload_csvNMDSDataFileDelimiter() + ");");
sasCode.add("%let sDataFileLocation = " + sasTempDir + "\\" + fFileName + ";");
sasCode.add("%let sNMDSOutputDataFileLoc = " + sasTempDir + "\\" + fNMDSFileName + ";");
sasCode.add("%let sAddtOutputDataFileLoc = " + sasTempDir + "\\" + fAddtFileName + ";");
sasCode.add("%let iLoadId = " + String.valueOf(aLoadId) + ";");
sasCode.add("%include \"&sECESASSourceDir\\ECE_UtilMacros.sas\";");
sasCode.add("%include \"&sECESASSourceDir\\" + aSourceSystemPreloadSasFile + "\";");
sasCode.add("%include \"&sECESASSourceDir\\ECE_NMDSLoad.sas\";");
sasCode.add("%preload(&sDataFileLocation, &sCommaDelimiter, &sNMDSOutputDataFileLoc, &sAddtOutputDataFileLoc, &sPipeDelimiter);");
sasCode.add("%loadNMDS(lOracle, &sNMDSOutputDataFileLoc, &sAddtOutputDataFileLoc, &sPipeDelimiter, &iLoadId);");
return sasCode;
}
//__________________________________________________
private void executeLoadSASCode(
ArrayList<String> aSasCode, String aUserName, String aPassword) throws Exception
{
SASExecutor aSASExecutor = new SASExecutor(
ECEConfig.getConfiguration().sas_server(),
ECEConfig.getConfiguration().sas_port(),
aUserName,
aPassword
);
aSASExecutor.execute(aSasCode);
log.info(aSASExecutor.getCompleteSasLog());
}
//__________________________________________________
/**
* Creates the MIS_UR_Workload_Contents records for
* the ECE Unit Record data that was just loaded
*
* #param aWorkloadId
* #param aMisLoadId
* #throws Exception
*/
private void createWorkloadContentRecords(long aWorkloadId, long aMisLoadId) throws Exception {
String selectionRule =
" from EceUnitRecord ECE_Unit_Record where ECE_Unit_Record.loadId = " +
String.valueOf(aMisLoadId)
;
MisWorkload misWorkload = entityManager.find(MisWorkload.class, aWorkloadId);
SeamManualTransaction manualTx = new SeamManualTransaction(
entityManager,
ECEConfig.getConfiguration().manualSeamTxTimeLimit()
);
manualTx.begin();
RecordPager oPager = new RecordPager(
entityManager,
selectionRule,
ECEConfig.getConfiguration().recordPagerDefaultPageSize()
);
Object nextRecord = null;
while ((nextRecord = oPager.getNextRecord()) != null) {
EceUnitRecord aEceUnitRecord = (EceUnitRecord)nextRecord;
MisUrWorkloadContents aContentsRecord = new MisUrWorkloadContents();
aContentsRecord.setEceUnitRecordId(aEceUnitRecord.getEceUnitRecordId());
aContentsRecord.setMisWorkload(misWorkload);
aContentsRecord.setProcessOutcome('C');
entityManager.persist(aContentsRecord);
}
manualTx.commit();
}
/**
* Removes the CSV temp files that are created for input
* into the SAS server and that are created as output.
*/
private void removeTempCSVFiles() {
String sasTempDir = ECEConfig.getConfiguration().sas_tempFileDir();
File dataInputCSV = new File(sasTempDir + "\\" + fFileName);
File nmdsOutputCSV = new File(sasTempDir + "\\" + fNMDSFileName);
File addtOutputCSV = new File(sasTempDir + "\\" + fAddtFileName);
if (dataInputCSV.exists()) {
dataInputCSV.delete();
}
if (nmdsOutputCSV.exists()) {
nmdsOutputCSV.delete();
}
if (addtOutputCSV.exists()) {
addtOutputCSV.delete();
}
}
}
SeamManualTransaction.java
public class SeamManualTransaction {
//___________________________________________________________
private boolean fObjectUsed = false;
private boolean fJoinExistingTransaction = true;
private int fTransactionTimeout = 60; // Default: 60 seconds
private UserTransaction fUserTx;
private EntityManager fEntityManager;
//___________________________________________________________
/**
* Set the transaction timeout in milliseconds (from minutes)
*
* #param aTimeoutInMins The number of minutes to keep the transaction active
*/
private void setTransactionTimeout(int aTimeoutInSecs) {
// 60 * aTimeoutInSecs = Timeout in Seconds
fTransactionTimeout = 60 * aTimeoutInSecs;
}
//___________________________________________________________
/**
* Constructor
*
* #param aEntityManager
*/
public SeamManualTransaction(EntityManager aEntityManager) {
fEntityManager = aEntityManager;
}
//___________________________________________________________
/**
* Constructor
*
* #param aEntityManager
* #param aTimeoutInSecs
*/
public SeamManualTransaction(EntityManager aEntityManager, int aTimeoutInSecs) {
setTransactionTimeout(aTimeoutInSecs);
fEntityManager = aEntityManager;
}
//___________________________________________________________
/**
* Constructor
*
* #param aEntityManager
* #param aTimeoutInSecs
* #param aJoinExistingTransaction
*/
public SeamManualTransaction(EntityManager aEntityManager, int aTimeoutInSecs, boolean aJoinExistingTransaction) {
setTransactionTimeout(aTimeoutInSecs);
fJoinExistingTransaction = aJoinExistingTransaction;
fEntityManager = aEntityManager;
}
//___________________________________________________________
/**
* Starts the new transaction
*
* #throws Exception
*/
public void begin() throws Exception {
if (fObjectUsed) {
throw new Exception(
SeamManualTransaction.class.getCanonicalName() +
" has been used. Create new instance."
);
}
fUserTx =
(UserTransaction) org.jboss.seam.Component.getInstance("org.jboss.seam.transaction.transaction");
fUserTx.setTransactionTimeout(fTransactionTimeout);
fUserTx.begin();
/* If entity manager is created before the transaction
* is started (ie. via Injection) then it must join the
* transaction
*/
if (fJoinExistingTransaction) {
fEntityManager.joinTransaction();
}
}
//___________________________________________________________
/**
* Commit the transaction to the database
*
* #throws Exception
*/
public void commit() throws Exception {
fObjectUsed = true;
fUserTx.commit();
}
//___________________________________________________________
/**
* Rolls the transaction back
*
* #throws Exception
*/
public void rollback() throws Exception {
fObjectUsed = true;
fUserTx.rollback();
}
//___________________________________________________________
}
In general, injecting an entityManager in a Seam component of scope APPLICATION is not right. An entity manager is something you create, use and close again, in a scope typically much shorter than APPLICATION scope.
Improve by choosing smaller scopes with a standard entityManager injection, or if you need the APPLICATION scope, inject an EntityManagerFactory instead, and create, use and close the entityManager yourself.
Look in your Seam components.xml to find the name of your EntityManagerFactory compoment.
Well, my first is advice is
If you are using an EJB application, prefer To use a Bean Managed Transaction instead of your custom SeamManualTransaction. When you use a Bean Managed Transaction, you, as a developer, Take care of calling begin and commit. You get this feature by using an UserTransaction component. You can create a Facade layer which begins and commit your Transaction. Something like
/**
* default scope when using #Stateless session bean is ScopeType.STATELESS
*
* So you do not need to declare #Scope(ScopeType.STATELESS) anymore
*
* A session bean can not use both BEAN and CONTAINER Transaction management at The same Time
*/
#Stateless
#Name("businessFacade")
#TransactionManagement(TransactionManagerType.BEAN)
public class BusinessFacade implements BusinessFacadeLocal {
private #Resource TimerService timerService;
private #Resource UserTransaction userTransaction;
/**
* You can use #In of you are using Seam capabilities
*/
private #PersistenceContext entityManager;
public void doSomething() {
try {
userTransaction.begin();
userTransaction.setTransactionTimeout(int seconds);
// business logic goes here
/**
* To enable your Timer service, just call
*
* timerService.createTimer(15*60*1000, 15*60*1000, <ANY_SERIALIZABLE_INFO_GOES_HERE>);
*/
userTransaction.commit();
} catch (Exception e) {
userTransaction.rollback();
}
}
#Timeout
public void doTimer(Timer timer) {
try {
userTransaction.begin();
timer.getInfo();
// logic goes here
userTransaction.commit();
} catch (Exception e) {
userTransaction.rollback();
}
}
}
Let's see UserTransaction.begin method API
Create a new transaction and associate it with the current thread
There is more:
The lifetime of a container-managed persistence context (injected Through #PersistenceContext annotation) corresponds to the scope of a transaction (between begin and commit method call) when using transaction-scoped persistence context
Now Let's see TimerService
It is a container-provided service that allows enterprise beans to be registered for
timer callback methods to occur at a specified time, after a specified elapsed time, or after specified intervals. The bean class of an enterprise bean that uses the timer
service must provide a timeout callback method. Timers can be created for stateless session beans, message-driven beans
I hope It can be useful To you

Categories

Resources