Package com.cognifide.slice.core.internal.module

Source Code of com.cognifide.slice.core.internal.module.SliceResourceModule$AnnotationFilter

package com.cognifide.slice.core.internal.module;

/*-
* #%L
* Slice - Core
* $Id:$
* $HeadURL:$
* %%
* Copyright (C) 2012 Cognifide Limited
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/

import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.regex.Pattern;

import org.objectweb.asm.ClassReader;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.cognifide.slice.api.qualifier.EmptyObject;
import com.cognifide.slice.commons.provider.SliceResourceProvider;
import com.cognifide.slice.core.internal.BundleClassesFinder;
import com.cognifide.slice.core.internal.BundleClassesFinder.ClassFilter;
import com.cognifide.slice.mapper.annotation.SliceResource;
import com.cognifide.slice.mapper.api.Mapper;
import com.google.inject.AbstractModule;
import com.google.inject.Inject;

/**
* The module is responsible for finding all classes annotated by {@link SliceResource}. Found classes are
* bound to their {@link SliceResourceProvider} providers
*
*/
public class SliceResourceModule extends AbstractModule {

  private static final Logger LOG = LoggerFactory.getLogger(SliceResourceModule.class);

  private final Collection<Class<?>> classes;

  public SliceResourceModule(final Collection<Class<?>> classes) {
    this.classes = classes;
  }

  public SliceResourceModule(final BundleContext bundleContext, final String bundleNameFilter,
      final String basePackage) {

    final Pattern bundleNamePattern = Pattern.compile(bundleNameFilter);
    List<Bundle> filteredBundles = new ArrayList<Bundle>();

    for (Bundle bundle : bundleContext.getBundles()) {
      if (bundleNamePattern.matcher(bundle.getSymbolicName()).matches()) {
        filteredBundles.add(bundle);
      }
    }

    BundleClassesFinder classFinder = new BundleClassesFinder(filteredBundles, basePackage);
    classFinder.addFilter(new AnnotationFilter());

    this.classes = classFinder.getClasses();
  }

  @Override
  protected void configure() {
    for (Class<?> clazz : this.classes) {
      try {
        bindToOwnProvider(clazz);
      } catch (Exception e) {
        LOG.error("Cannot register " + clazz + " class. Nested exception:", e);
      }
    }
  }

  /**
   * It binds a given class to appropriate providers. Firstly, the class is bound with {@link EmptyObject}
   * annotations what allows for creating empty objects (not mapped by {@link Mapper}), and then it is bound
   * to actual provider which uses mapper to fill the empty object with CRX values.
   *
   * @param <T>
   * @param sliceResourceClass
   */
  private <T> void bindToOwnProvider(final Class<T> sliceResourceClass) {
    bindEmptyObject(sliceResourceClass);
    bindActualObject(sliceResourceClass);
  }

  /**
   * This is an essential method which binds provided class with {@link EmptyObject} annotation. It allows
   * then to create an empty object of this class with all constructor parameters injected by Guice. Such an
   * empty object can be further processed by {@link Mapper} in order to fill appropriate fields.<br>
   * <br>
   * What the method actually does is to bind the provided class to either constructor annotated with
   * {@link Inject} annotation or the default (zero-argument) constructor. If none of these constructors are
   * declared by the class, the {@link IllegalArgumentException} is thrown.
   *
   * @param <T>
   * @param sliceResourceClass
   */
  private <T> void bindEmptyObject(final Class<T> sliceResourceClass) {
    Constructor<T> constructor = getInjectConstructor(sliceResourceClass);
    if (constructor == null) {
      throw new IllegalArgumentException(
          "Class must have either one (and only one) constructor annotated with @Inject or a zero-argument constructor that is not private");
    }
    bind(sliceResourceClass).annotatedWith(EmptyObject.class).toConstructor(constructor);
  }

  /**
   * It binds provided class to appropriate {@link SliceResourceProvider}. When a client requests to create
   * object of given class, bound provider will be used.
   *
   * @param <T>
   * @param sliceResourceClass
   */
  private <T> void bindActualObject(final Class<T> sliceResourceClass) {
    LOG.error("Binding " + sliceResourceClass + " to MRP");
    bind(sliceResourceClass).toProvider(new MappedResourceProvider<T>(sliceResourceClass));
  }

  @SuppressWarnings("unchecked")
  private <T> Constructor<T> getInjectConstructor(final Class<T> sliceResourceClass) {
    final Constructor<?>[] constructors = sliceResourceClass.getConstructors();
    for (final Constructor<?> c : constructors) {
      if (c.isAnnotationPresent(Inject.class) || c.isAnnotationPresent(javax.inject.Inject.class)) {
        return (Constructor<T>) c;
      }
    }

    return getDefaultConstructor(sliceResourceClass);
  }

  private <T> Constructor<T> getDefaultConstructor(final Class<T> sliceResourceClass) {
    try {
      return sliceResourceClass.getConstructor(new Class<?>[0]);
    } catch (SecurityException e) {
      LOG.error("Error while reading empty constructor", e);
    } catch (NoSuchMethodException e) {
      LOG.error("Error while reading empty constructor", e);
    }
    return null;
  }

  private static class AnnotationFilter implements ClassFilter {
    @Override
    public boolean accepts(ClassReader classReader) {
      final AnnotationReader annotationReader = new AnnotationReader();
      classReader.accept(annotationReader, ClassReader.SKIP_DEBUG);
      return annotationReader.isAnnotationPresent(SliceResource.class);
    }
  }

}
TOP

Related Classes of com.cognifide.slice.core.internal.module.SliceResourceModule$AnnotationFilter

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.