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