/** * Copyright 2013 Bayes Technologies * * 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. */ package me.bayes.vertx.vest; import io.vertx.core.impl.verticle.PackageHelper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.tools.JavaFileObject; import javax.ws.rs.Path; import javax.ws.rs.core.Application; import java.io.IOException; import java.lang.annotation.Annotation; import java.util.*; /** * <pre> * An abstract implementation of a jaxrs {@link javax.ws.rs.core.Application} for the Vest framework. * * This class should be extended by any application using vest inorder to set the * {@link javax.ws.rs.ApplicationPath} annotation for the starting context of your application. * Example can be seen in the {@link RootContextVestApplication} where the {@link javax.ws.rs.ApplicationPath} * annotation is used to ensure that the rest service is located on '/'. * </pre> * * @author Kevin Bayes * @since 1.0 * @version 1.0 */ public abstract class VestApplication extends Application { private final static Logger LOG = LoggerFactory.getLogger(VestApplication.class); /* * Classloader to load scanned classes. */ private ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); /* * Used if you want to scan packages for all classes annotated with * @Path. */ private Set<String> packagesToScan = new HashSet<String>(0); /* * Used to specify the classes annotated with @Path. */ private final Set<Class<?>> endpointClasses = new HashSet<Class<?>>(0); /* * Store all the singleton instances. */ private final Set<Object> singletons = new HashSet<Object>(0); /* * Map of shared properties */ private final Map<String, Object> properties = new HashMap<String, Object>(0); /* * (non-Javadoc) * @see javax.ws.rs.core.Application#getClasses() */ @Override public Set<Class<?>> getClasses() { return endpointClasses; } @Override public Set<Object> getSingletons() { return singletons; } @Override public Map<String, Object> getProperties() { return properties; } public Set<String> getPackagesToScan() { return packagesToScan; } /** * @param packagesToScan for classes annotated with {@link javax.ws.rs.Path}. */ public void addPackagesToScan(Collection<String> packagesToScan) { this.packagesToScan.addAll(packagesToScan); addScannedClasses(); } public void addPackagesToScan(String... packagesToScan) { this.packagesToScan.addAll(Arrays.asList(packagesToScan)); addScannedClasses(); } public Set<Class<?>> getEndpointClasses() { return endpointClasses; } /** * @param endpointClasses to add to the classes annotated with {@link javax.ws.rs.Path}. * If not annotated with {@link javax.ws.rs.Path} it will be ignored. */ public void addEndpointClasses(Collection<Class<?>> endpointClasses) { for(Class<?> clazz : endpointClasses) { if(clazz.getAnnotation(Path.class) != null) { this.endpointClasses.add(clazz); } } } public void addEndpointClasses(Class<?>... endpointClasses) { for(Class<?> clazz : endpointClasses) { if(clazz.getAnnotation(Path.class) != null) { this.endpointClasses.add(clazz); } } } /* * Use vertx's built in PackageHelper to scan for classes in the classpath. Sub packages are left out for now. * * TODO: Think about rather using Reflections jar, but this adds a dependency. */ private void addScannedClasses() { PackageHelper helper = new PackageHelper(classLoader); for(String packageName : packagesToScan) { final String folderLocationName = packageName.replaceAll("[.]", "/"); try { for(JavaFileObject javaObject : helper.find(packageName)) { try { final String clazzUriString = javaObject.toUri().toString(); final String clazzName = clazzUriString.substring( clazzUriString.lastIndexOf(folderLocationName), clazzUriString.lastIndexOf('.')) .replaceAll("/", "."); final Class<?> clazz = classLoader.loadClass(clazzName); final Annotation[] annotations = clazz.getAnnotations(); for(Annotation annotation : annotations) { if(annotation.annotationType().equals(Path.class)) { this.endpointClasses.add(clazz); break; } } } catch (ClassNotFoundException e) { LOG.error(String.format("Error occurred scanning package %s.", packageName), e); } } } catch (IOException e) { LOG.error(String.format("Error occurred scanning package %s.", packageName), e); } } } /** * Add singleton classes to access at a later stage * @param singleton */ public void addSingleton(Object... singleton) { for(Object obj : singleton) { if(this.singletons.contains(obj)) { LOG.warn("Object " + obj + " already exists."); } this.singletons.add(obj); } } /** * Add properties to the shared list. * * @param key * @param value */ public void addProperty(String key, Object value) { if(this.properties.containsKey(key)) { LOG.warn("Property " + key + " already exists."); } this.properties.put(key, value); } public Application getPropertyValue(String key, Class<Application> clazz) { return clazz.cast(properties.get(key)); } }