001/*
002 * (C) Copyright 2006-2007 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 *     Nuxeo - initial API and implementation
016 *
017 * $Id: DocumentViewCodecService.java 22535 2007-07-13 14:57:58Z atchertchian $
018 */
019
020package org.nuxeo.ecm.platform.url.service;
021
022import java.util.ArrayList;
023import java.util.Collections;
024import java.util.List;
025import java.util.Map;
026import java.util.concurrent.ConcurrentHashMap;
027
028import org.apache.commons.lang.StringUtils;
029import org.apache.commons.logging.Log;
030import org.apache.commons.logging.LogFactory;
031import org.nuxeo.ecm.platform.url.api.DocumentView;
032import org.nuxeo.ecm.platform.url.api.DocumentViewCodecManager;
033import org.nuxeo.ecm.platform.url.codec.api.DocumentViewCodec;
034import org.nuxeo.ecm.platform.url.codec.descriptor.DocumentViewCodecDescriptor;
035import org.nuxeo.runtime.model.ComponentContext;
036import org.nuxeo.runtime.model.ComponentInstance;
037import org.nuxeo.runtime.model.DefaultComponent;
038
039public class DocumentViewCodecService extends DefaultComponent implements DocumentViewCodecManager {
040
041    private static final long serialVersionUID = -4521897334653742494L;
042
043    private static final Log log = LogFactory.getLog(DocumentViewCodecService.class);
044
045    public static final String NAME = DocumentViewCodecService.class.getName();
046
047    public static final String CODECS_EXTENSION_POINT = "codecs";
048
049    protected String defaultCodecName;
050
051    protected final Map<String, DocumentViewCodecDescriptor> descriptors;
052
053    protected final Map<String, DocumentViewCodec> codecs;
054
055    public DocumentViewCodecService() {
056        descriptors = new ConcurrentHashMap<String, DocumentViewCodecDescriptor>();
057        codecs = new ConcurrentHashMap<String, DocumentViewCodec>();
058    }
059
060    @Override
061    public void deactivate(ComponentContext context) {
062        descriptors.clear();
063        codecs.clear();
064    }
065
066    @Override
067    @SuppressWarnings("unchecked")
068    public <T> T getAdapter(Class<T> adapter) {
069        if (adapter.isAssignableFrom(DocumentViewCodecManager.class)) {
070            return (T) this;
071        }
072        return null;
073    }
074
075    @Override
076    public void registerContribution(Object contribution, String extensionPoint, ComponentInstance contributor) {
077        if (CODECS_EXTENSION_POINT.equals(extensionPoint)) {
078            DocumentViewCodecDescriptor desc = (DocumentViewCodecDescriptor) contribution;
079            String codecName = desc.getName();
080            descriptors.put(codecName, desc);
081            if (desc.getDefaultCodec()) {
082                defaultCodecName = codecName;
083            }
084            // try to instantiate it
085            String className = desc.getClassName();
086            if (className == null) {
087                throw new IllegalArgumentException(String.format("Invalid class for codec '%s': check ERROR logs"
088                        + " at startup", codecName));
089            }
090            DocumentViewCodec codec;
091            try {
092                // Thread context loader is not working in isolated EARs
093                codec = (DocumentViewCodec) DocumentViewCodecManager.class.getClassLoader().loadClass(className).newInstance();
094            } catch (ReflectiveOperationException e) {
095                String msg = String.format("Caught error when instantiating codec '%s' with " + "class '%s' ",
096                        codecName, className);
097                throw new IllegalArgumentException(msg, e);
098            }
099            String prefix = desc.getPrefix();
100            if (prefix != null) {
101                codec.setPrefix(prefix);
102            }
103            codecs.put(codecName, codec);
104            log.debug("Added URL codec: " + codecName);
105        }
106    }
107
108    @Override
109    public void unregisterContribution(Object contribution, String extensionPoint, ComponentInstance contributor) {
110        if (CODECS_EXTENSION_POINT.equals(extensionPoint)) {
111            DocumentViewCodecDescriptor codecDesc = (DocumentViewCodecDescriptor) contribution;
112            String codecName = codecDesc.getName();
113            descriptors.remove(codecName);
114            codecs.remove(codecName);
115            log.debug("Removed URL codec: " + codecName);
116        }
117    }
118
119    public List<String> getDocumentViewCodecDescriptorNames() {
120        List<String> lst = new ArrayList<String>();
121        for (String k : descriptors.keySet()) {
122            if (descriptors.get(k).getEnabled()) {
123                lst.add(k);
124            }
125        }
126        return lst;
127    }
128
129    public String getDefaultCodecName() {
130        String name = defaultCodecName;
131        if (name == null) {
132            // take first one
133            List<String> descs = getDocumentViewCodecDescriptorNames();
134            if (descs != null && !descs.isEmpty()) {
135                Collections.sort(descs);
136                name = descs.get(0);
137            }
138        }
139        return name;
140    }
141
142    public DocumentViewCodec getCodec() {
143        return getCodec(defaultCodecName);
144    }
145
146    public DocumentViewCodec getCodec(String codecName) {
147        if (StringUtils.isBlank(codecName)) {
148            return null;
149        }
150        return codecs.get(codecName);
151    }
152
153    public String getUrlFromDocumentView(DocumentView docView, boolean needBaseUrl, String baseUrl) {
154        String url = null;
155        DocumentViewCodec codec = getCodec(getDefaultCodecName());
156        if (codec != null && codec.handleDocumentView(docView)) {
157            url = getUrlFromDocumentView(codec, docView, needBaseUrl, baseUrl);
158        }
159        if (url == null) {
160            for (String codecName : descriptors.keySet()) {
161                if (!codecName.equals(defaultCodecName)) {
162                    codec = getCodec(codecName);
163                    if (codec != null && codec.handleDocumentView(docView)) {
164                        url = getUrlFromDocumentView(codec, docView, needBaseUrl, baseUrl);
165                        if (url != null) {
166                            break;
167                        }
168                    }
169                }
170            }
171        }
172        return url;
173    }
174
175    public String getUrlFromDocumentView(String codecName, DocumentView docView, boolean needBaseUrl, String baseUrl) {
176        DocumentViewCodec codec = getCodec(codecName);
177        return getUrlFromDocumentView(codec, docView, needBaseUrl, baseUrl);
178    }
179
180    protected String getUrlFromDocumentView(DocumentViewCodec codec, DocumentView docView, boolean needBaseUrl,
181            String baseUrl) {
182        if (codec != null) {
183            String partialUrl = codec.getUrlFromDocumentView(docView);
184            if (partialUrl != null) {
185                if (needBaseUrl && !StringUtils.isBlank(baseUrl)) {
186                    if (baseUrl.endsWith("/") || partialUrl.startsWith("/")) {
187                        return baseUrl + partialUrl;
188                    } else {
189                        return baseUrl + "/" + partialUrl;
190                    }
191                } else {
192                    return partialUrl;
193                }
194            }
195        }
196        return null;
197    }
198
199    public DocumentView getDocumentViewFromUrl(String url, boolean hasBaseUrl, String baseUrl) {
200        DocumentView docView = null;
201        String finalUrl = getUrlWithoutBase(url, hasBaseUrl, baseUrl);
202        DocumentViewCodec codec = getCodec(getDefaultCodecName());
203        if (codec != null && codec.handleUrl(finalUrl)) {
204            docView = getDocumentViewFromUrl(codec, finalUrl);
205        }
206        if (docView == null) {
207            for (String codecName : descriptors.keySet()) {
208                if (!codecName.equals(defaultCodecName)) {
209                    codec = getCodec(codecName);
210                    if (codec != null && codec.handleUrl(finalUrl)) {
211                        docView = getDocumentViewFromUrl(codec, finalUrl);
212                        if (docView != null) {
213                            break;
214                        }
215                    }
216                }
217            }
218        }
219        return docView;
220    }
221
222    public DocumentView getDocumentViewFromUrl(String codecName, String url, boolean hasBaseUrl, String baseUrl) {
223        DocumentViewCodec codec = getCodec(codecName);
224        String finalUrl = getUrlWithoutBase(url, hasBaseUrl, baseUrl);
225        return getDocumentViewFromUrl(codec, finalUrl);
226    }
227
228    protected String getUrlWithoutBase(String url, boolean hasBaseUrl, String baseUrl) {
229        if (hasBaseUrl && baseUrl != null) {
230            if (url.startsWith(baseUrl)) {
231                url = url.substring(baseUrl.length());
232            }
233        }
234        return url;
235    }
236
237    protected DocumentView getDocumentViewFromUrl(DocumentViewCodec codec, String finalUrl) {
238        if (codec != null) {
239            DocumentView docView = codec.getDocumentViewFromUrl(finalUrl);
240            return docView;
241        }
242        return null;
243    }
244
245}