/* * Copyright (C) 2012 Jan Pokorsky * * 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 cz.cas.lib.proarc.common.config; import cz.cas.lib.proarc.common.export.Kramerius4ExportOptions; import cz.cas.lib.proarc.common.export.desa.DesaServices; import cz.cas.lib.proarc.common.imports.ImportProfile; import cz.cas.lib.proarc.common.object.ndk.NdkPlugin; import cz.cas.lib.proarc.common.urnnbn.UrnNbnConfiguration; import java.io.BufferedReader; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.URL; import java.util.Enumeration; import java.util.HashMap; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; 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.configuration.reloading.FileChangedReloadingStrategy; /** * Server side configurations. * * @author Jan Pokorsky */ public final class AppConfiguration { static { String iv = AppConfiguration.class.getPackage().getImplementationVersion(); VERSION = iv == null ? "Unknown" : iv; } public static final String PROPERTY_USER_HOME = "user.home"; /** environment variable name to override default application home */ public static final String ENV_APP_HOME = "PROARC_HOME"; public static final String DEFAULT_APP_HOME_NAME = ".proarc"; public static final String CONFIG_FILE_NAME = "proarc.cfg"; /** * The implementation version. E.g. {@code 1.0} */ public static final String VERSION; /** Path to configuration folder. * Internal configuration property interpolated on init. * Accessible as {@code ${proarc.home}} in properties files. */ public static final String PROPERTY_APP_HOME = "proarc.home"; private static final String PROPERTY_DIGOBJECT_PLUGINS = "digobject.plugins"; private static final String PROPERTY_FEDORA_CLIENT_PASSWORD = "fedora.client.password"; private static final String PROPERTY_FEDORA_CLIENT_URL = "fedora.client.url"; private static final String PROPERTY_FEDORA_CLIENT_USERNAME = "fedora.client.username"; private static final String PROPERTY_USERS_HOME = "proarc.users.home"; private static final Logger LOG = Logger.getLogger(AppConfiguration.class.getName()); private static final String DEFAULT_PROPERTIES_RESOURCE = "cz/cas/lib/proarc/common/config/proarc.properties"; private File configHome; private final Map<String, String> environment; /** read only configuration */ private final Configuration config; private final Map<ConfigurationProfile, Configuration> profileConfigCache = new HashMap<ConfigurationProfile, Configuration>(); private final Profiles profiles; AppConfiguration(Map<String, String> environment) throws IOException { this.environment = environment; this.config = init(); this.profiles = new Profiles(config, configHome); } /** * Gets default target folder for newly created user home folders. */ public File getDefaultUsersHome() throws IOException { String path = config.getString(PROPERTY_USERS_HOME); File users = new File(path); if (!checkFile(users, false, true, true, true)) { users.mkdirs(); } return users; } public String getFedoraUsername() { return config.getString(PROPERTY_FEDORA_CLIENT_USERNAME); } public String getFedoraPassword() { return config.getString(PROPERTY_FEDORA_CLIENT_PASSWORD); } public String getFedoraUrl() { return config.getString(PROPERTY_FEDORA_CLIENT_URL); } public Catalogs getCatalogs() { return new Catalogs(config); } public File getConfigHome() { return configHome; } public Profiles getProfiles() { return profiles; } private Configuration getProfileConfiguration(ConfigurationProfile cp) { if (ConfigurationProfile.DEFAULT.equals(cp.getId()) || ConfigurationProfile.DEFAULT_ARCHIVE_IMPORT.equals(cp.getId())) { return config; } Configuration profileConfig = profileConfigCache.get(cp); if (profileConfig != null) { return profileConfig; } File file = cp.getFile(); if (file != null) { profileConfig = buildConfiguration(file); profileConfigCache.put(cp, profileConfig); return profileConfig; } throw new IllegalStateException("Unknown profile file: " + cp.toString()); } public ImportProfile getImportConfiguration(ConfigurationProfile cp) { Configuration profileConfig = getProfileConfiguration(cp); return new ImportProfile(profileConfig, cp.getId()); } public ImportProfile getImportConfiguration() { return new ImportProfile(config, ConfigurationProfile.DEFAULT); } public Kramerius4ExportOptions getKramerius4Export() { return Kramerius4ExportOptions.from(config); } public Configuration getAuthenticators() { return config; } public DesaServices getDesaServices() { return new DesaServices(config); } public UrnNbnConfiguration getUrnNbnConfiguration() { return new UrnNbnConfiguration(config); } public String[] getPlugins() { String[] plugins = config.getStringArray(PROPERTY_DIGOBJECT_PLUGINS); if (plugins.length == 0) { plugins = new String[] { NdkPlugin.ID, }; } return plugins; } public File getWorkflowConfiguration() { File file = new File(getConfigHome(), "workflow.xml"); return file; } Configuration getConfiguration() { return config; } private Configuration init() throws IOException { this.configHome = initConfigFolder(environment.get(PROPERTY_USER_HOME), environment.get(PROPERTY_APP_HOME)); return buildConfiguration(new File(configHome, CONFIG_FILE_NAME)); } private Configuration buildConfiguration(File cfgFile) { CompositeConfiguration cc = new CompositeConfiguration(); buildConfiguration(cc, cfgFile); return cc; } private void buildConfiguration(CompositeConfiguration cc, File cfgFile) { try { // envConfig contains interpolated properties PropertiesConfiguration envConfig = new PropertiesConfiguration(); envConfig.addProperty(PROPERTY_APP_HOME, configHome.getPath()); cc.addConfiguration(envConfig); // external configuration editable by users; UTF-8 expected PropertiesConfiguration external = new PropertiesConfiguration(); external.setEncoding("UTF-8"); FileChangedReloadingStrategy reloading = new FileChangedReloadingStrategy(); external.setReloadingStrategy(reloading); external.setFile(cfgFile); cc.addConfiguration(external); try { // bundled default configurations Enumeration<URL> resources = AppConfiguration.class.getClassLoader() .getResources(DEFAULT_PROPERTIES_RESOURCE); for (URL resource; resources.hasMoreElements(); ) { resource = resources.nextElement(); LOG.log(Level.FINE, "classpath config: {0}", resource); cc.addConfiguration(new PropertiesConfiguration(resource)); } } catch (IOException ex) { LOG.log(Level.SEVERE, null, ex); } } catch (ConfigurationException ex) { LOG.log(Level.SEVERE, null, ex); } } /** * Copies default properties as proarc.cfg.template. * * @param configHome where to copy * @throws IOException * @throws ConfigurationException */ public void copyConfigTemplate(File configHome) throws AppConfigurationException { try { copyConfigTemplateImpl(configHome); } catch (IOException ex) { throw new AppConfigurationException(String.valueOf(configHome), ex); } } private void copyConfigTemplateImpl(File configHome) throws IOException { File cfgFile = new File(configHome, CONFIG_FILE_NAME + ".template"); if (!cfgFile.exists() || cfgFile.exists() && cfgFile.isFile() && cfgFile.canWrite()) { Enumeration<URL> resources = AppConfiguration.class.getClassLoader() .getResources(DEFAULT_PROPERTIES_RESOURCE); URL lastResource = null; while (resources.hasMoreElements()) { URL url = resources.nextElement(); lastResource = url; System.out.println(url.toExternalForm()); } if (lastResource == null) { throw new IllegalStateException(DEFAULT_PROPERTIES_RESOURCE); } InputStream resource = lastResource.openStream(); BufferedReader reader = new BufferedReader(new InputStreamReader(resource, "ISO-8859-1")); try { // we need platform dependent line separator => PrintWriter PrintWriter writer = new PrintWriter(cfgFile, "UTF-8"); try { for (String line; (line = reader.readLine()) != null;) { writer.println(line); } writer.println(); } finally { writer.close(); } } finally { reader.close(); } } } private static File initHome(String home) throws IOException { home = (home == null) ? "" : home; File homeFile = new File(home); checkFile(homeFile, true, true, true, true); return homeFile; } private static File initConfigFolder(String userHome, String configPath) throws IOException { File config; if (configPath != null) { config = new File(configPath); } else { File home = initHome(userHome); config = new File(home, DEFAULT_APP_HOME_NAME); } if (!checkFile(config, false, true, true, true)) { config.mkdir(); } LOG.log(Level.FINE, "config folder: {0}", config); return config; } /** * checks file/folder parameters * @return {@code true} iff {@code f} exists */ private static boolean checkFile(File f, boolean mustExist, Boolean expectDirectory, Boolean expectCanRead, Boolean expextCanWrite ) throws IOException { if (f.exists()) { if (expectDirectory != null) { if (expectDirectory && !f.isDirectory()) { throw new IOException(String.format("Not a folder: '%s'!", f)); } else if (!expectDirectory && f.isDirectory()) { throw new IOException(String.format("Not a file: '%s'!", f)); } } if (expectCanRead != null) { if (expectCanRead != f.canRead()) { throw new IOException(String.format("Invalid read permission (=%s) for: '%s'!", !expectCanRead, f)); } } if (expextCanWrite != null) { if (expextCanWrite != f.canWrite()) { throw new IOException(String.format("Invalid write permission (=%s) for: '%s'!", !expextCanWrite, f)); } } return true; } else if (mustExist) { if (expectDirectory != null && expectDirectory) { throw new FileNotFoundException(String.format("Folder '%s' not founf!", f)); } else { throw new FileNotFoundException(String.format("File '%s' not founf!", f)); } } return false; } }