ClassPathImpl.java
/*
* JPPF.
* Copyright (C) 2005-2019 JPPF Team.
* http://www.jppf.org
*
* 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.jppf.example.extendedclassloading;
import java.io.*;
import java.util.*;
import org.jppf.utils.FileUtils;
/**
* A default implementation of a <code>ClassPath</code>, backed by a {@link HashMap} for its elements.
* <p>Depending on which constructor is used, this classpath may be transient or pesrsisted locally.
* <p>The persistence only handles jar files in this implementation. These must be in a folder
* specified in the constructor, with a flat structure (no sub-folders).
* The file names in this folder have the format <code><i>actual_jar_name</i>-<i>signature</i>.jar</code>.
* <p>The persistence maintains a single text file named "index.txt" in this same folder, which contains the definition for this classpath.
* The classpath definition consists in a set of entries, each on a separate line, with the following format:<br/>
* <code><i>jar_file_name</i>=<i>signature</i></code>.
* <p>Example definition file content:
* <pre>ClientLib1.jar=1B43C50E293A4DD0DEC3FA1D7297D0AF
ClientLib2.jar=D23B5C671CE09DF91C41BE7153E949E1</pre>
* @author Laurent Cohen
*/
public class ClassPathImpl implements ClassPath {
/**
* Explicit serialVersionUID.
*/
private static final long serialVersionUID = 1L;
/**
* Location where the libraries are stored on the local file system.
*/
private final String rootDir;
/**
* Location listing the libraries to add or update on the local file system.
*/
private final String indexFile;
/**
* Represents the repository elements.
*/
private Map<String, String> elementsMap = new HashMap<>();
/**
* Create a transient repository that is not persisted.
*/
public ClassPathImpl() {
this.rootDir = null;
indexFile = null;
}
/**
* Create a classpath whose defintiion can be persisted in the specified folder.
* @param rootDir the folder where the libraries are stored on the local file system.
*/
public ClassPathImpl(final String rootDir) {
this.rootDir = rootDir;
indexFile = rootDir + "/index.txt";
}
@Override
public boolean addElement(final String key, final String signature) {
return elementsMap.put(key, signature) != null;
}
@Override
public boolean removeElement(final String key) {
final String signature = elementsMap.remove(key);
return signature != null;
}
@Override
public String getElementSignature(final String key) {
return elementsMap.get(key);
}
@Override
public Map<String, String> elements() {
return Collections.unmodifiableMap(new HashMap<>(elementsMap));
}
@Override
public Collection<String> elementNames() {
return Collections.unmodifiableSet(new HashSet<>(elementsMap.keySet()));
}
@Override
public int size() {
return elementsMap.size();
}
/**
* Load this classpath from a definition file.
* @return this classpath instance with the elements read from the definition file.
* @throws IOException if any I/O error occurs.
*/
public ClassPath loadFromDefinition() throws IOException {
if (rootDir == null) throw new IllegalStateException("this repository is not persisted and can't be loaded");
elementsMap.clear();
checkOrCreateDefinition();
List<String> lines = new ArrayList<>();
final Reader reader = FileUtils.getFileReader(indexFile);
lines = FileUtils.textFileAsLines(reader);
for (String line: lines) {
line = line.trim();
if (!line.isEmpty()) {
final String[] association = line.split("=");
addElement(association[0], association[1]);
}
}
return this;
}
/**
* Save this classpath to a definition file.
* @throws IOException if any I/O error occurs.
*/
public void saveToDefinition() throws IOException {
if (rootDir == null) throw new IllegalStateException("this repository is not persisted and can't be saved");
checkOrCreateDefinition();
Writer writer = null;
try {
writer = FileUtils.getFileWriter(indexFile);
for (Map.Entry<String, String> entry: this.elements().entrySet()) {
writer.write(entry.getKey() + '=' + entry.getValue() + '\n');
}
} finally {
if (writer != null) writer.close();
}
}
/**
* Check if the definition exists and create it if it doesn't.
* @throws IOException if the store could not be created.
*/
private void checkOrCreateDefinition() throws IOException {
final File idxFile = new File(indexFile);
if (!idxFile.exists()) {
final File dir = idxFile.getParentFile();
// if the folder does not exist, attemp to create it
if (!dir.exists()) {
if (!dir.mkdirs()) throw new IOException("could not create the library store at '" + dir.getCanonicalPath() + "'");
}
// create an empty index file
FileWriter writer = null;
try {
writer = new FileWriter(indexFile);
} finally {
if (writer != null) writer.close();
}
}
}
/**
* Load this <code>ClassPath</code> by scanning its root folder for jar files
* and computng their signatures.
* @return this classpath instance with the elements found in the root folder.
* @throws IOException if any I/O error occurs.
*/
public ClassPath loadFromFileSystem() throws IOException {
final File dir = new File(rootDir + '/');
// list all the jar files in the folder
final File[] jars = dir.listFiles(new FileFilter() {
@Override
public boolean accept(final File file) {
return !file.isDirectory() && file.getName().toLowerCase().endsWith(".jar");
}
});
if ((jars != null) && (jars.length > 0)) {
for (final File jar: jars) {
final String name = jar.getName();
final String signature = ClassPathHelper.computeSignature(jar);
addElement(name, signature);
}
}
return this;
}
/**
* Get the root directory location.
* @return the root folder where jar files are stored.
*/
public String getRootDir() {
return rootDir;
}
@Override
public String toString() {
return getClass().getSimpleName() + '[' + elementsMap + ']';
}
}