/** * Copyright (c) 2013-2016, The SeedStack authors <http://seedstack.org> * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ package org.seedstack.seed.core.internal.data; import io.nuun.kernel.api.plugin.InitState; import io.nuun.kernel.api.plugin.context.Context; import io.nuun.kernel.api.plugin.context.InitContext; import io.nuun.kernel.api.plugin.request.ClasspathScanRequest; import org.kametic.specifications.Specification; import org.seedstack.seed.DataConfig; import org.seedstack.seed.DataExporter; import org.seedstack.seed.DataImporter; import org.seedstack.seed.DataManager; import org.seedstack.seed.DataSet; import org.seedstack.seed.SeedException; import org.seedstack.seed.core.internal.AbstractSeedPlugin; import org.seedstack.shed.ClassLoaders; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.inject.Inject; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.Collection; import java.util.HashMap; import java.util.Map; /** * This plugin provides data import and export facilities. */ public class DataPlugin extends AbstractSeedPlugin { private static final Logger LOGGER = LoggerFactory.getLogger(DataPlugin.class); private final Specification<Class<?>> dataExporterSpecification = and(classImplements(DataExporter.class), classAnnotatedWith(DataSet.class)); private final Specification<Class<?>> dataImporterSpecification = and(classImplements(DataImporter.class), classAnnotatedWith(DataSet.class)); private final Map<String, Map<String, DataExporterDefinition<Object>>> allDataExporters = new HashMap<>(); private final Map<String, Map<String, DataImporterDefinition<Object>>> allDataImporters = new HashMap<>(); private boolean loadInitializationData; private boolean forceInitializationData; @Inject private DataManager dataManager; @Override public String name() { return "data"; } @Override public Collection<ClasspathScanRequest> classpathScanRequests() { return classpathScanRequestBuilder().specification(dataExporterSpecification).specification(dataImporterSpecification).build(); } @SuppressWarnings("unchecked") @Override public InitState initialize(InitContext initContext) { DataConfig dataConfig = getConfiguration(DataConfig.class); switch (dataConfig.getImportMode()) { case FORCE: forceInitializationData = true; // falls through case AUTO: loadInitializationData = true; break; } Collection<Class<?>> scannedDataExporterClasses = initContext.scannedTypesBySpecification().get(dataExporterSpecification); for (Class<?> scannedDataExporterClass : scannedDataExporterClasses) { if (DataExporter.class.isAssignableFrom(scannedDataExporterClass)) { DataSet dataSet = scannedDataExporterClass.getAnnotation(DataSet.class); Map<String, DataExporterDefinition<Object>> nameDataExporterDefinitionMap = allDataExporters.get(dataSet.group()); if (nameDataExporterDefinitionMap == null) { nameDataExporterDefinitionMap = new HashMap<>(); } Class exportedClass = getTypeParameter(scannedDataExporterClass, DataExporter.class); if (exportedClass == null) { throw SeedException.createNew(DataErrorCode.MISSING_TYPE_PARAMETER).put("class", scannedDataExporterClass); } DataExporterDefinition dataExporterDefinition = new DataExporterDefinition(dataSet.name(), dataSet.group(), exportedClass, scannedDataExporterClass); nameDataExporterDefinitionMap.put(dataSet.name(), dataExporterDefinition); allDataExporters.put(dataSet.group(), nameDataExporterDefinitionMap); } } Collection<Class<?>> scannedDataImporterClasses = initContext.scannedTypesBySpecification().get(dataImporterSpecification); for (Class<?> scannedDataImporterClass : scannedDataImporterClasses) { if (DataImporter.class.isAssignableFrom(scannedDataImporterClass)) { DataSet dataSet = scannedDataImporterClass.getAnnotation(DataSet.class); Map<String, DataImporterDefinition<Object>> nameDataImporterDefinitionMap = allDataImporters.get(dataSet.group()); if (nameDataImporterDefinitionMap == null) { nameDataImporterDefinitionMap = new HashMap<>(); } Class actualType = getTypeParameter(scannedDataImporterClass, DataImporter.class); if (actualType == null) { throw SeedException.createNew(DataErrorCode.MISSING_TYPE_PARAMETER).put("class", scannedDataImporterClass); } DataImporterDefinition dataImporterDefinition = new DataImporterDefinition(dataSet.name(), dataSet.group(), actualType, scannedDataImporterClass); nameDataImporterDefinitionMap.put(dataSet.name(), dataImporterDefinition); allDataImporters.put(dataSet.group(), nameDataImporterDefinitionMap); } } return InitState.INITIALIZED; } @Override public void start(Context context) { ClassLoader classLoader = ClassLoaders.findMostCompleteClassLoader(DataPlugin.class); if (loadInitializationData) { for (Map<String, DataImporterDefinition<Object>> dataImporterDefinitionMap : allDataImporters.values()) { for (DataImporterDefinition<Object> dataImporterDefinition : dataImporterDefinitionMap.values()) { String dataPath = String.format("META-INF/data/%s/%s.json", dataImporterDefinition.getGroup(), dataImporterDefinition.getName()); InputStream dataStream = classLoader.getResourceAsStream(dataPath); if (dataStream != null) { if (!dataManager.isInitialized(dataImporterDefinition.getGroup(), dataImporterDefinition.getName()) || forceInitializationData) { LOGGER.info("Importing initialization data for {}.{}", dataImporterDefinition.getGroup(), dataImporterDefinition.getName()); dataManager.importData(dataStream, dataImporterDefinition.getGroup(), dataImporterDefinition.getName(), true); } try { dataStream.close(); } catch (IOException e) { LOGGER.warn("Unable to close data resource " + dataPath, e); } } } } } } private Class getTypeParameter(Class<?> scannedDataImporterClass, Class<?> genericInterface) { Class actualType = null; // Get all generic interfaces implemented by the scanned class Type[] genericInterfaces = scannedDataImporterClass.getGenericInterfaces(); for (Type type : genericInterfaces) { if (type instanceof ParameterizedType) { Class anInterface = (Class) ((ParameterizedType) type).getRawType(); // If the interface is DataImporter get its type parameter if (genericInterface.isAssignableFrom(anInterface)) { actualType = (Class) ((ParameterizedType) type).getActualTypeArguments()[0]; } } } return actualType; } @Override public Object nativeUnitModule() { return new DataModule(allDataExporters, allDataImporters); } }