001    /*
002     * ----------------------------------------------------------------------
003     * Copyright (C) 2009 Enrique Lara (k957@68k.org)
004     *
005     * TinLizard is free software; you can redistribute it and/or
006     * modify it under the terms of the GNU Lesser General Public License
007     * as published by the Free Software Foundation; either version 3.0
008     * of the License, or (at your option) any later version.
009     *
010     * TinLizard is distributed in the hope that it will be useful,
011     * but WITHOUT ANY WARRANTY; without even the implied warranty of
012     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
013     * GNU Lesser General Public License for more details.
014     *
015     * You should have received a copy of the GNU Lesser General Public License
016     * along with TinLizard. If not, see http://www.gnu.org/licenses/.
017     * ----------------------------------------------------------------------
018     */
019    package tinlizard.dao.maven;
020    
021    import tinlizard.dao.ScmDao;
022    
023    import tinlizard.model.Codeline;
024    
025    import tinlizard.util.Messages;
026    
027    import java.io.File;
028    import java.io.FileInputStream;
029    import java.io.IOException;
030    import java.io.InputStream;
031    import java.util.Collection;
032    import java.util.Date;
033    import java.util.Iterator;
034    import java.util.List;
035    
036    import org.apache.log4j.Logger;
037    import org.apache.maven.model.Model;
038    import org.apache.maven.model.io.xpp3.MavenXpp3Reader;
039    import org.apache.maven.scm.ScmBranch;
040    import org.apache.maven.scm.ScmException;
041    import org.apache.maven.scm.ScmFile;
042    import org.apache.maven.scm.ScmFileSet;
043    import org.apache.maven.scm.ScmResult;
044    import org.apache.maven.scm.ScmVersion;
045    import org.apache.maven.scm.command.changelog.ChangeLogScmResult;
046    import org.apache.maven.scm.command.checkout.CheckOutScmResult;
047    import org.apache.maven.scm.command.status.StatusScmResult;
048    import org.apache.maven.scm.manager.NoSuchScmProviderException;
049    import org.apache.maven.scm.manager.ScmManager;
050    import org.apache.maven.scm.provider.ScmProvider;
051    import org.apache.maven.scm.provider.cvslib.AbstractCvsScmProvider;
052    import org.apache.maven.scm.repository.ScmRepository;
053    import org.apache.maven.scm.repository.ScmRepositoryException;
054    
055    import org.codehaus.plexus.util.FileUtils;
056    import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
057    
058    /**
059     * Maven API backed implementation of ScmDao.
060     */
061    public final class ScmDaoImpl implements ScmDao {
062        private static final Logger LOG = Logger.getLogger(ScmDaoImpl.class);
063        private ScmManager scmManager;
064        private String baseWorkingDirectory;
065    
066        public String getBaseWorkingDirectory() {
067            return baseWorkingDirectory;
068        }
069    
070        public void setBaseWorkingDirectory(final String baseWorkingDirectory) {
071            this.baseWorkingDirectory = baseWorkingDirectory;
072        }
073    
074        public ScmManager getScmManager() {
075            return scmManager;
076        }
077    
078        public void setScmManager(final ScmManager scmManager) {
079            this.scmManager = scmManager;
080        }
081    
082        private String getWD(final Codeline pb) {
083            return baseWorkingDirectory + "/tinlizard/projects/" + pb.getProject().getId() + "/" + pb.getId();
084        }
085    
086        public Model getPOM(final Codeline pb) {
087            File pomFile = getPOMFile(pb);
088            Model pom = null;
089    
090            try {
091                pom = readPOM(pomFile);
092            } catch (Exception e) {
093                throw new RuntimeException(Messages.error_0001(), e);
094            }
095    
096            return pom;
097        }
098    
099        private File getPOMFile(final Codeline pb) {
100            File pomFile = new File(getWD(pb) + "/pom.xml");
101    
102            if (!pomFile.exists()) {
103                CheckOutScmResult result = checkout(pb);
104    
105                if (!result.isSuccess()) {
106                    throw new RuntimeException(result.getProviderMessage());
107                }
108            }
109    
110            return pomFile;
111        }
112    
113        private Model readPOM(final File pomFile) throws IOException, XmlPullParserException {
114            MavenXpp3Reader pomReader = new MavenXpp3Reader();
115    
116            InputStream is = new FileInputStream(pomFile);
117            Model model = pomReader.read(is);
118    
119            return model;
120        }
121    
122        private ScmRepository getScmRepository(final Codeline pb) throws ScmRepositoryException, NoSuchScmProviderException {
123            ScmRepository repository = scmManager.makeScmRepository(pb.getScmConnection());
124    
125            return repository;
126        }
127    
128        private ScmRepository getScmRepository(final Model pom) throws ScmRepositoryException, NoSuchScmProviderException {
129            return scmManager.makeScmRepository(pom.getScm().getConnection());
130        }
131    
132        @SuppressWarnings("unchecked")
133        public boolean isValidSmUrl(final String scmUrl) {
134            List results = scmManager.validateScmRepository(scmUrl);
135    
136            //XXX signal problems, how?
137            return results.isEmpty();
138        }
139    
140        @SuppressWarnings("unchecked")
141        public ScmResult update(final Codeline pb) {
142            ScmResult result;
143            File workingDirectory = new File(getWD(pb));
144    
145            try {
146                if (!workingDirectory.exists()) {
147                    return checkout(pb);
148                }
149    
150                ScmRepository scmRepository = getScmRepository(pb);
151                ScmProvider provider = scmManager.getProviderByRepository(scmRepository);
152    
153                ScmVersion version = getVersion(pb);
154    
155                ScmFileSet fileSet;
156    
157                if (provider instanceof AbstractCvsScmProvider) {
158                    //For CVS, get just the pom.xml.
159                    scmRepository = scmManager.makeScmRepository(pb.getScmConnection() + "/pom.xml");
160    
161                    final List files = FileUtils.getFiles(workingDirectory, "pom.xml", provider.getScmSpecificFilename(), true);
162                    fileSet = new ScmFileSet(workingDirectory, files);
163                } else {
164                    final List files = FileUtils.getFiles(workingDirectory, null, provider.getScmSpecificFilename(), true);
165                    fileSet = new ScmFileSet(workingDirectory, files);
166                }
167    
168                result = scmManager.update(scmRepository, fileSet, version);
169            } catch (ScmException e) {
170                throw new RuntimeException(Messages.error_0007(workingDirectory), e);
171            } catch (IOException e) {
172                throw new RuntimeException(Messages.error_0008(workingDirectory), e);
173            }
174    
175            return result;
176        }
177    
178        @SuppressWarnings("unchecked")
179        private CheckOutScmResult checkout(final Codeline pb) {
180            CheckOutScmResult result = null;
181    
182            try {
183                ScmRepository repository = getScmRepository(pb);
184                File workingDirectory = new File(getWD(pb));
185                ScmVersion version = getVersion(pb);
186    
187                if (workingDirectory.exists()) {
188                    try {
189                        FileUtils.deleteDirectory(workingDirectory);
190                    } catch (Exception e) {
191                        throw new RuntimeException(Messages.error_0006(workingDirectory.getAbsolutePath()));
192                    }
193    
194                    if (workingDirectory.exists()) {
195                        throw new RuntimeException(Messages.error_0002(workingDirectory.getAbsolutePath()));
196                    }
197                }
198    
199                if (!workingDirectory.mkdirs()) {
200                    throw new RuntimeException(Messages.error_0003(workingDirectory.getAbsolutePath()));
201                }
202    
203                ScmProvider provider = scmManager.getProviderByRepository(repository);
204    
205                ScmFileSet fileSet;
206    
207                if (provider instanceof AbstractCvsScmProvider) {
208                    //For CVS, get just the pom.xml.
209                    fileSet = new ScmFileSet(workingDirectory, new File(workingDirectory, "pom.xml"));
210                } else {
211                    fileSet = new ScmFileSet(workingDirectory);
212                }
213    
214                result = scmManager.checkOut(repository, fileSet, version);
215    
216                if (LOG.isTraceEnabled() && result.isSuccess()) {
217                    List checkedOutFiles = result.getCheckedOutFiles();
218                    LOG.trace("Checked out these files: ");
219    
220                    for (Iterator it = checkedOutFiles.iterator(); it.hasNext();) {
221                        ScmFile file = (ScmFile) it.next();
222    
223                        LOG.trace(" " + file.getPath());
224                    }
225                }
226            } catch (ScmException e) {
227                throw new RuntimeException(Messages.error_0005(), e);
228            }
229    
230            return result;
231        }
232    
233        private ScmVersion getVersion(final Codeline pb) {
234            ScmVersion version = null;
235    
236            if (!pb.isMainLine()) {
237                version = new ScmBranch(pb.getName());
238            }
239    
240            return version;
241        }
242    
243        public void releaseFiles(final Codeline pb) {
244            try {
245                FileUtils.deleteDirectory(getWD(pb));
246            } catch (Exception e) {
247                LOG.error(Messages.error_0004(), e);
248            }
249        }
250    
251        public void releaseFiles(final Collection<Codeline> codelines) {
252            for (Codeline pb : codelines) {
253                releaseFiles(pb);
254            }
255        }
256    
257        public StatusScmResult getPOMStatus(final Codeline pb) {
258            StatusScmResult statusScmResult = null;
259    
260            try {
261                File pomFile = getPOMFile(pb);
262                ScmRepository repository = getScmRepository(pb);
263                ScmFileSet fileSet = new ScmFileSet(pomFile);
264    
265                statusScmResult = scmManager.status(repository, fileSet);
266            } catch (Exception e) {
267                LOG.error("Problems getting Pom Status.", e);
268            }
269    
270            return statusScmResult;
271        }
272    
273        public ChangeLogScmResult getChangeLogForNDaysAgo(final Codeline pb, final int numDays) {
274            return getChangeLog(pb, null, null, numDays);
275        }
276    
277        public ChangeLogScmResult getChangeLog(final Codeline pb, final Date startDate, final Date endDate, final int numDays) {
278            ChangeLogScmResult rval = null;
279    
280            try {
281                Model pom = getPOM(pb);
282                ScmRepository repository = getScmRepository(pom);
283    
284                ScmFileSet fileSet = new ScmFileSet(new File(getWD(pb)));
285    
286                ScmBranch codeline = new ScmBranch(pb.getName());
287                rval = scmManager.changeLog(repository, fileSet, startDate, endDate, numDays, codeline);
288            } catch (Exception e) {
289                throw new RuntimeException(Messages.error_0104(), e);
290            }
291    
292            return rval;
293        }
294    }