001/* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*- 002 * 003 * Copyright © 2019 microBean™. 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); 006 * you may not use this file except in compliance with the License. 007 * You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 014 * implied. See the License for the specific language governing 015 * permissions and limitations under the License. 016 */ 017package org.microbean.jackson.cdi; 018 019import java.lang.annotation.Annotation; 020 021import java.lang.reflect.Type; 022 023import java.util.Collection; 024import java.util.Objects; 025import java.util.HashSet; 026import java.util.Set; 027 028import javax.enterprise.context.ApplicationScoped; 029 030import javax.enterprise.event.Observes; 031 032import javax.enterprise.inject.spi.AfterBeanDiscovery; 033import javax.enterprise.inject.spi.BeanManager; 034import javax.enterprise.inject.spi.Extension; 035import javax.enterprise.inject.spi.InjectionPoint; 036import javax.enterprise.inject.spi.ProcessInjectionPoint; 037 038import com.fasterxml.jackson.databind.ObjectMapper; 039 040/** 041 * An {@link Extension} that permits {@link ObjectMapper} instances to 042 * be injected in CDI-based applications. 043 * 044 * @author <a href="https://about.me/lairdnelson" 045 * target="_parent">Laird Nelson</a> 046 */ 047public final class JacksonCdiExtension implements Extension { 048 049 private final Set<Set<Annotation>> qualifierSets; 050 051 /** 052 * Creates a new {@link JacksonCdiExtension}. 053 */ 054 public JacksonCdiExtension() { 055 super(); 056 this.qualifierSets = new HashSet<>(); 057 } 058 059 private final <T, X extends ObjectMapper> void processInjectionPoint(@Observes final ProcessInjectionPoint<T, X> event) { 060 Objects.requireNonNull(event); 061 final InjectionPoint injectionPoint = event.getInjectionPoint(); 062 if (injectionPoint != null) { 063 this.qualifierSets.add(injectionPoint.getQualifiers()); 064 } 065 } 066 067 private final void afterBeanDiscovery(@Observes final AfterBeanDiscovery event, final BeanManager beanManager) { 068 Objects.requireNonNull(event); 069 Objects.requireNonNull(beanManager); 070 for (final Set<Annotation> qualifiers : this.qualifierSets) { 071 final Annotation[] qualifiersArray; 072 if (qualifiers == null || qualifiers.isEmpty()) { 073 qualifiersArray = null; 074 } else { 075 qualifiersArray = qualifiers.toArray(new Annotation[qualifiers.size()]); 076 } 077 if (noBeans(beanManager, ObjectMapper.class, qualifiersArray)) { 078 event.addBean() 079 .scope(ApplicationScoped.class) 080 .addTransitiveTypeClosure(ObjectMapper.class) 081 .qualifiers(qualifiers) 082 .createWith(cc -> createObjectMapper(beanManager, qualifiersArray)); 083 } 084 } 085 this.qualifierSets.clear(); 086 } 087 088 private static final ObjectMapper createObjectMapper(final BeanManager beanManager, final Annotation[] qualifiersArray) { 089 Objects.requireNonNull(beanManager); 090 final ObjectMapper returnValue = new ObjectMapper(); 091 returnValue.findAndRegisterModules(); 092 beanManager.getEvent().select(ObjectMapper.class, qualifiersArray).fire(returnValue); 093 return returnValue; 094 } 095 096 private static final boolean noBeans(final BeanManager beanManager, final Type type, final Annotation[] qualifiersArray) { 097 final Collection<?> beans; 098 if (beanManager != null && type != null) { 099 if (qualifiersArray == null || qualifiersArray.length <= 0) { 100 beans = beanManager.getBeans(type); 101 } else { 102 beans = beanManager.getBeans(type, qualifiersArray); 103 } 104 } else { 105 beans = null; 106 } 107 return beans == null || beans.isEmpty(); 108 } 109 110}