001/* 002 * (C) Copyright 2006-2008 Nuxeo SAS (http://nuxeo.com/) and contributors. 003 * 004 * All rights reserved. This program and the accompanying materials 005 * are made available under the terms of the GNU Lesser General Public License 006 * (LGPL) version 2.1 which accompanies this distribution, and is available at 007 * http://www.gnu.org/licenses/lgpl.html 008 * 009 * This library is distributed in the hope that it will be useful, 010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 012 * Lesser General Public License for more details. 013 * 014 * Contributors: 015 * bstefanescu 016 * 017 * $Id$ 018 */ 019 020package org.nuxeo.ecm.webengine.model.impl; 021 022import java.util.ArrayList; 023import java.util.Arrays; 024import java.util.HashSet; 025import java.util.List; 026import java.util.Map; 027import java.util.concurrent.ConcurrentHashMap; 028 029import org.nuxeo.ecm.core.schema.DocumentType; 030import org.nuxeo.ecm.core.schema.SchemaManager; 031import org.nuxeo.ecm.webengine.WebEngine; 032import org.nuxeo.ecm.webengine.loader.ClassProxy; 033import org.nuxeo.ecm.webengine.loader.StaticClassProxy; 034import org.nuxeo.ecm.webengine.model.AdapterType; 035import org.nuxeo.ecm.webengine.model.Resource; 036import org.nuxeo.ecm.webengine.model.ResourceType; 037import org.nuxeo.runtime.api.Framework; 038import org.nuxeo.runtime.contribution.impl.AbstractContributionRegistry; 039 040/** 041 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a> 042 */ 043public class TypeRegistry extends AbstractContributionRegistry<String, TypeDescriptor> { 044 045 protected final Map<String, AbstractResourceType> types; 046 047 protected final Map<String, AdapterType> adapters; 048 049 protected final ModuleImpl module; 050 051 protected final WebEngine engine; // cannot use module.getEngine() since module may be null 052 053 protected ClassProxy docObjectClass; 054 055 public TypeRegistry(TypeRegistry parent, WebEngine engine, ModuleImpl module) { 056 super(parent); 057 types = new ConcurrentHashMap<String, AbstractResourceType>(); 058 adapters = new ConcurrentHashMap<String, AdapterType>(); 059 this.module = module; 060 this.engine = engine; 061 // register root type 062 if (parent == null) { 063 registerRootType(); 064 } else { 065 importParentContributions(); 066 } 067 } 068 069 public TypeRegistry(WebEngine engine, ModuleImpl module) { 070 this(null, engine, module); 071 } 072 073 protected void registerRootType() { 074 TypeDescriptor root = new TypeDescriptor(new StaticClassProxy(Resource.class), ResourceType.ROOT_TYPE_NAME, 075 null); 076 registerType(root); 077 } 078 079 public ResourceType getRootType() { 080 return types.get(ResourceType.ROOT_TYPE_NAME); 081 } 082 083 public ModuleImpl getModule() { 084 return module; 085 } 086 087 public ResourceType getType(String name) { 088 ResourceType type = types.get(name); 089 if (type == null) { // check for a non registered document type 090 if (registerDocumentTypeIfNeeded(name)) { 091 type = types.get(name); 092 } 093 } 094 return type; 095 } 096 097 public AdapterType getAdapter(String name) { 098 return adapters.get(name); 099 } 100 101 public AdapterType getAdapter(Resource target, String name) { 102 AdapterType adapter = adapters.get(name); 103 if (adapter != null && adapter.acceptResource(target)) { 104 return adapter; 105 } 106 return null; 107 } 108 109 public List<AdapterType> getAdapters(Resource resource) { 110 List<AdapterType> result = new ArrayList<AdapterType>(); 111 collectAdaptersFor(resource, resource.getType(), result); 112 return result; 113 } 114 115 public List<String> getAdapterNames(Resource resource) { 116 List<String> result = new ArrayList<String>(); 117 collectAdapterNamesFor(resource, resource.getType(), result); 118 return result; 119 } 120 121 public List<AdapterType> getEnabledAdapters(Resource resource) { 122 List<AdapterType> result = new ArrayList<AdapterType>(); 123 collectEnabledAdaptersFor(resource, resource.getType(), result); 124 return result; 125 } 126 127 public List<String> getEnabledAdapterNames(Resource resource) { 128 List<String> result = new ArrayList<String>(); 129 collectEnabledAdapterNamesFor(resource, resource.getType(), result); 130 return result; 131 } 132 133 protected void collectAdaptersFor(Resource ctx, ResourceType type, List<AdapterType> result) { 134 for (AdapterType adapter : getAdapters()) { 135 if (adapter.acceptResource(ctx)) { 136 result.add(adapter); 137 } 138 } 139 } 140 141 protected void collectAdapterNamesFor(Resource ctx, ResourceType type, List<String> result) { 142 for (AdapterType adapter : getAdapters()) { 143 if (adapter.acceptResource(ctx)) { 144 result.add(adapter.getAdapterName()); 145 } 146 } 147 } 148 149 protected void collectEnabledAdaptersFor(Resource ctx, ResourceType type, List<AdapterType> result) { 150 for (AdapterType adapter : getAdapters()) { 151 if (adapter.acceptResource(ctx) && adapter.isEnabled(ctx)) { 152 result.add(adapter); 153 } 154 } 155 } 156 157 protected void collectEnabledAdapterNamesFor(Resource ctx, ResourceType type, List<String> result) { 158 for (AdapterType adapter : getAdapters()) { 159 if (adapter.acceptResource(ctx) && adapter.isEnabled(ctx)) { 160 result.add(adapter.getAdapterName()); 161 } 162 } 163 } 164 165 public ResourceType[] getTypes() { 166 return types.values().toArray(new ResourceType[types.size()]); 167 } 168 169 public AdapterType[] getAdapters() { 170 return adapters.values().toArray(new AdapterType[adapters.size()]); 171 } 172 173 public void registerTypeDescriptor(TypeDescriptor td) { 174 if (td.isAdapter()) { 175 registerAdapter(td.asAdapterDescriptor()); 176 } else { 177 registerType(td); 178 } 179 } 180 181 public synchronized void registerType(TypeDescriptor td) { 182 if (td.superType != null && !types.containsKey(td.superType)) { 183 registerDocumentTypeIfNeeded(td.superType); 184 } 185 addFragment(td.type, td, td.superType); 186 } 187 188 public synchronized void registerAdapter(AdapterDescriptor td) { 189 addFragment(td.type, td, td.superType); 190 } 191 192 public void unregisterType(TypeDescriptor td) { 193 removeFragment(td.type, td); 194 } 195 196 public void unregisterAdapter(TypeDescriptor td) { 197 removeFragment(td.type, td); 198 } 199 200 protected boolean registerDocumentTypeIfNeeded(String typeName) { 201 // we have a special case for document types. 202 // If a web document type is not resolved then use a default web document type 203 // This avoid defining web types for every document type in the system. 204 // The web document type use by default the same type hierarchy as document types 205 SchemaManager mgr = Framework.getLocalService(SchemaManager.class); 206 if (mgr != null) { 207 DocumentType doctype = mgr.getDocumentType(typeName); 208 if (doctype != null) { // this is a document type - register a default web type 209 DocumentType superSuperType = (DocumentType) doctype.getSuperType(); 210 String superSuperTypeName = ResourceType.ROOT_TYPE_NAME; 211 if (superSuperType != null) { 212 superSuperTypeName = superSuperType.getName(); 213 } 214 try { 215 if (docObjectClass == null) { 216 docObjectClass = engine.getWebLoader().getClassProxy("org.nuxeo.ecm.core.rest.DocumentObject"); 217 } 218 TypeDescriptor superWebType = new TypeDescriptor(docObjectClass, typeName, superSuperTypeName); 219 registerType(superWebType); 220 return true; 221 } catch (ClassNotFoundException e) { 222 // TODO 223 System.err.println("Cannot find document resource class. Automatic Core Type support will be disabled "); 224 } 225 } 226 } 227 return false; 228 } 229 230 @Override 231 protected TypeDescriptor clone(TypeDescriptor object) { 232 return object.clone(); 233 } 234 235 @Override 236 protected void applyFragment(TypeDescriptor object, TypeDescriptor fragment) { 237 // a type fragment may be used to replace the type implementation class. 238 // Super type cannot be replaced 239 if (fragment.clazz != null) { 240 object.clazz = fragment.clazz; 241 } 242 if (object.isAdapter()) { 243 AdapterDescriptor so = (AdapterDescriptor) object; 244 AdapterDescriptor sf = (AdapterDescriptor) fragment; 245 if (sf.facets != null && sf.facets.length > 0) { 246 List<String> list = new ArrayList<String>(); 247 if (so.facets != null && so.facets.length > 0) { 248 list.addAll(Arrays.asList(so.facets)); 249 } 250 list.addAll(Arrays.asList(sf.facets)); 251 } 252 if (sf.targetType != null && !sf.targetType.equals(ResourceType.ROOT_TYPE_NAME)) { 253 so.targetType = sf.targetType; 254 } 255 } 256 } 257 258 @Override 259 protected void applySuperFragment(TypeDescriptor object, TypeDescriptor superFragment) { 260 // do not inherit from parents 261 } 262 263 @Override 264 protected void installContribution(String key, TypeDescriptor object) { 265 if (object.isAdapter()) { 266 installAdapterContribution(key, (AdapterDescriptor) object); 267 } else { 268 installTypeContribution(key, object); 269 } 270 } 271 272 protected void installTypeContribution(String key, TypeDescriptor object) { 273 AbstractResourceType type = new ResourceTypeImpl(engine, module, null, object.type, object.clazz, 274 object.visibility); 275 if (object.superType != null) { 276 type.superType = types.get(object.superType); 277 assert type.superType != null; // must never be null since the object is resolved 278 } 279 // import document facets if this type wraps a document type 280 SchemaManager mgr = Framework.getLocalService(SchemaManager.class); 281 if (mgr != null) { 282 DocumentType doctype = mgr.getDocumentType(type.getName()); 283 if (doctype != null) { 284 if (type.facets == null) { 285 type.facets = new HashSet<String>(); 286 } 287 type.facets.addAll(doctype.getFacets()); 288 } 289 } 290 // register the type 291 types.put(object.type, type); 292 } 293 294 protected void installAdapterContribution(String key, AdapterDescriptor object) { 295 AdapterTypeImpl type = new AdapterTypeImpl(engine, module, null, object.type, object.name, object.clazz, 296 object.visibility); 297 if (object.superType != null) { 298 type.superType = types.get(object.superType); 299 assert type.superType != null; // must never be null since the object is resolved 300 } 301 types.put(object.type, type); 302 adapters.put(object.name, type); 303 } 304 305 @Override 306 protected void updateContribution(String key, TypeDescriptor object, TypeDescriptor oldValue) { 307 if (object.isAdapter()) { 308 updateAdapterContribution(key, (AdapterDescriptor) object); 309 } else { 310 updateTypeContribution(key, object); 311 } 312 } 313 314 protected void updateTypeContribution(String key, TypeDescriptor object) { 315 // When a type is updated (i.e. reinstalled) we must not replace 316 // the existing type since it may contains some contributed actions. 317 // There are two methods to do this: 318 // 1. update the existing type 319 // 2. unresolve, reinstall then resolve the type contribution to force action reinstalling. 320 // we are using 1. 321 AbstractResourceType t = types.get(key); 322 if (t != null) { // update the type class 323 t.clazz = object.clazz; 324 t.loadAnnotations(engine.getAnnotationManager()); 325 t.flushCache(); 326 } else { // install the type - this should never happen since it is an update! 327 throw new IllegalStateException("Updating an object type which is not registered."); 328 } 329 } 330 331 protected void updateAdapterContribution(String key, AdapterDescriptor object) { 332 AbstractResourceType t = types.get(key); 333 if (t instanceof AdapterTypeImpl) { // update the type class 334 AdapterTypeImpl adapter = (AdapterTypeImpl) t; 335 adapter.clazz = object.clazz; 336 adapter.loadAnnotations(engine.getAnnotationManager()); 337 t.flushCache(); 338 } else { // install the type - this should never happen since it is an update! 339 throw new IllegalStateException("Updating an adapter type which is not registered: " + key); 340 } 341 } 342 343 @Override 344 protected void uninstallContribution(String key, TypeDescriptor value) { 345 AbstractResourceType t = types.remove(key); 346 if (t instanceof AdapterTypeImpl) { 347 adapters.remove(((AdapterTypeImpl) t).name); 348 } 349 } 350 351 @Override 352 protected boolean isMainFragment(TypeDescriptor object) { 353 return object.isMainFragment(); 354 } 355 356}