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