Java tutorial
/* * Copyright 2013 Sigurd Randoll. * * 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 de.digiway.rapidbreeze.server.infrastructure.objectstorage.migration; import de.digiway.rapidbreeze.server.infrastructure.objectstorage.ObjectStorage; import java.io.File; import java.io.IOException; import java.net.URL; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.Enumeration; import java.util.List; import org.apache.commons.lang3.Validate; /** * This class supports migration of {@linkplain ObjectStorage} content. This can * be used to create initial data or to modify existing data in an structured * way.<p/> * Each implementation of the interface {@linkplain MigrateScript} in the * configured package will be invoked in alphabetical order. * * @author Sigurd */ public class ObjectStorageMigrate { private ObjectStorage objectStorage; private List<Class<MigrateScript>> scripts; private ObjectStorageMigrate(Builder builder) { this.objectStorage = builder.objectStorage; this.scripts = getScripts(builder.migrationPackage); } private List<Class<MigrateScript>> getScripts(String packageName) { try { List<Class<MigrateScript>> newScripts = new ArrayList<>(); for (Class clazz : getClasses(packageName)) { if (MigrateScript.class.isAssignableFrom(clazz) && !clazz.isInterface()) { newScripts.add(clazz); } } Collections.sort(newScripts, new Comparator<Class<MigrateScript>>() { @Override public int compare(Class<MigrateScript> o1, Class<MigrateScript> o2) { return o1.getSimpleName().compareTo(o2.getSimpleName()); } }); return newScripts; } catch (ClassNotFoundException | IOException ex) { throw new IllegalArgumentException("Cannot read migration scripts.", ex); } } /** * Executes the migration. It will lookup the configured * {@linkplain ObjectStorage} to see which scripts already have been * executed. All new scripts will be invoked in alphabetical order. */ public void migrate() { try { for (Class<MigrateScript> migrateClass : scripts) { if (!objectStorage.exists(MigrateState.class, migrateClass.getSimpleName())) { MigrateScript instance = migrateClass.newInstance(); instance.execute(objectStorage); objectStorage.persist(new MigrateState(migrateClass.getSimpleName())); } } } catch (InstantiationException | IllegalAccessException ex) { throw new IllegalStateException( "Error while constructing migration script. Stopping migration at this point.", ex); } catch (RuntimeException ex) { throw new IllegalStateException( "An error occured during execution of the migration scripts. Stopping execution.", ex); } } /** * Builder to construct {@linkplain ObjectStorageMigrate} instances. */ public static class Builder { private String migrationPackage; private ObjectStorage objectStorage; public Builder migrationPackage(String value) { this.migrationPackage = value; return this; } public Builder objectStorage(ObjectStorage value) { this.objectStorage = value; return this; } public ObjectStorageMigrate build() { Validate.notEmpty(migrationPackage, "A valid package with the migration scripts must be provided."); Validate.notNull(objectStorage, "A valid " + ObjectStorage.class.getSimpleName() + " must be provided."); return new ObjectStorageMigrate(this); } } /** * 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 */ private static Class[] getClasses(String packageName) throws ClassNotFoundException, IOException { ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); assert classLoader != null; String path = packageName.replace('.', '/'); Enumeration<URL> resources = classLoader.getResources(path); List<File> dirs = new ArrayList<>(); while (resources.hasMoreElements()) { URL resource = resources.nextElement(); dirs.add(new File(resource.getFile())); } ArrayList<Class> classes = new ArrayList<>(); for (File directory : dirs) { classes.addAll(findClasses(directory, packageName)); } return classes.toArray(new Class[classes.size()]); } /** * Recursive method used to find all classes in a given directory and * subdirs. * * @param directory The base directory * @param packageName The package name for classes found inside the base * directory * @return The classes * @throws ClassNotFoundException */ private static List<Class> findClasses(File directory, String packageName) throws ClassNotFoundException { List<Class> classes = new ArrayList<>(); if (!directory.exists()) { return classes; } File[] files = directory.listFiles(); for (File file : files) { if (file.isDirectory()) { assert !file.getName().contains("."); classes.addAll(findClasses(file, packageName + "." + file.getName())); } else if (file.getName().endsWith(".class")) { classes.add(Class .forName(packageName + '.' + file.getName().substring(0, file.getName().length() - 6))); } } return classes; } }