PDA

View Full Version : Maintaing State while using HttpInvoker



shaby775
Oct 9th, 2004, 08:20 AM
Hi,
I am working on a project which is rich client based (Swing) connecting to server side using http protocol. So i am using spring on server side and remoting using HttpInvoker and it is working fine and i feel i dont need EJB right now (great work spring team!). Now i am facing a problem, we want to maintain some state on server side (preferably using web http session) as we donot want to use SFSB ejb's here. I certainly want to pass that state information to my remotely exposed service object, but i donot know how to do that. I am pasting my code here so please can anybody help me?



<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http&#58;//www.springframework.org/dtd/spring-beans.dtd">

<!--
- Application context definition for Petclinic on Hibernate.
-->
<beans>
<!-- ========================= RESOURCE DEFINITIONS ========================= -->
<!-- JNDI DataSource for J2EE environments -->
<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName"><value>java&#58;/OracleDS</value></property>
</bean>

<!-- Hibernate SessionFactory -->
<bean id="sessionFactory" class="org.springframework.orm.hibernate.LocalSessionFact oryBean">
<property name="dataSource"><ref local="dataSource"/></property>
<property name="mappingResources">
<value>com/sequelsys/common/model/scheduling/HolidaysSchedule.hbm.xml</value>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">net.sf.hibernate.dialect.Oracle9Dialect</prop>
<prop key="hibernate.show_sql">true</prop>
</props>
</property>
</bean>

<!-- Transaction manager for a single Hibernate SessionFactory &#40;alternative to JTA&#41; -->
<bean id="transactionManager" class="org.springframework.orm.hibernate.HibernateTransac tionManager">
<property name="sessionFactory"><ref local="sessionFactory"/></property>
</bean>

<bean id="holidayScheduleDAO" class="com.sequelsys.server.scheduling.dao.HolidaySchedul eDAO">
<property name="sessionFactory"><ref local="sessionFactory"/></property>
</bean>

<!-- ========================= BUSINESS OBJECT DEFINITIONS ========================= -->

<!--
- A parent bean definition which is a base definition for transaction proxies.
- It is markes as abstract, since it is never supposed to be instantiated itself.
- We set shared transaction attributes here, following our naming patterns.
- The attributes can still be overridden in child bean definitions.
-->
<bean id="baseTransactionProxy" class="org.springframework.transaction.interceptor.Transa ctionProxyFactoryBean" abstract="true">
<property name="transactionManager"><ref bean="transactionManager"/></property>
<property name="transactionAttributes">
<props>
<prop key="get*">PROPAGATION_REQUIRED,readOnly</prop>
<prop key="find*">PROPAGATION_REQUIRED,readOnly</prop>
<prop key="load*">PROPAGATION_REQUIRED,readOnly</prop>
<prop key="create*">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>

<bean id="holidayScheduleService" parent="baseTransactionProxy">
<property name="target">
<bean class="com.sequelsys.server.scheduling.service.HolidaySch eduleService">
<property name="holidayScheduleDAO"><ref local="holidayScheduleDAO"/></property>
</bean>
</property>
</bean>
</beans>




<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http&#58;//www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean name="/HolidayScheduleService" class="org.springframework.remoting.httpinvoker.HttpInvok erServiceExporter">
<property name="service"><ref bean="holidayScheduleService"/></property>
<property name="serviceInterface">
<value>com.sequelsys.server.scheduling.service.IHolidaySc heduleService</value>
</property>
</bean>
</beans>


So i want something (prefeably in web layer) taht can maintain state on user behalf and pass it to teh same object that is remotely exposed for swing side.

Ben Alex
Oct 9th, 2004, 05:32 PM
http://article.gmane.org/gmane.comp.java.springframework.user/3652/

shaby775
Oct 10th, 2004, 02:10 AM
Hi,
Thanks Ben for u r prompt reply but i am afraid that it is not the solution of my problem. Their are some requirements which need HttpSession atleast , i know spring philosophy is against SFSB but that doest mean that there should be no state at all. And by the way when u r using http for remoting it means u have web servers and all the web structure is already in place so why do not we should benefit to the available infrastructure i think instead of denying a very legitimate problem the people at spring should provide solution. I hope u people will not mind my boldness and i always agree that Spring is the best J2ee framework i have ever seen and i love it and i want to use it whenever possible but this Http sesson is causing problems for my cause of convincing my upper management and also i ma not using acegi security and we have some other things also to store in session.So kindly please help me. Looking forward with great hope from great people who wrote a great framework :)

oliverhutchison
Oct 10th, 2004, 06:15 PM
Shoaib,

are you absolutely sure that you need to maintain state on the server? If you post your use case perhaps we can suggest an alternative implementation that uses the existing (stateless) remoting support.

Ollie

katentim
Oct 23rd, 2004, 05:57 PM
I think adding Session support would be very useful. What if you need to get through a security product like Siteminder that is setup to use session cookies.

While there may be reasons not to use it, shouldn't that be the architect's decision, not a limitation of Spring?

katentim
Oct 24th, 2004, 07:26 AM
Shoaib,

You can use the session in the following way. For your client context, use the commons remote executor to maintain the same session:

<bean id="httpInvokerProxy" class="org.springframework.remoting.httpinvoker.HttpInvok erProxyFactoryBean">
<property name="serviceUrl">
<value>http&#58;//$&#123;serverName&#125;&#58;$&#123;httpPort&#125;$&#123;contextPath&#125;/remoting/RemoteService-httpinvoker</value>
</property>
<property name="serviceInterface">
<value>org.timnolan.springsample.domain.logic.RemoteServi ce</value>
</property>
<property name="httpInvokerRequestExecutor">
<ref bean="httpInvokerExecutor"/>
</property>
</bean>

<bean id="httpInvokerExecutor" class="org.springframework.remoting.httpinvoker.CommonsHt tpInvokerRequestExecutor"/>

To use the session, you can attach it to a ThreadLocal in some way (I've done this by subclassing the Dispatcher servlet, and using ThreadLocals inside a custom class, CurrentThread).


CurrentThread.setSessionHolder&#40;new SessionHolderImpl&#40;request&#41;&#41;;
super.doService&#40;request, response&#41;;
CurrentThread.cleanUp&#40;&#41;;

SessionHolderImpl implements SessionHolder interface to hide the Web dependencies.

public class SessionHolderImpl implements SessionHolder &#123;
private HttpSession session;

public SessionHolderImpl&#40;HttpServletRequest request&#41; &#123;
session = request.getSession&#40;&#41;;
&#125;

public Object getAttribute&#40;String name&#41; &#123;
return session.getAttribute&#40;name&#41;;
&#125;

public void setAttribute&#40;String name, Object value&#41; &#123;
session.setAttribute&#40;name, value&#41;;
&#125;

&#125;


Your code can then access the Session.

public Employee login&#40;String username, String password&#41; &#123;
if &#40;password==null&#41; &#123;
return null;
&#125;
Employee employeeFound = findEmployeeByUsername&#40;username&#41;;
if &#40;employeeFound!=null && employeeFound.isActive&#40;&#41; && password.equals&#40;employeeFound.getPassword&#40;&#41;&#41;&#41; &#123;
CurrentThread.getSessionHolder&#40;&#41;.setAttribute&#40;Cons tants.EMPLOYEE, employeeFound&#41;;
return employeeFound;
&#125;
return null;
&#125;

Having done I wouldn't recommend spreading this code everywhere. I've used it in one case to verify login which works very well. My Web and Remoting clients now use the same custom authentication.

shaby775
Oct 24th, 2004, 11:52 PM
Hi katentim,
Thanks for your solution i will try it soon ( can you send me the code for it) . Though i still believe that spring should have this support built in as it is handy in many cases and also not everybody can use the excellent acegi security system (you know the politics). So i rquest to developer of spring of addition of HttpSession .

Regards,
Shoaib Akhtar

katentim
Oct 25th, 2004, 07:23 AM
It shouldn't be too much to fill in the gaps from here, but anyway... CurrentThread looks like:

private static ThreadLocal sessionHolder = new ThreadLocal&#40;&#41;;

public static void setSessionHolder&#40;SessionHolder session&#41; &#123;
CurrentThread.sessionHolder.set&#40;session&#41;;
&#125;

public static SessionHolder getSessionHolder&#40;&#41; &#123;
if &#40;sessionHolder.get&#40;&#41;==null&#41; &#123;
return null;
&#125;
return &#40;SessionHolder&#41; sessionHolder.get&#40;&#41;;
&#125;

public static void cleanUp&#40;&#41; &#123;
//Clear all thread locals here
CurrentThread.sessionHolder.set&#40;null&#41;;
&#125;

And SessionHolder:

public interface SessionHolder &#123;
public abstract Object getAttribute&#40;String name&#41;;
public abstract void setAttribute&#40;String name, Object value&#41;;
&#125;

Good luck.

oliverhutchison
Oct 25th, 2004, 06:02 PM
Tim,

do you mind elaborating how you deal with things like session timeout? Also why do you need to store this employee object in the session for remoting requests (you use session holder elsewhere to check it)?

In our application we login on every request and place the "user" object into a thread local. This gives us the flexibility to use any kind of remoting protocol not just HTTP. To save out web clients having to login on every requrest we have filter that checks for a validated "user" in the session and if it's present does the login again using the details from the session.

Ollie

katentim
Oct 26th, 2004, 03:08 AM
how you deal with things like session timeout
Any request after a session timeout will be rejected and the remote application will have to login again (the remote application could automate this).

why do you need to store this employee object in the session for remoting requests
It contains roles and information for security checking and auditing.

...we login on every request...This gives us the flexibility to use any kind of remoting protocol not just HTTP
True. My approach was just useful for my scenario. Browser and remote client use exactly the same authentication and authorisation code.

dlkpochtaws
Oct 28th, 2004, 02:46 AM
are you absolutely sure that you need to maintain state on the server? If you post your use case perhaps we can suggest an alternative implementation that uses the existing (stateless) remoting support.



Hi Ollie.
From my point of view, server-side state is very important when you use Hibernate on the server and you have a remote client.
It simplies things when you do typical load - edit - save Business Object (BO) cycle
E.g.
1) User requested a BO to be shown in "Edit Dialog" (by Id)
2) Hibernate loads it and closes the database transaction
3) You push loaded instance to the server-side storage
4) You serialize instance to remote client.
5) User edits it, and then send it back to the server.
6) Server looks up the pushed instance in server-side storage and applies deserialized user changes to it
7) Server calls some kind of [Hibernate] Session.save(BO)

Please note, we can't serialize the whole BO instance on step 4), e.g. because of security reasons (current user is not allowed to see all fields in instance) or maybe because the BO is quite big and expensive to serialize (not all fields must be shown in current "Edit Dialog"). Because of that we can't recreate the edited BO in step 6) from deserialized information without server-side storage or additional SQL query to fetch it again from DB. The second approach (to fetch edited object from DB) has obvious performace penalty and has a side effect - this BO may have been changed since step 1), so user's changes will be mixed with other's changes.
IMHO this use case forces us to use some kind of server-side state management, and HttpSession very convenient in a case of HTTP remoting :-)

WBR, Dmitry Kirillov

oliverhutchison
Oct 28th, 2004, 03:36 AM
The second approach (to fetch edited object from DB) has obvious performace penalty

does it? Given that you already loaded the object in step 1 it's extremely likely that it's still in the Hibernate cache (assuming your using a cache). Also how many updates does your application actual handle? Is the small overhead of doing a DB fetch really significant? If it is, my gut feeling would be that the overhead of placing all of the loaded objects into the session would be even greater.


and has a side effect - this BO may have been changed since step 1), so user's changes will be mixed with other's changes.

If you take advantage of Hibernates optimistic concurrency support this is not an issue. How do you prevent/handle multiple concurrent updates with your statefull approach?

I'm know there are some valid situations where maintaining state on the server does make sense (transferring chunked data springs to mind) but most of the time you shouldn't need this feature.

Ollie

dlkpochtaws
Oct 28th, 2004, 11:57 PM
The second approach (to fetch edited object from DB) has obvious performace penalty

does it? Given that you already loaded the object in step 1 it's extremely likely that it's still in the Hibernate cache (assuming your using a cache).
Is the small overhead of doing a DB fetch really significant?

Do you mean transaction-level (first-level) cache or second level cache? Please note, we've closed the database transaction and discarded hibernate session after step 2) - so we don't have first-level cache. And since edited BOs are actively edited, they are not cached in second-level cache.
And the overhead to fetch DB for BO is significant in my system - due to some reasons discussed BOs are quite expensive to load from DB - they are spead accross some SQL tables, so there are several roundtrips to DB when we fetch each object.




and has a side effect - this BO may have been changed since step 1), so user's changes will be mixed with other's changes.

If you take advantage of Hibernates optimistic concurrency support this is not an issue.


The approach I use in my system is descibed in Section 8.2 of the book "Hibernate in Action". Your particular approach is closest to the section 8.2.2 in this book, named "Doing it the hard way" :-). Let me cite some paragraph from it:
"There is one problem with this notion: The user already used the possibly stale data to arrive at the decision to approve! Reloading the Item in the second request is useless, since the reloaded state will not be used for anything —at least, it can’t be used in deciding whether the auction should be approved, which is the important thing."

Yes I agree we can still use automatic Hibernate's version checking capability, but "The user already used the possibly stale data to arrive at the decision to approve".
And this book advises (Section 8.2.3) using detached object support of Hibernate, which is my preferred approach.

WBR, Dmitry.

oliverhutchison
Oct 29th, 2004, 01:02 AM
And since edited BOs are actively edited, they are not cached in second-level cache.

Can you please explain this? Why are you BO not cached in the second level cache? Given that you've said that they are very expensive to load this sound like a bad idea.


The approach I use in my system is descibed in Section 8.2 of the book "Hibernate in Action". Your particular approach is closest to the section 8.2.2 in this book, named "Doing it the hard way"

I've don't have a copy of HiA so I can't have a look at sections 8.2 and 8.2.2 but I can certainly say that the approach I suggested is not "the hard way" and is in fact "best practice". Especially when you are working with the detached object support. The following is from the Hibernate manual:


We very strongly recommend that you use version/timestamp columns for optimistic locking with Hibernate. This is the optimal strategy with respect to performance and is the only strategy that correctly handles modifications made to detached instances (ie. when Session.update() is used)

Ollie

dlkpochtaws
Oct 29th, 2004, 08:03 AM
And since edited BOs are actively edited, they are not cached in second-level cache.

Can you please explain this? Why are you BO not cached in the second level cache? Given that you've said that they are very expensive to load this sound like a bad idea.

May be I shall cache those BOs, but I prefer caching only reference (read-only) data. May be it worths trying caching these BOs, since they are expensive to load, I'll think it over. Although authors of HiA claim: "Bad candidates for second-level caching are: Data that is updated often". Because ot that I don't chache them.



I can certainly say that the approach I suggested is not "the hard way" and is in fact "best practice". Especially when you are working with the detached object support. The following is from the Hibernate manual:


Yes I do agree that Hibernate's ability to automate handling versions column is very useful. But the "hard way" also has other upleasant feature - you fetch data which is possible inconsistent that user has seen.
And please note words in your quote "strategy that correctly handles modifications made to detached instances" - detached instance means some kind of server-side state in my scenario.

WBR, Dmitry