/**
* GRANITE DATA SERVICES
* Copyright (C) 2006-2015 GRANITE DATA SERVICES S.A.S.
*
* This file is part of the Granite Data Services Platform.
*
* ***
*
* Community License: GPL 3.0
*
* This file 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.
*
* This file 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 this program. If not, see <http://www.gnu.org/licenses/>.
*
* ***
*
* Available Commercial License: GraniteDS SLA 1.0
*
* This is the appropriate option if you are creating proprietary
* applications and you are not prepared to distribute and share the
* source code of your application under the GPL v3 license.
*
* Please visit http://www.granitedataservices.com/license for more
* details.
*/
package org.granite.client.tide.impl;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.granite.client.configuration.ClassScanner;
import org.granite.client.platform.Platform;
import org.granite.client.tide.Application;
import org.granite.client.tide.Context;
import org.granite.client.tide.ContextManager;
import org.granite.client.tide.EventBus;
import org.granite.client.tide.Factory;
import org.granite.client.tide.InstanceStore;
import org.granite.client.tide.InstanceStoreFactory;
import org.granite.client.tide.Module;
import org.granite.util.TypeUtil;
/**
* @author William DRAI
*/
public class SimpleContextManager implements ContextManager {
static final String DEFAULT_CONTEXT = "__DEFAULT__CONTEXT__";
public static final String CONTEXT_CREATE = "org.granite.tide.contextCreate";
public static final String CONTEXT_DESTROY = "org.granite.tide.contextDestroy";
protected final Application application;
protected final EventBus eventBus;
private InstanceStoreFactory instanceStoreFactory = new DefaultInstanceStoreFactory();
private InstanceFactory instanceFactory = new InstanceFactory();
private Map<String, Context> contextsById = new HashMap<String, Context>();
private List<String> contextsToDestroy = new ArrayList<String>();
public SimpleContextManager() {
// CDI proxying...
this.application = new DefaultApplication();
this.eventBus = new SimpleEventBus();
}
/**
* Create a simple context manager using the specified application
* @param application application
*/
public SimpleContextManager(Application application) {
this.application = application;
this.eventBus = new SimpleEventBus();
}
/**
* Create a simple context manager using the specified application and event bus
* @param application application
* @param eventBus event bus
*/
public SimpleContextManager(Application application, EventBus eventBus) {
this.application = application;
this.eventBus = eventBus;
}
/**
* Set the instance store factory for this context manager
* @param instanceStoreFactory instance store factory
*/
public void setInstanceStoreFactory(InstanceStoreFactory instanceStoreFactory) {
this.instanceStoreFactory = instanceStoreFactory;
}
public static class DefaultInstanceStoreFactory implements InstanceStoreFactory {
@Override
public InstanceStore createStore(Context context, InstanceFactory instanceFactory) {
try {
TypeUtil.forName("javax.inject.Inject");
return new SimpleInjectInstanceStore(context, instanceFactory);
}
catch (ClassNotFoundException e) {
return new SimpleInstanceStore(context, instanceFactory);
}
}
}
/**
* Determine if the specified context is the global one
*
* @param context
* @return true if global
*/
public boolean isGlobal(Context context) {
return contextsById.get(DEFAULT_CONTEXT) == context;
}
public Context getContext() {
return getContext(null, null, true);
}
public Context getContext(String contextId) {
return getContext(contextId, null, true);
}
protected Context createContext(Context parentCtx, String contextId) {
Context ctx = new Context(this, parentCtx, contextId);
InstanceStore instanceStore = instanceStoreFactory.createStore(ctx, instanceFactory);
ctx.initContext(application, eventBus, instanceStore);
return ctx;
}
public Context getContext(String contextId, String parentContextId, boolean create) {
Context ctx = contextsById.get(contextId != null ? contextId : DEFAULT_CONTEXT);
if (ctx == null && create) {
Context parentCtx = contextsById.get(parentContextId == null ? DEFAULT_CONTEXT : parentContextId);
if (parentContextId != null && parentCtx == null)
throw new IllegalStateException("Parent context not found for id " + parentContextId);
ctx = createContext(parentCtx, contextId);
contextsById.put(contextId != null ? contextId : DEFAULT_CONTEXT, ctx);
instanceFactory.initContext(ctx);
if (contextId != null)
ctx.getEventBus().raiseEvent(ctx, CONTEXT_CREATE);
ctx.postInit();
}
return ctx;
}
public Context newContext(String contextId, String parentContextId) {
Context ctx = contextsById.get(contextId != null ? contextId : DEFAULT_CONTEXT);
if (ctx != null && ctx.isFinished()) {
ctx.clear();
contextsById.remove(contextId);
removeFromContextsToDestroy(contextId);
ctx = null;
}
if (ctx == null) {
Context parentCtx = contextsById.get(parentContextId != null ? parentContextId : DEFAULT_CONTEXT);
ctx = createContext(parentCtx, contextId);
if (contextId != null)
contextsById.put(contextId, ctx);
ctx.getEventBus().raiseEvent(ctx, CONTEXT_CREATE);
ctx.postInit();
}
return ctx;
}
public void destroyContext(String contextId) {
Context ctx = contextId != null ? contextsById.get(contextId) : null;
if (ctx != null) {
// Destroy child contexts
for (Context c : contextsById.values()) {
if (c.getParentContext() == ctx)
destroyContext(c.getContextId());
}
removeFromContextsToDestroy(contextId);
ctx.getEventBus().raiseEvent(ctx, CONTEXT_DESTROY);
contextsById.get(contextId).clear();
contextsById.remove(contextId);
}
}
public List<Context> getAllContexts() {
List<Context> contexts = new ArrayList<Context>();
for (Entry<String, Context> ectx : contextsById.entrySet()) {
if (!ectx.getKey().equals(DEFAULT_CONTEXT))
contexts.add(ectx.getValue());
}
return contexts;
}
// /**
// * Execute a function for each conversation context
// *
// * @param parentContext parent context
// * @param callback callback function
// * @param token token passed to the function
// */
// public function forEachChildContext(parentContext:Context, callback:Function, token:Object = null):void {
// for each (var ctx:Context in _ctx) {
// if (ctx.meta_parentContext === parentContext) {
// if (token)
// callback(ctx, token);
// else
// callback(ctx);
// }
// }
// }
public void destroyContexts() {
contextsToDestroy.clear();
Context globalCtx = contextsById.get(DEFAULT_CONTEXT);
List<String> contextIdsToDestroy = new ArrayList<String>();
for (Entry<String, Context> ectx : contextsById.entrySet()) {
if (!ectx.getKey().equals(DEFAULT_CONTEXT) && ectx.getValue().getParentContext() == globalCtx)
contextIdsToDestroy.add(ectx.getKey());
}
for (String contextId : contextIdsToDestroy)
destroyContext(contextId);
globalCtx.clear();
}
public void destroyFinishedContexts() {
for (String contextId : contextsToDestroy)
destroyContext(contextId);
contextsToDestroy.clear();
}
/**
* Remove context from the list of contexts to destroy
*
* @param contextId context id
*/
public void removeFromContextsToDestroy(String contextId) {
int idx = contextsToDestroy.indexOf(contextId);
if (idx >= 0)
contextsToDestroy.remove(idx);
}
/**
* Add context to the list of contexts to destroy
*
* @param contextId context id
*/
public void addToContextsToDestroy(String contextId) {
if (contextsToDestroy.contains(contextId))
return;
contextsToDestroy.add(contextId);
}
public Context retrieveContext(Context sourceContext, String contextId, boolean wasConversationCreated, boolean wasConversationEnded) {
Context context = null;
if (!isGlobal(sourceContext) && contextId == null && wasConversationEnded) {
// The conversation of the source context was ended
// Get results in the current conversation when finished
context = sourceContext;
context.markAsFinished();
}
else if (!isGlobal(sourceContext) && contextId == null && !sourceContext.isContextIdFromServer()) {
// A call to a non conversational component was issued from a conversation context
// Get results in the current conversation
context = sourceContext;
}
else if (!isGlobal(sourceContext) && contextId != null
&& (sourceContext.getContextId() == null || (!sourceContext.getContextId().equals(contextId) && !wasConversationCreated))) {
// The conversationId has been updated by the server
String previousContextId = sourceContext.getContextId();
context = sourceContext;
context.setContextId(contextId, true);
updateContextId(previousContextId, context);
}
else {
context = getContext(contextId);
if (contextId != null)
context.setContextId(contextId, true);
}
return context;
}
/**
* Defines new context for existing id
*
* @param previousContextId existing id
* @param context new context
*/
public void updateContextId(String previousContextId, Context context) {
if (previousContextId != null)
contextsById.remove(previousContextId);
contextsById.put(context.getContextId(), context);
}
/**
* Scan specific packages and configure found modules
* @param packageNames module package names
*/
public void scanModules(String... packageNames) {
ClassScanner classScanner = Platform.getInstance().newClassScanner();
Set<Class<?>> moduleClasses = classScanner.scan(new HashSet<String>(Arrays.asList(packageNames)), Module.class);
instanceFactory.initModules(moduleClasses);
}
/**
* Configure modules for specified class names
* @param moduleClassNames module classes
*/
public void initModules(String... moduleClassNames) {
Set<Class<?>> moduleClasses = new HashSet<Class<?>>();
for (String className : moduleClassNames) {
try {
Class<?> c = Class.forName(className);
moduleClasses.add(c);
}
catch (ClassNotFoundException e) {
throw new RuntimeException("Could not init module " + className);
}
}
instanceFactory.initModules(moduleClasses);
}
/**
* Configure modules for specified class names
* @param moduleClasses module classes
*/
public void initModules(Class<?>... moduleClasses) {
instanceFactory.initModules(new HashSet<Class<?>>(Arrays.asList(moduleClasses)));
}
public void registerFactory(String name, Factory<?> factory) {
instanceFactory.registerFactory(name, factory);
}
public void registerFactory(Class<?> type, Factory<?> factory) {
instanceFactory.registerFactory(type, factory);
}
}