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