001/** 002 * GRANITE DATA SERVICES 003 * Copyright (C) 2006-2013 GRANITE DATA SERVICES S.A.S. 004 * 005 * This file is part of the Granite Data Services Platform. 006 * 007 * *** 008 * 009 * Community License: GPL 3.0 010 * 011 * This file is free software: you can redistribute it and/or modify 012 * it under the terms of the GNU General Public License as published 013 * by the Free Software Foundation, either version 3 of the License, 014 * or (at your option) any later version. 015 * 016 * This file is distributed in the hope that it will be useful, but 017 * WITHOUT ANY WARRANTY; without even the implied warranty of 018 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 019 * GNU General Public License for more details. 020 * 021 * You should have received a copy of the GNU General Public License 022 * along with this program. If not, see <http://www.gnu.org/licenses/>. 023 * 024 * *** 025 * 026 * Available Commercial License: GraniteDS SLA 1.0 027 * 028 * This is the appropriate option if you are creating proprietary 029 * applications and you are not prepared to distribute and share the 030 * source code of your application under the GPL v3 license. 031 * 032 * Please visit http://www.granitedataservices.com/license for more 033 * details. 034 */ 035package org.granite.client.tide.impl; 036 037import java.util.ArrayList; 038import java.util.HashMap; 039import java.util.List; 040import java.util.Map; 041import java.util.Map.Entry; 042 043import org.granite.client.tide.Application; 044import org.granite.client.tide.Context; 045import org.granite.client.tide.ContextManager; 046import org.granite.client.tide.EventBus; 047import org.granite.client.tide.InstanceStore; 048import org.granite.client.tide.InstanceStoreFactory; 049 050/** 051 * @author William DRAI 052 */ 053public class SimpleContextManager implements ContextManager { 054 055 static final String DEFAULT_CONTEXT = "__DEFAULT__CONTEXT__"; 056 057 public static final String CONTEXT_CREATE = "org.granite.tide.contextCreate"; 058 public static final String CONTEXT_DESTROY = "org.granite.tide.contextDestroy"; 059 060 protected final Application application; 061 protected final EventBus eventBus; 062 private InstanceStoreFactory instanceStoreFactory = new DefaultInstanceStoreFactory(); 063 private Map<String, Context> contextsById = new HashMap<String, Context>(); 064 private List<String> contextsToDestroy = new ArrayList<String>(); 065 066 067 public SimpleContextManager() { 068 // CDI proxying... 069 this.application = new DefaultApplication(); 070 this.eventBus = new SimpleEventBus(); 071 } 072 073 /** 074 * Create a simple context manager using the specified application 075 * @param application application 076 */ 077 public SimpleContextManager(Application application) { 078 this.application = application; 079 this.eventBus = new SimpleEventBus(); 080 } 081 082 /** 083 * Create a simple context manager using the specified application and event bus 084 * @param application application 085 * @param eventBus event bus 086 */ 087 public SimpleContextManager(Application application, EventBus eventBus) { 088 this.application = application; 089 this.eventBus = eventBus; 090 } 091 092 /** 093 * Set the instance store factory for this context manager 094 * @param instanceStoreFactory instance store factory 095 */ 096 public void setInstanceStoreFactory(InstanceStoreFactory instanceStoreFactory) { 097 this.instanceStoreFactory = instanceStoreFactory; 098 } 099 100 public static class DefaultInstanceStoreFactory implements InstanceStoreFactory { 101 @Override 102 public InstanceStore createStore(Context context) { 103 return new SimpleInstanceStore(context); 104 } 105 } 106 107 108 /** 109 * Determine if the specified context is the global one 110 * 111 * @param context 112 * @return true if global 113 */ 114 public boolean isGlobal(Context context) { 115 return contextsById.get(DEFAULT_CONTEXT) == context; 116 } 117 118 public Context getContext() { 119 return getContext(null, null, true); 120 } 121 122 public Context getContext(String contextId) { 123 return getContext(contextId, null, true); 124 } 125 126 127 protected Context createContext(Context parentCtx, String contextId) { 128 Context ctx = new Context(this, parentCtx, contextId); 129 ctx.initContext(application, eventBus, instanceStoreFactory.createStore(ctx)); 130 return ctx; 131 } 132 133 public Context getContext(String contextId, String parentContextId, boolean create) { 134 Context ctx = contextsById.get(contextId != null ? contextId : DEFAULT_CONTEXT); 135 if (ctx == null && create) { 136 Context parentCtx = contextsById.get(parentContextId == null ? DEFAULT_CONTEXT : parentContextId); 137 if (parentContextId != null && parentCtx == null) 138 throw new IllegalStateException("Parent context not found for id " + parentContextId); 139 140 ctx = createContext(parentCtx, contextId); 141 contextsById.put(contextId != null ? contextId : DEFAULT_CONTEXT, ctx); 142 if (contextId != null) 143 ctx.getEventBus().raiseEvent(ctx, CONTEXT_CREATE); 144 ctx.postInit(); 145 } 146 return ctx; 147 } 148 149 public Context newContext(String contextId, String parentContextId) { 150 Context ctx = contextsById.get(contextId != null ? contextId : DEFAULT_CONTEXT); 151 if (ctx != null && ctx.isFinished()) { 152 ctx.clear(); 153 contextsById.remove(contextId); 154 removeFromContextsToDestroy(contextId); 155 ctx = null; 156 } 157 if (ctx == null) { 158 Context parentCtx = contextsById.get(parentContextId != null ? parentContextId : DEFAULT_CONTEXT); 159 ctx = createContext(parentCtx, contextId); 160 if (contextId != null) 161 contextsById.put(contextId, ctx); 162 ctx.getEventBus().raiseEvent(ctx, CONTEXT_CREATE); 163 ctx.postInit(); 164 } 165 return ctx; 166 } 167 168 public void destroyContext(String contextId) { 169 Context ctx = contextId != null ? contextsById.get(contextId) : null; 170 if (ctx != null) { 171 // Destroy child contexts 172 for (Context c : contextsById.values()) { 173 if (c.getParentContext() == ctx) 174 destroyContext(c.getContextId()); 175 } 176 177 removeFromContextsToDestroy(contextId); 178 ctx.getEventBus().raiseEvent(ctx, CONTEXT_DESTROY); 179 contextsById.get(contextId).clear(); 180 contextsById.remove(contextId); 181 } 182 } 183 184 public List<Context> getAllContexts() { 185 List<Context> contexts = new ArrayList<Context>(); 186 for (Entry<String, Context> ectx : contextsById.entrySet()) { 187 if (!ectx.getKey().equals(DEFAULT_CONTEXT)) 188 contexts.add(ectx.getValue()); 189 } 190 return contexts; 191 } 192 193// /** 194// * Execute a function for each conversation context 195// * 196// * @param parentContext parent context 197// * @param callback callback function 198// * @param token token passed to the function 199// */ 200// public function forEachChildContext(parentContext:Context, callback:Function, token:Object = null):void { 201// for each (var ctx:Context in _ctx) { 202// if (ctx.meta_parentContext === parentContext) { 203// if (token) 204// callback(ctx, token); 205// else 206// callback(ctx); 207// } 208// } 209// } 210 211 public void destroyContexts() { 212 contextsToDestroy.clear(); 213 214 Context globalCtx = contextsById.get(DEFAULT_CONTEXT); 215 List<String> contextIdsToDestroy = new ArrayList<String>(); 216 for (Entry<String, Context> ectx : contextsById.entrySet()) { 217 if (!ectx.getKey().equals(DEFAULT_CONTEXT) && ectx.getValue().getParentContext() == globalCtx) 218 contextIdsToDestroy.add(ectx.getKey()); 219 } 220 for (String contextId : contextIdsToDestroy) 221 destroyContext(contextId); 222 223 globalCtx.clear(); 224 } 225 226 public void destroyFinishedContexts() { 227 for (String contextId : contextsToDestroy) 228 destroyContext(contextId); 229 contextsToDestroy.clear(); 230 } 231 232 233 /** 234 * Remove context from the list of contexts to destroy 235 * 236 * @param contextId context id 237 */ 238 public void removeFromContextsToDestroy(String contextId) { 239 int idx = contextsToDestroy.indexOf(contextId); 240 if (idx >= 0) 241 contextsToDestroy.remove(idx); 242 } 243 244 /** 245 * Add context to the list of contexts to destroy 246 * 247 * @param contextId context id 248 */ 249 public void addToContextsToDestroy(String contextId) { 250 if (contextsToDestroy.contains(contextId)) 251 return; 252 contextsToDestroy.add(contextId); 253 } 254 255 256 public Context retrieveContext(Context sourceContext, String contextId, boolean wasConversationCreated, boolean wasConversationEnded) { 257 Context context = null; 258 if (!isGlobal(sourceContext) && contextId == null && wasConversationEnded) { 259 // The conversation of the source context was ended 260 // Get results in the current conversation when finished 261 context = sourceContext; 262 context.markAsFinished(); 263 } 264 else if (!isGlobal(sourceContext) && contextId == null && !sourceContext.isContextIdFromServer()) { 265 // A call to a non conversational component was issued from a conversation context 266 // Get results in the current conversation 267 context = sourceContext; 268 } 269 else if (!isGlobal(sourceContext) && contextId != null 270 && (sourceContext.getContextId() == null || (!sourceContext.getContextId().equals(contextId) && !wasConversationCreated))) { 271 // The conversationId has been updated by the server 272 String previousContextId = sourceContext.getContextId(); 273 context = sourceContext; 274 context.setContextId(contextId, true); 275 updateContextId(previousContextId, context); 276 } 277 else { 278 context = getContext(contextId); 279 if (contextId != null) 280 context.setContextId(contextId, true); 281 } 282 283 return context; 284 } 285 286 /** 287 * Defines new context for existing id 288 * 289 * @param previousContextId existing id 290 * @param context new context 291 */ 292 public void updateContextId(String previousContextId, Context context) { 293 if (previousContextId != null) 294 contextsById.remove(previousContextId); 295 contextsById.put(context.getContextId(), context); 296 } 297 298}