juzu.impl.plugin.application.ApplicationLifeCycle Maven / Gradle / Ivy
/*
* Copyright 2013 eXo Platform SAS
*
* 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 juzu.impl.plugin.application;
import juzu.Scope;
import juzu.impl.asset.AssetManager;
import juzu.impl.asset.AssetServer;
import juzu.impl.common.Filter;
import juzu.impl.common.JSON;
import juzu.impl.common.Name;
import juzu.impl.common.Tools;
import juzu.impl.fs.spi.ReadFileSystem;
import juzu.impl.inject.BeanDescriptor;
import juzu.impl.inject.spi.BeanLifeCycle;
import juzu.impl.inject.spi.InjectionContext;
import juzu.impl.inject.spi.Injector;
import juzu.impl.inject.spi.InjectorProvider;
import juzu.impl.inject.spi.spring.SpringInjector;
import juzu.impl.common.Logger;
import juzu.impl.metadata.Descriptor;
import juzu.impl.plugin.Plugin;
import juzu.impl.plugin.PluginContext;
import juzu.impl.plugin.application.descriptor.ApplicationDescriptor;
import juzu.impl.plugin.asset.AssetPlugin;
import juzu.impl.plugin.module.ModuleLifeCycle;
import juzu.impl.resource.ResourceResolver;
import javax.inject.Provider;
import java.io.Closeable;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.ServiceLoader;
/**
* The application life cycle.
*
* @author Julien Viet
*/
public class ApplicationLifeCycle implements Closeable {
/** Configuration: name. */
private final Name name;
/** Configuration: injector provider. */
private final InjectorProvider injectorProvider;
/** Contextual: logger. */
private final Logger log;
/** Contextual: resources. */
private final ReadFileSystem resources;
/** Contextual: resoure resolver. */
private final ResourceResolver resourceResolver;
/** Contextual: asset server. */
private final AssetServer assetServer;
/** Contextual: module. */
private final ModuleLifeCycle> moduleLifeCycle;
/** . */
private ApplicationDescriptor descriptor;
/** . */
private AssetManager stylesheetManager;
/** . */
private AssetManager scriptManager;
/** . */
private InjectionContext, ?> injectionContext;
/** . */
private BeanLifeCycle application;
/** The last used class loader : used for checking refresh. */
private ClassLoader classLoader;
/** . */
private Map plugins;
/** . */
private Map pluginDescriptors;
public ApplicationLifeCycle(
Logger log,
ModuleLifeCycle> moduleLifeCycle,
InjectorProvider injectorProvider,
Name name,
ReadFileSystem resources,
AssetServer assetServer,
ResourceResolver resourceResolver) {
//
this.log = log;
this.moduleLifeCycle = moduleLifeCycle;
this.injectorProvider = injectorProvider;
this.name = name;
this.resources = resources;
this.assetServer = assetServer;
this.resourceResolver = resourceResolver;
}
public Name getName() {
return name;
}
public Application getApplication() {
return application != null ? application.peek() : null;
}
public AssetManager getScriptManager() {
return scriptManager;
}
public AssetManager getStylesheetManager() {
return stylesheetManager;
}
public ApplicationDescriptor getDescriptor() {
return descriptor;
}
public P getPlugin(Class
pluginType) {
try {
BeanLifeCycle
pluginLifeCycle = injectionContext.get(pluginType);
return pluginLifeCycle != null ? pluginLifeCycle.get() : null;
}
catch (InvocationTargetException e) {
throw new UnsupportedOperationException("Handle me gracefully", e);
}
}
public boolean refresh() throws Exception {
if (application != null) {
if (classLoader != moduleLifeCycle.getClassLoader()) {
stop();
}
}
//
if (application == null) {
log.log("Building application");
start();
return true;
} else {
return false;
}
}
protected final void start() throws Exception {
ReadFileSystem> classes = moduleLifeCycle.getClasses();
//
Name fqn = name.append("Application");
//
Class> clazz = moduleLifeCycle.getClassLoader().loadClass(fqn.toString());
ApplicationDescriptor descriptor = ApplicationDescriptor.create(clazz);
// Take care of plugins
HashMap plugins = new HashMap();
for (ApplicationPlugin plugin : ServiceLoader.load(ApplicationPlugin.class)) {
plugins.put(plugin.getName(), plugin);
}
HashSet names = new HashSet(descriptor.getConfig().names());
HashMap configs = new HashMap();
for (ApplicationPlugin plugin : plugins.values()) {
String name = plugin.getName();
if (names.remove(name)) {
configs.put(plugin, descriptor.getConfig().getJSON(plugin.getName()));
} else {
configs.put(plugin, null);
}
}
if (names.size() > 0) {
throw new UnsupportedOperationException("Handle me gracefully : missing plugins " + names);
}
//
final ResourceResolver applicationResolver = new ResourceResolver() {
public URL resolve(String uri) {
return moduleLifeCycle.getClassLoader().getResource(uri.substring(1));
}
};
//
HashMap pluginDescriptors = new HashMap();
for (final Map.Entry entry : configs.entrySet()) {
ApplicationPlugin plugin = entry.getKey();
PluginContext pluginContext = new PluginContext() {
public JSON getConfig() {
return entry.getValue();
}
public ClassLoader getClassLoader() {
return moduleLifeCycle.getClassLoader();
}
public ResourceResolver getServerResolver() {
return resourceResolver;
}
public ResourceResolver getApplicationResolver() {
return applicationResolver;
}
};
plugin.setApplication(descriptor);
Descriptor pluginDescriptor = plugin.init(pluginContext);
if (pluginDescriptor != null) {
pluginDescriptors.put(plugin.getName(), pluginDescriptor);
}
}
//
for (Iterator i = plugins.keySet().iterator();i.hasNext();) {
String name = i.next();
if (!pluginDescriptors.containsKey(name)) {
i.remove();
}
}
//
Provider provider = injectorProvider;
Injector injector = provider.get();
injector.addFileSystem(classes);
injector.setClassLoader(moduleLifeCycle.getClassLoader());
//
if (injector instanceof SpringInjector) {
R springName = resources.getPath("spring.xml");
if (springName != null) {
URL configurationURL = resources.getURL(springName);
((SpringInjector)injector).setConfigurationURL(configurationURL);
}
}
//
log.log("Starting " + descriptor.getName());
InjectionContext, ?> injectionContext = doStart(descriptor, injector, plugins.values(), pluginDescriptors.values());
//
AssetPlugin assetPlugin = injectionContext.get(AssetPlugin.class).get();
BeanLifeCycle application = injectionContext.get(Application.class);
//
if (assetServer != null) {
assetServer.register(this);
}
//
this.injectionContext = injectionContext;
this.scriptManager = assetPlugin.getScriptManager();
this.stylesheetManager = assetPlugin.getStylesheetManager();
this.descriptor = descriptor;
this.application = application;
this.classLoader = moduleLifeCycle.getClassLoader();
this.plugins = plugins;
this.pluginDescriptors = pluginDescriptors;
// For application start (perhaps we could remove that)
try {
application.get();
}
catch (InvocationTargetException e) {
throw new UnsupportedOperationException("handle me gracefully", e);
}
}
private static InjectionContext doStart(
final ApplicationDescriptor descriptor,
Injector injector,
Collection plugins,
Collection pluginDescriptors) {
// Bind the application descriptor
injector.bindBean(ApplicationDescriptor.class, null, descriptor);
// Bind the application context
injector.declareBean(Application.class, null, null, null);
// Bind the scopes
for (Scope scope : Scope.values()) {
injector.addScope(scope);
}
// Bind the plugins
for (Plugin plugin : plugins) {
// Bind the plugin as a bean
Class aClass = plugin.getClass();
Object o = plugin;
injector.bindBean(aClass, null, o);
}
// Bind the beans
for (Descriptor pluginDescriptor : pluginDescriptors) {
for (BeanDescriptor bean : pluginDescriptor.getBeans()) {
bean.bind(injector);
}
}
// Filter the classes:
// any class beginning with juzu. is refused
// any class prefixed with the application package is accepted
// any other application class is refused (i.e a class having an ancestor package annotated with @Application)
Filter> filter = new Filter>() {
HashSet blackList = new HashSet();
public boolean accept(Class> elt) {
if (elt.getName().startsWith("juzu.")) {
return false;
} else if (elt.getPackage().getName().startsWith(descriptor.getPackageName())) {
return true;
} else {
for (String currentPkg = elt.getPackage().getName();currentPkg != null;currentPkg = Tools.parentPackageOf(currentPkg)) {
if (blackList.contains(currentPkg)) {
return false;
} else {
try {
Class> packageClass = descriptor.getApplicationLoader().loadClass(currentPkg + ".package-info");
juzu.Application ann = packageClass.getAnnotation(juzu.Application.class);
if (ann != null) {
blackList.add(currentPkg);
return false;
}
}
catch (ClassNotFoundException e) {
// Skip it
}
}
}
return true;
}
}
};
//
InjectionContext injectionContext;
try {
injectionContext = (InjectionContext)injector.create(filter);
}
catch (Exception e) {
throw new UnsupportedOperationException("handle me gracefully", e);
}
//
return injectionContext;
}
void stop() {
if (assetServer != null) {
assetServer.unregister(this);
}
Tools.safeClose(application);
Tools.safeClose(injectionContext);
application = null;
injectionContext = null;
stylesheetManager = null;
scriptManager = null;
descriptor = null;
classLoader = null;
plugins = null;
pluginDescriptors = null;
}
public void close() {
stop();
}
}