001    package org.apache.ojb.broker.ant;
002    
003    /* Copyright 2005 The Apache Software Foundation
004     *
005     * Licensed under the Apache License, Version 2.0 (the "License");
006     * you may not use this file except in compliance with the License.
007     * You may obtain a copy of the License at
008     *
009     *     http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    
018    import java.io.File;
019    import java.io.FileInputStream;
020    import java.util.ArrayList;
021    import java.util.Iterator;
022    import java.util.Properties;
023    
024    import org.apache.ddlutils.io.DatabaseIO;
025    import org.apache.ddlutils.model.Database;
026    import org.apache.ddlutils.task.DatabaseTaskBase;
027    import org.apache.ojb.broker.PBKey;
028    import org.apache.ojb.broker.metadata.DescriptorRepository;
029    import org.apache.ojb.broker.metadata.JdbcConnectionDescriptor;
030    import org.apache.ojb.broker.metadata.MetadataManager;
031    import org.apache.ojb.broker.metadata.RepositoryPersistor;
032    import org.apache.tools.ant.AntClassLoader;
033    import org.apache.tools.ant.BuildException;
034    import org.apache.tools.ant.DirectoryScanner;
035    import org.apache.tools.ant.Project;
036    import org.apache.tools.ant.types.FileSet;
037    
038    /**
039     * Task for inserting data XML that is defined in terms of the repository elements.
040     * 
041     * @author Thomas Dudziak
042     */
043    public class RepositoryDataTask extends DatabaseTaskBase
044    {
045        /** The sub tasks to execute. */
046        private ArrayList _commands = new ArrayList();
047        /** The alias of the jdbc connection to use (empty = default connection) */
048        private String _jcdAlias;
049        /** The properties file */
050        private File _ojbPropertiesFile;
051        /** The repository file */
052        private File _repositoryFile;
053        /** A single schema file to read. */
054        private File _singleSchemaFile = null;
055        /** The input files. */
056        private ArrayList _fileSets = new ArrayList();
057        /** Whether XML input files are validated against the internal or an external DTD. */
058        private boolean _useInternalDtd = true;
059    
060    
061        /**
062         * Sets the alias of the jdbc connection to use.
063         * 
064         * @param alias The alias of the connection
065         */
066        public void setJcdAlias(String alias)
067        {
068            _jcdAlias = alias;
069        }
070    
071        /**
072         * Returns the alias of the jdbc connection.
073         * 
074         * @return The alias
075         */
076        public String getJcdAlias()
077        {
078            return _jcdAlias;
079        }
080    
081        /**
082         * Returns the ojb properties file, per default 'OJB.properties' in the current directory.
083         * 
084         * @return The ojb properties file
085         */
086        public File getOjbPropertiesFile()
087        {
088            return _ojbPropertiesFile;
089        }
090        /**
091         * Sets the ojb properties file.
092         *
093         * @param ojbPropertiesFile The ojb properties file
094         */
095        public void setOjbPropertiesFile(File ojbPropertiesFile)
096        {
097            _ojbPropertiesFile = ojbPropertiesFile;
098        }
099    
100        /**
101         * Returns the repository file.
102         * 
103         * @return The repository file
104         */
105        public File getRepositoryFile()
106        {
107            return _repositoryFile;
108        }
109    
110        /**
111         * Sets the repository file (per default the one configured in the ojb properties file is used).
112         * 
113         * @param file The repository file
114         */
115        public void setRepositoryFile(File file)
116        {
117            _repositoryFile = file;
118        }
119    
120        /**
121         * Specifies whether XML schema files are validated against the internal or an external DTD.
122         *
123         * @param useInternalDtd <code>true</code> if input files are to be validated against the internal DTD
124         */
125        public void setUseInternalDtd(boolean useInternalDtd)
126        {
127            _useInternalDtd = useInternalDtd;
128        }
129    
130        /**
131         * Adds a fileset specifying the schema files.
132         * 
133         * @param fileset The additional input files
134         */
135        public void addConfiguredFileset(FileSet fileset)
136        {
137            _fileSets.add(fileset);
138        }
139    
140        /**
141         * Set the xml schema describing the application model.
142         *
143         * @param schemaFile The schema
144         */
145        public void setSchemaFile(File schemaFile)
146        {
147            _singleSchemaFile = schemaFile;
148        }
149    
150        /**
151         * Adds the "write dtd to file"-command.
152         * 
153         * @param command The command
154         */
155        public void addWriteDtdToFile(WriteDtdToFileCommand command)
156        {
157            _commands.add(command);
158        }
159    
160        /**
161         * Adds the "write data to database"-command.
162         * 
163         * @param command The command
164         */
165        public void addWriteDataToDatabase(WriteDataToDatabaseCommand command)
166        {
167            _commands.add(command);
168        }
169    
170        /**
171         * Adds the "write data sql to file"-command.
172         * 
173         * @param command The command
174         */
175        public void addWriteDataSqlToFile(WriteDataSqlToFileCommand command)
176        {
177            _commands.add(command);
178        }
179    
180        /**
181         * {@inheritDoc}
182         */
183        protected Database readModel()
184        {
185            DatabaseIO reader = new DatabaseIO();
186            Database   model  = null;
187    
188            reader.setUseInternalDtd(_useInternalDtd);
189            if ((_singleSchemaFile != null) && !_fileSets.isEmpty())
190            {
191                throw new BuildException("Please use either the schemafile attribute or the sub fileset element, but not both");
192            }
193            if (_singleSchemaFile != null)
194            {
195                model = readSingleSchemaFile(reader, _singleSchemaFile);
196            }
197            else
198            {
199                for (Iterator it = _fileSets.iterator(); it.hasNext();)
200                {
201                    FileSet          fileSet    = (FileSet)it.next();
202                    File             fileSetDir = fileSet.getDir(getProject());
203                    DirectoryScanner scanner    = fileSet.getDirectoryScanner(getProject());
204                    String[]         files      = scanner.getIncludedFiles();
205        
206                    for (int idx = 0; (files != null) && (idx < files.length); idx++)
207                    {
208                        Database curModel = readSingleSchemaFile(reader, new File(fileSetDir, files[idx]));
209        
210                        if (model == null)
211                        {
212                            model = curModel;
213                        }
214                        else if (curModel != null)
215                        {
216                            try
217                            {
218                                model.mergeWith(curModel);
219                            }
220                            catch (IllegalArgumentException ex)
221                            {
222                                throw new BuildException("Could not merge with schema from file "+files[idx]+": "+ex.getLocalizedMessage(), ex);
223                            }
224                        }
225                    }
226                }
227            }
228            return model;
229        }
230    
231        /**
232         * Reads a single schema file.
233         * 
234         * @param reader     The schema reader 
235         * @param schemaFile The schema file
236         * @return The model
237         */
238        private Database readSingleSchemaFile(DatabaseIO reader, File schemaFile)
239        {
240            Database model = null;
241    
242            if (!schemaFile.isFile())
243            {
244                log("Path "+schemaFile.getAbsolutePath()+" does not denote a schema file", Project.MSG_ERR);
245            }
246            else if (!schemaFile.canRead())
247            {
248                log("Could not read schema file "+schemaFile.getAbsolutePath(), Project.MSG_ERR);
249            }
250            else
251            {
252                try
253                {
254                    model = reader.read(schemaFile);
255                    log("Read schema file "+schemaFile.getAbsolutePath(), Project.MSG_INFO);
256                }
257                catch (Exception ex)
258                {
259                    throw new BuildException("Could not read schema file "+schemaFile.getAbsolutePath()+": "+ex.getLocalizedMessage(), ex);
260                }
261            }
262            return model;
263        }
264    
265        /**
266         * Initializes OJB for the purposes of this task.
267         * 
268         * @return The metadata manager used by OJB
269         */
270        private MetadataManager initOJB()
271        {
272            try
273            {
274                if (_ojbPropertiesFile == null)
275                {
276                    _ojbPropertiesFile = new File("OJB.properties");
277                    if (!_ojbPropertiesFile.exists())
278                    {
279                        throw new BuildException("Could not find OJB.properties, please specify it via the ojbpropertiesfile attribute");
280                    }
281                }
282                else
283                {
284                    if (!_ojbPropertiesFile.exists())
285                    {
286                        throw new BuildException("Could not load the specified OJB properties file "+_ojbPropertiesFile);
287                    }
288                    log("Using properties file "+_ojbPropertiesFile.getAbsolutePath(), Project.MSG_INFO);
289                    System.setProperty("OJB.properties", _ojbPropertiesFile.getAbsolutePath());
290                }
291    
292                MetadataManager     metadataManager = MetadataManager.getInstance();
293                RepositoryPersistor persistor       = new RepositoryPersistor();
294    
295                if (_repositoryFile != null)
296                {
297                    if (!_repositoryFile.exists())
298                    {
299                        throw new BuildException("Could not load the specified repository file "+_repositoryFile);
300                    }
301                    log("Loading repository file "+_repositoryFile.getAbsolutePath(), Project.MSG_INFO);
302    
303                    // this will load the info from the specified repository file
304                    // and merge it with the existing info (if it has been loaded)
305                    metadataManager.mergeConnectionRepository(persistor.readConnectionRepository(_repositoryFile.getAbsolutePath()));
306                    metadataManager.mergeDescriptorRepository(persistor.readDescriptorRepository(_repositoryFile.getAbsolutePath()));
307                }
308                else if (metadataManager.connectionRepository().getAllDescriptor().isEmpty() &&
309                         metadataManager.getGlobalRepository().getDescriptorTable().isEmpty())
310                {
311                    // Seems nothing was loaded, probably because we're not starting in the directory
312                    // that the properties file is in, and the repository file path is relative
313                    // So lets try to resolve this path and load the repository info manually
314                    Properties props = new Properties();
315    
316                    props.load(new FileInputStream(_ojbPropertiesFile));
317        
318                    String repositoryPath = props.getProperty("repositoryFile", "repository.xml");
319                    File   repositoryFile = new File(repositoryPath);
320        
321                    if (!repositoryFile.exists())
322                    {
323                        repositoryFile = new File(_ojbPropertiesFile.getParentFile(), repositoryPath);
324                    }
325                    metadataManager.mergeConnectionRepository(persistor.readConnectionRepository(repositoryFile.getAbsolutePath()));
326                    metadataManager.mergeDescriptorRepository(persistor.readDescriptorRepository(repositoryFile.getAbsolutePath()));
327                }
328                // we might have to determine the default pb key ourselves
329                if (metadataManager.getDefaultPBKey() == null)
330                {
331                    for (Iterator it = metadataManager.connectionRepository().getAllDescriptor().iterator(); it.hasNext();)
332                    {
333                        JdbcConnectionDescriptor descriptor = (JdbcConnectionDescriptor)it.next();
334    
335                        if (descriptor.isDefaultConnection())
336                        {
337                            metadataManager.setDefaultPBKey(new PBKey(descriptor.getJcdAlias(), descriptor.getUserName(), descriptor.getPassWord()));
338                            break;
339                        }
340                    }
341                }
342                return metadataManager;
343            }
344            catch (Exception ex)
345            {
346                if (ex instanceof BuildException)
347                {
348                    throw (BuildException)ex;
349                }
350                else
351                {
352                    throw new BuildException(ex);
353                }
354            }
355        }
356    
357        /**
358         * {@inheritDoc}
359         */
360        public void execute() throws BuildException
361        {
362            if (_commands.isEmpty())
363            {
364                log("No sub tasks specified, so there is nothing to do.", Project.MSG_INFO);
365                return;
366            }
367    
368            ClassLoader    sysClassLoader = Thread.currentThread().getContextClassLoader();
369            AntClassLoader newClassLoader = new AntClassLoader(getClass().getClassLoader(), true);
370    
371            // we're changing the thread classloader so that we can access resources
372            // from the classpath used to load this task's class
373            Thread.currentThread().setContextClassLoader(newClassLoader);
374            
375            try
376            {
377                MetadataManager      manager  = initOJB();
378                Database             dbModel  = readModel();
379                DescriptorRepository objModel = manager.getGlobalRepository();
380    
381                if (dbModel == null)
382                {
383                    throw new BuildException("No database model specified");
384                }
385                for (Iterator it = _commands.iterator(); it.hasNext();)
386                {
387                    Command cmd = (Command)it.next();
388    
389                    cmd.setPlatform(getPlatform());
390                    cmd.execute(this, dbModel, objModel);
391                }
392            }
393            finally
394            {
395                // rollback of our classloader change
396                Thread.currentThread().setContextClassLoader(sysClassLoader);
397            }
398        }
399    }