2881be5c3716bdbcc7cd597ef871207394a5e203
[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.Autowire;
42 import org.springframework.beans.factory.annotation.Configurable;
43 import org.springframework.stereotype.Component;
44
45 import eu.etaxonomy.cdm.api.application.ICdmApplicationConfiguration;
46 import eu.etaxonomy.cdm.api.service.ICommonService;
47 import eu.etaxonomy.cdm.model.common.CdmBase;
48
49 /**
50 * Convenience base class for lazy initialization handlers. Centralizes the basic plumbing of doing lazy
51 * initialization freeing subclasses to acts as essentially adapters to their intended entity mode and/or
52 * proxy generation strategy.
53 *
54 * @author Gavin King
55 */
56 @Component
57 @Configurable(dependencyCheck = true,autowire = Autowire.BY_TYPE)
58 public abstract class AbstractLazyInitializer implements LazyInitializer {
59 private static final Logger log = Logger.getLogger( AbstractLazyInitializer.class );
60
61 private String entityName;
62 private Serializable id;
63 private Object target;
64 private boolean initialized;
65 private boolean readOnly;
66 private boolean unwrap;
67 private transient SessionImplementor session;
68 private Boolean readOnlyBeforeAttachedToSession;
69
70 private String sessionFactoryUuid;
71 private boolean specjLazyLoad = false;
72
73 /**
74 * For serialization from the non-pojo initializers (HHH-3309)
75 */
76 protected AbstractLazyInitializer() {
77 }
78
79 /**
80 * Main constructor.
81 *
82 * @param entityName The name of the entity being proxied.
83 * @param id The identifier of the entity being proxied.
84 * @param session The session owning the proxy.
85 */
86 protected AbstractLazyInitializer(String entityName, Serializable id, SessionImplementor session) {
87 this.entityName = entityName;
88 this.id = id;
89 // initialize other fields depending on session state
90 if ( session == null ) {
91 unsetSession();
92 }
93 else {
94 setSession( session );
95 }
96 }
97
98 @Override
99 public final String getEntityName() {
100 return entityName;
101 }
102
103 @Override
104 public final Serializable getIdentifier() {
105 return id;
106 }
107
108 @Override
109 public final void setIdentifier(Serializable id) {
110 this.id = id;
111 }
112
113 @Override
114 public final boolean isUninitialized() {
115 return !initialized;
116 }
117
118 @Override
119 public final SessionImplementor getSession() {
120 return session;
121 }
122
123 @Override
124 public final void setSession(SessionImplementor s) throws HibernateException {
125 if ( s != session ) {
126 // check for s == null first, since it is least expensive
127 if ( s == null ) {
128 unsetSession();
129 }
130 else if ( isConnectedToSession() ) {
131 //TODO: perhaps this should be some other RuntimeException...
132 throw new HibernateException( "illegally attempted to associate a proxy with two open Sessions" );
133 }
134 else {
135 // s != null
136 session = s;
137 if ( readOnlyBeforeAttachedToSession == null ) {
138 // use the default read-only/modifiable setting
139 final EntityPersister persister = s.getFactory().getEntityPersister( entityName );
140 setReadOnly( s.getPersistenceContext().isDefaultReadOnly() || !persister.isMutable() );
141 }
142 else {
143 // use the read-only/modifiable setting indicated during deserialization
144 setReadOnly( readOnlyBeforeAttachedToSession.booleanValue() );
145 readOnlyBeforeAttachedToSession = null;
146 }
147 }
148 }
149 }
150
151 private static EntityKey generateEntityKeyOrNull(Serializable id, SessionImplementor s, String entityName) {
152 if ( id == null || s == null || entityName == null ) {
153 return null;
154 }
155 return s.generateEntityKey( id, s.getFactory().getEntityPersister( entityName ) );
156 }
157
158 @Override
159 public final void unsetSession() {
160 prepareForPossibleSpecialSpecjInitialization();
161 session = null;
162 readOnly = false;
163 readOnlyBeforeAttachedToSession = null;
164 }
165
166 @Override
167 public final void initialize() throws HibernateException {
168 // FIXME:Remoting is it true that session is null => we are in remoting mode
169 // if(session == null) {
170 // remoteInitialize();
171 // }
172 if ( !initialized ) {
173 if ( specjLazyLoad ) {
174 specialSpecjInitialization();
175 }
176 else if ( session == null ) {
177 throw new LazyInitializationException( "could not initialize proxy - no Session" );
178 }
179 else if ( !session.isOpen() ) {
180 throw new LazyInitializationException( "could not initialize proxy - the owning Session was closed" );
181 }
182 else if ( !session.isConnected() ) {
183 throw new LazyInitializationException( "could not initialize proxy - the owning Session is disconnected" );
184 }
185 else {
186 target = session.immediateLoad( entityName, id );
187 initialized = true;
188 checkTargetState();
189 }
190 }
191 else {
192 checkTargetState();
193 }
194 }
195
196 protected void specialSpecjInitialization() {
197 if ( session == null ) {
198 //we have a detached collection thats set to null, reattach
199 if ( sessionFactoryUuid == null ) {
200 throw new LazyInitializationException( "could not initialize proxy - no Session" );
201 }
202 try {
203 SessionFactoryImplementor sf = (SessionFactoryImplementor)
204 SessionFactoryRegistry.INSTANCE.getSessionFactory( sessionFactoryUuid );
205 SessionImplementor session = (SessionImplementor) sf.openSession();
206
207 // TODO: On the next major release, add an
208 // 'isJTA' or 'getTransactionFactory' method to Session.
209 boolean isJTA = session.getTransactionCoordinator()
210 .getTransactionContext().getTransactionEnvironment()
211 .getTransactionFactory()
212 .compatibleWithJtaSynchronization();
213
214 if ( !isJTA ) {
215 // Explicitly handle the transactions only if we're not in
216 // a JTA environment. A lazy loading temporary session can
217 // be created even if a current session and transaction are
218 // open (ex: session.clear() was used). We must prevent
219 // multiple transactions.
220 ( ( Session) session ).beginTransaction();
221 }
222
223 try {
224 target = session.immediateLoad( entityName, id );
225 }
226 finally {
227 // make sure the just opened temp session gets closed!
228 try {
229 if ( !isJTA ) {
230 ( ( Session) session ).getTransaction().commit();
231 }
232 ( (Session) session ).close();
233 }
234 catch (Exception e) {
235 log.warn( "Unable to close temporary session used to load lazy proxy associated to no session" );
236 }
237 }
238 initialized = true;
239 checkTargetState();
240 }
241 catch (Exception e) {
242 e.printStackTrace();
243 throw new LazyInitializationException( e.getMessage() );
244 }
245 }
246 else if ( session.isOpen() && session.isConnected() ) {
247 target = session.immediateLoad( entityName, id );
248 initialized = true;
249 checkTargetState();
250 }
251 else {
252 throw new LazyInitializationException( "could not initialize proxy - Session was closed or disced" );
253 }
254 }
255
256 protected void prepareForPossibleSpecialSpecjInitialization() {
257 if ( session != null ) {
258 specjLazyLoad = session.getFactory().getSettings().isInitializeLazyStateOutsideTransactionsEnabled();
259
260 if ( specjLazyLoad && sessionFactoryUuid == null ) {
261 try {
262 sessionFactoryUuid = (String) session.getFactory().getReference().get( "uuid" ).getContent();
263 }
264 catch (NamingException e) {
265 //not much we can do if this fails...
266 }
267 }
268 }
269 }
270
271 private void checkTargetState() {
272 if ( !unwrap ) {
273 if ( target == null ) {
274 getSession().getFactory().getEntityNotFoundDelegate().handleEntityNotFound( entityName, id );
275 }
276 }
277 }
278
279 /**
280 * Getter for property 'connectedToSession'.
281 *
282 * @return Value for property 'connectedToSession'.
283 */
284 protected final boolean isConnectedToSession() {
285 return getProxyOrNull() != null;
286 }
287
288 private Object getProxyOrNull() {
289 final EntityKey entityKey = generateEntityKeyOrNull( getIdentifier(), session, getEntityName() );
290 if ( entityKey != null && session != null && session.isOpen() ) {
291 return session.getPersistenceContext().getProxy( entityKey );
292 }
293 return null;
294 }
295
296 @Override
297 public final Object getImplementation() {
298 initialize();
299 return target;
300 }
301
302 @Override
303 public final void setImplementation(Object target) {
304 this.target = target;
305 initialized = true;
306 }
307
308 @Override
309 public final Object getImplementation(SessionImplementor s) throws HibernateException {
310 final EntityKey entityKey = generateEntityKeyOrNull( getIdentifier(), s, getEntityName() );
311 return (entityKey == null ? null : s.getPersistenceContext().getEntity( entityKey ));
312 }
313
314 /**
315 * Getter for property 'target'.
316 * <p/>
317 * Same as {@link #getImplementation()} except that this method will not force initialization.
318 *
319 * @return Value for property 'target'.
320 */
321 protected final Object getTarget() {
322 return target;
323 }
324
325 @Override
326 public final boolean isReadOnlySettingAvailable() {
327 return (session != null && !session.isClosed());
328 }
329
330 private void errorIfReadOnlySettingNotAvailable() {
331 if ( session == null ) {
332 throw new TransientObjectException(
333 "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."
334 );
335 }
336 if ( session.isClosed() ) {
337 throw new SessionException(
338 "Session is closed. The read-only/modifiable setting is only accessible when the proxy is associated with an open session."
339 );
340 }
341 }
342
343 @Override
344 public final boolean isReadOnly() {
345 errorIfReadOnlySettingNotAvailable();
346 return readOnly;
347 }
348
349 @Override
350 public final void setReadOnly(boolean readOnly) {
351 errorIfReadOnlySettingNotAvailable();
352 // only update if readOnly is different from current setting
353 if ( this.readOnly != readOnly ) {
354 final EntityPersister persister = session.getFactory().getEntityPersister( entityName );
355 if ( !persister.isMutable() && !readOnly ) {
356 throw new IllegalStateException( "cannot make proxies for immutable entities modifiable" );
357 }
358 this.readOnly = readOnly;
359 if ( initialized ) {
360 EntityKey key = generateEntityKeyOrNull( getIdentifier(), session, getEntityName() );
361 if ( key != null && session.getPersistenceContext().containsEntity( key ) ) {
362 session.getPersistenceContext().setReadOnly( target, readOnly );
363 }
364 }
365 }
366 }
367
368 /**
369 * Get the read-only/modifiable setting that should be put in affect when it is
370 * attached to a session.
371 * <p/>
372 * This method should only be called during serialization when read-only/modifiable setting
373 * is not available (i.e., isReadOnlySettingAvailable() == false)
374 *
375 * @return null, if the default setting should be used;
376 * true, for read-only;
377 * false, for modifiable
378 *
379 * @throws IllegalStateException if isReadOnlySettingAvailable() == true
380 */
381 protected final Boolean isReadOnlyBeforeAttachedToSession() {
382 if ( isReadOnlySettingAvailable() ) {
383 throw new IllegalStateException(
384 "Cannot call isReadOnlyBeforeAttachedToSession when isReadOnlySettingAvailable == true"
385 );
386 }
387 return readOnlyBeforeAttachedToSession;
388 }
389
390 /**
391 * Set the read-only/modifiable setting that should be put in affect when it is
392 * attached to a session.
393 * <p/>
394 * This method should only be called during deserialization, before associating
395 * the proxy with a session.
396 *
397 * @param readOnlyBeforeAttachedToSession, the read-only/modifiable setting to use when
398 * associated with a session; null indicates that the default should be used.
399 *
400 * @throws IllegalStateException if isReadOnlySettingAvailable() == true
401 */
402 /* package-private */
403 final void setReadOnlyBeforeAttachedToSession(Boolean readOnlyBeforeAttachedToSession) {
404 if ( isReadOnlySettingAvailable() ) {
405 throw new IllegalStateException(
406 "Cannot call setReadOnlyBeforeAttachedToSession when isReadOnlySettingAvailable == true"
407 );
408 }
409 this.readOnlyBeforeAttachedToSession = readOnlyBeforeAttachedToSession;
410 }
411
412 @Override
413 public boolean isUnwrap() {
414 return unwrap;
415 }
416
417 @Override
418 public void setUnwrap(boolean unwrap) {
419 this.unwrap = unwrap;
420 }
421
422 /** Below is section of code which makes remote service calls */
423
424 private static ICdmApplicationConfiguration configuration;
425
426 public static void setConfiguration(ICdmApplicationConfiguration conf) {
427 configuration = conf;
428 }
429
430
431 private void remoteInitialize() {
432
433 if(!initialized) {
434 int classid = ((Integer)getIdentifier()).intValue();
435 System.out.print("--> Remote Lazy Initializing" + getEntityName() + " with id " + classid);
436 Class clazz;
437 try {
438 clazz = (Class<? extends CdmBase>) Class.forName(getEntityName());
439 } catch (ClassNotFoundException e) {
440 throw new HibernateException("Class for " + getEntityName() + " not found", e);
441 }
442 if(configuration == null) {
443 throw new HibernateException("CdmApplicationRemoteConfiguration not initialized (null)");
444 }
445 ICommonService commonService = configuration.getCommonService();
446 if(commonService == null) {
447 throw new HibernateException("commonService not initialized (null)");
448 }
449
450 CdmBase cdmBase = CdmBase.deproxy(commonService.find(clazz,classid),clazz);
451 setImplementation(cdmBase);
452 System.out.println("....Done");
453 }
454 }
455
456
457 }