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