001/*
002 * (C) Copyright 2006-2018 Nuxeo (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.lang3.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<>();
059        codecs = new ConcurrentHashMap<>();
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            if (desc.getDefaultCodec()) {
083                defaultCodecName = codecName;
084            }
085            // try to instantiate it
086            String className = desc.getClassName();
087            if (className == null) {
088                throw new IllegalArgumentException(
089                        String.format("Invalid class for codec '%s': check ERROR logs" + " at startup", codecName));
090            }
091            DocumentViewCodec codec;
092            try {
093                // Thread context loader is not working in isolated EARs
094                codec = (DocumentViewCodec) DocumentViewCodecManager.class.getClassLoader()
095                                                                          .loadClass(className)
096                                                                          .newInstance();
097            } catch (ReflectiveOperationException e) {
098                String msg = String.format("Caught error when instantiating codec '%s' with " + "class '%s' ",
099                        codecName, className);
100                throw new IllegalArgumentException(msg, e);
101            }
102            String prefix = desc.getPrefix();
103            if (prefix != null) {
104                codec.setPrefix(prefix);
105            }
106            if (!descriptors.containsKey(codecName) || desc.getPriority() > descriptors.get(codecName).getPriority()) {
107                descriptors.put(codecName, desc);
108                codecs.put(codecName, codec);
109            }
110            log.debug("Added URL codec: " + codecName);
111        }
112    }
113
114    @Override
115    public void unregisterContribution(Object contribution, String extensionPoint, ComponentInstance contributor) {
116        if (CODECS_EXTENSION_POINT.equals(extensionPoint)) {
117            DocumentViewCodecDescriptor codecDesc = (DocumentViewCodecDescriptor) contribution;
118            String codecName = codecDesc.getName();
119            descriptors.remove(codecName);
120            codecs.remove(codecName);
121            log.debug("Removed URL codec: " + codecName);
122        }
123    }
124
125    public List<String> getDocumentViewCodecDescriptorNames() {
126        List<String> lst = new ArrayList<>();
127        for (String k : descriptors.keySet()) {
128            if (descriptors.get(k).getEnabled()) {
129                lst.add(k);
130            }
131        }
132        return lst;
133    }
134
135    @Override
136    public String getDefaultCodecName() {
137        String name = defaultCodecName;
138        if (name == null) {
139            // take first one
140            List<String> descs = getDocumentViewCodecDescriptorNames();
141            if (descs != null && !descs.isEmpty()) {
142                Collections.sort(descs);
143                name = descs.get(0);
144            }
145        }
146        return name;
147    }
148
149    public DocumentViewCodec getCodec() {
150        return getCodec(defaultCodecName);
151    }
152
153    @Override
154    public DocumentViewCodec getCodec(String codecName) {
155        if (StringUtils.isBlank(codecName)) {
156            return null;
157        }
158        return codecs.get(codecName);
159    }
160
161    @Override
162    public String getUrlFromDocumentView(DocumentView docView, boolean needBaseUrl, String baseUrl) {
163        String url = null;
164        DocumentViewCodec codec = getCodec(getDefaultCodecName());
165        if (codec != null && codec.handleDocumentView(docView)) {
166            url = getUrlFromDocumentView(codec, docView, needBaseUrl, baseUrl);
167        }
168        if (url == null) {
169            for (String codecName : descriptors.keySet()) {
170                if (!codecName.equals(defaultCodecName)) {
171                    codec = getCodec(codecName);
172                    if (codec != null && codec.handleDocumentView(docView)) {
173                        url = getUrlFromDocumentView(codec, docView, needBaseUrl, baseUrl);
174                        if (url != null) {
175                            break;
176                        }
177                    }
178                }
179            }
180        }
181        return url;
182    }
183
184    @Override
185    public String getUrlFromDocumentView(String codecName, DocumentView docView, boolean needBaseUrl, String baseUrl) {
186        DocumentViewCodec codec = getCodec(codecName);
187        return getUrlFromDocumentView(codec, docView, needBaseUrl, baseUrl);
188    }
189
190    protected String getUrlFromDocumentView(DocumentViewCodec codec, DocumentView docView, boolean needBaseUrl,
191            String baseUrl) {
192        if (codec != null) {
193            String partialUrl = codec.getUrlFromDocumentView(docView);
194            if (partialUrl != null) {
195                if (needBaseUrl && !StringUtils.isBlank(baseUrl)) {
196                    if (baseUrl.endsWith("/") || partialUrl.startsWith("/")) {
197                        return baseUrl + partialUrl;
198                    } else {
199                        return baseUrl + "/" + partialUrl;
200                    }
201                } else {
202                    return partialUrl;
203                }
204            }
205        }
206        return null;
207    }
208
209    @Override
210    public DocumentView getDocumentViewFromUrl(String url, boolean hasBaseUrl, String baseUrl) {
211        DocumentView docView = null;
212        String finalUrl = getUrlWithoutBase(url, hasBaseUrl, baseUrl);
213        DocumentViewCodec codec = getCodec(getDefaultCodecName());
214        if (codec != null && codec.handleUrl(finalUrl)) {
215            docView = getDocumentViewFromUrl(codec, finalUrl);
216        }
217        if (docView == null) {
218            for (String codecName : descriptors.keySet()) {
219                if (!codecName.equals(defaultCodecName)) {
220                    codec = getCodec(codecName);
221                    if (codec != null && codec.handleUrl(finalUrl)) {
222                        docView = getDocumentViewFromUrl(codec, finalUrl);
223                        if (docView != null) {
224                            break;
225                        }
226                    }
227                }
228            }
229        }
230        return docView;
231    }
232
233    @Override
234    public DocumentView getDocumentViewFromUrl(String codecName, String url, boolean hasBaseUrl, String baseUrl) {
235        DocumentViewCodec codec = getCodec(codecName);
236        String finalUrl = getUrlWithoutBase(url, hasBaseUrl, baseUrl);
237        return getDocumentViewFromUrl(codec, finalUrl);
238    }
239
240    protected String getUrlWithoutBase(String url, boolean hasBaseUrl, String baseUrl) {
241        if (hasBaseUrl && baseUrl != null) {
242            if (url.startsWith(baseUrl)) {
243                url = url.substring(baseUrl.length());
244            }
245        }
246        return url;
247    }
248
249    protected DocumentView getDocumentViewFromUrl(DocumentViewCodec codec, String finalUrl) {
250        if (codec != null) {
251            DocumentView docView = codec.getDocumentViewFromUrl(finalUrl);
252            return docView;
253        }
254        return null;
255    }
256
257}