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