/* * Copyright 2012 James Moger * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.moxie; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Set; import java.util.TreeSet; import org.moxie.Constants.MavenCacheStrategy; import org.moxie.utils.DeepCopier; import org.moxie.utils.FileUtils; import org.moxie.utils.StringUtils; public class MoxieCache extends IMavenCache { final File moxieRoot; final File moxiedataRoot; final File localReleasesRoot; final File localSnapshotsRoot; final File localRoot; final File remoteRoot; final File masterPrefixIndex; final IMavenCache dotM2Cache; MavenCacheStrategy m2Strategy; public MoxieCache(File moxieRoot) { this.moxieRoot = moxieRoot; this.moxiedataRoot = new File(moxieRoot, "data"); this.localRoot = new File(moxieRoot, Constants.LOCAL); this.remoteRoot = new File(moxieRoot, Constants.REMOTE); this.localReleasesRoot = new File(localRoot, "releases"); this.localSnapshotsRoot = new File(localRoot, "snapshots"); this.masterPrefixIndex = new File(moxiedataRoot, "prefixes.txt"); this.dotM2Cache = new MavenCache(new File(System.getProperty("user.home") + "/.m2/repository")); // initial folder creation moxieRoot.mkdirs(); moxiedataRoot.mkdirs(); localRoot.mkdirs(); remoteRoot.mkdirs(); localReleasesRoot.mkdirs(); localSnapshotsRoot.mkdirs(); } @Override public File getRootFolder() { return moxieRoot; } public void setMavenCacheStrategy(MavenCacheStrategy value) { m2Strategy = value; } /* * (non-Javadoc) * @see org.moxie.IMavenCache#getFiles(java.lang.String) */ @Override public List<File> getFiles(String extension) { List<File> list = new ArrayList<File>(); return list; } /* * (non-Javadoc) * @see org.moxie.IMavenCache#getArtifact(org.moxie.Dependency, java.lang.String) */ @Override public File getArtifact(Dependency dep, String ext) { if (dep instanceof SystemDependency) { return new File(((SystemDependency) dep).path); } File baseFolder = localReleasesRoot; // clone the original dep object for special checks below Dependency original = DeepCopier.copy(dep); // resolve dependency version - this updates the shared instance resolveRevision(dep); if (dep.isSnapshot()) { baseFolder = localSnapshotsRoot; } String path; if (dep.isMavenObject()) { path = Dependency.getArtifactPath(original, ext, Constants.MAVEN2_ARTIFACT_PATTERN); } else { path = Dependency.getArtifactPath(original, ext, Constants.FORGE_ARTIFACT_PATTERN); } File moxieFile = new File(baseFolder, path); if (!moxieFile.exists() && original.isMetaVersion()) { // try fully qualified revision path = Dependency.getArtifactPath(dep, ext, Constants.MAVEN2_ARTIFACT_PATTERN); moxieFile = new File(baseFolder, path); } if (!moxieFile.exists()) { // search in downloaded artifact folders File downloaded = findDownloadedArtifact(path); if (downloaded != null) { // found a downloaded file moxieFile = downloaded; } else if (MavenCacheStrategy.IGNORE != m2Strategy) { // look in .m2/repository File mavenFile = dotM2Cache.getArtifact(dep, ext); if (mavenFile.exists()) { if (MavenCacheStrategy.COPY == m2Strategy) { // transparently copy from Maven cache to Moxie cache try { FileUtils.copy(moxieFile.getParentFile(), mavenFile); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } else { // directly use artifact from Maven M2 repository moxieFile = mavenFile; } // update Moxie data with the M2 file:/ url origin Date now = new Date(); MoxieData moxiedata = readMoxieData(dep); moxiedata.setOrigin(dotM2Cache.getRootFolder().toURI().toString()); moxiedata.setLastDownloaded(now); moxiedata.setLastChecked(now); moxiedata.setLastUpdated(new Date(mavenFile.lastModified())); writeMoxieData(dep, moxiedata); } } } return moxieFile; } /* * (non-Javadoc) * @see org.moxie.IMavenCache#getMetadata(org.moxie.Dependency, java.lang.String) */ @Override public File getMetadata(Dependency dep, String ext) { File baseFolder = localReleasesRoot; String pattern = Constants.MAVEN2_METADATA_PATTERN; if (dep.isSnapshot()) { baseFolder = localSnapshotsRoot; pattern = Constants.MAVEN2_SNAPSHOT_PATTERN; } String path = Dependency.getArtifactPath(dep, ext, pattern); File moxieFile = new File(baseFolder, path); if (!moxieFile.exists()) { // search in downloaded artifact folders File downloaded = findDownloadedArtifact(path); if (downloaded != null) { // found a downloaded file moxieFile = downloaded; } else if (MavenCacheStrategy.IGNORE != m2Strategy) { // look in .m2/repository File mavenFile = dotM2Cache.getMetadata(dep, ext); if (mavenFile.exists()) { if (MavenCacheStrategy.COPY == m2Strategy) { // transparently copy from Maven cache to Moxie cache try { FileUtils.copy(moxieFile.getParentFile(), mavenFile); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } else { // directly use the Maven metadata file moxieFile = mavenFile; } } } } return moxieFile; } // Check the downloaded atifacts for the requested artifact protected File findDownloadedArtifact(String path) { File [] files = remoteRoot.listFiles(); if (files == null) { return null; } for (File repository : files) { if (repository.isDirectory()) { File file = new File(repository, path); if (file.exists()) { return file; } } } return null; } protected File getMoxieDataFile(Dependency dep) { // Resolve a clone of the dependency so we do not change // the original object. This is to resolve RELEASE and LATEST to a // numeric version. SNAPSHOT revisions are not part of the Moxie // data filename and as such they are irrelevant for this lookup. Dependency copy = DeepCopier.copy(dep); resolveRevision(copy); String path; if (dep.isMavenObject()) { path = Dependency.getArtifactPath(copy, "moxie", Constants.MAVEN2_ARTIFACT_PATTERN); } else { path = Dependency.getArtifactPath(copy, dep.extension, Constants.FORGE_METADATA_PATTERN); } // create a temp file instance so we can get artifact parent folder File moxieFile = new File(moxiedataRoot, path); // metadata.moxie return new File(moxieFile.getParentFile(), "metadata.moxie"); } public MoxieData readMoxieData(Dependency dep) { File moxieFile = getMoxieDataFile(dep); MoxieData moxiedata = new MoxieData(moxieFile); moxiedata.setArtifact(dep); dep.setOrigin(moxiedata.getOrigin()); return moxiedata; } public File writeMoxieData(Dependency dep, MoxieData moxiedata) { File file = getMoxieDataFile(dep); FileUtils.writeContent(file, moxiedata.toMaxML()); return file; } protected File getMoxieDataFile(String repositoryUrl) { String folder = StringUtils.urlToFolder(repositoryUrl); File moxieFile = new File(remoteRoot, folder + "/.meta/metadata.moxie"); return moxieFile; } public MoxieData readRepositoryMoxieData(String repositoryUrl) { File moxieFile = getMoxieDataFile(repositoryUrl); MoxieData moxiedata = new MoxieData(moxieFile); return moxiedata; } public File writeRepositoryMoxieData(String repositoryUrl, MoxieData moxiedata) { File file = getMoxieDataFile(repositoryUrl); FileUtils.writeContent(file, moxiedata.toMaxML()); return file; } public File writeRepositoryFile(String repositoryUrl, String filename, byte [] content) { String dir = StringUtils.urlToFolder(repositoryUrl); File repoDir = new File(remoteRoot, dir); File file = new File(repoDir, filename); FileUtils.writeContent(file, content); return file; } /* * (non-Javadoc) * @see org.moxie.IMavenCache#writeArtifact(org.moxie.Dependency, java.lang.String, java.lang.String) */ @Override public File writeArtifact(Dependency dep, String ext, String content) { byte [] bytes = null; try { bytes = content.getBytes("UTF-8"); } catch (UnsupportedEncodingException e) { bytes = content.getBytes(); } return writeArtifact(dep, ext, bytes); } @Override public File writeArtifact(Dependency dep, String ext, byte[] content) { File file; if (StringUtils.isEmpty(dep.origin)) { // local artifact because origin is undefined file = getArtifact(dep, ext); } else { // downloaded artifact String folder = StringUtils.urlToFolder(dep.origin); File repositoryRoot = new File(remoteRoot, folder); String path; if (dep.isMavenObject()) { path = Dependency.getArtifactPath(dep, ext, Constants.MAVEN2_ARTIFACT_PATTERN); } else { path = Dependency.getArtifactPath(dep, ext, Constants.FORGE_ARTIFACT_PATTERN); } file = new File(repositoryRoot, path); } FileUtils.writeContent(file, content); return file; } /* * (non-Javadoc) * @see org.moxie.IMavenCache#writeMetadata(org.moxie.Dependency, java.lang.String, java.lang.String) */ @Override public File writeMetadata(Dependency dep, String ext, String content) { byte [] bytes = null; try { bytes = content.getBytes("UTF-8"); } catch (UnsupportedEncodingException e) { bytes = content.getBytes(); } return writeMetadata(dep, ext, bytes); } /* * (non-Javadoc) * @see org.moxie.IMavenCache#writeMetadata(org.moxie.Dependency, java.lang.String, byte[]) */ @Override public File writeMetadata(Dependency dep, String ext, byte[] content) { File file; if (StringUtils.isEmpty(dep.origin)) { // local metadata because origin is undefined file = getMetadata(dep, ext); } else { // downloaded metadata String folder = StringUtils.urlToFolder(dep.origin); File repositoryRoot = new File(remoteRoot, folder); String path = Dependency.getArtifactPath(dep, ext, dep.isSnapshot() ? Constants.MAVEN2_SNAPSHOT_PATTERN : Constants.MAVEN2_METADATA_PATTERN); file = new File(repositoryRoot, path); } FileUtils.writeContent(file, content); return file; } /** * Returns the prefix index file for the repository url. * * @return the repository prefixes index file */ public File getPrefixesIndex(String repositoryUrl) { String folder = StringUtils.urlToFolder(repositoryUrl); File repositoryRoot = new File(remoteRoot, folder); return new File(repositoryRoot, Constants.PREFIXES); } /** * Reads the aggregate prefixes index for the entire cache. * * @return aggregate prefixes index */ public Set<String> getPrefixes() { return readPrefixesIndex(masterPrefixIndex); } /** * Reads the prefixes for the specified remote repository. * * @param url * @return the prefixes index */ public Set<String> getPrefixes(String url) { File index = getPrefixesIndex(url); if (index.exists()) { return readPrefixesIndex(index); } return new TreeSet<String>(); } /** * Creates/updates a prefixes index used by smart maven clients to do * automatic dependency routing. * * @return the index file */ @Override public File updatePrefixesIndex() { // generate a prefix index for each remote cache List<File> indexes = new ArrayList<File>(); File [] remotes = remoteRoot.listFiles(); if (remotes != null) { for (File remote : remotes) { if (remote.isDirectory()) { File index = updatePrefixesIndex(remote); indexes.add(index); } } } // generate a prefix index for the local releases and snapshots cache indexes.add(updatePrefixesIndex(localReleasesRoot)); indexes.add(updatePrefixesIndex(localSnapshotsRoot)); // merge all prefix indexes together Set<String> prefixes = new TreeSet<String>(); for (File index : indexes) { prefixes.addAll(readPrefixesIndex(index)); } // write aggregate/merged index return writePrefixes(masterPrefixIndex, prefixes); } /** * Writes the prefixes index. * * @return a prefixes index file */ @Override public File writePrefixes(Set<String> prefixes) { return writePrefixes(masterPrefixIndex, prefixes); } }