/* * Copyright (C) 2007 - 2011 GeoSolutions S.A.S. * http://www.geo-solutions.it * * GPLv3 + Classpath exception * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package it.geosolutions.geostore.services.rest.utils; import java.io.File; import java.io.IOException; import java.io.Serializable; import java.net.URL; import java.net.URLDecoder; import java.util.ArrayList; import java.util.Arrays; import java.util.Enumeration; import java.util.List; import javax.xml.bind.JAXBContext; import org.apache.log4j.Logger; /** * A JAXBContext of public GeoStore classes. Useful when unmarhasalling geostore classes. * * @author ETj (etj at geo-solutions.it) */ public class GeoStoreJAXBContext { private final static Logger LOGGER = Logger.getLogger(GeoStoreJAXBContext.class); private final static JAXBContext context; static { context = createNewContext(); } /** * Use this method if you need to extend your context, or use the getContext() method if you need the standard context. */ public static JAXBContext createNewContext() { JAXBContext tmpContext = null; try { // This procedure has been commented out since it has problems in dealing with classes inside jar files. // An explicit enumeration of the classes has been implemented instead. // String coreModelPackage = Resource.class.getPackage().getName(); // String serviceApiPackage = ShortResource.class.getPackage().getName(); // String restApiPackage = RESTResource.class.getPackage().getName(); // // List<Class> coreModelClasses = getClasses(coreModelPackage); // List<Class> serviceApiClasses = getClasses(serviceApiPackage); // List<Class> restApiClasses = getClasses(restApiPackage); List<Class> allClasses = getGeoStoreClasses(); if (LOGGER.isDebugEnabled()) LOGGER.debug("Initializing GeoStoreJAXBContext with " + allClasses.size() + " classes " + allClasses); tmpContext = JAXBContext.newInstance(allClasses.toArray(new Class[allClasses.size()])); } catch (Exception ex) { LOGGER.error("Can't create GeoStore context: " + ex.getMessage(), ex); } return tmpContext; } public static List<Class> getGeoStoreClasses() { // retrieve the explicit set of classes List coreModelClasses = getModelClasses(); List serviceApiClasses = getAPIclasses(); List restApiClasses = getRESTclasses(); // merge the classes and create the context List<Class> allClasses = new ArrayList<Class>(coreModelClasses.size() + serviceApiClasses.size() + restApiClasses.size()); allClasses.addAll(coreModelClasses); allClasses.addAll(serviceApiClasses); allClasses.addAll(restApiClasses); return allClasses; } private static List<Class<? extends Serializable>> getModelClasses() { return Arrays.asList(it.geosolutions.geostore.core.model.Attribute.class, it.geosolutions.geostore.core.model.Category.class, it.geosolutions.geostore.core.model.UserGroup.class, it.geosolutions.geostore.core.model.Resource.class, it.geosolutions.geostore.core.model.SecurityRule.class, it.geosolutions.geostore.core.model.StoredData.class, it.geosolutions.geostore.core.model.User.class, it.geosolutions.geostore.core.model.UserAttribute.class, it.geosolutions.geostore.core.model.UserGroup.class, it.geosolutions.geostore.core.model.enums.AccessType.class, it.geosolutions.geostore.core.model.enums.DataType.class, it.geosolutions.geostore.core.model.enums.Role.class); } private static List<Class<? extends Serializable>> getAPIclasses() { return Arrays.asList(it.geosolutions.geostore.services.dto.ShortAttribute.class, it.geosolutions.geostore.services.dto.ShortResource.class, it.geosolutions.geostore.services.dto.search.AndFilter.class, it.geosolutions.geostore.services.dto.search.AttributeFilter.class, it.geosolutions.geostore.services.dto.search.BaseField.class, it.geosolutions.geostore.services.dto.search.CategoryFilter.class, it.geosolutions.geostore.services.dto.search.FieldFilter.class, it.geosolutions.geostore.services.dto.search.NotFilter.class, it.geosolutions.geostore.services.dto.search.OrFilter.class, it.geosolutions.geostore.services.dto.search.SearchFilter.class, it.geosolutions.geostore.services.dto.search.SearchOperator.class); } private static List<Class<?>> getRESTclasses() { return Arrays .asList(it.geosolutions.geostore.services.rest.model.CategoryList.class, it.geosolutions.geostore.services.rest.model.UserGroupList.class, it.geosolutions.geostore.services.rest.model.RESTResource.class, it.geosolutions.geostore.services.rest.model.RESTCategory.class, it.geosolutions.geostore.services.rest.model.StoredDataList.class, it.geosolutions.geostore.services.rest.model.ShortAttributeList.class, it.geosolutions.geostore.services.rest.model.UserList.class, it.geosolutions.geostore.services.rest.model.ShortResourceList.class, it.geosolutions.geostore.services.rest.model.RESTStoredData.class, it.geosolutions.geostore.services.rest.model.RESTQuickBackup.class, it.geosolutions.geostore.services.rest.model.RESTQuickBackup.RESTBackupCategory.class, it.geosolutions.geostore.services.rest.model.RESTQuickBackup.RESTBackupResource.class); } public static JAXBContext getContext() { if (context == null) throw new IllegalStateException("Context has not been properly initialized"); return context; } /** * Scans all classes accessible from the context class loader which belong to the given package and subpackages. * * @param packageName The base package * @return The classes * @throws ClassNotFoundException * @throws IOException */ @SuppressWarnings("unchecked") private static List<Class> getClasses(String packageName) throws ClassNotFoundException, IOException { if (LOGGER.isDebugEnabled()) LOGGER.debug("Loading classes from " + packageName); ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); assert classLoader != null; String path = packageName.replace('.', '/'); Enumeration<URL> resources = classLoader.getResources(path); List<File> dirs = new ArrayList<File>(); while (resources.hasMoreElements()) { URL resource = resources.nextElement(); String fileName = resource.getFile(); if (LOGGER.isDebugEnabled()) LOGGER.debug(" adding resource " + fileName); String fileNameDecoded = URLDecoder.decode(fileName, "UTF-8"); dirs.add(new File(fileNameDecoded)); } ArrayList<Class> classes = new ArrayList<Class>(); for (File directory : dirs) { classes.addAll(findClasses(directory, packageName)); } return classes; } /** * Recursive method used to find all classes in a given directory and subdirs. * * This method is not useful if classes are inside a jar file. * * @param directory The base directory * @param packageName The package name for classes found inside the base directory * @return The classes * @throws ClassNotFoundException */ @SuppressWarnings("unchecked") private static List<Class> findClasses(File directory, String packageName) throws ClassNotFoundException { List<Class> classes = new ArrayList<Class>(); if (!directory.exists()) { LOGGER.debug("Directory not found :" + directory); return classes; } File[] files = directory.listFiles(); for (File file : files) { String fileName = file.getName(); if (file.isDirectory()) { assert !fileName.contains("."); classes.addAll(findClasses(file, packageName + "." + fileName)); } else if (fileName.endsWith(".class") && !fileName.contains("$")) { Class _class; try { _class = Class.forName(packageName + '.' + fileName.substring(0, fileName.length() - 6)); } catch (ExceptionInInitializerError e) { // happen, for example, in classes, which depend on // Spring to inject some beans, and which fail, // if dependency is not fulfilled _class = Class.forName( packageName + '.' + fileName.substring(0, fileName.length() - 6), false, Thread.currentThread().getContextClassLoader()); } classes.add(_class); } } return classes; } }