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