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