001/*
002  GRANITE DATA SERVICES
003  Copyright (C) 2011 GRANITE DATA SERVICES S.A.S.
004
005  This file is part of Granite Data Services.
006
007  Granite Data Services is free software; you can redistribute it and/or modify
008  it under the terms of the GNU Library General Public License as published by
009  the Free Software Foundation; either version 2 of the License, or (at your
010  option) any later version.
011
012  Granite Data Services is distributed in the hope that it will be useful, but
013  WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
014  FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License
015  for more details.
016
017  You should have received a copy of the GNU Library General Public License
018  along with this library; if not, see <http://www.gnu.org/licenses/>.
019*/
020
021package org.granite.tide.seam.lazy;
022
023import static org.jboss.seam.annotations.Install.FRAMEWORK;
024
025import java.io.Serializable;
026import java.util.HashSet;
027import java.util.Set;
028
029import javax.persistence.EntityManager;
030
031import org.granite.tide.TidePersistenceManager;
032import org.granite.tide.data.DataMergeContext;
033import org.jboss.seam.Component;
034import org.jboss.seam.ScopeType;
035import org.jboss.seam.annotations.Install;
036import org.jboss.seam.annotations.Logger;
037import org.jboss.seam.annotations.Name;
038import org.jboss.seam.annotations.Scope;
039import org.jboss.seam.annotations.Transactional;
040import org.jboss.seam.annotations.intercept.BypassInterceptors;
041import org.jboss.seam.core.Expressions;
042import org.jboss.seam.log.Log;
043
044/**
045 * Initializes a request for a passed in entity and a lazy property.
046 
047 * @author CIngram,VDanda
048 */
049@Name("org.granite.tide.seam.seamInitializer")
050@Scope(ScopeType.CONVERSATION)
051@Install(precedence=FRAMEWORK)
052@BypassInterceptors
053public class SeamInitializer implements Serializable {
054
055    private static final long serialVersionUID = 1L;
056        
057    @Logger Log log;
058    
059    private transient TidePersistenceManager pm = null;
060    
061    private Set<Object> loadedEntities = new HashSet<Object>();
062    
063    
064    /**
065     * Initiliazes the property for the passed in entity. It is attached to an associated context
066     * and then the property is accessed.
067     * @return Returns result from initializing the property.   
068     */
069        @Transactional
070    public Object lazyInitialize(Object entity, String[] propertyNames) {
071                boolean removeAfterCall = false;
072                
073                restoreLoadedEntities();
074                
075                try {
076                    if (entity instanceof String) {
077                String expression = "${" + entity + "}";
078                Expressions.ValueExpression<Object> valueExpr = Expressions.instance().createValueExpression(expression, Object.class);
079                entity = valueExpr.getValue();
080                    }
081                    
082                        if (pm == null) {
083                                removeAfterCall = true;
084                            pm = tryToDetermineInitiailzer();
085                                if (pm == null)
086                                    throw new RuntimeException("TideInitializer is null, Entities with Lazy relationships have to be retrieved in a conversation, or the EntityManager name must be entityManager");
087                        }       
088                        
089                        Object initializedEntity = pm.attachEntity(entity, propertyNames);
090
091                        saveLoadedEntities();
092                
093                        return initializedEntity;
094                } finally {
095                        if (removeAfterCall)
096                                Component.forName("org.granite.tide.seam.seamInitializer").destroy(this);
097                }
098        }
099        
100        
101        /**
102         * Try to determine what type of persistence the application is using. 
103         * If the EntityManager is stored under entityManager or if the Hibernate session is 
104         * stored under session. Then the context will be found and used. This is only called if a 
105         * ITidePersistenceManager is not found, probably because the query was not run in a conversation.
106         * @return The appropriate manager for the persistence context being used, if it can be determined
107         * otherwise a null is returned. 
108         */  
109        protected TidePersistenceManager tryToDetermineInitiailzer() {
110                EntityManager em = findEntityManager();
111                if (em != null) 
112                        return TidePersistenceFactory.createTidePersistence(null, em);
113                
114                return null;
115        }
116        
117        /**
118         * Try to find the entityManager if possible. Assume that the entityManager is stored under
119         * entityManager.
120         * @return The Current Entity manager
121         */
122        protected EntityManager findEntityManager() {
123                return (EntityManager) Component.getInstance("entityManager");
124        }
125        
126        /**
127         * @return A instance of this component for the conversation.
128         */
129    public static SeamInitializer instance() { 
130        return (SeamInitializer)Component.getInstance(SeamInitializer.class);
131    }
132        
133    public void setTidePersistenceManager(TidePersistenceManager pm) {
134        this.pm = pm;
135    }
136    
137    public TidePersistenceManager getTidePersistenceManager() {
138        return this.pm;
139    }
140    
141    public void restoreLoadedEntities() {
142        DataMergeContext.restoreLoadedEntities(loadedEntities);
143    }
144    
145    public void saveLoadedEntities() {
146        for (Object entity : DataMergeContext.getLoadedEntities())
147                loadedEntities.add(entity);
148    }
149}
150