package org.kevoree.bootstrap.kernel;
import org.jboss.shrinkwrap.resolver.api.maven.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.kevoree.*;
import org.kevoree.api.BootstrapService;
import org.kevoree.api.Context;
import org.kevoree.api.ModelService;
import org.kevoree.api.PlatformService;
import org.kevoree.bootstrap.Bootstrap;
import org.kevoree.bootstrap.reflect.Injector;
import org.kevoree.core.ContextAwareAdapter;
import org.kevoree.core.KevoreeCoreBean;
import org.kevoree.kcl.api.FlexyClassLoader;
import org.kevoree.log.Log;
import org.kevoree.pmodeling.api.KMFContainer;
import java.io.*;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.file.Paths;
import java.util.List;
import java.util.Set;
import java.util.logging.LogManager;
/**
* Created with IntelliJ IDEA.
* User: duke
* Date: 25/11/2013
* Time: 19:41
*/
public class KevoreeCLKernel implements BootstrapService {
private Bootstrap bs;
private Injector injector;
private File propFile = null;
public KevoreeCLKernel(Bootstrap b, Injector injector) {
this.bs = b;
this.injector = injector;
// be sure that the logging of the Maven resolver won't be too verbose
// but let the user modify the behavior if needed
String loggingProp = System.getProperty("java.util.logging.config.file");
if (loggingProp == null) {
propFile = Paths.get(System.getProperty("user.home"), ".kevoree", "java-logging.properties").toFile();
if (!propFile.exists()) {
propFile.getParentFile().mkdirs();
try {
PrintWriter writer = new PrintWriter(propFile);
writer.println("# Specify the handlers to create in the root logger");
writer.println("handlers= java.util.logging.ConsoleHandler");
writer.println("# Set the default logging level for new ConsoleHandler instances");
writer.println("java.util.logging.ConsoleHandler.level= SEVERE");
writer.close();
} catch (FileNotFoundException e) {
throw new RuntimeException("Unable to write " + propFile.getAbsolutePath(), e);
}
}
} else {
propFile = new File(loggingProp);
}
}
private Boolean offline = false;
@Override
public FlexyClassLoader get(DeployUnit deployUnit) {
return this.get("mvn:"+deployUnit.getUrl());
}
@Override
public FlexyClassLoader get(String key) {
return this.bs.getKernel().get(key);
}
private String getKey(MavenArtifactInfo artifact) {
return "mvn:" + artifact.getCoordinate().getGroupId() + ":" + artifact.getCoordinate().getArtifactId() +
":" + artifact.getCoordinate().getVersion();
}
private void installDependencies(ConfigurableMavenResolverSystem resolver, FlexyClassLoader parentCl, MavenArtifactInfo artifact, int depth) {
String indent = "";
int count = 0;
while (count < depth) {
indent += " ";
count++;
}
for (MavenArtifactInfo dep : artifact.getDependencies()) {
String key = getKey(dep);
if (!dep.getScope().equals(ScopeType.TEST)) {
long before = System.currentTimeMillis();
FlexyClassLoader depCl = get(key);
if (depCl == null) {
if (dep.getCoordinate().getType().equals(PackagingType.POM)) {
Log.debug("{} + {} ({}ms)", indent, key, (System.currentTimeMillis() - before));
installDependencies(resolver, parentCl, dep, depth+1);
} else {
File depJar;
try {
depJar = resolver
.resolve(dep.getCoordinate().toCanonicalForm())
.withoutTransitivity()
.asSingleFile();
} catch (Exception e) {
Log.error(indent + " ! " + key);
throw e;
}
if (depJar != null && depJar.exists()) {
if (dep.getScope().equals(ScopeType.RUNTIME)) {
try {
parentCl.load(depJar);
} catch (IOException e) {
Log.error("Unable to load jar {} in class loader {}", depJar.getAbsolutePath(), parentCl.getKey());
}
} else {
depCl = bs.getKernel().put(key, depJar);
parentCl.attachChild(depCl);
}
Log.debug("{} + {} ({}ms)", indent, key, (System.currentTimeMillis() - before));
installDependencies(resolver, depCl, dep, depth+1);
} else {
Log.error("{} Unable to resolve {}", indent, key);
}
}
} else {
if (!dep.getScope().equals(ScopeType.RUNTIME)) {
parentCl.attachChild(depCl);
Log.debug("{} = {} already loaded", indent, key, (System.currentTimeMillis() - before));
}
}
}
}
}
@Override
public FlexyClassLoader installDeployUnit(DeployUnit deployUnit) {
FlexyClassLoader fcl = get(deployUnit);
if (fcl != null) {
return fcl;
} else {
ConfigurableMavenResolverSystem resolver = getResolver(deployUnit);
long before = System.currentTimeMillis();
Log.info("Resolving ............. {}", deployUnit.getUrl());
MavenResolvedArtifact artifact = resolver
.resolve(deployUnit.getUrl())
.withoutTransitivity()
.asSingleResolvedArtifact();
File duJar = artifact.asFile();
if (duJar != null && duJar.exists()) {
String key = getKey(artifact);
fcl = bs.getKernel().put(key, duJar);
installDependencies(resolver, fcl, artifact, 0);
Log.info("Resolved in {}ms", (System.currentTimeMillis() - before));
} else {
Log.error("Unable to resolve {}", deployUnit.getUrl());
}
}
return fcl;
}
private ConfigurableMavenResolverSystem getResolver(DeployUnit deployUnit) {
String offlineProp = System.getProperty("offline", "false");
boolean offline = Boolean.valueOf(offlineProp);
ConfigurableMavenResolverSystem resolver = Maven
.configureResolver()
.useLegacyLocalRepo(true)
.workOffline(offline);
try {
LogManager.getLogManager().readConfiguration(new FileInputStream(propFile));
} catch (IOException e) {
throw new RuntimeException("Unable to read " + propFile.getAbsolutePath());
}
// hacky way to treat Maven repositories as I don't want to change the Kevoree MM
for (Value val : deployUnit.getFilters()) {
if (val.getName().startsWith("repo_")) {
try {
resolver.withRemoteRepo(val.getName().substring(5), new URL(val.getValue()), "default");
} catch (MalformedURLException e) {
throw new RuntimeException("Invalid repository URL: " + val.getValue());
}
}
}
return resolver;
}
@Override
public void removeDeployUnit(DeployUnit deployUnit) {
bs.getKernel().drop(deployUnit.getUrl());
}
@Nullable
@Override
public FlexyClassLoader installTypeDefinition(Instance instance) {
TypeDefinition td = instance.getTypeDefinition();
DeployUnit du = validateFilters(instance, td.select("deployUnits[]/filters[name=platform,value=java]"));
return installDeployUnit(du);
}
@Override
public void setOffline(boolean b) {
offline = b;
}
private String nodeName;
public void setNodeName(String nName) {
nodeName = nName;
}
public KevoreeCoreBean core;
public void setCore(KevoreeCoreBean core) {
this.core = core;
}
@Override
@SuppressWarnings(value = "unchecked")
public synchronized Object createInstance(final Instance instance, final FlexyClassLoader classLoader) {
try {
final String mainClassName = searchMainClassName(instance);
final Class clazz = classLoader.loadClass(mainClassName);
final Object newInstance = clazz.newInstance();
injector.register(Context.class, new InstanceContext(instance.path(), nodeName, instance.getName()));
injector.register(ModelService.class, new ContextAwareAdapter(core, instance.path()));
injector.register(PlatformService.class, core);
injector.inject(newInstance);
return newInstance;
} catch (final Exception e) {
Log.error("Error while creating instance \"{}\" of type {}", e, instance.getName(),
instance.getTypeDefinition().getName());
}
return null;
}
private String searchMainClassName(final Instance instance) {
TypeDefinition td = instance.getTypeDefinition();
DeployUnit du = validateFilters(instance, td.select("deployUnits[]/filters[name=platform,value=java]"));
String tag = "class:" + td.getName() + ":" + td.getVersion();
Value tdefClassName = du.findFiltersByID(tag);
if (tdefClassName != null) {
return tdefClassName.getValue();
} else {
throw new RuntimeException("Cannot find meta-data \"" + tag + "\" in DeployUnit " + du.getHashcode() + "/" + du.getName() + "/" + du.getVersion());
}
}
private DeployUnit validateFilters(Instance instance, List<KMFContainer> filters) {
if (filters.size() > 1) {
String filtersStr = "";
for (int i=0; i < filters.size(); i++) {
filtersStr += filters.get(i).eContainer().path();
if (i < filters.size() - 1) {
filtersStr += ", ";
}
}
throw new RuntimeException("Instance " + instance.path() + " has " + filters.size() + " deployUnits ("+filtersStr+") that matches platform \"java\" (must only be one)");
}
return (DeployUnit) filters.get(0).eContainer();
}
@NotNull
@Override
public void injectDictionary(Instance instance, Object target, boolean defaultOnly) {
if (instance.getTypeDefinition() == null || instance.getTypeDefinition().getDictionaryType() == null) {
return;
}
for (DictionaryAttribute att : instance.getTypeDefinition().getDictionaryType().getAttributes()) {
String defValue = null;
String value = null;
if (att.getFragmentDependant()) {
FragmentDictionary fdico = instance.findFragmentDictionaryByID(nodeName);
if (fdico != null) {
Value tempValue = fdico.findValuesByID(att.getName());
if (tempValue != null) {
value = tempValue.getValue();
}
}
}
if (value == null) {
if (instance.getDictionary() != null) {
Value tempValue = instance.getDictionary().findValuesByID(att.getName());
if (tempValue != null) {
value = tempValue.getValue();
}
}
}
if (att.getDefaultValue() != null && !att.getDefaultValue().equals("")) {
defValue = att.getDefaultValue();
}
if (defaultOnly) {
if (defValue != null && value == null) {
internalInjectField(att.getName(), defValue, target);
}
} else {
if (value == null && defValue != null) {
value = defValue;
}
if (value != null) {
internalInjectField(att.getName(), value, target);
}
}
}
}
@NotNull
@Override
public void injectDictionaryValue(Value dictionaryValue, Object target) {
internalInjectField(dictionaryValue.getName(), dictionaryValue.getValue(), target);
}
private boolean internalInjectField(String fieldName, String value, Object target) {
if (target != null && value != null) {
try {
boolean isSet = false;
String setterName = "set";
setterName = setterName + fieldName.substring(0, 1).toUpperCase();
if (fieldName.length() > 1) {
setterName = setterName + fieldName.substring(1);
}
Method setter = lookupSetter(setterName, target.getClass());
if (setter != null && setter.getParameterTypes().length == 1) {
if (!setter.isAccessible()) {
setter.setAccessible(true);
}
Class pClazz = setter.getParameterTypes()[0];
if (pClazz.equals(boolean.class)) {
setter.invoke(target, Boolean.parseBoolean(value));
isSet = true;
}
if (pClazz.equals(Boolean.class)) {
setter.invoke(target, Boolean.parseBoolean(value));
isSet = true;
}
if (pClazz.equals(int.class)) {
setter.invoke(target, Integer.parseInt(value));
isSet = true;
}
if (pClazz.equals(Integer.class)) {
setter.invoke(target, Integer.parseInt(value));
isSet = true;
}
if (pClazz.equals(long.class)) {
setter.invoke(target, Long.parseLong(value));
isSet = true;
}
if (pClazz.equals(Long.class)) {
setter.invoke(target, Long.parseLong(value));
isSet = true;
}
if (pClazz.equals(double.class)) {
setter.invoke(target, Double.parseDouble(value));
isSet = true;
}
if (pClazz.equals(Double.class)) {
setter.invoke(target, Double.parseDouble(value));
isSet = true;
}
if (pClazz.equals(String.class)) {
setter.invoke(target, value);
isSet = true;
}
if (pClazz.equals(short.class)) {
setter.invoke(target, Short.parseShort(value));
isSet = true;
}
if (pClazz.equals(Short.class)) {
setter.invoke(target, Short.parseShort(value));
isSet = true;
}
if (pClazz.equals(float.class)) {
setter.invoke(target, Float.parseFloat(value));
isSet = true;
}
if (pClazz.equals(Float.class)) {
setter.invoke(target, Float.parseFloat(value));
isSet = true;
}
if (pClazz.equals(byte.class)) {
setter.invoke(target, Byte.parseByte(value));
isSet = true;
}
if (pClazz.equals(Byte.class)) {
setter.invoke(target, Byte.parseByte(value));
isSet = true;
}
if (value.length() == 1) {
if (pClazz.equals(char.class)) {
setter.invoke(target, value.charAt(0));
isSet = true;
}
}
}
if (!isSet) {
Field f = lookup(fieldName, target.getClass());
if (!f.isAccessible()) {
f.setAccessible(true);
}
if (f.getType().equals(boolean.class)) {
f.setBoolean(target, Boolean.parseBoolean(value));
}
if (f.getType().equals(Boolean.class)) {
f.set(target, Boolean.parseBoolean(value));
}
if (f.getType().equals(int.class)) {
f.setInt(target, Integer.parseInt(value));
}
if (f.getType().equals(Integer.class)) {
f.set(target, Integer.parseInt(value));
}
if (f.getType().equals(long.class)) {
f.setLong(target, Long.parseLong(value));
}
if (f.getType().equals(Long.class)) {
f.set(target, Long.parseLong(value));
}
if (f.getType().equals(double.class)) {
f.setDouble(target, Double.parseDouble(value));
}
if (f.getType().equals(Double.class)) {
f.set(target, Double.parseDouble(value));
}
if (f.getType().equals(String.class)) {
f.set(target, value);
}
if (f.getType().equals(short.class)) {
f.set(target, Short.parseShort(value));
}
if (f.getType().equals(Short.class)) {
f.set(target, Short.parseShort(value));
}
if (f.getType().equals(float.class)) {
f.set(target, Float.parseFloat(value));
}
if (f.getType().equals(Float.class)) {
f.set(target, Float.parseFloat(value));
}
if (f.getType().equals(byte.class)) {
f.set(target, Byte.parseByte(value));
}
if (f.getType().equals(Byte.class)) {
f.set(target, Byte.parseByte(value));
}
if (value.length() == 1) {
if (f.getType().equals(char.class)) {
f.set(target, value.charAt(0));
}
}
}
return true;
} catch (Exception e) {
Log.error("Unable to inject value \"{}\" in {} parameter \"{}\"", e.getCause(), value, target.getClass().getName(), fieldName);
return false;
}
} else {
return false;
}
}
public Method lookupSetter(String name, Class clazz) {
Method f = null;
for (Method loopMethod : clazz.getDeclaredMethods()) {
if (name.equals(loopMethod.getName())) {
f = loopMethod;
}
}
if (f != null) {
return f;
} else {
for (Class loopClazz : clazz.getInterfaces()) {
f = lookupSetter(name, loopClazz);
if (f != null) {
return f;
}
}
if (clazz.getSuperclass() != null) {
f = lookupSetter(name, clazz.getSuperclass());
if (f != null) {
return f;
}
}
}
return f;
}
public Field lookup(String name, Class clazz) {
Field f = null;
for (Field loopf : clazz.getDeclaredFields()) {
if (name.equals(loopf.getName())) {
f = loopf;
}
}
if (f != null) {
return f;
} else {
for (Class loopClazz : clazz.getInterfaces()) {
f = lookup(name, loopClazz);
if (f != null) {
return f;
}
}
if (clazz.getSuperclass() != null) {
f = lookup(name, clazz.getSuperclass());
if (f != null) {
return f;
}
}
}
return f;
}
@Override
public <T> void registerService(Class<T> serviceClass, T serviceImpl) {
injector.register(serviceClass, serviceImpl);
}
@Override
public <T> void unregisterService(Class<T> serviceClass) {
injector.unregister(serviceClass);
}
@Nullable
@Override
public File resolve(String url, Set<String> repos) {
return bs.getKernel().getResolver().resolve(url, (Set<String>) repos);
}
}