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.io.File; 025import java.io.IOException; 026import java.net.URISyntaxException; 027import java.net.URL; 028import java.text.ParseException; 029import java.util.Set; 030import java.util.concurrent.ConcurrentHashMap; 031import java.util.concurrent.ConcurrentMap; 032 033import org.nuxeo.ecm.webengine.WebEngine; 034import org.nuxeo.ecm.webengine.WebException; 035import org.nuxeo.ecm.webengine.loader.ClassProxy; 036import org.nuxeo.ecm.webengine.model.Module; 037import org.nuxeo.ecm.webengine.model.Resource; 038import org.nuxeo.ecm.webengine.model.ResourceType; 039import org.nuxeo.ecm.webengine.model.TemplateNotFoundException; 040import org.nuxeo.ecm.webengine.model.TypeVisibility; 041import org.nuxeo.ecm.webengine.model.WebContext; 042import org.nuxeo.ecm.webengine.scripting.ScriptFile; 043import org.nuxeo.ecm.webengine.security.Guard; 044import org.nuxeo.ecm.webengine.security.PermissionService; 045import org.nuxeo.runtime.annotations.AnnotationManager; 046 047import com.sun.jersey.server.spi.component.ResourceComponentConstructor; 048 049/** 050 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a> 051 */ 052public abstract class AbstractResourceType implements ResourceType { 053 054 protected final WebEngine engine; 055 056 protected final Module owner; 057 058 protected final ResourceComponentConstructor constructor; 059 060 protected final String name; 061 062 protected int visibility = TypeVisibility.DEFAULT; 063 064 protected AbstractResourceType superType; 065 066 protected volatile ClassProxy clazz; 067 068 protected volatile Guard guard = Guard.DEFAULT; 069 070 protected volatile Set<String> facets; 071 072 protected volatile ConcurrentMap<String, ScriptFile> templateCache; 073 074 075 protected AbstractResourceType(WebEngine engine, Module module, AbstractResourceType superType, String name, 076 ClassProxy clazz, ResourceComponentConstructor constructor, int visibility) { 077 this.engine = engine; 078 owner = module; 079 this.superType = superType; 080 this.name = name; 081 this.clazz = clazz; 082 this.constructor = constructor; 083 this.visibility = visibility; 084 templateCache = new ConcurrentHashMap<String, ScriptFile>(); 085 AnnotationManager mgr = engine.getAnnotationManager(); 086 loadAnnotations(mgr); 087 } 088 089 public int getVisibility() { 090 return visibility; 091 } 092 093 protected abstract void loadAnnotations(AnnotationManager annoMgr); 094 095 @Override 096 public ResourceType getSuperType() { 097 return superType; 098 } 099 100 public Module getOwnerModule() { 101 return owner; 102 } 103 104 @Override 105 public Guard getGuard() { 106 return guard; 107 } 108 109 @Override 110 public Set<String> getFacets() { 111 return facets; 112 } 113 114 @Override 115 public boolean hasFacet(String facet) { 116 return facets != null && facets.contains(facet); 117 } 118 119 @Override 120 public String getName() { 121 return name; 122 } 123 124 @Override 125 @SuppressWarnings("unchecked") 126 public Class<Resource> getResourceClass() { 127 return (Class<Resource>) clazz.get(); 128 } 129 130 @Override 131 public <T extends Resource> T newInstance(Class<T> typeof, WebContext context) { 132 try { 133 return typeof.cast(constructor.construct(context.getServerHttpContext())); 134 } catch (ReflectiveOperationException e) { 135 throw WebException.wrap("Failed to instantiate web object: " + clazz, e); 136 } 137 } 138 139 @Override 140 public boolean isEnabled(Resource ctx) { 141 return guard.check(ctx); 142 } 143 144 @Override 145 public boolean isDerivedFrom(String type) { 146 if (type.equals(name)) { 147 return true; 148 } 149 if (superType != null) { 150 return superType.isDerivedFrom(type); 151 } 152 return false; 153 } 154 155 @Override 156 public void flushCache() { 157 templateCache = new ConcurrentHashMap<String, ScriptFile>(); 158 } 159 160 protected void loadGuardFromAnnoation(Class<?> c) { 161 org.nuxeo.ecm.webengine.model.Guard ag = c.getAnnotation(org.nuxeo.ecm.webengine.model.Guard.class); 162 if (ag != null) { 163 String g = ag.value(); 164 if (g != null && g.length() > 0) { 165 try { 166 guard = PermissionService.parse(g); 167 } catch (ParseException e) { 168 throw WebException.wrap("Failed to parse guard: " + g + " on WebObject " + c.getName(), e); 169 } 170 } else { 171 Class<?> gc = ag.type(); 172 if (gc != null) { 173 try { 174 guard = (Guard) gc.newInstance(); 175 } catch (ReflectiveOperationException e) { 176 throw WebException.wrap("Failed to instantiate guard handler: " + gc.getName() 177 + " on WebObject " + c.getName(), e); 178 } 179 } 180 } 181 } 182 } 183 184 @Override 185 public String toString() { 186 return name + " extends " + superType + " [" + getResourceClass().getName() + "]"; 187 } 188 189 @Override 190 public ScriptFile getView(Module module, String name) { 191 ScriptFile file = findView(module, name); 192 if (file == null) { 193 throw new TemplateNotFoundException(this, name); 194 } 195 return file; 196 } 197 198 public ScriptFile findView(Module module, String name) { 199 ScriptFile file = templateCache.get(name); 200 if (file != null) { 201 return file; 202 } 203 file = findSkinTemplate(module, name); 204 if (file == null) { 205 file = findTypeTemplate(module, name); 206 } 207 if (file == null) { 208 AbstractResourceType t = (AbstractResourceType) getSuperType(); 209 if (t != null) { 210 file = t.findView(module, name); 211 } 212 } 213 if (file != null) { 214 templateCache.put(name, file); 215 } 216 return file; 217 } 218 219 protected ScriptFile findSkinTemplate(Module module, String name) { 220 return module.getFile(new StringBuilder().append("views").append(File.separatorChar).append(this.name).append( 221 File.separatorChar).append(name).toString()); 222 } 223 224 protected ScriptFile findTypeTemplate(Module module, String name) { 225 String path = resolveResourcePath(clazz.getClassName(), name); 226 URL url = clazz.get().getResource(path); 227 if (url != null) { 228 if (!"file".equals(url.getProtocol())) { 229 // TODO ScriptFile is not supporting URLs .. must refactor ScriptFile 230 return null; 231 } 232 try { 233 return new ScriptFile(new File(url.toURI())); 234 } catch (IOException | URISyntaxException e) { 235 throw WebException.wrap("Failed to convert URL to URI: " + url, e); 236 } 237 } 238 return null; 239 } 240 241 protected String resolveResourcePath(String className, String fileName) { 242 // compute resource path for resource class name 243 String path = className; 244 int p = path.lastIndexOf('.'); 245 if (p > -1) { 246 path = path.substring(0, p); 247 path = path.replace('.', '/'); 248 return new StringBuilder().append('/').append(path).append('/').append(fileName).toString(); 249 } 250 return new StringBuilder().append('/').append(fileName).toString(); 251 } 252 253}