001 /*******************************************************************************
002 * Copyright (c) PicoContainer Organization. All rights reserved.
003 * ---------------------------------------------------------------------------
004 * The software in this package is published under the terms of the BSD style
005 * license a copy of which has been included with this distribution in the
006 * license.html file.
007 ******************************************************************************/
008
009 package org.picocontainer.persistence.hibernate;
010
011 import java.sql.Connection;
012
013 import org.hibernate.HibernateException;
014 import org.hibernate.Interceptor;
015 import org.hibernate.Session;
016 import org.hibernate.SessionFactory;
017 import org.picocontainer.Disposable;
018 import org.picocontainer.Startable;
019
020 /**
021 * <p>
022 * Session implementation which allows request scoping while supporting
023 * lifecycle events. If you register this component within either a request
024 * PicoContainer or a request-scoped storage object, then this component will
025 * close sessions whenever stop() is called by the container, and is reusable
026 * until dispose() is called.
027 * </p>
028 * <p>
029 * This allows for the "One Session Per Request" pattern often used in
030 * Hibernate.
031 * <p>
032 *
033 * @author Jose Peleteiro
034 * @author Michael Rimov
035 * @author Mauro Talevi
036 */
037 @SuppressWarnings("serial")
038 public final class ScopedSession extends AbstractSessionDecorator implements Startable, Disposable {
039
040 /**
041 * Session factory that creates the delegate sessions.
042 */
043 private final SessionFactory factory;
044
045 /**
046 * Current delegate session, lazily created.
047 */
048 private Session session = null;
049
050 /**
051 * Session-specific interceptor.
052 */
053 private Interceptor interceptor = null;
054
055 /**
056 * Flag indicating object should not be used again.
057 */
058 private boolean disposed = false;
059
060 /**
061 * Creates a ScopedSession with factory and <code>null</code> interceptor
062 *
063 * @param factory session factory to create the session
064 */
065 public ScopedSession(final SessionFactory factory) {
066 this.factory = factory;
067 }
068
069 /**
070 * Creates a ScopedSession with factory and interceptor
071 *
072 * @param factory session factory to create the session
073 * @param interceptor interceptor to use with created session
074 */
075 public ScopedSession(final SessionFactory factory, final Interceptor interceptor) {
076 this(factory);
077 setInterceptor(interceptor);
078 }
079
080 /** {@inheritDoc} */
081 @Override
082 public SessionFactory getSessionFactory() {
083 return factory;
084 }
085
086 /**
087 * Obtain hibernate session in lazy way.
088 */
089 @Override
090 public Session getDelegate() {
091 if (disposed) {
092 throw new IllegalStateException("Component has already been disposed by parent container.");
093 }
094
095 if (session == null) {
096 try {
097 session = interceptor == null ? factory.openSession() : factory.openSession(interceptor);
098 } catch (RuntimeException ex) {
099 throw handleException(ex);
100 }
101 }
102
103 return session;
104 }
105
106 /**
107 * {@inheritDoc}
108 * <p>
109 * Because this implementation decorates a delegate session, it removes the
110 * delegate session, but it does allow re-referencing once close() has been
111 * called. It simply grabs a new Hibernate session.
112 * </p>
113 */
114 @Override
115 public Connection close() {
116 try {
117 return getDelegate().close();
118 } catch (HibernateException ex) {
119 session = null;
120 throw handleException(ex);
121 } finally {
122 session = null;
123 }
124 }
125
126 @Override
127 public void invalidateDelegate() {
128 if (session != null) {
129 try {
130 session.clear();
131 session.close();
132 } catch (HibernateException ex) {
133 session = null;
134 throw handleException(ex);
135 } finally {
136 session = null;
137 }
138 }
139 }
140
141 /**
142 * Returns the current interceptor.
143 *
144 * @return The Interceptor
145 */
146 public Interceptor getInterceptor() {
147 return interceptor;
148 }
149
150 /**
151 * Sets a new hibernate session interceptor. This is only applicable if
152 * there is no current session. If this session object has been used, then
153 * please call close() first.
154 *
155 * @param interceptor the Interceptor to apply to this session.
156 * @throws IllegalStateException if this session has already been utilized
157 * after creation.
158 */
159 public void setInterceptor(final Interceptor interceptor) throws IllegalStateException {
160 if (session != null) {
161 throw new IllegalStateException("Cannot apply interceptor after session has been utilized");
162 }
163
164 this.interceptor = interceptor;
165 }
166
167 /**
168 * Add some insurance against potential memory leaks. Make sure that Session
169 * is closed.
170 * <p>
171 * {@inheritDoc}
172 * </p>
173 */
174 @Override
175 protected void finalize() throws Throwable {
176 try {
177 if (session != null) {
178 session.close();
179 }
180 } finally {
181 super.finalize();
182 }
183 }
184
185 /** {@inheritDoc} * */
186 @Override
187 public int hashCode() {
188 if (session == null) {
189 return 13;
190 }
191
192 return session.hashCode();
193
194 }
195
196 /** {@inheritDoc} * */
197 @Override
198 public String toString() {
199 return ScopedSession.class.getName() + " using current Session : " + session;
200 }
201
202 /**
203 * {@inheritDoc}
204 * <p>
205 * Currently does nothing. Session is lazily created .
206 * </p>
207 */
208 public void start() {
209 // currently does nothing. Session is lazily created
210 }
211
212 /**
213 * {@inheritDoc}
214 * <p>
215 * Closes and invalidates any sessions that are still open.
216 * </p>
217 */
218 public void stop() {
219 this.close();
220
221 }
222
223 /**
224 * {@inheritDoc}
225 * <p>
226 * Prevents any further utilization once called.
227 * </p>
228 */
229 public void dispose() {
230 if (this.session != null) {
231 close();
232 }
233
234 disposed = true;
235 }
236
237 }