001    /*
002      Copyright (C) 2001-2003 Lionel Seinturier <Lionel.Seinturier@lip6.fr>
003    
004      This program is free software; you can redistribute it and/or modify
005      it under the terms of the GNU Lesser General Public License as
006      published by the Free Software Foundation; either version 2 of the
007      License, or (at your option) any later version.
008    
009      This program is distributed in the hope that it will be useful,
010      but WITHOUT ANY WARRANTY; without even the implied warranty of
011      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
012      GNU Lesser General Public License for more details.
013    
014      You should have received a copy of the GNU Lesser General Public License
015      along with this program; if not, write to the Free Software
016      Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
017    
018    package org.objectweb.jac.aspects.distrans.persistence;
019    
020    import org.objectweb.jac.aspects.distrans.JOTMHelper;
021    import org.objectweb.jac.core.AspectComponent;
022    
023    import java.sql.SQLException;
024    import java.util.HashMap;
025    import java.util.Map;
026    
027    import javax.transaction.TransactionManager;
028    
029    import org.enhydra.jdbc.standard.StandardXADataSource;
030    
031    /**
032     * Transaction-enabled persistence storage.
033     * An instance this AC is mandatory with DisTransAC.
034     * This class delegates most of the work (apart from data sources
035     * registering) to a technical implementation of the persistence API
036     * PersistenceItf.
037     * Current implementations of this API: SimpleDbPersistence.
038     *  
039     * Relies on jac.aspects.distrans.JOTMHelper
040     * to retrieve the JOTM instance used by JAC.
041     * 
042     * @author Lionel Seinturier <Lionel.Seinturier@lip6.fr>
043     * @version 1.0
044     */
045    public class PersistenceAC extends AspectComponent {
046        
047        public PersistenceAC() {}
048    
049        /** A map storing data sources. */
050        private Map sources = new HashMap();
051        
052        /**
053         * Define a data source name that will be later on used by the remaining
054         * configuration methods of this AC.
055         * 
056         * @param sourceName  the data source name
057         * @param driver    the JDBC driver name (eg org.postgresql.Driver)
058         * @param url       the JDBC URL (eg jdbc:postgresql://localhost/test)
059         * @param user      the login to use
060         * @param password  the password to use
061         */
062        public void defineDataSource(
063            String sourceName,
064            String driver, String url, String user, String password ) {
065                
066            StandardXADataSource xads = new StandardXADataSource();
067            try {
068                            xads.setDriverName(driver);
069                    } catch (SQLException e) {
070                            e.printStackTrace();
071                            System.exit(1);
072                    }
073            xads.setUrl(url);
074            xads.setUser(user);
075            xads.setPassword(password);
076            TransactionManager tm = JOTMHelper.get().getTransactionManager();
077            xads.setTransactionManager(tm);
078            
079            sources.put( sourceName, xads );
080        }
081        
082        
083        /**
084         * The instance implementing the technical API for persistence.
085         * SimpleDbPersistence implements it.
086         */
087        private PersistenceItf storage;
088        
089        public void setStorageType( String classname ) {
090            try {
091                            _setStorageType(classname);
092                return;
093                    } catch (ClassNotFoundException e) {
094                            e.printStackTrace();
095                    } catch (InstantiationException e) {
096                            e.printStackTrace();
097                    } catch (IllegalAccessException e) {
098                            e.printStackTrace();
099                    }
100            System.exit(1);
101        }
102        
103        private void _setStorageType( String classname )
104            throws ClassNotFoundException, InstantiationException, IllegalAccessException {
105    
106            Class storageClass = Class.forName(classname);
107            if (!PersistenceItf.class.isAssignableFrom(storageClass))
108                throw new RuntimeException(classname+" must implement jac.aspects.distrans.persistence.PersistenceItf");
109            
110            storage = (PersistenceItf) storageClass.newInstance();
111        }
112        
113        
114        /**
115         * Initialize the persistence storage.
116         * If the storage already exists, do not reinitialize it. 
117         * 
118         * @param className   the class name for which we want to create a storage
119         * @param sourceName  the data source name
120         */
121        public void initStorageIfNeeded( String className, String sourceName ) {
122    
123            if ( storage == null )
124                throw new RuntimeException("setStorageType() must be called first");
125            
126            StandardXADataSource ds = (StandardXADataSource) sources.get(sourceName);
127            if ( ds == null )
128                throw new RuntimeException("Unknown data source "+sourceName);
129    
130            storage.initStorageIfNeeded(className,ds);    
131        }
132        
133        /**
134         * Initialize the persistence storage.
135         * If the storage already exists, reinitialize it. 
136         * 
137         * @param className   the class name for which we want to create a storage
138         * @param sourceName  the data source name
139         */
140        public void initStorage( String className, String sourceName ) {
141    
142            if ( storage == null )
143                throw new RuntimeException("setStorageType() must be called first");
144            
145            StandardXADataSource ds = (StandardXADataSource) sources.get(sourceName);
146            if ( ds == null )
147                throw new RuntimeException("Unknown data source "+sourceName);
148        
149            storage.initStorage(className,ds);    
150        }
151    
152        /**
153         * All objects matching the objectNameExpression
154         * are made persistent to a SQL database represented by the data source.
155         * These objects are ressources that will potentially be used
156         * later on in transactions.
157         * 
158         * Even if the objectNameExpression can be any regular expression,
159         * it is assumed to designate instances storable in existing
160         * storages (eventually call initStorageIfNeeded before).
161         * 
162         * @param objectNameExpression  the object name expression
163         * @param sourceName            the source name
164         */
165        public void registerPersistentRessource(
166            String objectNameExpression, String sourceName ) {
167                   
168            if ( storage == null )
169                throw new RuntimeException("setStorageType() must be called first");
170            
171            StandardXADataSource ds = (StandardXADataSource) sources.get(sourceName);
172            if ( ds == null )
173                throw new RuntimeException("Unknown data source "+sourceName);
174            
175            /**
176             * Wrap methods that perform write operations.
177             * Modifier methods in all the classes
178             * for all the objects matching objectNameExpression.
179             */
180            WriteWrapper pw = new WriteWrapper(this,storage,ds);         
181            pointcut(
182                objectNameExpression, "ALL", "MODIFIERS",
183                pw, null
184            );
185            
186            /**
187             * Wrap methods that perform read operations.
188             * Accessor and modifier methods in all the classes
189             * for all the objects matching objectNameExpression.
190             * Note: modifiers read fields before modifying them.
191             */
192            ReadWrapper rw = new ReadWrapper(this,storage,ds);      
193            pointcut(
194                objectNameExpression, "ALL", "MODIFIERS || ACCESSORS",
195                rw, null
196            );
197        }
198        
199    }