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