001/* 002 * (C) Copyright 2015 Nuxeo SA (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-2.1.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 * Nicolas Chapurlat <nchapurlat@nuxeo.com> 016 */ 017 018package org.nuxeo.ecm.core.io.registry; 019 020import java.lang.reflect.Type; 021import java.util.Collection; 022import java.util.HashMap; 023import java.util.Map; 024import java.util.Set; 025import java.util.concurrent.ConcurrentHashMap; 026import java.util.concurrent.ConcurrentSkipListSet; 027 028import javax.ws.rs.core.MediaType; 029 030import org.apache.commons.lang3.reflect.TypeUtils; 031import org.apache.commons.logging.Log; 032import org.apache.commons.logging.LogFactory; 033import org.nuxeo.ecm.core.io.registry.context.RenderingContext; 034import org.nuxeo.ecm.core.io.registry.reflect.MarshallerInspector; 035import org.nuxeo.runtime.model.ComponentContext; 036import org.nuxeo.runtime.model.ComponentInstance; 037import org.nuxeo.runtime.model.DefaultComponent; 038 039/** 040 * Implementation of {@link MarshallerRegistry}. 041 * <p> 042 * This implementation is based on {@link MarshallerInspector} class which is able to create marshaller instance and 043 * inject properties. This class also manage marshaller's priorities. 044 * </p> 045 * 046 * @since 7.2 047 */ 048public class MarshallerRegistryImpl extends DefaultComponent implements MarshallerRegistry { 049 050 private final Log log = LogFactory.getLog(MarshallerRegistryImpl.class); 051 052 /** 053 * All {@link Writer}'s {@link MarshallerInspector} ordered by their priority. 054 */ 055 private static final Set<MarshallerInspector> writers = new ConcurrentSkipListSet<MarshallerInspector>(); 056 057 /** 058 * {@link Writer}'s {@link MarshallerInspector} organized by their managed {@link MediaType}. 059 */ 060 private static final Map<MediaType, Set<MarshallerInspector>> writersByMediaType = new ConcurrentHashMap<MediaType, Set<MarshallerInspector>>(); 061 062 /** 063 * All {@link Reader}'s {@link MarshallerInspector} ordered by their priority. 064 */ 065 private static final Set<MarshallerInspector> readers = new ConcurrentSkipListSet<MarshallerInspector>(); 066 067 /** 068 * {@link Reader}'s {@link MarshallerInspector} organized by their managed {@link MediaType}. 069 */ 070 private static final Map<MediaType, Set<MarshallerInspector>> readersByMediaType = new ConcurrentHashMap<MediaType, Set<MarshallerInspector>>(); 071 072 /** 073 * {@link MarshallerInspector} organized by their managed {@link Marshaller} class. 074 */ 075 private static final Map<Class<?>, MarshallerInspector> marshallersByType = new ConcurrentHashMap<Class<?>, MarshallerInspector>(); 076 077 @Override 078 public void activate(ComponentContext context) { 079 super.activate(context); 080 clear(); 081 } 082 083 @Override 084 public void deactivate(ComponentContext context) { 085 clear(); 086 super.deactivate(context); 087 } 088 089 @Override 090 public void registerContribution(Object contribution, String extensionPoint, ComponentInstance contributor) { 091 if (extensionPoint.equals("marshallers")) { 092 MarshallerRegistryDescriptor mrd = (MarshallerRegistryDescriptor) contribution; 093 if (mrd.isEnable()) { 094 register(mrd.getClazz()); 095 } else { 096 deregister(mrd.getClazz()); 097 } 098 } 099 } 100 101 @Override 102 public void unregisterContribution(Object contribution, String extensionPoint, ComponentInstance contributor) { 103 if (extensionPoint.equals("marshallers")) { 104 MarshallerRegistryDescriptor mrd = (MarshallerRegistryDescriptor) contribution; 105 if (mrd.isEnable()) { 106 deregister(mrd.getClazz()); 107 } else { 108 register(mrd.getClazz()); 109 } 110 } 111 } 112 113 @Override 114 public void register(Class<?> marshaller) { 115 if (marshaller == null) { 116 throw new MarshallingException("Cannot register null marshaller"); 117 } 118 MarshallerInspector inspector = new MarshallerInspector(marshaller); 119 if (!inspector.isWriter() && !inspector.isReader()) { 120 throw new MarshallingException( 121 "The marshaller registry just supports Writer and Reader for now. You have to implement " 122 + Writer.class.getName() + " or " + Reader.class.getName()); 123 } 124 if (marshallersByType.get(marshaller) != null) { 125 log.warn("The marshaller " + marshaller.getName() + " is already registered."); 126 return; 127 } else { 128 marshallersByType.put(marshaller, inspector); 129 } 130 if (inspector.isWriter()) { 131 writers.add(inspector); 132 for (MediaType mediaType : inspector.getSupports()) { 133 Set<MarshallerInspector> inspectors = writersByMediaType.get(mediaType); 134 if (inspectors == null) { 135 inspectors = new ConcurrentSkipListSet<MarshallerInspector>(); 136 writersByMediaType.put(mediaType, inspectors); 137 } 138 inspectors.add(inspector); 139 } 140 } 141 if (inspector.isReader()) { 142 readers.add(inspector); 143 for (MediaType mediaType : inspector.getSupports()) { 144 Set<MarshallerInspector> inspectors = readersByMediaType.get(mediaType); 145 if (inspectors == null) { 146 inspectors = new ConcurrentSkipListSet<MarshallerInspector>(); 147 readersByMediaType.put(mediaType, inspectors); 148 } 149 inspectors.add(inspector); 150 } 151 } 152 } 153 154 @Override 155 public void deregister(Class<?> marshaller) throws MarshallingException { 156 if (marshaller == null) { 157 log.warn("Cannot register null marshaller"); 158 return; 159 } 160 MarshallerInspector inspector = new MarshallerInspector(marshaller); 161 if (!inspector.isWriter() && !inspector.isReader()) { 162 throw new MarshallingException( 163 "The marshaller registry just supports Writer and Reader for now. You have to implement " 164 + Writer.class.getName() + " or " + Reader.class.getName()); 165 } 166 marshallersByType.remove(marshaller); 167 if (inspector.isWriter()) { 168 writers.remove(inspector); 169 for (MediaType mediaType : inspector.getSupports()) { 170 Set<MarshallerInspector> inspectors = writersByMediaType.get(mediaType); 171 if (inspectors != null) { 172 inspectors.remove(inspector); 173 } 174 } 175 } 176 if (inspector.isReader()) { 177 readers.remove(inspector); 178 for (MediaType mediaType : inspector.getSupports()) { 179 Set<MarshallerInspector> inspectors = readersByMediaType.get(mediaType); 180 if (inspectors != null) { 181 inspectors.remove(inspector); 182 } 183 } 184 } 185 } 186 187 @Override 188 public <T> Writer<T> getWriter(RenderingContext ctx, Class<T> marshalledClazz, Type genericType, MediaType mediatype) { 189 Set<MarshallerInspector> candidates = writersByMediaType.get(mediatype); 190 return (Writer<T>) getMarshaller(ctx, marshalledClazz, genericType, mediatype, candidates, writers, false); 191 } 192 193 @Override 194 public <T> Writer<T> getUniqueWriter(RenderingContext ctx, Class<T> marshalledClazz, Type genericType, 195 MediaType mediatype) { 196 Set<MarshallerInspector> candidates = writersByMediaType.get(mediatype); 197 return (Writer<T>) getMarshaller(ctx, marshalledClazz, genericType, mediatype, candidates, writers, true); 198 } 199 200 @Override 201 @SuppressWarnings("unchecked") 202 public <T> Collection<Writer<T>> getAllWriters(RenderingContext ctx, Class<T> marshalledClazz, Type genericType, 203 MediaType mediatype) { 204 Set<MarshallerInspector> candidates = writersByMediaType.get(mediatype); 205 Collection<Marshaller<T>> founds = getAllMarshallers(ctx, marshalledClazz, genericType, mediatype, candidates, 206 writers); 207 return (Collection<Writer<T>>) (Collection<?>) founds; 208 } 209 210 @Override 211 public <T> Writer<T> getWriter(RenderingContext ctx, Class<T> marshalledClazz, MediaType mediatype) { 212 return getWriter(ctx, marshalledClazz, marshalledClazz, mediatype); 213 } 214 215 @Override 216 public <T> Reader<T> getReader(RenderingContext ctx, Class<T> marshalledClazz, Type genericType, MediaType mediatype) { 217 Set<MarshallerInspector> candidates = readersByMediaType.get(mediatype); 218 return (Reader<T>) getMarshaller(ctx, marshalledClazz, genericType, mediatype, candidates, readers, false); 219 } 220 221 @Override 222 public <T> Reader<T> getUniqueReader(RenderingContext ctx, Class<T> marshalledClazz, Type genericType, 223 MediaType mediatype) { 224 Set<MarshallerInspector> candidates = readersByMediaType.get(mediatype); 225 return (Reader<T>) getMarshaller(ctx, marshalledClazz, genericType, mediatype, candidates, readers, true); 226 } 227 228 @Override 229 @SuppressWarnings("unchecked") 230 public <T> Collection<Reader<T>> getAllReaders(RenderingContext ctx, Class<T> marshalledClazz, Type genericType, 231 MediaType mediatype) { 232 Set<MarshallerInspector> candidates = readersByMediaType.get(mediatype); 233 Collection<Marshaller<T>> founds = getAllMarshallers(ctx, marshalledClazz, genericType, mediatype, candidates, 234 readers); 235 return (Collection<Reader<T>>) (Collection<?>) founds; 236 } 237 238 @Override 239 public <T> Reader<T> getReader(RenderingContext ctx, Class<T> marshalledClazz, MediaType mediatype) { 240 return getReader(ctx, marshalledClazz, marshalledClazz, mediatype); 241 } 242 243 public <T> Marshaller<T> getMarshaller(RenderingContext ctx, Class<T> marshalledClazz, Type genericType, 244 MediaType mediatype, Set<MarshallerInspector> customs, Set<MarshallerInspector> wildcards, 245 boolean forceInstantiation) { 246 if (customs != null) { 247 Marshaller<T> found = searchCandidate(ctx, marshalledClazz, genericType, mediatype, customs, 248 forceInstantiation); 249 if (found != null) { 250 return found; 251 } 252 } 253 return searchCandidate(ctx, marshalledClazz, genericType, mediatype, wildcards, forceInstantiation); 254 } 255 256 public <T> Collection<Marshaller<T>> getAllMarshallers(RenderingContext ctx, Class<T> marshalledClazz, 257 Type genericType, MediaType mediatype, Set<MarshallerInspector> customs, Set<MarshallerInspector> wildcards) { 258 Map<MarshallerInspector, Marshaller<T>> result = new HashMap<MarshallerInspector, Marshaller<T>>(); 259 if (customs != null) { 260 result.putAll(searchAllCandidates(ctx, marshalledClazz, genericType, mediatype, customs)); 261 } 262 result.putAll(searchAllCandidates(ctx, marshalledClazz, genericType, mediatype, wildcards)); 263 return result.values(); 264 } 265 266 @SuppressWarnings("unchecked") 267 private <T> Marshaller<T> searchCandidate(RenderingContext ctx, Class<T> marshalledClazz, Type genericType, 268 MediaType mediatype, Set<MarshallerInspector> candidates, boolean forceInstantiation) { 269 for (MarshallerInspector inspector : candidates) { 270 // checks the managed class is compatible 271 if (inspector.getMarshalledType().isAssignableFrom(marshalledClazz)) { 272 // checks the generic type is compatible 273 if (genericType == null || marshalledClazz.equals(inspector.getGenericType()) 274 || TypeUtils.isAssignable(genericType, inspector.getGenericType())) { 275 Marshaller<T> marshaller = null; 276 if (forceInstantiation) { 277 marshaller = (Marshaller<T>) inspector.getNewInstance(ctx, false); 278 } else { 279 marshaller = inspector.getInstance(ctx); 280 } 281 // checks the marshaller accepts the request 282 if (marshaller.accept(marshalledClazz, genericType, mediatype)) { 283 return marshaller; 284 } 285 } 286 } 287 } 288 return null; 289 } 290 291 private <T> Map<MarshallerInspector, Marshaller<T>> searchAllCandidates(RenderingContext ctx, 292 Class<T> marshalledClazz, Type genericType, MediaType mediatype, Set<MarshallerInspector> candidates) { 293 Map<MarshallerInspector, Marshaller<T>> result = new HashMap<MarshallerInspector, Marshaller<T>>(); 294 for (MarshallerInspector inspector : candidates) { 295 // checks the managed class is compatible 296 if (inspector.getMarshalledType().isAssignableFrom(marshalledClazz)) { 297 // checks the generic type is compatible 298 if (genericType == null || marshalledClazz.equals(inspector.getGenericType()) 299 || TypeUtils.isAssignable(genericType, inspector.getGenericType())) { 300 // checks the marshaller accepts the request 301 Marshaller<T> marshaller = inspector.getInstance(ctx); 302 if (marshaller.accept(marshalledClazz, genericType, mediatype)) { 303 result.put(inspector, marshaller); 304 } 305 } 306 } 307 } 308 return result; 309 } 310 311 @Override 312 public <T> T getInstance(RenderingContext ctx, Class<T> marshallerClass) { 313 MarshallerInspector inspector = marshallersByType.get(marshallerClass); 314 if (inspector == null) { 315 inspector = new MarshallerInspector(marshallerClass); 316 } 317 return inspector.getInstance(ctx); 318 } 319 320 @Override 321 public <T> T getUniqueInstance(RenderingContext ctx, Class<T> marshallerClass) { 322 MarshallerInspector inspector = marshallersByType.get(marshallerClass); 323 if (inspector == null) { 324 inspector = new MarshallerInspector(marshallerClass); 325 } 326 @SuppressWarnings("unchecked") 327 T result = (T) inspector.getNewInstance(ctx, false); 328 return result; 329 } 330 331 @Override 332 public void clear() { 333 marshallersByType.clear(); 334 writersByMediaType.clear(); 335 writers.clear(); 336 readersByMediaType.clear(); 337 readers.clear(); 338 } 339 340}