erging from remoting-1.0 branch
[taxeditor.git] / eu.etaxonomy.taxeditor.cdmlib / src / main / java / org / hibernate / proxy / AbstractLazyInitializer.java
1 /*
2 * Hibernate, Relational Persistence for Idiomatic Java
3 *
4 * Copyright (c) 2008-2011, Red Hat Inc. or third-party contributors as
5 * indicated by the @author tags or express copyright attribution
6 * statements applied by the authors. All third-party contributions are
7 * distributed under license by Red Hat Inc.
8 *
9 * This copyrighted material is made available to anyone wishing to use, modify,
10 * copy, or redistribute it subject to the terms and conditions of the GNU
11 * Lesser General Public License, as published by the Free Software Foundation.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
16 * for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public License
19 * along with this distribution; if not, write to:
20 * Free Software Foundation, Inc.
21 * 51 Franklin Street, Fifth Floor
22 * Boston, MA 02110-1301 USA
23 */
24 package org.hibernate.proxy;
25
26 import java.io.Serializable;
27
28 import javax.naming.NamingException;
29
30 import org.hibernate.HibernateException;
31 import org.hibernate.LazyInitializationException;
32 import org.hibernate.Session;
33 import org.hibernate.SessionException;
34 import org.hibernate.TransientObjectException;
35 import org.hibernate.engine.spi.EntityKey;
36 import org.hibernate.engine.spi.SessionFactoryImplementor;
37 import org.hibernate.engine.spi.SessionImplementor;
38 import org.hibernate.internal.SessionFactoryRegistry;
39 import org.hibernate.persister.entity.EntityPersister;
40 import org.jboss.logging.Logger;
41 import org.springframework.beans.factory.annotation.Configurable;
42
43 /**
44 * Convenience base class for lazy initialization handlers. Centralizes the basic plumbing of doing lazy
45 * initialization freeing subclasses to acts as essentially adapters to their intended entity mode and/or
46 * proxy generation strategy.
47 *
48 * @author Gavin King
49 */
50 @Configurable(dependencyCheck = true)
51 public abstract class AbstractLazyInitializer implements LazyInitializer {
52 private static final Logger log = Logger.getLogger( AbstractLazyInitializer.class );
53
54 private String entityName;
55 private Serializable id;
56 private Object target;
57 private boolean initialized;
58 private boolean readOnly;
59 private boolean unwrap;
60 private transient SessionImplementor session;
61 private Boolean readOnlyBeforeAttachedToSession;
62
63 private String sessionFactoryUuid;
64 private boolean specjLazyLoad = false;
65
66 /**
67 * For serialization from the non-pojo initializers (HHH-3309)
68 */
69 protected AbstractLazyInitializer() {
70 }
71
72 /**
73 * Main constructor.
74 *
75 * @param entityName The name of the entity being proxied.
76 * @param id The identifier of the entity being proxied.
77 * @param session The session owning the proxy.
78 */
79 protected AbstractLazyInitializer(String entityName, Serializable id, SessionImplementor session) {
80 this.entityName = entityName;
81 this.id = id;
82 // initialize other fields depending on session state
83 if ( session == null ) {
84 unsetSession();
85 }
86 else {
87 setSession( session );
88 }
89 }
90
91 @Override
92 public final String getEntityName() {
93 return entityName;
94 }
95
96 @Override
97 public final Serializable getIdentifier() {
98 return id;
99 }
100
101 @Override
102 public final void setIdentifier(Serializable id) {
103 this.id = id;
104 }
105
106 @Override
107 public final boolean isUninitialized() {
108 return !initialized;
109 }
110
111 @Override
112 public final SessionImplementor getSession() {
113 return session;
114 }
115
116 @Override
117 public final void setSession(SessionImplementor s) throws HibernateException {
118 if ( s != session ) {
119 // check for s == null first, since it is least expensive
120 if ( s == null ) {
121 unsetSession();
122 }
123 else if ( isConnectedToSession() ) {
124 //TODO: perhaps this should be some other RuntimeException...
125 throw new HibernateException( "illegally attempted to associate a proxy with two open Sessions" );
126 }
127 else {
128 // s != null
129 session = s;
130 if ( readOnlyBeforeAttachedToSession == null ) {
131 // use the default read-only/modifiable setting
132 final EntityPersister persister = s.getFactory().getEntityPersister( entityName );
133 setReadOnly( s.getPersistenceContext().isDefaultReadOnly() || !persister.isMutable() );
134 }
135 else {
136 // use the read-only/modifiable setting indicated during deserialization
137 setReadOnly( readOnlyBeforeAttachedToSession.booleanValue() );
138 readOnlyBeforeAttachedToSession = null;
139 }
140 }
141 }
142 }
143
144 private static EntityKey generateEntityKeyOrNull(Serializable id, SessionImplementor s, String entityName) {
145 if ( id == null || s == null || entityName == null ) {
146 return null;
147 }
148 return s.generateEntityKey( id, s.getFactory().getEntityPersister( entityName ) );
149 }
150
151 @Override
152 public final void unsetSession() {
153 prepareForPossibleSpecialSpecjInitialization();
154 session = null;
155 readOnly = false;
156 readOnlyBeforeAttachedToSession = null;
157 }
158
159 @Override
160 public final void initialize() throws HibernateException {
161 if ( !initialized ) {
162 if ( specjLazyLoad ) {
163 specialSpecjInitialization();
164 }
165 else if ( session == null ) {
166 throw new LazyInitializationException( "could not initialize proxy - no Session" );
167 }
168 else if ( !session.isOpen() ) {
169 throw new LazyInitializationException( "could not initialize proxy - the owning Session was closed" );
170 }
171 else if ( !session.isConnected() ) {
172 throw new LazyInitializationException( "could not initialize proxy - the owning Session is disconnected" );
173 }
174 else {
175 target = session.immediateLoad( entityName, id );
176 initialized = true;
177 checkTargetState();
178 }
179 }
180 else {
181 checkTargetState();
182 }
183 }
184
185 protected void specialSpecjInitialization() {
186 if ( session == null ) {
187 //we have a detached collection thats set to null, reattach
188 if ( sessionFactoryUuid == null ) {
189 throw new LazyInitializationException( "could not initialize proxy - no Session" );
190 }
191 try {
192 SessionFactoryImplementor sf = (SessionFactoryImplementor)
193 SessionFactoryRegistry.INSTANCE.getSessionFactory( sessionFactoryUuid );
194 SessionImplementor session = (SessionImplementor) sf.openSession();
195
196 // TODO: On the next major release, add an
197 // 'isJTA' or 'getTransactionFactory' method to Session.
198 boolean isJTA = session.getTransactionCoordinator()
199 .getTransactionContext().getTransactionEnvironment()
200 .getTransactionFactory()
201 .compatibleWithJtaSynchronization();
202
203 if ( !isJTA ) {
204 // Explicitly handle the transactions only if we're not in
205 // a JTA environment. A lazy loading temporary session can
206 // be created even if a current session and transaction are
207 // open (ex: session.clear() was used). We must prevent
208 // multiple transactions.
209 ( ( Session) session ).beginTransaction();
210 }
211
212 try {
213 target = session.immediateLoad( entityName, id );
214 }
215 finally {
216 // make sure the just opened temp session gets closed!
217 try {
218 if ( !isJTA ) {
219 ( ( Session) session ).getTransaction().commit();
220 }
221 ( (Session) session ).close();
222 }
223 catch (Exception e) {
224 log.warn( "Unable to close temporary session used to load lazy proxy associated to no session" );
225 }
226 }
227 initialized = true;
228 checkTargetState();
229 }
230 catch (Exception e) {
231 e.printStackTrace();
232 throw new LazyInitializationException( e.getMessage() );
233 }
234 }
235 else if ( session.isOpen() && session.isConnected() ) {
236 target = session.immediateLoad( entityName, id );
237 initialized = true;
238 checkTargetState();
239 }
240 else {
241 throw new LazyInitializationException( "could not initialize proxy - Session was closed or disced" );
242 }
243 }
244
245 protected void prepareForPossibleSpecialSpecjInitialization() {
246 if ( session != null ) {
247 specjLazyLoad = session.getFactory().getSettings().isInitializeLazyStateOutsideTransactionsEnabled();
248
249 if ( specjLazyLoad && sessionFactoryUuid == null ) {
250 try {
251 sessionFactoryUuid = (String) session.getFactory().getReference().get( "uuid" ).getContent();
252 }
253 catch (NamingException e) {
254 //not much we can do if this fails...
255 }
256 }
257 }
258 }
259
260 private void checkTargetState() {
261 if ( !unwrap ) {
262 if ( target == null ) {
263 getSession().getFactory().getEntityNotFoundDelegate().handleEntityNotFound( entityName, id );
264 }
265 }
266 }
267
268 /**
269 * Getter for property 'connectedToSession'.
270 *
271 * @return Value for property 'connectedToSession'.
272 */
273 protected final boolean isConnectedToSession() {
274 return getProxyOrNull() != null;
275 }
276
277 private Object getProxyOrNull() {
278 final EntityKey entityKey = generateEntityKeyOrNull( getIdentifier(), session, getEntityName() );
279 if ( entityKey != null && session != null && session.isOpen() ) {
280 return session.getPersistenceContext().getProxy( entityKey );
281 }
282 return null;
283 }
284
285 @Override
286 public final Object getImplementation() {
287 initialize();
288 return target;
289 }
290
291 @Override
292 public final void setImplementation(Object target) {
293 this.target = target;
294 initialized = true;
295 }
296
297 @Override
298 public final Object getImplementation(SessionImplementor s) throws HibernateException {
299 final EntityKey entityKey = generateEntityKeyOrNull( getIdentifier(), s, getEntityName() );
300 return (entityKey == null ? null : s.getPersistenceContext().getEntity( entityKey ));
301 }
302
303 /**
304 * Getter for property 'target'.
305 * <p/>
306 * Same as {@link #getImplementation()} except that this method will not force initialization.
307 *
308 * @return Value for property 'target'.
309 */
310 protected final Object getTarget() {
311 return target;
312 }
313
314 @Override
315 public final boolean isReadOnlySettingAvailable() {
316 return (session != null && !session.isClosed());
317 }
318
319 private void errorIfReadOnlySettingNotAvailable() {
320 if ( session == null ) {
321 throw new TransientObjectException(
322 "Proxy is detached (i.e, session is null). The read-only/modifiable setting is only accessible when the proxy is associated with an open session."
323 );
324 }
325 if ( session.isClosed() ) {
326 throw new SessionException(
327 "Session is closed. The read-only/modifiable setting is only accessible when the proxy is associated with an open session."
328 );
329 }
330 }
331
332 @Override
333 public final boolean isReadOnly() {
334 errorIfReadOnlySettingNotAvailable();
335 return readOnly;
336 }
337
338 @Override
339 public final void setReadOnly(boolean readOnly) {
340 errorIfReadOnlySettingNotAvailable();
341 // only update if readOnly is different from current setting
342 if ( this.readOnly != readOnly ) {
343 final EntityPersister persister = session.getFactory().getEntityPersister( entityName );
344 if ( !persister.isMutable() && !readOnly ) {
345 throw new IllegalStateException( "cannot make proxies for immutable entities modifiable" );
346 }
347 this.readOnly = readOnly;
348 if ( initialized ) {
349 EntityKey key = generateEntityKeyOrNull( getIdentifier(), session, getEntityName() );
350 if ( key != null && session.getPersistenceContext().containsEntity( key ) ) {
351 session.getPersistenceContext().setReadOnly( target, readOnly );
352 }
353 }
354 }
355 }
356
357 /**
358 * Get the read-only/modifiable setting that should be put in affect when it is
359 * attached to a session.
360 * <p/>
361 * This method should only be called during serialization when read-only/modifiable setting
362 * is not available (i.e., isReadOnlySettingAvailable() == false)
363 *
364 * @return null, if the default setting should be used;
365 * true, for read-only;
366 * false, for modifiable
367 *
368 * @throws IllegalStateException if isReadOnlySettingAvailable() == true
369 */
370 protected final Boolean isReadOnlyBeforeAttachedToSession() {
371 if ( isReadOnlySettingAvailable() ) {
372 throw new IllegalStateException(
373 "Cannot call isReadOnlyBeforeAttachedToSession when isReadOnlySettingAvailable == true"
374 );
375 }
376 return readOnlyBeforeAttachedToSession;
377 }
378
379 /**
380 * Set the read-only/modifiable setting that should be put in affect when it is
381 * attached to a session.
382 * <p/>
383 * This method should only be called during deserialization, before associating
384 * the proxy with a session.
385 *
386 * @param readOnlyBeforeAttachedToSession, the read-only/modifiable setting to use when
387 * associated with a session; null indicates that the default should be used.
388 *
389 * @throws IllegalStateException if isReadOnlySettingAvailable() == true
390 */
391 /* package-private */
392 final void setReadOnlyBeforeAttachedToSession(Boolean readOnlyBeforeAttachedToSession) {
393 if ( isReadOnlySettingAvailable() ) {
394 throw new IllegalStateException(
395 "Cannot call setReadOnlyBeforeAttachedToSession when isReadOnlySettingAvailable == true"
396 );
397 }
398 this.readOnlyBeforeAttachedToSession = readOnlyBeforeAttachedToSession;
399 }
400
401 @Override
402 public boolean isUnwrap() {
403 return unwrap;
404 }
405
406 @Override
407 public void setUnwrap(boolean unwrap) {
408 this.unwrap = unwrap;
409 }
410 }