This question already has answers here:
How do servlets work? Instantiation, sessions, shared variables and multithreading
(8 answers)
Closed 6 years ago.
I created a small web application using jsp and servlet. My ajax post method call the java class for every three seconds. I want to know for every 3 secs, java class variables isBootRunning,istest1Running,istest1Running is initialized to "null" or not.
If it will initialized for every request, how to prevent this initialization.
My JSP:
setInterval(function(){
TestReport();
}, 3000);
function TestReport(){
var tbname = $("#tbname").attr('class');
var userName = $("#userName").attr('class');
var name = tbname;
var url ="TestReport";
var params = {
tbname: tbname,
userName:userName
};
$.post('TestReport', {
tbname: tbname,
userName:userName,
}, function(responseText) {
alert(responseText);
});
}
My Servlet:
public class TestReport extends HttpServlet {
private static final long serialVersionUID = 1L;
String isBootRunning = null;
String istest1Running = null;
String istest2Running = null;
protected void doPost(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
File f1 = new File("myfirstpath");//this directory is visible for 10 mins only
File f2 = new File("mythirdpath");//this directory is visible for 10 mins only
File f3 = new File("mythirdpath");//this directory is visible for 10 mins only
if (f1.exists() && f1.isDirectory()) {
isBootRunning = "Running";
istest1Running = "Scheduled";
istest2Running = "Scheduled";
} else if(f2.exists() && f2.isDirectory()){
istest1Running = "Running";
istest2Running = "Scheduled";
if(isBootRunning=="Running"){
//here my logic
}
} else if(f2.exists() && f2.isDirectory()){
istest2Running = "Running";
if(isBootRunning=="Running"){
//here my logic
}
if(istest1Running=="Running"){
//here my logic
}
}
}
}
You are facing this issue because every time you make a new ajax request to your servlet the results of previous request is not stored/saved.
This issue can be solved using HttpSession. You have to save and fetch the string objects isBootRunning, istest1Running, istest2Running in the session object as below:
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
try{
HttpSession session =null;
if(request.getSession().isNew()){
session= request.getSession();//new session
}else{
session= request.getSession(false);//current session
}
if(null != session && null != session.getAttribute("isBootRunning") && null != session.getAttribute("istest1Running") && null != session.getAttribute("istest2Running")){
yourLogic(session);//compute your logic for not null values
}
else{
session.setAttribute("isBootRunning", "");
session.setAttribute("istest1Running", "");
session.setAttribute("istest2Running", "");
yourLogic(session);//compute your logic for null values
}
}catch(Exception e){
e.printStackTrace();
}
}
private void yourLogic(HttpSession session) {
File f1 = new File("myfirstpath");//this directory is visible for 10 mins only
File f2 = new File("mythirdpath");//this directory is visible for 10 mins only
File f3 = new File("mythirdpath");//this directory is visible for 10 mins only
String isBootRunning = (String)session.getAttribute("isBootRunning");
String istest1Running = (String)session.getAttribute("istest1Running");;
String istest2Running = (String)session.getAttribute("istest2Running");;
if (f1.exists() && f1.isDirectory()) {
session.setAttribute("isBootRunning", "Running");
session.setAttribute("istest1Running", "Scheduled");
session.setAttribute("istest2Running", "Scheduled");
} else if(f2.exists() && f2.isDirectory()){
session.setAttribute("istest1Running", "Scheduled");
session.setAttribute("istest2Running", "Scheduled");
if(isBootRunning=="Running"){
//here my logic
}
} else if(f2.exists() && f2.isDirectory()){
session.setAttribute("istest2Running", "Scheduled");
istest2Running = "Running";
if(isBootRunning=="Running"){
//here my logic
}
if(istest1Running=="Running"){
//here my logic
}
}
}
Here, your String objects are stored in session object. And it is quite safe to use session because session management is done your web container and it never breaches the integrity of a user.
This will prevent the initialization of objects for later requests.
You have to write to get variable:
String isBootRunning = (String) getServletContext().getAttribute("isBootRunning");
You have to write to set variable:
getServletContext().setAttribute("isBootRunning", isBootRunning);
Another thing is that current design is quite bad (possible race condition). Application/web containers are multithreaded. As you are not using any sychonronization, you may not see the result when request is served by another thread.
Related
I have written some REST APIs using Java Servlets on Tomcat. These are my first experiences with Java and APIs and Tomcat. As I research and read about servlets, methods and parameter passing, and more recently thread safety, I realize I need some review, suggestions, and tutorial guidance from those of you who I see are far more experienced. I have found many questions / answers that seem to address pieces but my lack of experience clouds the clarity I desire.
The code below shows the top portion of one servlet example along with an example private method. I have "global" variables defined at the class level so that I may track the success of a method and determine if I need to send an error response. I do this because the method(s) already return a value.
Are those global variables creating an unsafe thread environment
Since the response is not visible in the private methods, how else might I determine the need to stop the process and send an error response if those global variables are unsafe
Though clipped for space, should I be doing all of the XML handling in the doGet method
Should I be calling all of the different private methods for the various data retrieval tasks and data handling
Should each method that accesses the same database open a Connection or should the doGet method create a Connection and pass it to each method
Assist, suggest, teach, guide to whatever you feel appropriate, or point me to the right learning resources so I may learn how to do better. Direct and constructive criticism welcome -- bashing and derogatory statements not preferred.
#WebServlet(name = "SubPlans", urlPatterns = {"*omitted*"})
public class SubPlans extends HttpServlet {
private transient ServletConfig servletConfig;
private String planSpecialNotes,
planAddlReqLinks,
legalTermsHeader,
legalTermsMemo,
httpReturnMsg;
private String[] subPlanInd = new String[4];
private boolean sc200;
private int httpReturnStatus;
private static final long serialVersionUID = 1L;
{
httpReturnStatus = 0;
httpReturnMsg = "";
sc200 = true;
planAddlReqLinks = null;
planSpecialNotes = null;
legalTermsHeader = "";
legalTermsMemo = null;
}
#Override
public void init(ServletConfig servletConfig)
throws ServletException {
this.servletConfig = servletConfig;
}
#Override
public ServletConfig getServletConfig() {
return servletConfig;
}
#Override
public String getServletInfo() {
return "SubPlans";
}
#Override
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
List<HashMap<String, Object>> alSubDeps = new ArrayList<HashMap<String, Object>>();
String[] coverageDates = new String[6],
depDates = new String[8];
String eeAltId = null,
eeSSN = null,
carrier = null,
logosite = null,
fmtSSN = "X",
subSQL = null,
healthPlan = null,
dentalPlan = null,
visionPlan = null,
lifePlan = null,
tier = null,
healthGroupNum = null,
effdate = null,
holdEffDate = null,
planDesc = "",
planYear = "",
summaryBenefitsLink = null;
int[][] effdates = new int[6][4];
int holdDistrictNumber = 0,
districtNumber = 0,
holdUnit = 0,
unit = 0;
boolean districtHasHSA = false;
XMLOutputFactory outputFactory = XMLOutputFactory.newInstance();
try {
eeAltId = request.getParameter("*omitted*");
if ( eeAltId != null ) {
Pattern p = Pattern.compile(*omitted*);
Matcher m = p.matcher(eeAltId);
if ( m.find(0) ) {
eeSSN = getSSN(eeAltId);
} else {
httpReturnStatus = 412;
httpReturnMsg = "Alternate ID format incorrect.";
System.err.println("Bad alternate id format " + eeAltId);
sc200 = false;
}
} else {
httpReturnStatus = 412;
httpReturnMsg = "Alternate ID missing.";
System.err.println("alternate id not provided.");
sc200 = false;
}
if ( sc200 ) {
coverageDates = determineDates();
subSQL = buildSubSQLStatement(eeSSN, coverageDates);
alSubDeps = getSubDeps(subSQL);
if ( sc200 ) {
XMLStreamWriter writer = outputFactory.createXMLStreamWriter(response.getOutputStream());
writer.writeStartDocument("1.0");
writer.writeStartElement("subscriber");
// CLIPPED //
writer.writeEndElement(); // subscriber
writer.writeEndDocument();
if ( sc200 ) {
response.setStatus(HttpServletResponse.SC_OK);
writer.flush();
} else {
response.sendError(httpReturnStatus, httpReturnMsg);
}
}
}
} catch (Exception e) {
e.printStackTrace();
System.err.println("Error writing XML");
System.err.println(e);
}
}
#Override
public void destroy() {
}
private String getPlanDescription(String planID) {
String planDesc = null;
String sqlEE = "SELECT ...";
Connection connGPD = null;
Statement stGPD = null;
ResultSet rsGPD = null;
try {
connGPD = getDbConnectionEE();
try {
stGPD = connGPD.createStatement();
planDesc = "Statement error";
try {
rsGPD = stGPD.executeQuery(sqlEE);
if ( !rsGPD.isBeforeFirst() )
planDesc = "No data";
else {
rsGPD.next();
planDesc = rsGPD.getString("Plan_Description");
}
} catch (Exception rsErr) {
httpReturnStatus = 500;
httpReturnMsg = "Error retrieving plan description.";
System.err.println("getPlanDescription: " + httpReturnMsg + " " + httpReturnStatus);
System.err.println(rsErr);
sc200 = false;
} finally {
if ( rsGPD != null ) {
try {
rsGPD.close();
} catch (Exception rsErr) {
System.err.println("getPlanDescription: Error closing result set.");
System.err.println(rsErr);
}
}
}
} catch (Exception stErr) {
httpReturnStatus = 500;
httpReturnMsg = "Error creating plan description statement.";
System.err.println("getPlanDescription: " + httpReturnMsg + " " + httpReturnStatus);
System.err.println(stErr);
sc200 = false;
} finally {
if ( stGPD != null ) {
try {
stGPD.close();
} catch (Exception stErr) {
System.err.println("getPlanDescription: Error closing query statement.");
System.err.println(stErr);
}
}
}
} catch (Exception connErr) {
httpReturnStatus = 500;
httpReturnMsg = "Error closing database.";
System.err.println("getPlanDescription: " + httpReturnMsg + " " + httpReturnStatus);
System.err.println(connErr);
sc200 = false;
} finally {
if ( connGPD != null ) {
try {
connGPD.close();
} catch (Exception connErr) {
System.err.println("getPlanDescription: Error closing connection.");
System.err.println(connErr);
}
}
}
return planDesc.trim();
}
I have "global" variables defined at the class level
You have instance variables declared at the class level. There are no globals in Java.
so that I may track the success of a method and determine if I need to send an error response.
Poor technique.
I do this because the method(s) already return a value.
You should use exceptions for this if the return values are already taken.
Are those global variables creating an unsafe thread environment
Those instance variables are creating an unsafe thread environment.
Since the response is not visible in the private methods, how else might I determine the need to stop the process and send an error response if those global variables are unsafe?
Via exceptions thrown by the methods, see above. If there is no exception, send an OK response, whatever form that takes, otherwise whatever error response is appropriate to the exception.
Though clipped for space, should I be doing all of the XML handling in the doGet method
Not if it's long or repetitive (used in other places too).
Should I be calling all of the different private methods for the various data retrieval tasks and data handling
Sure, why not?
Should each method that accesses the same database open a Connection or should the doGet() method create a Connection and pass it to each method
doGet() should open the connection, pass it to each method, and infallibly close it.
NB You don't need the ServletConfig variable, or the init() or getServletConfig() methods. If you remove all this you can get it from the base class any time you need it via the getServletConfig() method you have pointlessly overridden.
The variables you have defined are instance members. They are not global and are not class-level. They are variables scoped to one instance of your servlet class.
The servlet container typically creates one instance of your servlet and sends all requests to that one instance. So you will have concurrent requests overwriting these variables’ contents unpredictably.
It can be ok for a servlet to have static variables or instance member variables, but only if their contents are thread safe and they contain no state specific to a request. For instance it would be normal to have a (log4j or java.util.logging) Logger object as a static member, where the logger is specifically designed to be called concurrently without the threads interfering with each other.
For error handling use exceptions to fail fast once something goes wrong.
Servlets are painful to write and hard to test. Consider using a MVC web framework instead. Frameworks like spring or dropwizard provide built-in capabilities that make things like data access and error handling easier, but most importantly they encourage patterns where you write separate well-focused classes that each do one thing well (and can be reasoned about and tested independently). The servlet approach tends to lead people to cram disparate functions into one increasingly-unmanageable class file, which seems to be the road you’re headed down.
Here is the code of my servlet.
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// TODO Auto-generated method stub
Map m=request.getParameterMap();
Set s = m.entrySet();
Iterator it = s.iterator();
int index=0;
while (it.hasNext()) {
Map.Entry<String,String[]> entry = (Map.Entry<String,String[]>) it.next();
String key = entry.getKey();
String[] value = entry.getValue();
System.out.println("Value is " + value[0].toString());
switch(key) {
case "RegId":
RegId = value[0].toString();
break;
case "isTrackingRequested":
isTrackingRequested = Boolean.valueOf(value[0]);
break;
}
}
// Create a session object if it is already not created.
HttpSession session = request.getSession(true);
if (session.isNew()) {
session.setAttribute("id",isTrackingRequested);
}
ScheduledExecutorService ses = Executors.newSingleThreadScheduledExecutor();
ses.scheduleAtFixedRate(new Runnable() {
#Override
public void run() {
boolean isTrackingRequestednew = (boolean) session.getAttribute("id");
// code to run
if (isTrackingRequestednew) {
try {
System.out.println("===========================================================================");
System.out.println("new track status is " + isTrackingRequestednew);
System.out.println("===========================================================================");
} catch (Exception e) {
}
} else {
ses.shutdown();
}
}
}, 0, 1, TimeUnit.SECONDS);
}
I am trying to track a vehicle by using a ScheduledExcecutorService. I am using a flag isTrackingRequested to check if user has requested the tracking. So I am saving the values in the session, but whenever I request for the tracking to stop, the previously set session attribute shows null value.
In short, I am trying to access a previously set session variable, but I end up getting null. No solution I have tried seems to work.
whenever I request for the tracking to stop, the previously set session attribute shows null value.
A reasonably likely explanation is that the requests are not in the same session. Maintaining a session requires cooperation from the client, which is not guaranteed to be given. The most common mechanisms for associating requests with sessions are cookies and URL rewriting. If the client refuses cookies and is making its requests to a static URL then every request will likely be in its own session.
That's one of the lesser of your problems, however. You also have these:
On every POST request, you create a new ScheduledExecutorService and a new task for it to manage. Surely that's not what you intended.
Added: You do not update existing sessions with the tracking state carried by requests belonging to those sessions. Only if the session is newly created for the request being serviced do you set the session attribute.
Moreover, when last I studied the JavaEE specs (a version ago) JavaEE components such as servlets were not permitted to start their own threads, but yours does -- many of them -- within all the ScheduledExecutorServices. That doesn't mean starting a new thread (or creating a ScheduledExecutorService) will necessarily fail, but your violation of the specs does mean that you cannot rely on the JavaEE APIs to behave as documented.
Moreover, your code is not properly synchronized. You access shared state (the Session) without proper synchronization.
Furthermore, you appear to have no mechanism to shut down tracking when a session expires or is manually terminated.
To do this properly, the tracking should be performed in a separate service running outside the servlet container. Alternatively, you could hack it together with only the scheduler itself running outside the container, and all the tracked state living inside. The scheduler would then need only to serve as a clock by sending a periodic request to a different servlet in the same container.
You would do well to decouple your task from the session. Instead of having it get the tracking state from the session, give it a member variable for that, and store a reference to the task itself in the session. Modify the task's state directly in response to requests, instead of passing that information indirectly via the session. And make sure all accesses to that object's shared state are properly synchronized!
Added: Furthermore, I suggest that you make the task implement HttpSessionBindingListener, so that when it is unbound from the session -- either manually or as a result of the session reaching the end of its life -- it can cancel itself.
Added: Additionally, note that modern JavaEE requires the container to make a ScheduledExecutorService available to enterprise components. You should be able to obtain a reference to it via the JNDI name java:comp/DefaultManagedScheduledExecutorService (see section EE.5.21 in the Java EE 7 platform specification). It would be wise to use this container-provided service instead of attempting to set up your own.
There are a few bugs in the code you provided. Value of isTrackingRequested should be checked every time as requester may send a value false to stop tracking. Also null value of isTrackingRequested should be taken into account. If it is null then it probably means user wants to carry on as with the previous decision.
These have been corrected in the code below, this should be working now.
protected void doPost(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
String RegId = String.valueOf(request.getParameter("RegId"));
// Create a session object if it is already not created.
final HttpSession session = request.getSession(true);
String trackingRequestParam = request.getParameter("isTrackingRequested");
boolean isTrackingRequested = false;
if(trackingRequestParam != null) {
isTrackingRequested = Boolean.valueOf(trackingRequestParam);
session.setAttribute("id", isTrackingRequested);
}
if(trackingRequestParam != null && isTrackingRequested) {
final ScheduledExecutorService ses = Executors
.newSingleThreadScheduledExecutor();
session.setAttribute("isRunning", true);
ses.scheduleAtFixedRate(new Runnable() {
#Override
public void run() {
boolean isTrackingRequestednew = Boolean.valueOf(String.valueOf(session.getAttribute("id") ));
System.out.println("inside repeater : " + session.getAttribute("id") + " : " + isTrackingRequestednew);
// code to run
if (isTrackingRequestednew) {
try {
System.out.println("===========================================================================");
System.out.println("new track status is " + isTrackingRequestednew);
System.out.println("===========================================================================");
} catch (Exception e) {
}
} else {
ses.shutdown();
}
}
}, 0, 1, TimeUnit.SECONDS);
}
}
Edits *****
I'm adding entire TestServlet code that I used here. I have converted POST method to GET method for ease of testing.
TestServlet
package test;
import java.io.IOException;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
#WebServlet("/TestServlet")
public class TestServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
Boolean isLogout = Boolean.valueOf(String.valueOf(request.getParameter("logout")));
String RegId = String.valueOf(request.getParameter("RegId"));
// Create a session object if it is already not created.
final HttpSession session = request.getSession(true);
String trackingRequestParam = request.getParameter("isTrackingRequested");
boolean isTrackingRequested = false;
if(trackingRequestParam != null) {
isTrackingRequested = Boolean.valueOf(trackingRequestParam);
session.setAttribute("id", isTrackingRequested);
}
if(isLogout) {
session.invalidate();
}
if(trackingRequestParam != null && isTrackingRequested) {
final ScheduledExecutorService ses = Executors
.newSingleThreadScheduledExecutor();
session.setAttribute("isRunning", true);
ses.scheduleAtFixedRate(new Runnable() {
#Override
public void run() {
try {
boolean isTrackingRequestednew = Boolean.valueOf(String.valueOf(session.getAttribute("id") ));
System.out.println("inside repeater : " + session.getAttribute("id") + " : " + isTrackingRequestednew);
// code to run
if (isTrackingRequestednew) {
try {
System.out.println("===========================================================================");
System.out.println("new track status is " + isTrackingRequestednew);
System.out.println("===========================================================================");
} catch (Exception e) {
}
} else {
ses.shutdown();
}
} catch (Exception ex) {
ex.printStackTrace();
ses.shutdown();
}
}
}, 0, 1, TimeUnit.SECONDS);
}
}
protected void doPost(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
}
}
Entire TestServlet
Testing
Hit below URLs (TestWeb was context name in my case, replace it with yours)
http://localhost:8080/TestWeb/TestServlet?isTrackingRequested=true<br>
//then after a few seconds<br>
http://localhost:8080/TestWeb/TestServlet?isTrackingRequested=false
Output (Test was done on Tomcat 7.0.59)
inside repeater : true : true
===========================================================================
new track status is true
===========================================================================
inside repeater : true : true
===========================================================================
new track status is true
===========================================================================
inside repeater : true : true
===========================================================================
new track status is true
===========================================================================
inside repeater : true : true
===========================================================================
new track status is true
===========================================================================
inside repeater : true : true
===========================================================================
new track status is true
===========================================================================
inside repeater : true : true
===========================================================================
new track status is true
===========================================================================
inside repeater : false : false
After tracking was set to false, it stops printing as executor was shutdown.
Note: Clear your cookies to start a new session.
Edit2 ***
Added code to support manual logout call URL below to logout.
http://localhost:8080/TestWeb/TestServlet?logout=true
I'm writing a Spring web application and I'm mapping the "/do" URL path to the following Controller's method
#Controller
public class MyController
{
#RequestMapping(value="/do", method=RequestMethod.GET)
public String do()
{
File f = new File("otherMethodEnded.tmp");
while (!f.exists())
{
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
}
}
// ok, let's continue
}
}
The otherMethodEnded.tmp file is written by one another Controller's method, so when the client calls the second URL I expect the first method to exit the while loop.
Everything works, except when the client calls the "/do" URL and then closes the connection before the response was received. The problem is that the server remains in the while (!f.exists()) loop even though the client is down and cannot call the second URL to unlock the while loop.
I would try to retrieve the connection status of the "/do" URL and exit the loop when the connection is closed by the client, but I cannot find any way to do so.
I tried with the HttpServletRequest.getSession(false) method but the returned HttpSession object is always not null, so the HttpServletRequest object is not updated in case of connection close of the client.
How can I check whether the client is still waiting for the risponse or not?
The simplest way to verify something is not right is to define a timeout value and then during your loop test if your time spent waiting has exceeded the timeout.
something like:
#Controller
public class MyController
{
private static final long MAX_LOOP_TIME = 1000 * 60 * 5; // 5 minutes? choose a value
#RequestMapping(value="/do", method=RequestMethod.GET)
public String do()
{
File f = new File("otherMethodEnded.tmp");
long startedAt = System.currentTimeMillis()
boolean forcedExit = false;
while (!forcedExit && !f.exists())
{
try {
Thread.sleep(5000);
if (System.currentTimeMillis() - startedAt > MAX_LOOP_TIME) {
forcedExit = true;
}
} catch (InterruptedException e) {
forcedExit = true;
}
}
// ok, let's continue
// if forcedExit , handle error scenario?
}
}
Additionally: InterruptedException is not something to blindly catch and ignore. see this discussion
In your case I would really exit the while loop if you're interrupted.
You only know if the client is no longer waiting on your connection when you notice the output stream you write to (response.outputstream) is closed. But there isn't a way to detect it.
(see this question for details)
Seeing as you've indicated your client does occasional callbacks, you could on the clientside poll if the other call has been completed. If this other call has completed, do the operation, otherwise return directly and have the client do the call again. (assuming you are sending json, but adapt as you require)
something like
public class MyController
{
#RequestMapping(value="/do", method=RequestMethod.GET)
public String do()
{
File f = new File("otherMethodEnded.tmp");
if (f.exists()) {
// do what you set out to do
// ok, let's continue
// and return with a response that indicates the call did what it did
// view that returns json { "result" : "success" }
return "viewThatSIgnalsToClientThatOperationSucceeded";
} else {
// view that returns json: { "result" : "retry" }
return "viewThatSignalsToClientToRetryIn5Seconds";
}
}
}
Then the clientside would run something like: (pseudojavascript as it's been a while)
val callinterval = setInterval(function() { checkServer() }, 5000);
function checkServer() {
$.ajax({
// ...
success: successFunction
});
}
function successFunction(response) {
// connection succeeded
var json = $.parseJSON(response);
if (json.result === "retry") {
// interval will call this again
} else {
clearInterval(callinterval);
if (json.result === "success") {
// do the good stuff
} else if (json.result === "failure") {
// report that the server reported an error
}
}
}
Ofcourse this is just semi-serious code but it's roughly how i'd try it if I were to have the dependency. If this is regarding afile upload, keep in mind that this file may not contain all of the bytes yet. file exists != file = completely uploaded, unless you use move it. cp / scp / etc. is not atomic.
Sometimes when I use multiple Modeshape actions inside one function I get this error:
javax.jcr.RepositoryException: The session with an ID of '060742fc6' has been closed and can no longer be used.
I couldn't find any explanations of this on the web. Here is what I call:
myFunction( service.doSomething ( service.getStuff ( id, "en_EN" ).getPath() ) );
doSomething, getStuff:
#Interceptors({Foo.class, TraceInterceptor.class})
#Override
public Node doSomething(final String bar) throws RepositoryException {
return modeshape.execute(new JcrHandler<Node>() {
#Override
public Node execute(Session session) throws RepositoryException {
return session.getNode(bar);
}
});
}
#Interceptors(TraceInterceptor.class)
#Override
public ObjectExtended getStuff(final String query, final String language)
throws RepositoryException {
return modeshape.execute(new JcrHandler<ObjectExtended>() {
#Override
public ObjectExtendedexecute(Session session)
throws RepositoryException {
QueryManager queryManager = session.getWorkspace().getQueryManager();
ObjectExtendeditem = null;
String queryWrapped =
"select * from [th:this] as c where name(c)='lang_"
+ language + "' and c.[th:mylabel] "
+ "= '" + queryStr + "'";
LOGGER.debug("Query: " + queryWrapped);
Query query =
queryManager.createQuery(queryWrapped,Query.JCR_SQL2);
QueryResult result = query.execute();
NodeIterator iter = result.getNodes();
while (iter.hasNext()) {
Node node = iter.nextNode().getParent();
if (node.isNodeType("th:term")) {
item = new ObjectExtended();
item.setLabel(getLabel(language, node));
item.setPath(node.getPath());
}
}
return item;
}
});
}
Why is this happening please? What am I doing wrong?
That error message means one of two thing: either the repository is being shutdown, or the Session.logout() method is being called.
None of the above code shows how your sessions are being managed, and you don't say whether you are using a framework. But I suspect that somehow you are holding onto a Session too long (perhaps after your framework is closing the session), or the Session is leaking to multiple threads, and one thread is attempting to use it after the other has closed it.
The latter could be a real problem: while passing a single Session instance from one thread to another is okay (as long as the original thread no longer uses it), but per the JCR 2.0 specification Session instances are not threadsafe and should not be concurrently used by multiple threads.
If you're creating the Session in your code, it's often good to use a try-finally block:
Session session = null;
try {
session = ... // acquire the session
// use the session, including 0 or more calls to 'save()'
} catch ( RepositoryException e ) {
// handle it
} finally {
if ( session != null ) {
try {
session.logout();
} finally {
session = null;
}
}
}
Note that logout() does not throw a RepositoryException, so the above form usually works well. Of course, if you know you're not using session later on in the method, you don't need the inner try-finally to null the session reference:
Session session = null;
try {
session = ... // acquire the session
// use the session, including 0 or more calls to 'save()'
} catch ( RepositoryException e ) {
// handle it
} finally {
if ( session != null ) session.logout();
}
This kind of logic can easily be encapsulated.
I am a bit new to WS portal & have a requirement wherein on a specific scenario we want the user to logout & redirect him to the default portal login page.
I have tried to invalidate the session clear the cookies & do a response.sendRedirect as in response.sendRedirect("/wps/myportal/"); ,
but in vain.
Please guide.
You can configure logout page using wp_configservice:
redirect.logout=true
redirect.logout.ssl=false
redirect.logout.url=protocol://host_name/logout_page
After configuring it, clicking on standard logout will redirect you to the specified page.
If you're using JSF portlets, you can do something along the lines of the following - this should actually log the user session out as well as returning them to the default login page.
In reality you'll probably want to consider caching the initial context lookup by moving out the lookup code ( Context ctx = new InitialContext();
PortletServiceHome stateMgrServiceHome = (PortletServiceHome) ctx.lookup("portletservice/com.ibm.portal.state.service.PortletStateManagerService");)
public void doLogOut()
{
String logoutUrl = generateLogoutURL(getPortletRequest(), getPortletResponse());
redirectToUrl(logoutUrl);
}
public String generateLogoutURL(PortletRequest request, PortletResponse response) throws RpmPortalException {
final String methodName = "generateLogoutURL";
String logoutURL = "";
Context ctx = new InitialContext();
PortletServiceHome stateMgrServiceHome = (PortletServiceHome) ctx.lookup("portletservice/com.ibm.portal.state.service.PortletStateManagerService");
PortletStateManager stateMgr = null;
LogoutActionAccessorController logoutCtrl = null;
try {
stateMgr = stateMgrService.getPortletStateManager(request, response);
final URLFactory urlFactory = stateMgr.getURLFactory();
EngineURL url = urlFactory.newURL(null);
LogoutActionAccessorFactory logoutFct = (LogoutActionAccessorFactory) stateMgr.getAccessorFactory(LogoutActionAccessorFactory.class);
logoutCtrl = logoutFct.newLogoutActionController(url.getState());
logoutURL = url.writeDispose(new StringWriter()).toString();
} catch (StateException e) {
//do whatever you want
} catch (IOException e) {
//do whatever you want
} finally {
if (stateMgr != null) {
stateMgr.dispose();
}
if (logoutCtrl != null) {
logoutCtrl.dispose();
}
}
return logoutURL;
}
public void redirectToUrl(String url) {
try {
FacesContext context = getFacesContext();
if (context != null) {
context.getExternalContext().redirect(url);
context.responseComplete();
}
} catch (IOException e) {
//Do whatever you want
}
}
In the standard configuration of Portal, directing a user to any page with /wps/portal... as the root instead of /wps/myportal/... will force the user to log out and the session to end. So you could just create a friendly URL for your login page and redirect the user to /wps/portal/friendlyLoginUrl
Just an addition to what zargarf said, by default the following js is executed when you click on logout is mentioned below :
javascript:if(stproxy && stproxy.isLoggedIn){stproxy.login.logout();}