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