/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE file at the root of the source
* tree and available online at
*
* https://github.com/keeps/roda
*/
package org.roda.core;
import java.io.BufferedReader;
import java.io.Console;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.StandardCopyOption;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import javax.xml.validation.Schema;
import org.apache.commons.configuration.CompositeConfiguration;
import org.apache.commons.configuration.Configuration;
import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.PropertiesConfiguration;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.solr.client.solrj.SolrClient;
import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.client.solrj.embedded.EmbeddedSolrServer;
import org.apache.solr.client.solrj.impl.CloudSolrClient;
import org.apache.solr.client.solrj.impl.HttpSolrClient;
import org.apache.solr.client.solrj.response.QueryResponse;
import org.apache.solr.common.SolrDocument;
import org.apache.solr.common.SolrDocumentList;
import org.reflections.Reflections;
import org.reflections.scanners.ResourcesScanner;
import org.reflections.util.ClasspathHelper;
import org.reflections.util.ConfigurationBuilder;
import org.reflections.util.FilterBuilder;
import org.roda.core.common.LdapUtility;
import org.roda.core.common.Messages;
import org.roda.core.common.RodaUtils;
import org.roda.core.common.UserUtility;
import org.roda.core.common.iterables.CloseableIterable;
import org.roda.core.common.monitor.TransferUpdateStatus;
import org.roda.core.common.monitor.TransferredResourcesScanner;
import org.roda.core.data.common.RodaConstants;
import org.roda.core.data.common.RodaConstants.NodeType;
import org.roda.core.data.common.RodaConstants.PreservationAgentType;
import org.roda.core.data.common.RodaConstants.PreservationEventType;
import org.roda.core.data.common.RodaConstants.SolrType;
import org.roda.core.data.common.RodaConstants.StorageType;
import org.roda.core.data.exceptions.AlreadyExistsException;
import org.roda.core.data.exceptions.AuthorizationDeniedException;
import org.roda.core.data.exceptions.GenericException;
import org.roda.core.data.exceptions.IllegalOperationException;
import org.roda.core.data.exceptions.NotFoundException;
import org.roda.core.data.exceptions.RequestNotValidException;
import org.roda.core.data.exceptions.RoleAlreadyExistsException;
import org.roda.core.data.v2.common.Pair;
import org.roda.core.data.v2.index.IndexResult;
import org.roda.core.data.v2.index.facet.Facets;
import org.roda.core.data.v2.index.filter.BasicSearchFilterParameter;
import org.roda.core.data.v2.index.filter.EmptyKeyFilterParameter;
import org.roda.core.data.v2.index.filter.Filter;
import org.roda.core.data.v2.index.filter.FilterParameter;
import org.roda.core.data.v2.index.filter.SimpleFilterParameter;
import org.roda.core.data.v2.index.sort.Sorter;
import org.roda.core.data.v2.index.sublist.Sublist;
import org.roda.core.data.v2.ip.IndexedFile;
import org.roda.core.data.v2.ip.TransferredResource;
import org.roda.core.data.v2.ip.metadata.IndexedPreservationAgent;
import org.roda.core.data.v2.ip.metadata.IndexedPreservationEvent;
import org.roda.core.data.v2.user.Group;
import org.roda.core.data.v2.user.RODAMember;
import org.roda.core.data.v2.user.User;
import org.roda.core.index.IndexService;
import org.roda.core.index.utils.SolrUtils;
import org.roda.core.migration.MigrationManager;
import org.roda.core.model.ModelService;
import org.roda.core.plugins.PluginManager;
import org.roda.core.plugins.PluginManagerException;
import org.roda.core.plugins.PluginOrchestrator;
import org.roda.core.plugins.orchestrate.AkkaDistributedPluginOrchestrator;
import org.roda.core.plugins.orchestrate.AkkaEmbeddedPluginOrchestrator;
import org.roda.core.plugins.orchestrate.akka.distributed.AkkaDistributedPluginWorker;
import org.roda.core.storage.DefaultStoragePath;
import org.roda.core.storage.Resource;
import org.roda.core.storage.StorageService;
import org.roda.core.storage.fedora.FedoraStorageService;
import org.roda.core.storage.fs.FSUtils;
import org.roda.core.storage.fs.FileStorageService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.codahale.metrics.JmxReporter;
import com.codahale.metrics.MetricRegistry;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.joran.JoranConfigurator;
import ch.qos.logback.core.joran.spi.JoranException;
/**
* @author Hélder Silva <hsilva@keep.pt>
*/
public class RodaCoreFactory {
private static final Logger LOGGER = LoggerFactory.getLogger(RodaCoreFactory.class);
private static boolean instantiated = false;
private static boolean instantiatedWithoutErrors = true;
private static NodeType nodeType;
private static boolean migrationMode = false;
private static List<Path> toDeleteDuringShutdown = new ArrayList<>();
// Core related objects
private static Path rodaHomePath;
private static Path storagePath;
private static Path indexDataPath;
private static Optional<Path> tempIndexConfigsPath;
private static Path dataPath;
private static Path logPath;
private static Path configPath;
private static Path workingDirectoryPath;
private static Path reportDirectoryPath;
private static Path exampleConfigPath;
private static Path defaultPath;
private static StorageService storage;
private static ModelService model;
private static IndexService index;
private static SolrClient solr;
private static boolean FEATURE_OVERRIDE_INDEX_CONFIGS = true;
private static boolean TEST_DEPLOY_SOLR = true;
private static boolean TEST_DEPLOY_LDAP = true;
private static boolean TEST_DEPLOY_SCANNER = true;
private static boolean TEST_DEPLOY_ORCHESTRATOR = true;
private static boolean TEST_DEPLOY_PLUGIN_MANAGER = true;
private static boolean TEST_DEPLOY_DEFAULT_RESOURCES = true;
private static SolrType TEST_SOLR_TYPE = null;
// Metrics related objects
private static MetricRegistry metricsRegistry;
private static JmxReporter jmxMetricsReporter;
// Orchestrator related objects
private static PluginManager pluginManager;
// FIXME we should have only "one" orchestrator
private static PluginOrchestrator pluginOrchestrator;
private static AkkaDistributedPluginOrchestrator akkaDistributedPluginOrchestrator;
private static AkkaDistributedPluginWorker akkaDistributedPluginWorker;
private static boolean FEATURE_DISTRIBUTED_AKKA = false;
private static LdapUtility ldapUtility;
private static Path rodaApacheDSDataDirectory = null;
// TransferredResources related objects
private static TransferredResourcesScanner transferredResourcesScanner;
// Configuration related objects
private static CompositeConfiguration rodaConfiguration = null;
private static List<String> configurationFiles = null;
// Caches
private static CacheLoader<Pair<String, String>, Optional<Schema>> RODA_SCHEMAS_LOADER = new SchemasCacheLoader();
private static LoadingCache<Pair<String, String>, Optional<Schema>> RODA_SCHEMAS_CACHE = CacheBuilder.newBuilder()
.build(RODA_SCHEMAS_LOADER);
private static LoadingCache<Locale, Messages> I18N_CACHE = CacheBuilder.newBuilder()
.build(new CacheLoader<Locale, Messages>() {
@Override
public Messages load(Locale locale) throws Exception {
return new Messages(locale, getConfigPath().resolve(RodaConstants.CORE_I18N_FOLDER));
}
});
private static Map<String, Map<String, String>> rodaPropertiesCache = null;
/** Private empty constructor */
private RodaCoreFactory() {
}
public static boolean instantiatedWithoutErrors() {
return instantiatedWithoutErrors;
}
public static void instantiate() {
NodeType nodeType = NodeType
.valueOf(getSystemProperty(RodaConstants.CORE_NODE_TYPE, RodaConstants.DEFAULT_NODE_TYPE.name()));
if (nodeType == RodaConstants.NodeType.MASTER) {
instantiateMaster();
} else if (nodeType == RodaConstants.NodeType.TEST) {
instantiateTest();
} else if (nodeType == RodaConstants.NodeType.WORKER) {
instantiateWorker();
} else {
LOGGER.error("Unknown node type '{}'", nodeType);
}
}
public static void instantiateMaster() {
instantiate(NodeType.MASTER);
}
public static void instantiateWorker() {
instantiate(NodeType.WORKER);
}
public static void instantiateTest(boolean deploySolr, boolean deployLdap, boolean deployTransferredResourcesScanner,
boolean deployOrchestrator, boolean deployPluginManager, boolean deployDefaultResources) {
TEST_DEPLOY_SOLR = deploySolr;
TEST_DEPLOY_LDAP = deployLdap;
TEST_DEPLOY_SCANNER = deployTransferredResourcesScanner;
TEST_DEPLOY_ORCHESTRATOR = deployOrchestrator;
TEST_DEPLOY_PLUGIN_MANAGER = deployPluginManager;
TEST_DEPLOY_DEFAULT_RESOURCES = deployDefaultResources;
instantiated = false;
instantiate(NodeType.TEST);
}
public static void instantiateTest(boolean deploySolr, boolean deployLdap, boolean deployTransferredResourcesScanner,
boolean deployOrchestrator, boolean deployPluginManager, boolean deployDefaultResources, SolrType solrType) {
TEST_SOLR_TYPE = solrType;
instantiateTest(deploySolr, deployLdap, deployTransferredResourcesScanner, deployOrchestrator, deployPluginManager,
deployDefaultResources);
}
public static void instantiateTest() {
instantiated = false;
instantiate(NodeType.TEST);
}
private static void instantiate(NodeType nodeType) {
RodaCoreFactory.nodeType = nodeType;
if (!instantiated) {
try {
// determine RODA HOME
rodaHomePath = determineRodaHomePath();
LOGGER.debug("RODA HOME is {}", rodaHomePath);
// instantiate essential directories
instantiateEssentialDirectories(nodeType);
LOGGER.debug("Finished instantiating essential directories");
// load core configurations
rodaConfiguration = new CompositeConfiguration();
configurationFiles = new ArrayList<>();
rodaPropertiesCache = new HashMap<>();
addConfiguration("roda-core.properties");
addConfiguration("roda-core-formats.properties");
LOGGER.debug("Finished loading roda-core.properties & roda-core-formats.properties");
addConfiguration("roda-roles.properties");
LOGGER.debug("Finished loading roda-roles.properties");
// initialize working directory
initializeWorkingDirectory();
// initialize reports directory
initializeReportsDirectory();
// initialize metrics stuff
initializeMetrics();
// instantiate storage and model service
instantiateStorageAndModel();
LOGGER.debug("Finished instantiating storage & model");
// instantiate solr and index service
instantiateSolrAndIndexService();
LOGGER.debug("Finished instantiating solr & index");
instantiateNodeSpecificObjects(nodeType);
LOGGER.debug("Finished instantiating node specific objects");
// verify if is necessary to perform a model/index migration
MigrationManager migrationManager = new MigrationManager(dataPath);
if (NodeType.MASTER == nodeType
&& migrationManager.isNecessaryToPerformMigration(getSolr(), tempIndexConfigsPath)) {
// migrationManager.setupModelMigrations();
// migrationManager.performModelMigrations();
throw new GenericException("It's necessary to do a model/index migration");
}
instantiateDefaultObjects();
LOGGER.debug("Finished instantiating default objects");
// instantiate plugin manager
// 20160920 hsilva: this must be the last thing to be instantiated as
// problems may araise when instantiating objects at the same time the
// plugin manager is loading both internal & external plugins (it looks
// like Reflections is the blame)
instantiatePluginManager();
LOGGER.debug("Finished instantiating plugin manager");
instantiated = true;
} catch (ConfigurationException e) {
LOGGER.error("Error loading roda properties", e);
instantiatedWithoutErrors = false;
} catch (URISyntaxException e) {
LOGGER.error("Error instantiating solr/index model", e);
instantiatedWithoutErrors = false;
} catch (GenericException e) {
if (!migrationMode) {
LOGGER.error("Error instantiating storage model", e);
}
instantiatedWithoutErrors = false;
} catch (Exception e) {
LOGGER.error("Error instantiating " + RodaCoreFactory.class.getSimpleName(), e);
instantiatedWithoutErrors = false;
}
// last log message that state if system was loaded without errors or not
LOGGER.info("RODA Core loading completed {}",
migrationMode ? "(migration mode)"
: (instantiatedWithoutErrors ? "with success!"
: "with some errors!!! See logs because these errors might cause instability in the system."));
}
}
private static void initializeWorkingDirectory() {
try {
workingDirectoryPath = Paths
.get(getRodaConfiguration().getString("core.workingdirectory", getSystemProperty("java.io.tmpdir", "tmp")));
Files.createDirectories(workingDirectoryPath);
} catch (IOException e) {
throw new RuntimeException("Unable to create RODA WORKING DIRECTORY " + workingDirectoryPath + ". Aborting...",
e);
}
}
private static void initializeReportsDirectory() {
try {
reportDirectoryPath = getRodaHomePath().resolve(RodaConstants.CORE_REPORT_FOLDER);
Files.createDirectories(reportDirectoryPath);
} catch (IOException e) {
throw new RuntimeException("Unable to create RODA Reports DIRECTORY " + reportDirectoryPath + ". Aborting...", e);
}
}
private static void initializeMetrics() {
metricsRegistry = new MetricRegistry();
if (getSystemProperty("com.sun.management.jmxremote", null) != null) {
jmxMetricsReporter = JmxReporter.forRegistry(metricsRegistry).inDomain("RODA").build();
jmxMetricsReporter.start();
}
}
private static Path determineRodaHomePath() {
Path rodaHomePath;
if (System.getProperty(RodaConstants.INSTALL_FOLDER_SYSTEM_PROPERTY) != null) {
rodaHomePath = Paths.get(System.getProperty(RodaConstants.INSTALL_FOLDER_SYSTEM_PROPERTY));
} else if (System.getenv(RodaConstants.INSTALL_FOLDER_ENVIRONEMNT_VARIABLE) != null) {
rodaHomePath = Paths.get(System.getenv(RodaConstants.INSTALL_FOLDER_ENVIRONEMNT_VARIABLE));
} else {
// last attempt (using user home and hidden directory called .roda)
String userHome = System.getProperty("user.home");
rodaHomePath = Paths.get(userHome, ".roda");
if (!FSUtils.exists(rodaHomePath)) {
try {
Files.createDirectories(rodaHomePath);
} catch (IOException e) {
throw new RuntimeException("Unable to create RODA HOME " + rodaHomePath + ". Aborting...", e);
}
}
// set roda.home in order to correctly configure logging even if no
// property has been defined
System.setProperty(RodaConstants.INSTALL_FOLDER_SYSTEM_PROPERTY, rodaHomePath.toString());
}
// instantiate essential directories
configPath = rodaHomePath.resolve(RodaConstants.CORE_CONFIG_FOLDER);
exampleConfigPath = rodaHomePath.resolve(RodaConstants.CORE_EXAMPLE_CONFIG_FOLDER);
defaultPath = rodaHomePath.resolve(RodaConstants.CORE_DEFAULT_FOLDER);
dataPath = rodaHomePath.resolve(RodaConstants.CORE_DATA_FOLDER);
logPath = dataPath.resolve(RodaConstants.CORE_LOG_FOLDER);
storagePath = dataPath.resolve(RodaConstants.CORE_STORAGE_FOLDER);
indexDataPath = dataPath.resolve(RodaConstants.CORE_INDEX_FOLDER);
// configure logback
if (nodeType != NodeType.TEST) {
configureLogback();
}
return rodaHomePath;
}
private static void configureLogback() {
try {
LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();
JoranConfigurator configurator = new JoranConfigurator();
configurator.setContext(context);
context.reset();
configurator.doConfigure(getConfigurationFile("logback.xml"));
} catch (JoranException e) {
LOGGER.error("Error configuring logback", e);
}
}
private static void instantiateEssentialDirectories(NodeType nodeType) {
List<Path> essentialDirectories = new ArrayList<>();
essentialDirectories.add(configPath);
essentialDirectories.add(rodaHomePath.resolve(RodaConstants.CORE_LOG_FOLDER));
essentialDirectories.add(dataPath);
essentialDirectories.add(logPath);
essentialDirectories.add(storagePath);
essentialDirectories.add(indexDataPath);
essentialDirectories.add(exampleConfigPath);
for (Path path : essentialDirectories) {
try {
if (!FSUtils.exists(path)) {
Files.createDirectories(path);
}
} catch (IOException e) {
LOGGER.error("Unable to create " + path, e);
instantiatedWithoutErrors = false;
}
}
if (!nodeType.equals(NodeType.TEST)) {
// copy configs folder from classpath to example folder
try {
FSUtils.deletePathQuietly(exampleConfigPath);
Files.createDirectories(exampleConfigPath);
copyFilesFromClasspath(RodaConstants.CORE_CONFIG_FOLDER + "/", exampleConfigPath, true,
Arrays.asList(RodaConstants.CORE_CONFIG_FOLDER + "/" + RodaConstants.CORE_LDAP_FOLDER,
RodaConstants.CORE_CONFIG_FOLDER + "/" + RodaConstants.CORE_INDEX_FOLDER,
RodaConstants.CORE_CONFIG_FOLDER + "/" + RodaConstants.CORE_I18N_FOLDER + "/"
+ RodaConstants.CORE_I18N_CLIENT_FOLDER,
RodaConstants.CORE_CONFIG_FOLDER + "/" + RodaConstants.CORE_I18N_FOLDER + "/"
+ RodaConstants.CORE_I18_GWT_XML_FILE));
} catch (IOException e) {
LOGGER.error("Unable to create " + exampleConfigPath, e);
instantiatedWithoutErrors = false;
}
}
}
private static void instantiateDefaultObjects() {
if (TEST_DEPLOY_DEFAULT_RESOURCES) {
try {
CloseableIterable<Resource> resources = storage.listResourcesUnderContainer(DefaultStoragePath.parse(""), true);
Iterator<Resource> resourceIterator = resources.iterator();
boolean hasFileResources = false;
while (resourceIterator.hasNext() && !hasFileResources) {
Resource resource = resourceIterator.next();
if (!resource.isDirectory()) {
hasFileResources = true;
}
}
IOUtils.closeQuietly(resources);
if (!hasFileResources) {
copyFilesFromClasspath(RodaConstants.CORE_DEFAULT_FOLDER + "/", rodaHomePath, true);
// 20160712 hsilva: it needs to be this way as the resources are
// copied to the file system and storage can be of a different type
// (e.g. fedora)
FileStorageService fileStorageService = new FileStorageService(storagePath);
index.reindexRisks(fileStorageService);
index.reindexFormats(fileStorageService);
index.reindexAIPs();
// reindex other default objects HERE
}
} catch (AuthorizationDeniedException | RequestNotValidException | NotFoundException | GenericException e) {
LOGGER.error("Cannot load default objects", e);
}
}
}
private static void copyFilesFromClasspath(String classpathPrefix, Path destinationDirectory,
boolean removeClasspathPrefixFromFinalPath) {
copyFilesFromClasspath(classpathPrefix, destinationDirectory, removeClasspathPrefixFromFinalPath,
Collections.emptyList());
}
private static void copyFilesFromClasspath(String classpathPrefix, Path destinationDirectory,
boolean removeClasspathPrefixFromFinalPath, List<String> excludePaths) {
List<ClassLoader> classLoadersList = new LinkedList<>();
classLoadersList.add(ClasspathHelper.contextClassLoader());
Reflections reflections = new Reflections(
new ConfigurationBuilder().filterInputsBy(new FilterBuilder().include(FilterBuilder.prefix(classpathPrefix)))
.setScanners(new ResourcesScanner())
.setUrls(ClasspathHelper.forClassLoader(classLoadersList.toArray(new ClassLoader[] {}))));
Set<String> resources = reflections.getResources(Pattern.compile(".*"));
LOGGER.info("Copy files from classpath prefix={}, destination={}, removePrefix={}, excludePaths={}, #resources={}",
classpathPrefix, destinationDirectory, removeClasspathPrefixFromFinalPath, excludePaths, resources.size());
for (String resource : resources) {
boolean exclude = false;
for (String excludePath : excludePaths) {
if (resource.startsWith(excludePath)) {
exclude = true;
break;
}
}
if (exclude) {
continue;
}
InputStream originStream = RodaCoreFactory.class.getClassLoader().getResourceAsStream(resource);
Path destinyPath;
String resourceFileName = resource;
// Removing ":" escape
resourceFileName = resourceFileName.replace("::", ":");
if (removeClasspathPrefixFromFinalPath) {
destinyPath = destinationDirectory.resolve(resourceFileName.replaceFirst(classpathPrefix, ""));
} else {
destinyPath = destinationDirectory.resolve(resourceFileName);
}
try {
// create all parent directories
Files.createDirectories(destinyPath.getParent());
// copy file
Files.copy(originStream, destinyPath, StandardCopyOption.REPLACE_EXISTING);
} catch (IOException e) {
LOGGER.error("Error copying file from classpath: {} to {} (reason: {})", originStream, destinyPath,
e.getMessage());
instantiatedWithoutErrors = false;
} finally {
RodaUtils.closeQuietly(originStream);
}
}
}
private static void copyFilesFromClasspath(String classpathPrefix, Path destinationDirectory) {
copyFilesFromClasspath(classpathPrefix, destinationDirectory, false);
}
private static void instantiatePluginManager() {
if (nodeType == NodeType.MASTER || nodeType == NodeType.WORKER || TEST_DEPLOY_PLUGIN_MANAGER) {
try {
pluginManager = PluginManager.instantiatePluginManager(getConfigPath(), getPluginsPath());
} catch (PluginManagerException e) {
LOGGER.error("Error instantiating PluginManager", e);
instantiatedWithoutErrors = false;
}
}
}
private static void instantiateStorageAndModel() throws GenericException {
storage = instantiateStorage();
LOGGER.debug("Finished instantiating storage...");
model = new ModelService(storage);
LOGGER.debug("Finished instantiating model...");
}
private static StorageService instantiateStorage() throws GenericException {
StorageType storageType = StorageType.valueOf(
getRodaConfiguration().getString(RodaConstants.CORE_STORAGE_TYPE, RodaConstants.DEFAULT_STORAGE_TYPE.toString()));
if (storageType == RodaConstants.StorageType.FEDORA4) {
String url = getRodaConfiguration().getString(RodaConstants.CORE_STORAGE_FEDORA4_URL,
"http://localhost:8983/solr/");
String username = getRodaConfiguration().getString(RodaConstants.CORE_STORAGE_FEDORA4_USERNAME, "");
String password = getRodaConfiguration().getString(RodaConstants.CORE_STORAGE_FEDORA4_PASSWORD, "");
if (StringUtils.isNotBlank(username) && StringUtils.isNotBlank(password)) {
LOGGER.debug("Going to instantiate Fedora with url '{}' username '{}' & password '{}'", url, username,
password);
return new FedoraStorageService(url, username, password);
} else {
LOGGER.debug("Going to instantiate Fedora with url '{}'", url);
return new FedoraStorageService(url);
}
} else if (storageType == RodaConstants.StorageType.FILESYSTEM) {
LOGGER.debug("Going to instantiate Filesystem on '{}'", storagePath);
String trashDirName = getRodaConfiguration().getString("core.storage.filesystem.trash", "trash");
return new FileStorageService(storagePath, trashDirName);
} else {
LOGGER.error("Unknown storage service '{}'", storageType.name());
throw new GenericException();
}
}
/**
* <p>
* Warnings like
* </p>
* <code>2016-03-21 11:21:34,319 WARN org.apache.solr.core.Config - Beginning with Solr 5.5, <maxMergeDocs> is deprecated, configure it on the relevant <mergePolicyFactory> instead.</code>
* <br/>
* <code>2016-03-21 11:21:34,327 WARN org.apache.solr.core.Config - Beginning with Solr 5.5, <mergeFactor> is deprecated, configure it on the relevant <mergePolicyFactory> instead.</code>
* <p>
* are due to a bug, explained in
* https://issues.apache.org/jira/browse/SOLR-8734, as we don't declare those
* parameters in RODA solr configurations. The warning will be removed and as
* soon as that happens, this messages should be deleted as well.
* </p>
*
*/
private static void instantiateSolrAndIndexService() throws URISyntaxException {
if (nodeType == NodeType.MASTER) {
tempIndexConfigsPath = Optional.empty();
Path solrHome = configPath.resolve(RodaConstants.CORE_INDEX_FOLDER);
if (!FSUtils.exists(solrHome) || FEATURE_OVERRIDE_INDEX_CONFIGS) {
try {
Path tempConfig = Files.createTempDirectory(getWorkingDirectory(), RodaConstants.CORE_INDEX_FOLDER);
toDeleteDuringShutdown.add(tempConfig);
tempIndexConfigsPath = Optional.of(tempConfig);
copyFilesFromClasspath(RodaConstants.CORE_CONFIG_FOLDER + "/" + RodaConstants.CORE_INDEX_FOLDER + "/",
tempConfig);
solrHome = tempConfig.resolve(RodaConstants.CORE_CONFIG_FOLDER).resolve(RodaConstants.CORE_INDEX_FOLDER);
LOGGER.info("Using SOLR home: {}", solrHome);
} catch (IOException e) {
LOGGER.error("Error creating temporary SOLR home", e);
instantiatedWithoutErrors = false;
}
}
// instantiate solr
solr = instantiateSolr(solrHome);
// instantiate index related object
index = new IndexService(solr, model);
} else if (nodeType == NodeType.TEST && TEST_DEPLOY_SOLR) {
try {
Path tempConfig = Files.createTempDirectory(getWorkingDirectory(), RodaConstants.CORE_INDEX_FOLDER);
copyFilesFromClasspath(RodaConstants.CORE_CONFIG_FOLDER + "/" + RodaConstants.CORE_INDEX_FOLDER + "/",
tempConfig, true);
// instantiate solr
solr = instantiateSolr(tempConfig);
// instantiate index related object
index = new IndexService(solr, model);
} catch (IOException e) {
LOGGER.error("Unable to instantiate Solr in TEST mode", e);
instantiatedWithoutErrors = false;
}
}
}
private static SolrClient instantiateSolr(Path solrHome) {
SolrType solrType = SolrType.valueOf(
getRodaConfiguration().getString(RodaConstants.CORE_SOLR_TYPE, RodaConstants.DEFAULT_SOLR_TYPE.toString()));
if (TEST_SOLR_TYPE != null) {
solrType = TEST_SOLR_TYPE;
}
if (solrType == RodaConstants.SolrType.HTTP) {
String solrBaseUrl = getRodaConfiguration().getString(RodaConstants.CORE_SOLR_HTTP_URL,
"http://localhost:8983/solr/");
return new HttpSolrClient(solrBaseUrl);
} else if (solrType == RodaConstants.SolrType.HTTP_CLOUD) {
String solrCloudZooKeeperUrls = getRodaConfiguration().getString(RodaConstants.CORE_SOLR_HTTP_CLOUD_URLS,
"localhost:2181,localhost:2182,localhost:2183");
return new CloudSolrClient(solrCloudZooKeeperUrls);
} else {
// default to Embedded
setSolrSystemProperties();
return new EmbeddedSolrServer(solrHome, "test");
}
}
private static void setSolrSystemProperties() {
System.setProperty("solr.data.dir", indexDataPath.toString());
System.setProperty("solr.data.dir.aip", indexDataPath.resolve(RodaConstants.CORE_AIP_FOLDER).toString());
System.setProperty("solr.data.dir.representations",
indexDataPath.resolve(RodaConstants.CORE_REPRESENTATION_FOLDER).toString());
System.setProperty("solr.data.dir.file", indexDataPath.resolve(RodaConstants.CORE_FILE_FOLDER).toString());
System.setProperty("solr.data.dir.preservationevent",
indexDataPath.resolve(RodaConstants.CORE_PRESERVATIONEVENT_FOLDER).toString());
System.setProperty("solr.data.dir.preservationagent",
indexDataPath.resolve(RodaConstants.CORE_PRESERVATIONAGENT_FOLDER).toString());
System.setProperty("solr.data.dir.actionlog",
indexDataPath.resolve(RodaConstants.CORE_ACTIONLOG_FOLDER).toString());
System.setProperty("solr.data.dir.members", indexDataPath.resolve(RodaConstants.CORE_MEMBERS_FOLDER).toString());
System.setProperty("solr.data.dir.transferredresource",
indexDataPath.resolve(RodaConstants.CORE_TRANSFERREDRESOURCE_FOLDER).toString());
System.setProperty("solr.data.dir.job", indexDataPath.resolve(RodaConstants.CORE_JOB_FOLDER).toString());
System.setProperty("solr.data.dir.jobreport",
indexDataPath.resolve(RodaConstants.CORE_JOBREPORT_FOLDER).toString());
System.setProperty("solr.data.dir.risk", indexDataPath.resolve(RodaConstants.CORE_RISK_FOLDER).toString());
System.setProperty("solr.data.dir.agent", indexDataPath.resolve(RodaConstants.CORE_AGENT_FOLDER).toString());
System.setProperty("solr.data.dir.format", indexDataPath.resolve(RodaConstants.CORE_FORMAT_FOLDER).toString());
System.setProperty("solr.data.dir.notification",
indexDataPath.resolve(RodaConstants.CORE_NOTIFICATION_FOLDER).toString());
System.setProperty("solr.data.dir.riskincidence",
indexDataPath.resolve(RodaConstants.CORE_RISKINCIDENCE_FOLDER).toString());
System.setProperty("solr.data.dir.dip", indexDataPath.resolve(RodaConstants.CORE_DIP_FOLDER).toString());
System.setProperty("solr.data.dir.dipfile", indexDataPath.resolve(RodaConstants.CORE_DIP_FILE_FOLDER).toString());
}
private static void instantiateNodeSpecificObjects(NodeType nodeType) {
if (nodeType == NodeType.MASTER) {
instantiateMasterNodeSpecificObjects();
} else if (nodeType == NodeType.WORKER) {
instantiateWorkerNodeSpecificObjects();
} else if (nodeType == NodeType.TEST) {
instantiateTestNodeSpecificObjects();
} else {
LOGGER.error("Unknown node type '{}'", nodeType);
instantiatedWithoutErrors = false;
}
}
private static void instantiateMasterNodeSpecificObjects() {
if (FEATURE_DISTRIBUTED_AKKA) {
akkaDistributedPluginOrchestrator = new AkkaDistributedPluginOrchestrator(
getSystemProperty(RodaConstants.CORE_NODE_HOSTNAME, RodaConstants.DEFAULT_NODE_HOSTNAME),
getSystemProperty(RodaConstants.CORE_NODE_PORT, RodaConstants.DEFAULT_NODE_PORT));
akkaDistributedPluginOrchestrator.cleanUnfinishedJobs();
} else {
// pluginOrchestrator = new EmbeddedActionOrchestrator();
pluginOrchestrator = new AkkaEmbeddedPluginOrchestrator();
pluginOrchestrator.cleanUnfinishedJobs();
}
startApacheDS();
instantiateTransferredResourcesScanner();
processPreservationEventTypeProperties();
}
private static void instantiateWorkerNodeSpecificObjects() {
akkaDistributedPluginWorker = new AkkaDistributedPluginWorker(
getSystemProperty(RodaConstants.CORE_CLUSTER_HOSTNAME, RodaConstants.DEFAULT_NODE_HOSTNAME),
getSystemProperty(RodaConstants.CORE_CLUSTER_PORT, RodaConstants.DEFAULT_NODE_PORT),
getSystemProperty(RodaConstants.CORE_NODE_HOSTNAME, RodaConstants.DEFAULT_NODE_HOSTNAME),
getSystemProperty(RodaConstants.CORE_NODE_PORT, "0"));
}
private static void instantiateTestNodeSpecificObjects() {
if (TEST_DEPLOY_LDAP) {
startApacheDS();
}
if (TEST_DEPLOY_SCANNER) {
instantiateTransferredResourcesScanner();
}
if (TEST_DEPLOY_ORCHESTRATOR) {
pluginOrchestrator = new AkkaEmbeddedPluginOrchestrator();
}
}
public static void addLogger(String loggerConfigurationFile) {
URL loggerConfigurationFileUrl = getConfigurationFile(loggerConfigurationFile);
if (loggerConfigurationFileUrl != null) {
System.setProperty("roda.logback.include", loggerConfigurationFileUrl.toString());
configureLogback();
}
}
private static String getSystemProperty(String property, String defaultValue) {
String ret = defaultValue;
if (System.getProperty(property) != null) {
ret = System.getProperty(property);
}
return ret;
}
public static void shutdown() throws IOException {
if (instantiated) {
if (nodeType == NodeType.MASTER) {
solr.close();
stopApacheDS();
pluginManager.shutdown();
pluginOrchestrator.shutdown();
} else if (nodeType == NodeType.WORKER) {
pluginManager.shutdown();
} else if (nodeType == NodeType.TEST) {
if (TEST_DEPLOY_SOLR) {
solr.close();
}
if (TEST_DEPLOY_LDAP) {
stopApacheDS();
}
if (TEST_DEPLOY_ORCHESTRATOR) {
pluginOrchestrator.shutdown();
}
// final cleanup
FSUtils.deletePathQuietly(workingDirectoryPath);
}
// stop jmx metrics reporter
if (getSystemProperty("com.sun.management.jmxremote", null) != null) {
jmxMetricsReporter.stop();
}
// delete resources that are no longer needed
toDeleteDuringShutdown.forEach(e -> FSUtils.deletePathQuietly(e));
}
}
public static MetricRegistry getMetrics() {
return metricsRegistry;
}
/**
* Start ApacheDS.
*/
private static void startApacheDS() {
rodaApacheDSDataDirectory = RodaCoreFactory.getDataPath().resolve(RodaConstants.CORE_LDAP_FOLDER);
try {
final Configuration rodaConfig = RodaCoreFactory.getRodaConfiguration();
final boolean ldapStartServer = rodaConfig.getBoolean("ldap.startServer", false);
final int ldapPort = rodaConfig.getInt("ldap.port", RodaConstants.CORE_LDAP_DEFAULT_PORT);
final String ldapBaseDN = rodaConfig.getString("ldap.baseDN", "dc=roda,dc=org");
final String ldapPeopleDN = rodaConfig.getString("ldap.peopleDN", "ou=users,dc=roda,dc=org");
final String ldapGroupsDN = rodaConfig.getString("ldap.groupsDN", "ou=groups,dc=roda,dc=org");
final String ldapRolesDN = rodaConfig.getString("ldap.rolesDN", "ou=groups,dc=roda,dc=org");
final String ldapAdminDN = rodaConfig.getString("ldap.adminDN", "ou=groups,dc=roda,dc=org");
final String ldapAdminPassword = rodaConfig.getString("ldap.adminPassword", "roda");
final String ldapPasswordDigestAlgorithm = rodaConfig.getString("ldap.passwordDigestAlgorithm", "MD5");
final List<String> ldapProtectedUsers = RodaUtils.copyList(rodaConfig.getList("ldap.protectedUsers"));
final List<String> ldapProtectedGroups = RodaUtils.copyList(rodaConfig.getList("ldap.protectedGroups"));
final String rodaGuestDN = rodaConfig.getString("ldap.rodaGuestDN", "uid=guest,ou=users,dc=roda,dc=org");
final String rodaAdminDN = rodaConfig.getString("ldap.rodaAdminDN", "uid=admin,ou=users,dc=roda,dc=org");
final String rodaAdministratorsDN = rodaConfig.getString("ldap.rodaAdministratorsDN",
"cn=administrators,ou=groups,dc=roda,dc=org");
RodaCoreFactory.ldapUtility = new LdapUtility(ldapStartServer, ldapPort, ldapBaseDN, ldapPeopleDN, ldapGroupsDN,
ldapRolesDN, ldapAdminDN, ldapAdminPassword, ldapPasswordDigestAlgorithm, ldapProtectedUsers,
ldapProtectedGroups, rodaGuestDN, rodaAdminDN, rodaApacheDSDataDirectory);
ldapUtility.setRODAAdministratorsDN(rodaAdministratorsDN);
UserUtility.setLdapUtility(ldapUtility);
if (!FSUtils.exists(rodaApacheDSDataDirectory)) {
Files.createDirectories(rodaApacheDSDataDirectory);
final List<String> ldifFileNames = Arrays.asList("users.ldif", "groups.ldif", "roles.ldif");
final List<String> ldifs = new ArrayList<>();
for (String ldifFileName : ldifFileNames) {
final InputStream ldifInputStream = RodaCoreFactory
.getConfigurationFileAsStream(RodaConstants.CORE_LDAP_FOLDER + "/" + ldifFileName);
ldifs.add(IOUtils.toString(ldifInputStream, RodaConstants.DEFAULT_ENCODING));
RodaUtils.closeQuietly(ldifInputStream);
}
RodaCoreFactory.ldapUtility.initDirectoryService(ldifs);
indexUsersAndGroupsFromLDAP();
} else {
RodaCoreFactory.ldapUtility.initDirectoryService();
}
createRoles(rodaConfig);
indexUsersAndGroupsFromLDAP();
} catch (final Exception e) {
LOGGER.error("Error starting up embedded ApacheDS", e);
instantiatedWithoutErrors = false;
}
}
private static void stopApacheDS() {
try {
RodaCoreFactory.ldapUtility.stopService();
} catch (final Exception e) {
LOGGER.error("Error while shutting down ApacheDS embedded server", e);
}
}
/**
* For each role in roda-roles.properties create the role in LDAP if it don't
* exist already.
*
* @param rodaConfig
* roda configuration
* @throws GenericException
* if something unexpected happens creating roles.
*/
private static void createRoles(final Configuration rodaConfig) throws GenericException {
final Iterator<String> keys = rodaConfig.getKeys("core.roles");
final Set<String> roles = new HashSet<>();
while (keys.hasNext()) {
roles.addAll(Arrays.asList(rodaConfig.getStringArray(keys.next())));
}
for (final String role : roles) {
try {
if (StringUtils.isNotBlank(role)) {
RodaCoreFactory.ldapUtility.addRole(role);
LOGGER.debug("Created LDAP role {}", role);
}
} catch (final RoleAlreadyExistsException e) {
LOGGER.debug("Role {} already exists.", role);
LOGGER.trace(e.getMessage(), e);
}
}
}
private static void indexUsersAndGroupsFromLDAP()
throws GenericException, IllegalOperationException, NotFoundException, AlreadyExistsException {
for (User user : model.listUsers()) {
LOGGER.debug("User to be indexed: {}", user);
model.notifyUserUpdated(user);
}
for (Group group : model.listGroups()) {
LOGGER.debug("Group to be indexed: {}", group);
model.notifyGroupUpdated(group);
}
}
public static void instantiateTransferredResourcesScanner() {
try {
String transferredResourcesFolder = getRodaConfiguration().getString("transferredResources.folder",
RodaConstants.CORE_TRANSFERREDRESOURCE_FOLDER);
Path transferredResourcesFolderPath = dataPath.resolve(transferredResourcesFolder);
if (!FSUtils.exists(transferredResourcesFolderPath)) {
Files.createDirectories(transferredResourcesFolderPath);
}
transferredResourcesScanner = new TransferredResourcesScanner(transferredResourcesFolderPath, getIndexService());
} catch (final Exception e) {
LOGGER.error("Error starting Transferred Resources Scanner: " + e.getMessage(), e);
instantiatedWithoutErrors = false;
}
}
public static boolean getTransferredResourcesScannerUpdateStatus(Optional<String> folderRelativePath) {
return TransferUpdateStatus.getInstance().isUpdatingStatus(folderRelativePath);
}
public static void setTransferredResourcesScannerUpdateStatus(Optional<String> folderRelativePath,
boolean isUpdating) {
TransferUpdateStatus.getInstance().setUpdatingStatus(folderRelativePath, isUpdating);
}
public static StorageService getStorageService() {
return storage;
}
public static ModelService getModelService() {
return model;
}
public static IndexService getIndexService() {
return index;
}
public static SolrClient getSolr() {
return solr;
}
public static void setSolr(SolrClient solr) {
RodaCoreFactory.solr = solr;
}
public static PluginManager getPluginManager() {
return PluginManager.getInstance();
}
public static PluginOrchestrator getPluginOrchestrator() {
return pluginOrchestrator;
}
public static AkkaDistributedPluginOrchestrator getAkkaDistributedPluginOrchestrator() {
return akkaDistributedPluginOrchestrator;
}
public static TransferredResourcesScanner getTransferredResourcesScanner() {
return transferredResourcesScanner;
}
public static NodeType getNodeType() {
return nodeType;
}
public static Path getRodaHomePath() {
return rodaHomePath;
}
public static Path getConfigPath() {
return configPath;
}
public static Path getDefaultPath() {
return defaultPath;
}
public static Path getWorkingDirectory() {
return workingDirectoryPath;
}
public static Path getReportsDirectory() {
return reportDirectoryPath;
}
public static Path getDataPath() {
return dataPath;
}
public static Path getStoragePath() {
return storagePath;
}
public static Path getLogPath() {
return logPath;
}
public static Path getPluginsPath() {
return configPath.resolve(RodaConstants.CORE_PLUGINS_FOLDER);
}
public static void closeSolrServer() {
try {
solr.close();
} catch (IOException e) {
LOGGER.error("Error while shutting down solr", e);
}
}
/*
* Configuration related functionalities
*/
public static void addConfiguration(String configurationFile) throws ConfigurationException {
Configuration configuration = getConfiguration(configurationFile);
rodaConfiguration.addConfiguration(configuration);
configurationFiles.add(configurationFile);
}
public static Configuration getConfiguration(String configurationFile) throws ConfigurationException {
Path config = RodaCoreFactory.getConfigPath().resolve(configurationFile);
PropertiesConfiguration propertiesConfiguration = new PropertiesConfiguration();
propertiesConfiguration.setDelimiterParsingDisabled(true);
propertiesConfiguration.setEncoding(RodaConstants.DEFAULT_ENCODING);
if (FSUtils.exists(config)) {
LOGGER.trace("Loading configuration from file {}", config);
propertiesConfiguration.load(config.toFile());
RodaPropertiesReloadStrategy rodaPropertiesReloadStrategy = new RodaPropertiesReloadStrategy();
rodaPropertiesReloadStrategy.setRefreshDelay(5000);
propertiesConfiguration.setReloadingStrategy(rodaPropertiesReloadStrategy);
} else {
InputStream inputStream = RodaCoreFactory.class
.getResourceAsStream("/" + RodaConstants.CORE_CONFIG_FOLDER + "/" + configurationFile);
if (inputStream != null) {
LOGGER.trace("Loading configuration from classpath {}", configurationFile);
propertiesConfiguration.load(inputStream);
} else {
LOGGER.trace("Configuration {} doesn't exist", configurationFile);
}
}
return propertiesConfiguration;
}
public static URL getConfigurationFile(String configurationFile) {
Path config = RodaCoreFactory.getConfigPath().resolve(configurationFile);
URL configUri;
if (FSUtils.exists(config) && !FSUtils.isDirectory(config)
&& config.toAbsolutePath().startsWith(getConfigPath().toAbsolutePath().toString())) {
try {
configUri = config.toUri().toURL();
} catch (MalformedURLException e) {
LOGGER.error("Configuration {} doesn't exist", configurationFile);
configUri = null;
}
} else {
URL resource = RodaCoreFactory.class
.getResource("/" + RodaConstants.CORE_CONFIG_FOLDER + "/" + configurationFile);
if (resource != null) {
configUri = resource;
} else {
LOGGER.error("Configuration {} doesn't exist", configurationFile);
configUri = null;
}
}
return configUri;
}
public static InputStream getConfigurationFileAsStream(String configurationFile) {
Path config = getConfigPath().resolve(configurationFile);
InputStream inputStream = null;
try {
if (FSUtils.exists(config) && !FSUtils.isDirectory(config)
&& config.toAbsolutePath().startsWith(getConfigPath().toAbsolutePath().toString())) {
inputStream = Files.newInputStream(config);
LOGGER.trace("Loading configuration from file {}", config);
}
} catch (IOException e) {
// do nothing
}
if (inputStream == null) {
inputStream = RodaCoreFactory.class
.getResourceAsStream("/" + RodaConstants.CORE_CONFIG_FOLDER + "/" + configurationFile);
LOGGER.trace("Loading configuration from classpath {}", configurationFile);
}
return inputStream;
}
public static InputStream getConfigurationFileAsStream(String configurationFile, String fallbackConfigurationFile) {
InputStream inputStream = getConfigurationFileAsStream(configurationFile);
if (inputStream == null) {
inputStream = getConfigurationFileAsStream(fallbackConfigurationFile);
}
return inputStream;
}
public static InputStream getDefaultFileAsStream(String defaultFile, ClassLoader... extraClassLoaders) {
Path defaultPath = getDefaultPath().resolve(defaultFile);
InputStream inputStream = null;
try {
if (FSUtils.exists(defaultPath) && !FSUtils.isDirectory(defaultPath)
&& defaultPath.toAbsolutePath().startsWith(getDefaultPath().toAbsolutePath().toString())) {
inputStream = Files.newInputStream(defaultPath);
LOGGER.debug("Trying to load default from file {}", defaultPath);
}
} catch (IOException e) {
// do nothing
}
if (inputStream == null) {
String fileClassPath = "/" + RodaConstants.CORE_DEFAULT_FOLDER + "/" + defaultFile;
inputStream = RodaCoreFactory.class.getResourceAsStream(fileClassPath);
LOGGER.debug("Trying to load default file from classpath {}", fileClassPath);
}
if (inputStream == null) {
String fileClassPath = RodaConstants.CORE_DEFAULT_FOLDER + "/" + defaultFile;
for (ClassLoader classLoader : extraClassLoaders) {
LOGGER.debug("Trying to load default file from extra class loader {}", fileClassPath);
inputStream = classLoader.getResourceAsStream(fileClassPath);
if (inputStream != null) {
break;
}
}
}
return inputStream;
}
public static void clearRodaCachableObjectsAfterConfigurationChange() {
rodaPropertiesCache.clear();
RODA_SCHEMAS_CACHE.invalidateAll();
I18N_CACHE.invalidateAll();
processPreservationEventTypeProperties();
LOGGER.info("Reloaded roda configurations after file change!");
}
private static void processPreservationEventTypeProperties() {
String prefix = "core.preservation_event_type";
for (PreservationEventType preservationEventType : PreservationEventType.values()) {
String value = getRodaConfigurationAsString(prefix, preservationEventType.name());
if (StringUtils.isNotBlank(value)) {
preservationEventType.setText(value);
} else {
preservationEventType.setText(preservationEventType.getOriginalText());
}
}
}
public static Optional<Schema> getRodaSchema(String metadataType, String metadataVersion) {
Optional<Schema> schema = Optional.empty();
try {
schema = RODA_SCHEMAS_CACHE.get(Pair.of(metadataType, metadataVersion));
} catch (ExecutionException e) {
if (StringUtils.isNotBlank(metadataType)) {
try {
schema = RODA_SCHEMAS_CACHE.get(Pair.of(metadataType, null));
} catch (ExecutionException e2) {
// Do nothing
}
}
}
return schema;
}
public static Configuration getRodaConfiguration() {
return rodaConfiguration;
}
public static String getConfigurationKey(String... keyParts) {
StringBuilder sb = new StringBuilder();
for (String part : keyParts) {
if (sb.length() != 0) {
sb.append('.');
}
sb.append(part);
}
return sb.toString();
}
public static String getRodaConfigurationAsString(String... keyParts) {
return rodaConfiguration.getString(getConfigurationKey(keyParts));
}
public static int getRodaConfigurationAsInt(int defaultValue, String... keyParts) {
return rodaConfiguration.getInt(getConfigurationKey(keyParts), defaultValue);
}
public static List<String> getRodaConfigurationAsList(String... keyParts) {
String[] array = rodaConfiguration.getStringArray(getConfigurationKey(keyParts));
return Arrays.asList(array).stream().filter(v -> StringUtils.isNotBlank(v)).collect(Collectors.toList());
}
public static int getRodaConfigurationAsInt(String... keyParts) {
return getRodaConfigurationAsInt(0, keyParts);
}
public static List<String> getFixityAlgorithms() {
List<String> algorithms = RodaCoreFactory.getRodaConfigurationAsList("core", "premis", "fixity", "algorithms");
if (algorithms == null || algorithms.isEmpty()) {
algorithms = RodaConstants.DEFAULT_ALGORITHMS;
}
return algorithms;
}
public static Set<String> getFilenamesInsideConfigFolder(String folder) throws IOException {
Set<String> fileNames = new HashSet<>();
// get from external config
Set<String> externalFileNames = new HashSet<>();
Path configPath = RodaCoreFactory.getConfigPath().resolve(folder);
Files.walkFileTree(configPath, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
externalFileNames.add(file.getFileName().toString());
return FileVisitResult.CONTINUE;
}
});
fileNames.addAll(externalFileNames);
// get from internal config
List<ClassLoader> classLoadersList = new LinkedList<>();
classLoadersList.add(ClasspathHelper.contextClassLoader());
Set<String> internalFilesPath = new Reflections(new ConfigurationBuilder()
.filterInputsBy(
new FilterBuilder().include(FilterBuilder.prefix("" + RodaConstants.CORE_CONFIG_FOLDER + "/" + folder)))
.setScanners(new ResourcesScanner())
.setUrls(ClasspathHelper.forClassLoader(classLoadersList.toArray(new ClassLoader[0]))))
.getResources(Pattern.compile(".*"));
for (String internalFilePath : internalFilesPath) {
fileNames.add(Paths.get(internalFilePath).getFileName().toString());
}
return fileNames;
}
public static Map<String, String> getPropertiesFromCache(String cacheName, List<String> prefixesToCache) {
if (rodaPropertiesCache.get(cacheName) == null) {
fillInPropertiesToCache(cacheName, prefixesToCache);
}
return rodaPropertiesCache.get(cacheName);
}
private static void fillInPropertiesToCache(String cacheName, List<String> prefixesToCache) {
if (rodaPropertiesCache.get(cacheName) == null) {
HashMap<String, String> newCacheEntry = new HashMap<>();
Configuration configuration = RodaCoreFactory.getRodaConfiguration();
Iterator<String> keys = configuration.getKeys();
while (keys.hasNext()) {
String key = String.class.cast(keys.next());
String value = configuration.getString(key, "");
for (String prefixToCache : prefixesToCache) {
if (key.startsWith(prefixToCache)) {
newCacheEntry.put(key, value);
break;
}
}
}
rodaPropertiesCache.put(cacheName, newCacheEntry);
}
}
public static Messages getI18NMessages(Locale locale) {
checkForChangesInI18N();
try {
return I18N_CACHE.get(locale);
} catch (ExecutionException e) {
LOGGER.debug("Could not load messages", e);
return null;
}
}
private static void checkForChangesInI18N() {
// i18n is cached and that cache is re-done when changes occur to
// roda-*.properties (for convenience)
getRodaConfiguration().getString("");
}
/*
* Command-line accessible functionalities
*/
public static void runReindex(List<String> args) {
String entity = args.get(2);
if (StringUtils.isNotBlank(entity)) {
if ("users_and_groups".equalsIgnoreCase(entity)) {
try {
indexUsersAndGroupsFromLDAP();
} catch (IllegalOperationException | GenericException | NotFoundException | AlreadyExistsException e) {
LOGGER.error("Unable to reindex users & groups from LDAP.", e);
}
}
}
}
private static void runSolrQuery(List<String> args) {
String collection = args.get(2);
String solrQueryString = args.get(3);
try {
QueryResponse executeSolrQuery = SolrUtils.executeSolrQuery(solr, collection, solrQueryString);
SolrDocumentList results = executeSolrQuery.getResults();
System.out.println("Size: " + results.getNumFound() + "; Returned: " + results.size());
for (SolrDocument solrDocument : results) {
System.out.println(">" + solrDocument);
}
} catch (SolrServerException | IOException e) {
e.printStackTrace(System.err);
}
}
private static void printIndexMembers(List<String> args, Filter filter, Sorter sorter, Sublist sublist, Facets facets)
throws GenericException, RequestNotValidException {
System.out.println("Index list " + args.get(2));
IndexResult<RODAMember> users = index.find(RODAMember.class, filter, sorter, sublist, facets, new ArrayList<>());
for (RODAMember rodaMember : users.getResults()) {
System.out.println("\t" + rodaMember);
}
}
private static void printCountSips(Sorter sorter, Sublist sublist, Facets facets)
throws GenericException, RequestNotValidException {
Filter filter = new Filter(new SimpleFilterParameter(RodaConstants.TRANSFERRED_RESOURCE_ISFILE, "true"));
long countFiles = index.count(TransferredResource.class, filter);
filter = new Filter(new SimpleFilterParameter(RodaConstants.TRANSFERRED_RESOURCE_ISFILE, "false"));
long countDirectories = index.count(TransferredResource.class, filter);
Filter f1 = new Filter();
FilterParameter p1 = new EmptyKeyFilterParameter(RodaConstants.TRANSFERRED_RESOURCE_PARENT_ID);
FilterParameter p2 = new BasicSearchFilterParameter(RodaConstants.TRANSFERRED_RESOURCE_ISFILE, "false");
f1.add(p1);
f1.add(p2);
long countSIP = index.count(TransferredResource.class, f1);
System.out.println("Total number of directories: " + countDirectories);
System.out.println("Total number of files: " + countFiles);
System.out.println("Total number of SIPs: " + countSIP);
}
private static void printFiles(Sorter sorter, Sublist sublist) throws GenericException, RequestNotValidException {
Filter filter = new Filter(new SimpleFilterParameter(RodaConstants.FILE_SEARCH, "OLA-OLÁ-1234-XXXX_K"));
IndexResult<IndexedFile> res = index.find(IndexedFile.class, filter, sorter, sublist, new ArrayList<>());
for (IndexedFile sf : res.getResults()) {
System.out.println(sf.toString());
}
}
private static void printEvents(Sorter sorter, Sublist sublist) throws GenericException, RequestNotValidException {
Filter filter = new Filter(
new SimpleFilterParameter(RodaConstants.PRESERVATION_EVENT_TYPE, "format identification"));
IndexResult<IndexedPreservationEvent> res = index.find(IndexedPreservationEvent.class, filter, sorter, sublist,
new ArrayList<>());
for (IndexedPreservationEvent ipe : res.getResults()) {
System.out.println(ipe.toString());
}
}
private static void printAgents(Sorter sorter, Sublist sublist) throws GenericException, RequestNotValidException {
Filter filter = new Filter(
new SimpleFilterParameter(RodaConstants.PRESERVATION_AGENT_TYPE, PreservationAgentType.SOFTWARE.toString()));
IndexResult<IndexedPreservationAgent> res = index.find(IndexedPreservationAgent.class, filter, sorter, sublist,
new ArrayList<>());
for (IndexedPreservationAgent ipa : res.getResults()) {
System.out.println(ipa.toString());
}
}
private static String readPassword(final String message) throws IOException {
final Console console = System.console();
if (console == null) {
final BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
System.out.print(String.format("%s (INSECURE - password will be shown): ", message));
return reader.readLine();
} else {
return new String(console.readPassword("%s: ", message));
}
}
private static void resetAdminAccess() throws GenericException {
try {
final String password = readPassword("New admin password");
final String passwordConfirmation = readPassword("Repeat admin password");
if (password.equals(passwordConfirmation)) {
RodaCoreFactory.ldapUtility.resetAdminAccess(password);
try {
indexUsersAndGroupsFromLDAP();
} catch (final Exception e) {
LOGGER.warn("Error reindexing users and groups - " + e.getMessage(), e);
System.err.println("Error reindexing users and groups (" + e.getMessage() + ").");
}
System.out.println("Password for 'admin' changed successfully.");
} else {
throw new GenericException("Passwords don't match.");
}
} catch (final IOException e) {
throw new GenericException(e.getMessage(), e);
} finally {
try {
RodaCoreFactory.shutdown();
} catch (final Exception e) {
e.printStackTrace(System.err);
}
}
}
private static void printMainUsage() {
System.err.println("WARNING: if using Apache Solr embedded, the index related commands");
System.err.println("cannot be run while RODA is running (i.e. deployed in Tomcat for example).");
System.err.println("Stop RODA before running index commands.");
System.err.println();
System.err.println("Usage: java -jar roda-core.jar command [arguments]");
System.err.println("Available commands:");
System.err.println("\tindex");
System.err
.println("\t\treindex aip|job|risk|agent|format|notification|transferred_resources|actionlogs|users_and_groups");
System.err.println("\t\tlist users|groups|sips|file");
System.err.println("\torphans [newParentID]");
System.err.println("\tfixity");
System.err.println("\tantivirus");
System.err.println("\tpremisskeleton");
System.err.println("\treset admin");
System.err.println("\tmigrate model");
System.err.println("\tmigrate index");
}
private static void printResetUsage() {
System.err.println("Reset command parameters:");
System.err.println("\tadmin - resets admin user password and grant it all permissions.");
}
private static void printMigrateUsage() {
System.err.println("Migrate command parameters:");
System.err.println("\tmodel - performs model related migrations.");
}
private static void mainMasterTasks(final List<String> args) throws GenericException, RequestNotValidException {
if ("index".equals(args.get(0))) {
if ("list".equals(args.get(1)) && ("users".equals(args.get(2)) || "groups".equals(args.get(2)))) {
final Filter filter = new Filter(
new SimpleFilterParameter(RodaConstants.MEMBERS_IS_USER, "users".equals(args.get(2)) ? "true" : "false"));
printIndexMembers(args, filter, null, new Sublist(0, 10000), null);
} else if ("list".equals(args.get(1)) && ("sips".equals(args.get(2)))) {
printCountSips(null, new Sublist(0, 10000), null);
} else if ("list".equals(args.get(1)) && ("file".equals(args.get(2)))) {
printFiles(null, new Sublist(0, 10000));
} else if ("list".equals(args.get(1)) && ("event".equals(args.get(2)))) {
printEvents(null, new Sublist(0, 10000));
} else if ("list".equals(args.get(1)) && ("agent".equals(args.get(2)))) {
printAgents(null, new Sublist(0, 10000));
} else if ("query".equals(args.get(1)) && args.size() == 4 && StringUtils.isNotBlank(args.get(2))
&& StringUtils.isNotBlank(args.get(3))) {
runSolrQuery(args);
} else if ("reindex".equals(args.get(1))) {
runReindex(args);
}
} else if ("reset".equals(args.get(0))) {
final List<String> resetParams = args.subList(1, args.size());
if (resetParams.isEmpty()) {
printResetUsage();
} else {
final String resetParam = resetParams.get(0);
if ("admin".equals(resetParam)) {
resetAdminAccess();
} else {
System.err.println("ERROR: Unknown parameter '" + resetParam + "'");
printResetUsage();
}
}
} else if ("migrate".equals(args.get(0))) {
final List<String> migrateParams = args.subList(1, args.size());
if (migrateParams.isEmpty()) {
printMigrateUsage();
} else {
final String migrateParam = migrateParams.get(0);
MigrationManager migrationManager = new MigrationManager(RodaCoreFactory.dataPath);
if ("model".equals(migrateParam)) {
migrationManager.setupModelMigrations();
migrationManager.performModelMigrations();
} else {
printMigrateUsage();
}
}
} else {
printMainUsage();
}
}
private static void preInstantiateSteps(List<String> args) {
if (!args.isEmpty() && "migrate".equals(args.get(0))) {
migrationMode = true;
}
}
public static void main(final String[] argsArray)
throws InterruptedException, GenericException, RequestNotValidException {
final List<String> args = Arrays.asList(argsArray);
preInstantiateSteps(args);
instantiate();
if (getNodeType() == NodeType.MASTER) {
if (!args.isEmpty()) {
mainMasterTasks(args);
} else {
printMainUsage();
}
} else if (getNodeType() == NodeType.WORKER) {
Thread.currentThread().join();
} else {
printMainUsage();
}
System.exit(0);
}
}