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}