package io.eguan.rest.jaxrs;
/*
* #%L
* Project eguan
* %%
* Copyright (C) 2012 - 2017 Oodrive
* %%
* 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.Method;
import java.util.Collections;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
import javax.annotation.Nonnull;
import javax.ws.rs.Path;
import javax.ws.rs.core.Application;
import javax.ws.rs.ext.Provider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Container class for JAX-RS annotated classes and singleton instances.
*
* @author oodrive
* @author pwehrle
*
*/
public class JaxRsAppContext {
private static final Logger LOGGER = LoggerFactory.getLogger(JaxRsAppContext.class);
private final Set<Object> resources = new HashSet<Object>();
private final Set<Object> providers = new HashSet<Object>();
public JaxRsAppContext() {
super();
}
/**
* Adds a resource instance.
*
* Resource classes must have at least one (inherited) {@link Path} annotation, either at class or method level.
*
* @param resource
* a functional (singleton) instance of a resource class
* @throws NullPointerException
* if the argument is <code>null</code>
* @throws IllegalArgumentException
* if no (even inherited) {@link Path} annotation can be found on the resource class or methods
* @see Path
*/
public final void addResource(@Nonnull final Object resource) throws NullPointerException, IllegalArgumentException {
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("adding resource instance {}", resource);
}
final Class<? extends Object> resClass = Objects.requireNonNull(resource).getClass();
if (!hasPathAnnotation(resClass)) {
throw new IllegalArgumentException("Missing @Path annotation on resource class "
+ resClass.getCanonicalName());
}
this.resources.add(resource);
}
/**
* Adds all elements of the array as resources.
*
* @param resources
* a non-<code>null</code> array of (singleton) instances
* @throws NullPointerException
* if any of the resources is <code>null</code>
* @throws IllegalArgumentException
* if no {@link Path} annotation can be found on any of the resources' classes or methods
* @see #addResource(Object)
*/
public final void addAllResources(@Nonnull final Object[] resources) throws NullPointerException,
IllegalArgumentException {
for (final Object currObject : resources) {
addResource(currObject);
}
}
/**
* Adds a {@link Provider} instance.
*
* Provider classes (or any of their superclasses or -interfaces) must be annotated with {@link Provider}.
*
* @param provider
* an non-<code>null</code> {@link Object} instance
* @throws NullPointerException
* if the argument is <code>null</code
* @throws IllegalArgumentException
* if no (inherited) {@link Provider} annotation can be found on the provider's class
* @see Provider
*/
public final void addProvider(@Nonnull final Object provider) throws NullPointerException, IllegalArgumentException {
if (LOGGER.isTraceEnabled()) {
LOGGER.trace("adding provider instance {}", provider);
}
final Class<?> provClass = Objects.requireNonNull(provider.getClass());
if (!hasProviderAnnotation(provClass)) {
throw new IllegalArgumentException("Missing @Provider annotation on class " + provClass.getCanonicalName());
}
this.providers.add(provider);
}
/**
* Adds all elements of the array as providers.
*
* @param provider
* a non-<code>null</code> array of (singleton) instances
* @throws NullPointerException
* if any of the providers is <code>null</code>
* @throws IllegalArgumentException
* if no {@link Provider} annotation can be found on any of the providers' classes
* @see #addProvider(Object)
*/
public final void addAllProviders(@Nonnull final Object[] providers) throws NullPointerException,
IllegalArgumentException {
for (final Object currObject : providers) {
addProvider(currObject);
}
}
/**
* Gets all singleton {@link Object} instances to be inserted either as resources or providers into the
* {@link Application JAX-RS application} context.
*
* @return a (possibly empty) {@link Set} of {@link Object}s
*/
public final Set<Object> getSingletonInstances() {
final HashSet<Object> result = new HashSet<Object>(resources);
result.addAll(providers);
return Collections.unmodifiableSet(result);
}
/**
* Recursively determines if the provided {@link Class} or any of its superclasses, methods or interfaces has a
* {@link Path} annotation.
*
* @param clazz
* the {@link Class} to check
* @return <code>true</code> if a {@link Path} annotation was found, <code>false</code> otherwise
*/
private static boolean hasPathAnnotation(final Class<?> clazz) {
if (clazz == null) {
return false;
}
boolean hasPathAnnotation = (clazz.getAnnotation(Path.class) != null);
if (!hasPathAnnotation) {
for (final Method currMethod : clazz.getMethods()) {
hasPathAnnotation |= (currMethod.getAnnotation(Path.class) != null);
}
}
if (!hasPathAnnotation) {
hasPathAnnotation |= hasPathAnnotation(clazz.getSuperclass());
}
if (!hasPathAnnotation) {
for (final Class<?> currInterface : clazz.getInterfaces()) {
hasPathAnnotation |= hasPathAnnotation(currInterface);
}
}
return hasPathAnnotation;
}
/**
* Recursively determines if the provided {@link Class} or any of its superclasses or interfaces has a
* {@link Provider} annotation.
*
* @param clazz
* the {@link Class} to check
* @return <code>true</code> if a {@link Provider} annotation was found, <code>false</code> otherwise
*/
private static boolean hasProviderAnnotation(final Class<?> clazz) {
if (clazz == null) {
return false;
}
boolean hasProviderAnnotation = (clazz.getAnnotation(Provider.class) != null);
if (!hasProviderAnnotation) {
hasProviderAnnotation |= hasProviderAnnotation(clazz.getSuperclass());
}
if (!hasProviderAnnotation) {
for (final Class<?> currInterface : clazz.getInterfaces()) {
hasProviderAnnotation |= hasProviderAnnotation(currInterface);
}
}
return hasProviderAnnotation;
}
}