package org.lognavigator.service; import static org.lognavigator.util.Constants.*; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.util.Arrays; import java.util.Set; import javax.annotation.PostConstruct; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBException; import javax.xml.bind.Unmarshaller; import net.schmizz.sshj.common.IOUtils; import org.lognavigator.bean.LogAccessConfig; import org.lognavigator.bean.LogNavigatorConfig; import org.lognavigator.bean.LogAccessConfig.LogAccessType; import org.lognavigator.exception.ConfigException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.core.io.Resource; import org.springframework.stereotype.Service; import org.springframework.util.StringUtils; @Service public class DefaultConfigService implements ConfigService { private static final Logger LOGGER = LoggerFactory.getLogger(DefaultConfigService.class); private static final String DEFAULT_LOGNAVIGATOR_CONFIG_LOCATION = "classpath:lognavigator.xml"; private static final int DEFAULT_FILE_LIST_MAX_COUNT = 1000; private static final boolean DEFAULT_FILE_LIST_BLOCK_EXTERNAL_PATHS = false; /** All log access configurations loaded from lognavigator.xml */ Set<LogAccessConfig> logAccessConfigs; /** last time that lognavigator.xml was modified */ long logNavigatorConfigLastModified; /** JAXBContext used to load lognavigator.xml content */ JAXBContext logNavigatorConfigJaxbContext; /** lognavigator main configuration location */ @Value("${lognavigator.config:" + DEFAULT_LOGNAVIGATOR_CONFIG_LOCATION + "}") Resource logNavigatorConfigResource; /** max file count in screen listing files */ @Value("${filelist.maxcount:" + DEFAULT_FILE_LIST_MAX_COUNT + "}") int fileListMaxCount; /** is block external paths enabled */ @Value("${filelist.blockexternalpaths:" + DEFAULT_FILE_LIST_BLOCK_EXTERNAL_PATHS + "}") private boolean fileListBlockExternalPaths; /** forbidden commands list */ @Value("${forbidden.commands:" + DEFAULT_FORBIDDEN_COMMANDS + "}") String forbiddenCommands = DEFAULT_FORBIDDEN_COMMANDS; /** default encoding used to read command output */ @Value("${default.encoding:" + DEFAULT_ENCODING_OPTION + "}") String defaultEncoding = DEFAULT_ENCODING_OPTION; @Override public synchronized Set<LogAccessConfig> getLogAccessConfigs() throws ConfigException { reloadLogNavigatorConfigIfNecessary(); return logAccessConfigs; } @Override public synchronized LogAccessConfig getLogAccessConfig(String id) throws ConfigException { // Search the LogAccessConfig with the param id for (LogAccessConfig logAccessConfig : getLogAccessConfigs()) { if (logAccessConfig.getId().equals(id)) { return logAccessConfig; } } // No LogAccessConfig found throw new ConfigException("logAccessConfigId " + id + " doesn't correspond to any known log access config"); } @Override public int getFileListMaxCount() { return fileListMaxCount; } @Override public boolean getFileListBlockExternalPaths() { return fileListBlockExternalPaths; } @Override public String getForbiddenCommands() { return forbiddenCommands; } @Override public String getDefaultEncoding(String logAccessConfigId) { LogAccessConfig logAccessConfig = getLogAccessConfig(logAccessConfigId); if (logAccessConfig.getDefaultEncoding() != null) { return logAccessConfig.getDefaultEncoding(); } else { return defaultEncoding; } } /** * Load or Reload the set of LogAccessConfig beans, from lognavigator config file */ synchronized void reloadLogNavigatorConfigIfNecessary() throws ConfigException { // Does config file exist ? if (!logNavigatorConfigResource.exists()) { throw new ConfigException("The config file " + logNavigatorConfigResource + " does not exist"); } // Should we reload config file ? (because it has been modified since last reload) long lastModified; try { lastModified = logNavigatorConfigResource.lastModified(); boolean needReload = (lastModified > this.logNavigatorConfigLastModified); if (!needReload) { return; } } catch (IOException e) { throw new ConfigException("Error when trying to access lognavigator config file " + logNavigatorConfigResource, e); } // Load lognavigator XML configuration InputStream logNavigatorConfigInputStream = null; try { logNavigatorConfigInputStream = logNavigatorConfigResource.getInputStream(); Unmarshaller unmarshaller = logNavigatorConfigJaxbContext.createUnmarshaller(); LogNavigatorConfig logNavigatorConfig = (LogNavigatorConfig) unmarshaller.unmarshal(logNavigatorConfigInputStream); this.logAccessConfigs = logNavigatorConfig.getLogAccessConfigs(); } catch (IOException e) { throw new ConfigException("I/O error when trying to load lognavigator config file " + logNavigatorConfigResource, e); } catch (JAXBException e) { throw new ConfigException("XML load error when trying to load lognavigator config file " + logNavigatorConfigResource, e); } finally { IOUtils.closeQuietly(logNavigatorConfigInputStream); } validateConfiguration(); // Update the lastModified date information for config file this.logNavigatorConfigLastModified = lastModified; } /** * Validate loaded configuration * @throws ConfigException if configuration is invalid */ synchronized void validateConfiguration() throws ConfigException { // Case where configuration is empty => config error if (this.logAccessConfigs.isEmpty()) { throw new ConfigException("lognavigator config file " + logNavigatorConfigResource + " is empty : at least one configuration must be defined"); } for (LogAccessConfig logAccessConfig : this.logAccessConfigs) { if (logAccessConfig.getType() == null) { throw new ConfigException("unknown type for log-access-config '" + logAccessConfig.getId() + "'. Valid values are : " + Arrays.asList(LogAccessType.values())); } switch (logAccessConfig.getType()) { case LOCAL: if (StringUtils.isEmpty(logAccessConfig.getDirectory())) { throw new ConfigException("'directory' attribute must be defined for log-access-config '" + logAccessConfig.getId() + "'"); } File directoryFile = new File(logAccessConfig.getDirectory()); if (!directoryFile.isAbsolute()) { logAccessConfig.setDirectory(directoryFile.getAbsolutePath()); } break; case HTTPD: if (StringUtils.isEmpty(logAccessConfig.getUrl())) { throw new ConfigException("'url' attribute must be defined for log-access-config '" + logAccessConfig.getId() + "'"); } if (!logAccessConfig.getUrl().endsWith("/")) { logAccessConfig.setUrl(logAccessConfig.getUrl() + "/"); } break; case SSH: if (StringUtils.isEmpty(logAccessConfig.getUser())) { throw new ConfigException("'user' attribute must be defined for log-access-config '" + logAccessConfig.getId() + "'"); } if (StringUtils.isEmpty(logAccessConfig.getHost())) { throw new ConfigException("'host' attribute must be defined for log-access-config '" + logAccessConfig.getId() + "'"); } if (StringUtils.isEmpty(logAccessConfig.getDirectory())) { throw new ConfigException("'directory' attribute must be defined for log-access-config '" + logAccessConfig.getId() + "'"); } break; default: throw new IllegalStateException("unmanaged log access config type : " + logAccessConfig.getType() + "'"); } } } /** * Init the Spring Service */ @PostConstruct public synchronized void init() { try { logNavigatorConfigJaxbContext = JAXBContext.newInstance(LogNavigatorConfig.class); reloadLogNavigatorConfigIfNecessary(); } catch (JAXBException e) { LOGGER.error("Error while loading configuration file {}", logNavigatorConfigResource, e); } catch (ConfigException e) { LOGGER.error("Error while loading configuration file {}", logNavigatorConfigResource, e); } } }