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}