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