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