I am using valueUnbound method of HttpSessionBindingListener to release lock(an entry from the database), before session is about to expire:
#Override
public void valueUnbound(HttpSessionBindingEvent event) {
String user = (String) event.getSession().getAttribute("currentUsr");
removeLock(user);
}
When the lock is set, I am setting up the username as a session variable.
I need this "username" in my remove lock method. But the getAttribute is throwing an exception:
java.lang.IllegalStateException: getAttribute: Session already invalidated
I need help in getting the session variable?? or is there any other way to get the username?
No, since session has been invalidated.
Although, I figured out the solution, I am setting the attribute via servlet context in
valueBound method and getting it through the : event.getSession().getServletContext().getAttribute("cUser");
it works fine. Thank You EJP
I got your point EJP, you are right , I am making it complex, I can get it from event.getValue() . +1 to your answer, Thank You.
Although, I figured out the solution, I am setting the attribute via servlet context in valueBound method and getting it through the : event.getSession().getServletContext().getAttribute("cUser");
So.. You are storing session scoped data in the application scope. Do you realize that this way the data is shared among all visitors of the webapp? Visitor X would then see the attribute set by visitor Y which has visited the website at a later moment. It makes the problem only worse.
Anyway, as to the concrete problem, as the exception message is trying to tell you, the session has already been invalidated at that point. There are two ways to solve this:
Make currentUsr a property of the class which is implementing HttpSessionBindingListener, so that you don't need to grab it as a distinct session attribute.
Use a HttpSessionListener instead. The sessionDestroyed() method is called right before invalidation, so you should still have access to all attributes.
Related
private void doSomething(someProcessModel process){
CustomerModel customer = process.getCustomerModel();
customer.getFoos().stream()
.filter(foo -> foo.getCountryCode().equals(process.getCountryCode()))
.findFirst()
.ifPresent(foo -> {
if(foo.getSomeNumber() == null){
foo.setSomeNumber("1234567");
modelService.save(foo);
}
});
}
As seen in the code snippet above, I have a 'CustomerModel' that has an attribute 'Foos'. It's a one-to-many relationship. As you can see I have done some filtering and in the end, I want to update the value of 'someNumber' attribute of 'Foo' if it is null. I've confirmed that everything is working as the "someNumber" attribute's value is updated during the debugging. It doesn't save at all as I have done my checking in the HMC. I have also validated that the Interceptor doesn't have any condition that would throw an error. There is nothing being shown in the log either.
I am wondering is it a legal approach to do the "modelService.save()' inside the 'ifPresent()' method? What could be the possible issue here?
I have found the root cause now as I have face the same issue again.
Context to my original question
To give more context to my original question, the #doSomething method resides in a Hybris Business Process action class and I have ended the action prematurely while I am debugging it (by stopping the debugging) once the #doSomething method is ran.
Root cause
The mentioned problem happened when I was debugging the action class. I assumed that the ModelService#save will persist the current state of the business process once it has been ran. However, the Hybris OOTB business process will do a rollback if there is any error (and I believe it was caused by me stopping the debugging half-way).
SAP Commerce Documentation:
All actions are performed inside their own transaction. This means that changes made inside the action bean run method are rolled back in case of an error.
Solution
Let the action runs completely!
Good to know
Based on the SAP Documentation and this blog post, there will be times that we will need to bypass a business process rollback even though there is an exception being thrown and there are ways to achieve that. More info can be found in this SAP Commerce Documentation and the mentioned blog post.
However, in some circumstances it may be required to let a business exception reach the outside but also commit the transaction and deal with the exception outside. Therefore, it is possible to make the task engine not roll back the changes made during a task which failed.
You have to be cautious with a list in models as they are immutable, you have to set the whole new list. Also you called save only on a particular model, which changes its Jalo reference, that's why your list is not updated. Mutating stream and collecting it in the end will create new list that's why you can stream over the list directly from the model.
private void doSomething(someProcessModel process){
CustomerModel customer = process.getCustomerModel();
ArrayList<FooModel> foos = doSomethingOnFoos(customer.getFoos());
customer.setFoos(foos);
modelService.saveAll(foos, customer);
}
//compare the value you know exists with something that might be NULL as equals can handle that, but not the other way around
private ArrayList<FooModel> doSomethingOnFoos(ArrayList<FooModel> fooList) {
return fooList.stream()
.filter(Objects::nonNull)
.filter(foo -> process.getCountryCode().equals(foo.getCountryCode()))
.filter(foo -> Objects.isNull(foo.getSomeNumber()))
.map(foo -> foo.setSomeNumber(1234))
.collect(toList());
}
In our Rails application we want to store an instance of a class in a session. This is because we want to set the class with some parameters when the user first logs into the application and then re-use this class by pulling it back out of the same session. When their session expires or they log out, this instance of the class is destroyed.
We're doing this to avoid using a Singleton class because that would live at Application-level and be available on different processes and stick around longer than the user's session, and have security implications due to it also being available to other users who haven't created a session yet.
So this is how it works:
session[:example_class] = ExampleClass.new(field_one: 'field_one', field_two: 'field_two')
This works fine!
However if I then do this:
current_instance = session[:example_class]
current_instance.do_something
session[:example_class] = current_instance
Whereby I am calling a method on this instance or whatever and then want to push that updated instance back into the session again so it's stored somewhere... we get this error:
TypeError in HomeController#index
ExampleClass can't be referred to from /Users/cameron/.rbenv/versions/2.5.5/lib/ruby/gems/2.5.0/bundler/gems/activerecord-session_store-376ed7f7aba1/lib/active_record/session_store.rb:64:in `dump'
And that method that is failing in session_store.rb is:
def self.dump(value)
::Base64.encode64(Marshal.dump(value))
end
And the value it's trying to store is:
{"_csrf_token"=>"nrw4m2ZAECwD3TiaPZoaSt4vL1DvjO+COnBpUQGwpXs=", "example_class"=>#<ExampleClass:0x00007f7fa7b1b998 #field_one="field_one", #field_two="field_two">}
Why can I write the class in the first time around... but afterwards it throws that error?
And how we get around this?
I can't answer you fully why it fails, but I wouln't rely on implicit serialization of Ruby objects.
I would find a way to serialize the object of ExampleClass explicitly (similar to as_json) - convert to a hash, then store the hash in the session. When you need this object again, initialize ExampleClass instance with the params from session and then serialize it back to session.
I don't have an answer for why the above error happened but it turns out if you're storing an instance of a class inside the session, you're in fact storing the instance and not just the current state of the class as I originally thought it was doing.
This basically means that whenever you interact with that class and change it's attributes the session is still reading from the same instance you are interacting with and therefore has the same information without having to write back into the session.
In short you have the behaviour of a Singleton class but using a session to maintain state.
RaceRegistration domain has embedded raceParticipant and raceParticipant has a field bibNumber which is Integer.
I have a method for nulling out all bibNumbers of registrations but without flush:true in save, the nulling out of bibs dont work. The bibs are not set to null.
def nullifyBibNumbers(Long id){
...
def regss = RaceRegistration.createCriteria().list(){
eq('compositeEvent', event)
}
regss.each{ r ->
r.raceParticipant.bibNumber = null
r.save()
}
render "Bibs resetted!"
}
If i add flush:true then the bibs are set to null.
regss.each{ r ->
r.raceParticipant.bibNumber = null
r.save(flush: true)
}
I am wondering why you need flush in order for the value to be set to null? I am guessing the problem is with regard to how i am obtain the registration list using createCriteria(). I appreciate any help in this dilemma i am facing. Thanks!
As you probably figured out, save(flush: true) forces Hibernate to write any pending changes to the database. Without the explicit flush, you're relying on a Hibernate transaction to automatically flush when the transaction commits.
The reason only an explicit flush is working for you is because you're not calling save() within a transaction.
The cleanest fix is to create a Grails service, put nullifyBibNumbers() in it, and make the service transactional. That will cause nullifyBibNumbers() to get wrapped in a transaction so that you can use save() without an explicit flush.
If nullifyBibNumbers() is already in a service, you can add #Transactional to the service class, just keep in mind that it will make all methods (perhaps only the public ones?) transactional. Having said that, you can use #NotTransactional on a method to disable transactions.
The value is null in your domain object. But you are talking about null in the database, I guess?
It shouldn't matter. This is basic ORM. As a developer you don't care about when the flush is done. Typically this would be at the end of a transaction. The ORM will then flush all of the changes for that transaction at once.
It works on what is called the first-level cache during the transaction, and tries to avoid going to the db until it is explicitly requested (flush:true) or required (end of transaction).
Without the using of
save(flush: true)
The object will not be persisted immediately.
You can follow the documentation link and see the following information:
The save method informs the persistence context that an instance
should be saved or updated. The object will not be persisted
immediately unless the flush argument is used.
Related to the null issue you are facing make sure that the following condition are met.
The save method returns null if validation failed and the instance was
not persisted, or the instance itself if successful.
You do not need the flush in order for the value to be set to null.
The flush only care of a quick update of the database.
ok i fixed this problem using HQL instead of domain saves. Still i would appreciate why save() didnt work and save(flush:true) saved the data. Thanks!
RaceRegistration.executeUpdate("update RaceRegistration set raceParticipant.bibNumber = null where compositeEvent.id = :ev", [ev: id])
I've already searched via google and on stackoverflow, but could not find any similar problem to mine.
In my project I'm handling a ViewExpiredException properly and show a custom page to the user that the current session has timed out. This works great, but I want to do something BEFORE this message gets shown to the user. Actually I'm working with 2 different sessions here, one on the frontend side and one on the backend, so the idea is to NOT start a new backend session when the current one timed out.
Is there any possibility to fetch the ViewExpiredException while I'm inside the doFilter method, so I do not start a new backend session (simply because it is not needed)? Or is there any other way?
I already tried to fetch the current context via
FacesContext fc = FacesContext.getCurrentInstance();
But obviously the context is null, because the session timed out.
Inside the ExceptionHandlerWrapper I have access to the UnhandledExceptionQueuedEvents, but this does not help me here since I need this information earlier.
I hope I made my problem clear enough.
Thanks in advance for any help!
Regards
Sebastian
Generally ViewExpiredException is thrown when a POST request is fired while the session is timed out. So, this should do in the filter:
boolean post = "POST".equals(request.getMethod());
boolean timedout = request.getRequestedSessionId() != null && !request.isRequestedSessionIdValid();
if (post && timedout) {
// JSF will guaranteed throw ViewExpiredException when state saving is set to server.
}
But this does not cover all possible cases. ViewExpiredException can also occur when the session hasn't timed out. For example, when the client has passed an invalid javax.faces.ViewState parameter, or when the associated view has been pruned from the LRU map which can by default hold 15 views. This is however not detectable inside a servlet filter before FilterChain#doFilter() is called. You really need to be inside the JSF context. You could do the backend session creating job in a PhaseListener. E.g. in beforephase of apply request values phase, which is guaranteed to be invoked only when there's a vaild view.
By the way, the FacesContext is not null in the filter because the session has timed out, but because the FacesServlet, the one responsible for creating it, hasn't been invoked yet at that point. You know, filters run before servlets.
We have a custom tag similar to g:set which sets the current user into PageScope <n:currentUser var=”foobar”> It works great until we have a flowaction.
For a flowaction view state, which uses above tag, it will throw Lazy initialization exception “could not initialize proxy - no Session”, even though the user is loaded in the same request and set in pagescope.
Doesn’t webflow respect OpenSessionInView ! What’s going wrong here.
What could be the solution other then eager fetching and passing the modal explicitly.
(The tag is in a layout actually, which is applied for the view of the view state)
UPDATE
I just noticed, that even when accessing the the object right after it is loaded, it still gives the same error. So its not PageScope things causing the issue
Inside the tag
User user = User.get(x)
println user.foo.bar gives the same error
It looks like, for flow actions, session isn't kept open at all, and it seems to close right after the operation is complete.
Thanks
I've seen this error before, and not related to the webflow, but using a tag inside a layout. In this case the layout is handled after the session is closed and you need to create a new session manually.
def currentUser = { attrs ->
User.withTransaction {
User user = User.get(x)
}
}
The JIRA have the status won't fix because is not a good practice to make GORM queries inside TagLib's.