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.net.URI;
025import java.net.URISyntaxException;
026import java.security.Principal;
027import java.text.ParseException;
028import java.util.List;
029import java.util.Set;
030
031import javax.ws.rs.core.Response;
032
033import org.nuxeo.common.utils.URIUtils;
034import org.nuxeo.ecm.core.api.CoreSession;
035import org.nuxeo.ecm.webengine.WebException;
036import org.nuxeo.ecm.webengine.model.AdapterResource;
037import org.nuxeo.ecm.webengine.model.LinkDescriptor;
038import org.nuxeo.ecm.webengine.model.Module;
039import org.nuxeo.ecm.webengine.model.Resource;
040import org.nuxeo.ecm.webengine.model.ResourceType;
041import org.nuxeo.ecm.webengine.model.Template;
042import org.nuxeo.ecm.webengine.model.View;
043import org.nuxeo.ecm.webengine.model.WebContext;
044import org.nuxeo.ecm.webengine.model.exceptions.WebSecurityException;
045import org.nuxeo.ecm.webengine.security.PermissionService;
046
047/**
048 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a>
049 */
050// DO NOT MODIFY class declaration! Cannot use WebResourceType<?> since groovy
051// doesn't supports wildcards for now
052@SuppressWarnings("unchecked")
053public abstract class AbstractResource<T extends ResourceType> implements Resource {
054
055    protected WebContext ctx;
056
057    protected AbstractResource<?> next;
058
059    protected AbstractResource<?> prev;
060
061    protected String path;
062
063    protected T type;
064
065    @Override
066    public Resource initialize(WebContext ctx, ResourceType type, Object... args) {
067        this.ctx = ctx;
068        this.type = (T) type;
069        path = ctx.getUriInfo().getMatchedURIs().get(0);
070        // quote path component to replace special characters (except slash and
071        // @ chars)
072        path = URIUtils.quoteURIPathComponent(path, false, false);
073        // avoid paths ending in / -> this will mess-up URLs in FTL files.
074        if (path.endsWith("/")) {
075            path = path.substring(0, path.length() - 1);
076        }
077        // resteasy doesn't return correct paths - that should be relative as
078        // is JAX-RS specs on resteasy paths begin with a /
079        StringBuilder buf = new StringBuilder(64).append(ctx.getBasePath());
080        if (!path.startsWith("/")) {
081            buf.append('/');
082        }
083        path = buf.append(path).toString();
084        if (!this.type.getGuard().check(this)) {
085            throw new WebSecurityException("Failed to initialize object: " + path
086                    + ". Object is not accessible in the current context", path);
087        }
088        initialize(args);
089        return this;
090    }
091
092    protected void initialize(Object... args) {
093        // do nothing
094    }
095
096    @Override
097    public boolean isAdapter() {
098        return type.getClass() == AdapterTypeImpl.class;
099    }
100
101    @Override
102    public boolean isRoot() {
103        return this == ctx.getRoot();
104    }
105
106    @Override
107    public void setRoot(boolean isRoot) {
108        AbstractWebContext ctx = (AbstractWebContext) this.ctx;
109        if (isRoot) {
110            ctx.root = this;
111        } else {
112            if (ctx.root == this) {
113                ctx.root = null;
114            }
115        }
116    }
117
118    @Override
119    public boolean isInstanceOf(String type) {
120        return this.type.isDerivedFrom(type);
121    }
122
123    @Override
124    public Response redirect(String uri) {
125        try {
126            if (!uri.contains("://")) {
127                // not an absolute URI
128                if (uri.startsWith("/")) {
129                    uri = ctx.getServerURL().append(uri).toString();
130                } else {
131                    uri = ctx.getServerURL().append('/').append(uri).toString();
132                }
133            }
134            return Response.seeOther(new URI(uri)).build();
135        } catch (URISyntaxException e) {
136            throw WebException.wrap(e);
137        }
138    }
139
140    @Override
141    public AdapterResource getActiveAdapter() {
142        return next != null && next.isAdapter() ? (AdapterResource) next : null;
143    }
144
145    @Override
146    public void dispose() {
147        ctx = null;
148        type = null;
149        path = null;
150    }
151
152    @Override
153    public Set<String> getFacets() {
154        return type.getFacets();
155    }
156
157    @Override
158    public boolean hasFacet(String facet) {
159        return type.hasFacet(facet);
160    }
161
162    @Override
163    public T getType() {
164        return type;
165    }
166
167    @Override
168    public WebContext getContext() {
169        return ctx;
170    }
171
172    @Override
173    public Module getModule() {
174        return ctx.getModule();
175    }
176
177    @Override
178    public Resource getNext() {
179        return next;
180    }
181
182    @Override
183    public void setNext(Resource next) {
184        this.next = (AbstractResource<?>) next;
185    }
186
187    @Override
188    public Resource getPrevious() {
189        return prev;
190    }
191
192    @Override
193    public void setPrevious(Resource previous) {
194        this.prev = (AbstractResource<?>) previous;
195    }
196
197    @Override
198    public String getName() {
199        int e = path.endsWith("/") ? path.length() - 1 : path.length();
200        int p = path.lastIndexOf('/', e - 1);
201        return p > -1 ? path.substring(p + 1) : path;
202    }
203
204    @Override
205    public String getPath() {
206        return path;
207    }
208
209    @Override
210    public String getTrailingPath() {
211        int len = path.length();
212        String urlPath = ctx.getUrlPath();
213        return len < urlPath.length() ? urlPath.substring(len) : "/";
214    }
215
216    @Override
217    public String getNextSegment() {
218        String p = getTrailingPath();
219        if (p != null && !"/".equals(p)) {
220            int s = p.startsWith("/") ? 1 : 0;
221            int k = p.indexOf('/', s);
222            if (k == -1) {
223                return s > 0 ? p.substring(s) : p;
224            } else {
225                return p.substring(s, k);
226            }
227        }
228        return null;
229    }
230
231    @Override
232    public String getURL() {
233        return ctx.getServerURL().append(path).toString();
234    }
235
236    @Override
237    public List<LinkDescriptor> getLinks(String category) {
238        return ctx.getModule().getActiveLinks(this, category);
239    }
240
241    @Override
242    public <A> A getAdapter(Class<A> adapter) {
243        if (adapter == CoreSession.class) {
244            return adapter.cast(ctx.getCoreSession());
245        } else if (adapter == Principal.class) {
246            return adapter.cast(ctx.getPrincipal());
247        }
248        if (adapter == WebContext.class) {
249            return adapter.cast(ctx);
250        }
251        if (Resource.class.isAssignableFrom(adapter)) {
252            return adapter.cast(this);
253        }
254        return null;
255    }
256
257    @Override
258    public Resource newObject(String type, Object... args) {
259        return ctx.newObject(type, args);
260    }
261
262    @Override
263    public AdapterResource newAdapter(String type, Object... args) {
264        return ctx.newAdapter(this, type, args);
265    }
266
267    @Override
268    public Template getView(String viewId) {
269        return new View(this, viewId).resolve();
270    }
271
272    @Override
273    public Template getTemplate(String fileName) {
274        return new Template(this, getModule().getFile(fileName));
275    }
276
277    @Override
278    public boolean checkGuard(String guard) throws ParseException {
279        return PermissionService.parse(guard).check(this);
280    }
281
282    @Override
283    public String toString() {
284        return type.getName() + " [" + path + "]";
285    }
286
287}