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; 036 037import java.lang.annotation.Annotation; 038import java.util.Collections; 039import java.util.HashMap; 040import java.util.List; 041import java.util.Map; 042 043import org.granite.client.tide.data.EntityManager; 044import org.granite.client.tide.data.impl.EntityManagerImpl; 045import org.granite.client.tide.data.impl.JavaBeanDataManager; 046import org.granite.client.tide.data.impl.RemoteInitializerImpl; 047import org.granite.client.tide.data.spi.DataManager; 048import org.granite.client.tide.impl.DefaultApplication; 049import org.granite.client.tide.impl.SimpleEventBus; 050import org.granite.client.tide.impl.SimpleInstanceStore; 051import org.granite.logging.Logger; 052 053/** 054 * General Tide context implementation 055 * It can either wrap a Spring or CDI container or be used separately 056 * 057 * Currently only one context can be active at a time 058 * 059 * A context is created by a context manager 060 * 061 * @see org.granite.client.tide.ContextManager 062 * 063 * @author William DRAI 064 */ 065public class Context { 066 067 static final Logger log = Logger.getLogger(Context.class); 068 069 private String contextId = null; 070 private boolean isContextIdFromServer = false; 071 private boolean finished = false; 072 073 private ContextManager contextManager = null; 074 075 private InstanceStore instanceStore = new SimpleInstanceStore(this); 076 private Map<String, Object> initialBeans = new HashMap<String, Object>(); 077 078 private Application application = new DefaultApplication(); 079 private EventBus eventBus = new SimpleEventBus(); 080 081 private DataManager dataManager = new JavaBeanDataManager(); 082 private EntityManager entityManager; 083 084 085 protected Context() { 086 // CDI proxying... 087 } 088 089 /** 090 * Create a context using the specified manager and context id 091 * Should not be used directly 092 * @param contextManager context manager 093 * @param parentCtx parent context for conversation contexts (not supported yet) 094 * @param contextId context id 095 */ 096 public Context(ContextManager contextManager, Context parentCtx, String contextId) { 097 this.contextManager = contextManager; 098 // TODO: conversation contexts 099 // parentCtx 100 this.contextId = contextId; 101 } 102 103 /** 104 * Managed for this context 105 * @return context manager 106 */ 107 public ContextManager getContextManager() { 108 return contextManager; 109 } 110 111 /** 112 * Entity manager for this context 113 * @return entity manager 114 */ 115 public EntityManager getEntityManager() { 116 return entityManager; 117 } 118 119 /** 120 * Set the data manager for this context 121 * @param dataManager data manager 122 * @see org.granite.client.tide.data.spi.DataManager 123 */ 124 public void setDataManager(DataManager dataManager) { 125 this.dataManager = dataManager; 126 } 127 128 /** 129 * Data manager for this context 130 * @return data manager 131 */ 132 public DataManager getDataManager() { 133 return dataManager; 134 } 135 136 /** 137 * Map of beans defined before the initialization of the context so they can be registered in the DI container 138 * @return map of initialization beans keyed by name 139 */ 140 public Map<String, Object> getInitialBeans() { 141 return Collections.unmodifiableMap(initialBeans); 142 } 143 144 /** 145 * Initialize the context 146 * @param application application for this context (depends on the target platform/framework) 147 * @param eventBus event bus for this context (depends on the framework and/or the DI container) 148 * @param instanceStore instance store (depends on the DI container) 149 */ 150 public void initContext(Application application, EventBus eventBus, InstanceStore instanceStore) { 151 this.application = application; 152 this.eventBus = eventBus; 153 this.instanceStore = instanceStore; 154 application.initContext(this, initialBeans); 155 this.entityManager = new EntityManagerImpl("", dataManager); 156 this.entityManager.setRemoteInitializer(new RemoteInitializerImpl(this)); 157 } 158 159 /** 160 * Event bus for this context 161 * @return event bus 162 */ 163 public EventBus getEventBus() { 164 return eventBus; 165 } 166 167 public void postInit() { 168 // TODO: postInit ? 169 } 170 171 /** 172 * Parent context for conversation contexts 173 * @return parent context 174 */ 175 public Context getParentContext() { 176 return null; 177 } 178 179 /** 180 * Context id 181 * @return context id 182 */ 183 public String getContextId() { 184 return contextId; 185 } 186 187 /** 188 * Indicate that the context id has been defined by the server 189 * Unused for now 190 * @return true if id received from server 191 */ 192 public boolean isContextIdFromServer() { 193 return isContextIdFromServer; 194 } 195 196 /** 197 * Indicate that the context is eligible for destruction 198 * @return true is finished 199 */ 200 public boolean isFinished() { 201 return finished; 202 } 203 204 /** 205 * Update the context id 206 * @param contextId context id 207 * @param fromServer is this id received from the server ? 208 */ 209 public void setContextId(String contextId, boolean fromServer) { 210 String previousContextId = this.contextId; 211 this.contextId = contextId; 212 this.isContextIdFromServer = fromServer; 213 contextManager.updateContextId(previousContextId, this); 214 } 215 216 /** 217 * Return a component instance by its name in the container 218 * InstanceStore implementations are free to (but don't have to) automatically create a suitable component instance 219 * with the expected name when no instance exists 220 * @param name component name 221 * @param <T> component type 222 * @return component instance 223 */ 224 public <T> T byName(String name) { 225 return instanceStore.byName(name, this); 226 } 227 228 /** 229 * Return a component instance by its name in the container 230 * Does not create a default proxy ({@link org.granite.client.tide.impl.ComponentImpl}) if no instance exists 231 * @param name component name 232 * @param <T> component type 233 * @return component instance or null if not found 234 */ 235 public <T> T byNameNoProxy(String name) { 236 return instanceStore.getNoProxy(name, this); 237 } 238 239 /** 240 * Return a component instance looked up by its type 241 * If more than one instance is found, throws a runtime exception 242 * @param type expected component type 243 * @param <T> expected component type 244 * @return component instance 245 */ 246 public <T> T byType(Class<T> type) { 247 return instanceStore.byType(type, this); 248 } 249 250 /** 251 * Return an array of all component instances implementing the expected type 252 * @param type expected component type 253 * @param <T> expected component type 254 * @return array of component instances 255 */ 256 public <T> T[] allByType(Class<T> type) { 257 return instanceStore.allByType(type, this, true); 258 } 259 /** 260 * Return an array of all component instances implementing the expected type 261 * @param type expected component type 262 * @param create if true, should create an instance if none is existing 263 * @param <T> expected component type 264 * @return array of component instances or null if no instance found 265 */ 266 public <T> T[] allByType(Class<T> type, boolean create) { 267 return instanceStore.allByType(type, this, create); 268 } 269 270 /** 271 * Return a map of all component instances annotated with the specified annotation 272 * @param annotationClass annotation 273 * @return map of component instances keyed by name 274 */ 275 public Map<String, Object> allByAnnotatedWith(Class<? extends Annotation> annotationClass) { 276 return instanceStore.allByAnnotatedWith(annotationClass, this); 277 } 278 279 /** 280 * Return a list of all component names in this context 281 * @return list of names 282 */ 283 public List<String> allNames() { 284 return instanceStore.allNames(); 285 } 286 287 /** 288 * Set a component instance as a managed instance with the specified name in the context 289 * May not work with all containers (Spring and CDI are static and cannot be modified after initialization) 290 * @param name component name 291 * @param instance component instance 292 * @param <T> component type 293 * @return component instance 294 */ 295 public <T> T set(String name, T instance) { 296 return instanceStore.set(name, instance); 297 } 298 299 /** 300 * Set a component instance as a managed instance in the context 301 * May not work with all containers (Spring and CDI are static and cannot be modified after initialization) 302 * @param instance component instance 303 * @param <T> component type 304 * @return component instance 305 */ 306 public <T> T set(T instance) { 307 return instanceStore.set(instance); 308 } 309 310 /** 311 * Remove the component instance having the specified name from the context 312 * May not work with all containers (Spring and CDI are static and cannot be modified after initialization) 313 * @param name component name 314 */ 315 public void remove(String name) { 316 instanceStore.remove(name); 317 } 318 319 /** 320 * Clear all data and instances in the context 321 */ 322 public void clear() { 323 entityManager.clear(); 324 instanceStore.clear(); 325 } 326 327 /** 328 * Initialize an instance when it is added to the context 329 * @param instance component instance 330 * @param name component name 331 */ 332 public void initInstance(Object instance, String name) { 333 if (name != null && instance instanceof NameAware) 334 ((NameAware)instance).setName(name); 335 if (instance instanceof ContextAware) 336 ((ContextAware)instance).setContext(this); 337 if (instance instanceof Initializable) 338 ((Initializable)instance).init(); 339 if (instance.getClass().isAnnotationPresent(ApplicationConfigurable.class)) 340 application.configure(instance); 341 } 342 343 /** 344 * Check that this context is not finished 345 * @throws org.granite.client.tide.InvalidContextException when context finished 346 */ 347 public void checkValid() { 348 if (finished) 349 throw new InvalidContextException(contextId, "Invalid context"); 350 } 351 352 /** 353 * Convenience method to defer execution of a method in the main UI thread 354 * @param runnable runnable method 355 */ 356 public void callLater(Runnable runnable) { 357 application.execute(runnable); 358 } 359 360 /** 361 * Mark this context as eligible for destruction 362 */ 363 public void markAsFinished() { 364 this.finished = true; 365 } 366}