/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
package org.apache.deltaspike.data.impl;
import java.lang.reflect.InvocationHandler;
import java.util.LinkedList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.enterprise.event.Observes;
import javax.enterprise.inject.spi.AfterBeanDiscovery;
import javax.enterprise.inject.spi.AnnotatedType;
import javax.enterprise.inject.spi.BeanManager;
import javax.enterprise.inject.spi.BeforeBeanDiscovery;
import javax.enterprise.inject.spi.BeforeShutdown;
import javax.enterprise.inject.spi.Extension;
import javax.enterprise.inject.spi.ProcessAnnotatedType;
import org.apache.deltaspike.core.spi.activation.Deactivatable;
import org.apache.deltaspike.core.util.ClassDeactivationUtils;
import org.apache.deltaspike.data.api.AbstractEntityRepository;
import org.apache.deltaspike.data.api.AbstractFullEntityRepository;
import org.apache.deltaspike.data.api.Repository;
import org.apache.deltaspike.data.impl.meta.RepositoryComponents;
/**
* The main extension class for Repositories, based on PartialBeans. Handles following events:<br/>
* <br/>
* <b>{@code @Observes BeforeBeanDiscovery}</b>:
* Scans the classpath for <code>persistence.xml</code> and extracts relevant information out of it.
* This includes mainly entity definitions (type, primary keys) which are not declared with annotations.<br/>
* <br/>
* <b>{@code @Observes ProcessAnnotatedType<X>}</b>:
* Looks for types annotated with {@link Repository}. Repositories are validated and preprocessed -
* all the methods on the repository are checked and analyzed for better runtime performance.<br/>
* <br/>
* <b>{@code @Observes AfterBeanDiscovery<X>}</b>:
* Raises any definition errors discovered before.
*/
public class RepositoryExtension implements Extension, Deactivatable
{
private static final Logger log = Logger.getLogger(RepositoryExtension.class.getName());
private static RepositoryComponents staticComponents = new RepositoryComponents();
private final List<RepositoryDefinitionException> definitionExceptions =
new LinkedList<RepositoryDefinitionException>();
private Boolean isActivated = true;
private RepositoryComponents components = new RepositoryComponents();
void beforeBeanDiscovery(@Observes BeforeBeanDiscovery before)
{
isActivated = ClassDeactivationUtils.isActivated(getClass());
}
@SuppressWarnings("unchecked")
<X> void processAnnotatedType(@Observes ProcessAnnotatedType<X> event)
{
if (!isActivated)
{
return;
}
if (isVetoed(event.getAnnotatedType()))
{
event.veto();
}
else if (isRepository(event.getAnnotatedType()))
{
Class<X> repoClass = event.getAnnotatedType().getJavaClass();
try
{
log.log(Level.FINER, "getHandlerClass: Repository annotation detected on {0}",
event.getAnnotatedType());
if (Deactivatable.class.isAssignableFrom(repoClass)
&& !ClassDeactivationUtils.isActivated((Class<? extends Deactivatable>) repoClass))
{
log.log(Level.FINER, "Class {0} is Deactivated", repoClass);
return;
}
components.add(repoClass);
staticComponents.add(repoClass);
}
catch (RepositoryDefinitionException e)
{
definitionExceptions.add(e);
}
catch (Exception e)
{
definitionExceptions.add(new RepositoryDefinitionException(repoClass, e));
}
}
}
<X> void addDefinitionErrors(@Observes AfterBeanDiscovery afterBeanDiscovery, BeanManager beanManager)
{
if (!isActivated)
{
return;
}
for (RepositoryDefinitionException ex : definitionExceptions)
{
afterBeanDiscovery.addDefinitionError(ex);
}
}
private <X> boolean isRepository(AnnotatedType<X> annotatedType)
{
return (annotatedType.isAnnotationPresent(Repository.class) ||
annotatedType.getJavaClass().isAnnotationPresent(Repository.class)) &&
!InvocationHandler.class.isAssignableFrom(annotatedType.getJavaClass());
}
private <X> boolean isVetoed(AnnotatedType<X> annotated)
{
Class<X> javaClass = annotated.getJavaClass();
return javaClass.equals(AbstractEntityRepository.class) ||
javaClass.equals(AbstractFullEntityRepository.class);
}
public RepositoryComponents getComponents()
{
RepositoryComponents result = new RepositoryComponents();
if (components.getRepositories().isEmpty() && !staticComponents.getRepositories().isEmpty())
{
result.addAll(staticComponents.getRepositories());
}
if (!components.getRepositories().isEmpty())
{
result.addAll(components.getRepositories());
}
return result;
}
protected void cleanup(@Observes BeforeShutdown beforeShutdown)
{
//we can reset it in any case,
//because every application produced a copy as application-scoped bean (see RepositoryComponentsFactory)
staticComponents.getRepositories().clear();
}
}