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.core.api.NuxeoException;
034import org.nuxeo.ecm.webengine.WebEngine;
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.TypeVisibility;
040import org.nuxeo.ecm.webengine.model.WebContext;
041import org.nuxeo.ecm.webengine.model.exceptions.WebResourceNotFoundException;
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    protected AbstractResourceType(WebEngine engine, Module module, AbstractResourceType superType, String name,
075            ClassProxy clazz, ResourceComponentConstructor constructor, int visibility) {
076        this.engine = engine;
077        owner = module;
078        this.superType = superType;
079        this.name = name;
080        this.clazz = clazz;
081        this.constructor = constructor;
082        this.visibility = visibility;
083        templateCache = new ConcurrentHashMap<>();
084        AnnotationManager mgr = engine.getAnnotationManager();
085        loadAnnotations(mgr);
086    }
087
088    public int getVisibility() {
089        return visibility;
090    }
091
092    protected abstract void loadAnnotations(AnnotationManager annoMgr);
093
094    @Override
095    public ResourceType getSuperType() {
096        return superType;
097    }
098
099    public Module getOwnerModule() {
100        return owner;
101    }
102
103    @Override
104    public Guard getGuard() {
105        return guard;
106    }
107
108    @Override
109    public Set<String> getFacets() {
110        return facets;
111    }
112
113    @Override
114    public boolean hasFacet(String facet) {
115        return facets != null && facets.contains(facet);
116    }
117
118    @Override
119    public String getName() {
120        return name;
121    }
122
123    @Override
124    @SuppressWarnings("unchecked")
125    public Class<Resource> getResourceClass() {
126        return (Class<Resource>) clazz.get();
127    }
128
129    @Override
130    public <T extends Resource> T newInstance(Class<T> typeof, WebContext context) {
131        try {
132            return typeof.cast(constructor.construct(context.getServerHttpContext()));
133        } catch (ReflectiveOperationException e) {
134            throw new NuxeoException("Failed to instantiate web object: " + clazz, e);
135        }
136    }
137
138    @Override
139    public boolean isEnabled(Resource ctx) {
140        return guard.check(ctx);
141    }
142
143    @Override
144    public boolean isDerivedFrom(String type) {
145        if (type.equals(name)) {
146            return true;
147        }
148        if (superType != null) {
149            return superType.isDerivedFrom(type);
150        }
151        return false;
152    }
153
154    @Override
155    public void flushCache() {
156        templateCache = new ConcurrentHashMap<>();
157    }
158
159    protected void loadGuardFromAnnoation(Class<?> c) {
160        org.nuxeo.ecm.webengine.model.Guard ag = c.getAnnotation(org.nuxeo.ecm.webengine.model.Guard.class);
161        if (ag != null) {
162            String g = ag.value();
163            if (g != null && g.length() > 0) {
164                try {
165                    guard = PermissionService.parse(g);
166                } catch (ParseException e) {
167                    throw new NuxeoException("Failed to parse guard: " + g + " on WebObject " + c.getName(), e);
168                }
169            } else {
170                Class<?> gc = ag.type();
171                if (gc != null) {
172                    try {
173                        guard = (Guard) gc.getDeclaredConstructor().newInstance();
174                    } catch (ReflectiveOperationException e) {
175                        throw new NuxeoException(
176                                "Failed to instantiate guard handler: " + gc.getName() + " on WebObject " + c.getName(),
177                                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 WebResourceNotFoundException("Template " + name + " not found for object of type " + getName());
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")
221                                                 .append(File.separatorChar)
222                                                 .append(this.name)
223                                                 .append(File.separatorChar)
224                                                 .append(name)
225                                                 .toString());
226    }
227
228    protected ScriptFile findTypeTemplate(Module module, String name) {
229        String path = resolveResourcePath(clazz.getClassName(), name);
230        URL url = clazz.get().getResource(path);
231        if (url != null) {
232            if (!"file".equals(url.getProtocol())) {
233                // TODO ScriptFile is not supporting URLs .. must refactor ScriptFile
234                return null;
235            }
236            try {
237                return new ScriptFile(new File(url.toURI()));
238            } catch (IOException | URISyntaxException e) {
239                throw new NuxeoException("Failed to convert URL to URI: " + url, e);
240            }
241        }
242        return null;
243    }
244
245    protected String resolveResourcePath(String className, String fileName) {
246        // compute resource path for resource class name
247        String path = className;
248        int p = path.lastIndexOf('.');
249        if (p > -1) {
250            path = path.substring(0, p);
251            path = path.replace('.', '/');
252            return new StringBuilder().append('/').append(path).append('/').append(fileName).toString();
253        }
254        return new StringBuilder().append('/').append(fileName).toString();
255    }
256
257}