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