/*
* Copyright (C) 2015 Alexander Christian <alex(at)root1.de>. All rights reserved.
*
* This logicFile is part of KAD Logic Plugin (KLP).
*
* KLP is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* KLP is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with KLP. If not, see <http://www.gnu.org/licenses/>.
*/
package de.root1.kad.logicplugin;
import de.root1.jrc.CompileException;
import de.root1.jrc.CompileResult;
import de.root1.jrc.JavaRuntimeCompiler;
import java.io.File;
import java.io.IOException;
import java.net.URLClassLoader;
import java.security.NoSuchAlgorithmException;
import java.util.Objects;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
*
* @author achristian
*/
public class SourceContainer {
private Logger log = LoggerFactory.getLogger(getClass());
/**
* e.g. /opt/kad/scripts/de/mypackage/MyClass.java
*/
private File logicFile;
/**
* e.g. /opt/kad/scripts
*/
private File srcDir;
/**
* e.g. de.mypackage
*/
private String packageName;
/**
* e.g. MyClass
*/
private String className;
private String checksum;
/**
* e.g. de/mypackage
*/
private String packagePath;
/**
* e.g. MyClass.java
*/
private String javaSourceFile;
/**
* Site in bytes of the java source logicFile
*/
private long fileSize;
private ClassLoader kadClassloader;
private final File libDir;
private Logic logic;
public SourceContainer(File srcDir, File libDir, File logicFile) throws IOException {
// shorten paths in logicFile objects if necessary
srcDir = de.root1.kad.utils.Utils.shortenFile(srcDir);
libDir = de.root1.kad.utils.Utils.shortenFile(libDir);
logicFile = de.root1.kad.utils.Utils.shortenFile(logicFile);
this.logicFile = logicFile;
this.srcDir = srcDir;
this.libDir = libDir;
String basedirpath = srcDir.getAbsolutePath();
String filepath = logicFile.getAbsolutePath();
if (!filepath.startsWith(basedirpath)) {
throw new IllegalArgumentException("Given basedir is not a parent of file");
}
try {
checksum = de.root1.kad.utils.Utils.createSHA1(logicFile);
} catch (NoSuchAlgorithmException ex) {
throw new RuntimeException("Checksumcreation failed", ex);
}
String pathToFile = filepath.substring(basedirpath.length() + 1);
log.debug("pathToFile={}", pathToFile);
javaSourceFile = pathToFile;
if (pathToFile.contains(File.separator)) {
javaSourceFile = pathToFile.substring(pathToFile.lastIndexOf(File.separator) + 1);
}
packagePath = pathToFile.substring(0, pathToFile.lastIndexOf(File.separator));
log.debug("packagePath={}", packagePath);
packageName = packagePath.replace(File.separator, ".");
log.debug("packageName={}", packageName);
log.debug("javaSourceFile=" + javaSourceFile);
className = javaSourceFile.substring(0, javaSourceFile.lastIndexOf("."));
log.debug("className={}", className);
fileSize = logicFile.length();
}
public String getChecksum() {
return checksum;
}
/**
* e.g. de.mypackage.MyClass
*
* @return
*/
public String getCanonicalClassName() {
return packageName + "." + className;
}
/**
*
* @return new logic instance
*/
public void loadLogic() throws LoadSourceException {
try {
JavaRuntimeCompiler jrc = new JavaRuntimeCompiler();
ClassLoader cl = getClass().getClassLoader();
if (cl instanceof URLClassLoader) {
jrc.setClassPath((URLClassLoader) getClass().getClassLoader());
jrc.addClassPath((URLClassLoader) kadClassloader);
}
jrc.addClassPath(srcDir);
jrc.addClassPath(libDir);
File compiledScriptsFolder = new File(System.getProperty("kad.basedir"), "compiledLogic");
// ensure directory is available
compiledScriptsFolder.mkdirs();
CompileResult compileResult = jrc.compileToFile(getCanonicalClassName(), getFile(), compiledScriptsFolder.getCanonicalFile());
log.debug("compile result: {}", compileResult);
SourceClassLoader scl = new SourceClassLoader(this.getClass().getClassLoader(), libDir);
scl.setCompileResult(compileResult);
Class<?> sourceClass = scl.loadClass(getCanonicalClassName());
if (!Logic.class.isAssignableFrom(sourceClass)) {
throw new CompileException("Class '" + packageName + "." + className + "' is not of type " + Logic.class.getCanonicalName() + ": " + sourceClass, null, null);
}
logic = (Logic) sourceClass.newInstance();
} catch (CompileException | ClassNotFoundException | InstantiationException | IllegalAccessException | IOException ex) {
throw new LoadSourceException(ex);
}
}
/**
* Returns logic instance, if already created/loaded with {@link #loadLogic() };
* @return Logic instance
*/
public Logic getLogic() {
return logic;
}
@Override
public int hashCode() {
int hash = 5;
hash = 79 * hash + Objects.hashCode(this.logicFile);
hash = 79 * hash + (int) (this.fileSize ^ (this.fileSize >>> 32));
return hash;
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final SourceContainer other = (SourceContainer) obj;
if (!Objects.equals(this.logicFile, other.logicFile)) {
return false;
}
return this.fileSize == other.fileSize;
}
/**
* e.g. /opt/kad/scripts/de/mypackage/MyClass.java
*
* @return
*/
public File getFile() {
return logicFile;
}
/**
* e.g. /opt/kad/logic/src
*
* @return
*/
public File getSrcDir() {
return srcDir;
}
/**
* e.g. /opt/kad/logic/lib
* @return
*/
public File getLibDir() {
return libDir;
}
/**
* e.g. de.mypackage
*
* @return
*/
public String getPackageName() {
return packageName;
}
/**
* e.g. MyClass
*
* @return
*/
public String getClassName() {
return className;
}
/**
* e.g. de/mypackage
*
* @return
*/
public String getPackagePath() {
return packagePath;
}
/**
* e.g. MyClass.java as String
*
* @return
*/
public String getJavaSourceFile() {
return javaSourceFile;
}
public long getFileSize() {
return fileSize;
}
void setKadClassloader(ClassLoader kadClassLoader) {
this.kadClassloader = kadClassLoader;
}
@Override
public String toString() {
return "SourceContainer("+getCanonicalClassName()+")";
}
}