Java tutorial
/* * * Copyright 2013 Netflix, Inc. * * 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 com.netflix.nicobar.core.persistence; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.net.URISyntaxException; import java.nio.file.DirectoryStream; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.attribute.FileTime; import java.util.Enumeration; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.jar.JarEntry; import java.util.jar.JarFile; import java.util.jar.JarOutputStream; import org.apache.commons.io.IOUtils; import com.netflix.nicobar.core.archive.GsonScriptModuleSpecSerializer; import com.netflix.nicobar.core.archive.JarScriptArchive; import com.netflix.nicobar.core.archive.ModuleId; import com.netflix.nicobar.core.archive.ScriptArchive; import com.netflix.nicobar.core.archive.ScriptModuleSpec; import com.netflix.nicobar.core.archive.ScriptModuleSpecSerializer; /** * {@link ArchiveRepository} implementation which stores {@link ScriptArchive}s in individual * jar files in a single root directory * * @author James Kojo */ public class JarArchiveRepository implements ArchiveRepository { private static final ScriptModuleSpecSerializer DEFAULT_SERIALIZER = new GsonScriptModuleSpecSerializer(); public static class Builder { private final Path rootDir; private String repositoryId; private ScriptModuleSpecSerializer moduleSpecSerializer = DEFAULT_SERIALIZER; private String respositoryDescription; public Builder(Path rootDir) { this.rootDir = rootDir; } public Builder setRepostoryId(String repositoryId) { this.repositoryId = repositoryId; return this; } public Builder setRepositoryDescription(String respositoryDescription) { this.respositoryDescription = respositoryDescription; return this; } public Builder setModuleSpecSerializer(ScriptModuleSpecSerializer moduleSpecSerializer) { this.moduleSpecSerializer = moduleSpecSerializer; return this; } public JarArchiveRepository build() { String buildRepositoryId = repositoryId; if (buildRepositoryId == null) { buildRepositoryId = rootDir.toString(); } String buildDescription = respositoryDescription; if (buildDescription == null) { buildDescription = JarArchiveRepository.class.getSimpleName() + ": " + rootDir.toString(); } return new JarArchiveRepository(rootDir, buildRepositoryId, buildDescription, moduleSpecSerializer); } } /** * Directory filter which finds readable jar files */ protected final static DirectoryStream.Filter<Path> JAR_FILE_FILTER = new DirectoryStream.Filter<Path>() { public boolean accept(Path path) throws IOException { return (Files.isRegularFile(path) && Files.isReadable(path)) && path.toString().endsWith(".jar"); } }; private final Path rootDir; private final String repositoryId; private final ScriptModuleSpecSerializer moduleSpecSerializer; private final String repositoryDescription; private final RepositoryView defaultView = new DefaultView(); protected JarArchiveRepository(Path rootDir, String repositoryId, String repositoryDescription, ScriptModuleSpecSerializer moduleSpecSerializer) { this.rootDir = Objects.requireNonNull(rootDir, "rootDir"); this.repositoryId = Objects.requireNonNull(repositoryId, "repositoryId"); this.moduleSpecSerializer = Objects.requireNonNull(moduleSpecSerializer, "moduleSpecSerializer"); this.repositoryDescription = Objects.requireNonNull(repositoryDescription, "repositoryDescription"); } @Override public String getRepositoryId() { return repositoryId; } /** * The default view reports all archives inserted into this repository. * @return the default view into all archives. */ @Override public RepositoryView getDefaultView() { return defaultView; } /** * No named views supported by this repository! * Throws UnsupportedOperationException. */ @Override public RepositoryView getView(String view) { throw new UnsupportedOperationException(); } @Override public void insertArchive(JarScriptArchive jarScriptArchive) throws IOException { Objects.requireNonNull(jarScriptArchive, "jarScriptArchive"); ScriptModuleSpec moduleSpec = jarScriptArchive.getModuleSpec(); ModuleId moduleId = moduleSpec.getModuleId(); Path moduleJarPath = getModuleJarPath(moduleId); Files.deleteIfExists(moduleJarPath); JarFile sourceJarFile; try { sourceJarFile = new JarFile(jarScriptArchive.getRootUrl().toURI().getPath()); } catch (URISyntaxException e) { throw new IOException(e); } JarOutputStream destJarFile = new JarOutputStream(new FileOutputStream(moduleJarPath.toFile())); try { String moduleSpecFileName = moduleSpecSerializer.getModuleSpecFileName(); Enumeration<JarEntry> sourceEntries = sourceJarFile.entries(); while (sourceEntries.hasMoreElements()) { JarEntry sourceEntry = sourceEntries.nextElement(); if (sourceEntry.getName().equals(moduleSpecFileName)) { // avoid double entry for module spec continue; } destJarFile.putNextEntry(sourceEntry); if (!sourceEntry.isDirectory()) { InputStream inputStream = sourceJarFile.getInputStream(sourceEntry); IOUtils.copy(inputStream, destJarFile); IOUtils.closeQuietly(inputStream); } destJarFile.closeEntry(); } // write the module spec String serialized = moduleSpecSerializer.serialize(moduleSpec); JarEntry moduleSpecEntry = new JarEntry(moduleSpecSerializer.getModuleSpecFileName()); destJarFile.putNextEntry(moduleSpecEntry); IOUtils.write(serialized, destJarFile); destJarFile.closeEntry(); } finally { IOUtils.closeQuietly(sourceJarFile); IOUtils.closeQuietly(destJarFile); } // update the timestamp on the jar file to indicate that the module has been updated Files.setLastModifiedTime(moduleJarPath, FileTime.fromMillis(jarScriptArchive.getCreateTime())); } /** * Unsupported. */ @Override public void insertArchive(JarScriptArchive jarScriptArchive, Map<String, Object> initialDeploySpecs) throws IOException { throw new UnsupportedOperationException("This repository does not support deployment specs."); } @Override public Set<ScriptArchive> getScriptArchives(Set<ModuleId> moduleIds) throws IOException { Set<ScriptArchive> scriptArchives = new LinkedHashSet<ScriptArchive>(); for (ModuleId moduleId : moduleIds) { Path moduleJar = getModuleJarPath(moduleId); if (Files.exists(moduleJar)) { JarScriptArchive scriptArchive = new JarScriptArchive.Builder(moduleJar).build(); scriptArchives.add(scriptArchive); } } return scriptArchives; } @Override public void deleteArchive(ModuleId moduleId) throws IOException { Path moduleJar = getModuleJarPath(moduleId); if (Files.exists(moduleJar)) { Files.delete(moduleJar); } } /** * Translated a module id to an absolute path of the module jar */ protected Path getModuleJarPath(ModuleId moduleId) { Path moduleJarPath = rootDir.resolve(moduleId + ".jar"); return moduleJarPath; } protected class DefaultView implements RepositoryView { @Override public String getName() { return "Default View"; } @Override public Map<ModuleId, Long> getArchiveUpdateTimes() throws IOException { Map<ModuleId, Long> updateTimes = new LinkedHashMap<ModuleId, Long>(); try (DirectoryStream<Path> archiveJars = Files.newDirectoryStream(rootDir, JAR_FILE_FILTER)) { for (Path archiveJar : archiveJars) { Path absoluteArchiveFile = rootDir.resolve(archiveJar); long lastUpdateTime = Files.getLastModifiedTime(absoluteArchiveFile).toMillis(); String moduleName = archiveJar.getFileName().toString(); if (moduleName.endsWith(".jar")) { moduleName = moduleName.substring(0, moduleName.lastIndexOf(".jar")); } ModuleId moduleId = ModuleId.fromString(moduleName); updateTimes.put(moduleId, lastUpdateTime); } } return updateTimes; } @Override public RepositorySummary getRepositorySummary() throws IOException { Map<ModuleId, Long> archiveUpdateTimes = getArchiveUpdateTimes(); long maxUpdateTime = 0; for (Long updateTime : archiveUpdateTimes.values()) { if (updateTime > maxUpdateTime) { maxUpdateTime = updateTime; } } return new RepositorySummary(getRepositoryId(), repositoryDescription, archiveUpdateTimes.size(), maxUpdateTime); } @Override public List<ArchiveSummary> getArchiveSummaries() throws IOException { List<ArchiveSummary> summaries = new LinkedList<ArchiveSummary>(); Set<ModuleId> moduleIds = getArchiveUpdateTimes().keySet(); Set<ScriptArchive> scriptArchives = getScriptArchives(moduleIds); for (ScriptArchive scriptArchive : scriptArchives) { ScriptModuleSpec moduleSpec = scriptArchive.getModuleSpec(); long lastUpdateTime = scriptArchive.getCreateTime(); summaries.add(new ArchiveSummary(moduleSpec.getModuleId(), moduleSpec, lastUpdateTime, null)); } return summaries; } } }