Java tutorial
/** * Copyright 2012 the project-owners * * 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 inti.ws.spring.resource.config; import inti.ws.spring.resource.WebResource; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.util.ArrayList; import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import javax.inject.Inject; import javax.servlet.ServletContext; import javax.servlet.http.HttpServletRequest; import org.apache.commons.io.IOUtils; import org.apache.http.HttpHeaders; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.InitializingBean; import org.springframework.core.io.Resource; import org.springframework.core.io.UrlResource; import org.springframework.stereotype.Component; import org.springframework.web.context.support.ServletContextResource; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; @Component public class ResourceConfigProviderImpl implements InitializingBean, ResourceConfigProvider { private static final String DEFAULT_CONFIG_NAME = "_default"; private static final String CONFIG_EXTENSION = ".json"; private static final String HOSTS_DIR = "/WEB-INF/hosts/"; private static final String HOSTS_META_DIR = "META-INF/hosts/index"; private static final Logger LOGGER = LoggerFactory.getLogger(ResourceConfigProviderImpl.class); @Inject protected ServletContext ctx; @Inject protected ConfigParserProvider configParserProvider; protected boolean updateConfig = false; protected Map<String, Long> lastModifies = new HashMap<String, Long>(); protected Map<String, List<String>> hostToConfigfileMapping = new HashMap<>(); protected Map<String, List<ResourceConfig>> configs = new HashMap<>(); protected ObjectMapper mapper = new ObjectMapper(); @Override public void afterPropertiesSet() throws Exception { updateConfig = true; Set<String> paths = ctx.getResourcePaths(HOSTS_DIR); Enumeration<URL> urls = Thread.currentThread().getContextClassLoader().getResources(HOSTS_META_DIR); Set<String> metaPaths = getPaths(urls); String host; metaPaths.addAll(paths); LOGGER.debug("afterPropertiesSet - available paths={}", metaPaths); for (String path : metaPaths) { if (path.endsWith(CONFIG_EXTENSION)) { LOGGER.debug("afterPropertiesSet - scanning config-file={}", path); host = path.substring(path.lastIndexOf('/') + 1, path.lastIndexOf(CONFIG_EXTENSION)); List<String> resources = hostToConfigfileMapping.get(host); if (resources == null) { resources = new ArrayList<>(); hostToConfigfileMapping.put(host, resources); } resources.add(path); update(host); } } updateConfig = !(System.getProperty("DEBUG_MODE") == null || !System.getProperty("DEBUG_MODE").equals("true")); } protected Set<String> getPaths(Enumeration<URL> urls) { URL url; InputStream input; List<String> lines; Set<String> paths = new HashSet<>(); String tmp; if (urls != null) { while (urls.hasMoreElements()) { try { url = urls.nextElement(); LOGGER.debug("getPaths - checking url: {}", url); input = url.openStream(); try { lines = IOUtils.readLines(input); for (String line : lines) { if (line.trim().length() > 0) { tmp = url.toExternalForm(); paths.add(tmp.substring(0, tmp.length() - 5) + line); } } LOGGER.debug("getPaths - got paths {} from url {}", paths, url); } finally { try { input.close(); } catch (IOException exception) { LOGGER.error("error while reading hosts config", exception); } } } catch (IOException exception) { LOGGER.error("error while reading hosts config", exception); } } } return paths; } @SuppressWarnings({ "unchecked", "rawtypes" }) @Override public ResourceConfig getResourceConfig(HttpServletRequest request, String pluginName) { List<ResourceConfig> configs = getResourceConfigs(request); List<ResourceConfig> candidates = new ArrayList<>(); // search all candicates for (ResourceConfig config : configs) { if (config.getName().equals(pluginName)) { candidates.add(config); } } if (!candidates.isEmpty()) { ResourceConfig config = new ResourceConfig(); Map<String, ? extends WebResource> resources = new HashMap<>(); // put all resources in a new ResourceConfig object for (ResourceConfig resourceConfig : candidates) { resources.putAll((Map) resourceConfig.getResources()); } config.setResources(resources); return config; } return null; } @Override public List<ResourceConfig> getResourceConfigs(HttpServletRequest request) { String key = getKey(request); if (updateConfig) { try { update(key); } catch (Exception exception) { LOGGER.error("", exception); } } return configs.get(key); } protected String getKey(HttpServletRequest request) { String key = request.getHeader(HttpHeaders.HOST); int index; if (key == null) { key = DEFAULT_CONFIG_NAME; } else if ((index = key.indexOf(":")) != -1) { key = key.substring(0, index); } if (!configs.containsKey(key)) { key = DEFAULT_CONFIG_NAME; } return key; } protected void update(String key) throws Exception { Resource configFile; JsonNode configNode; InputStream inputStream; LOGGER.debug("update - paths for key {}: {}", key, hostToConfigfileMapping.get(key)); for (String path : hostToConfigfileMapping.get(key)) { if (path.startsWith("/")) { configFile = new ServletContextResource(ctx, path); } else { configFile = new UrlResource(path); } LOGGER.debug("update - updating {} for key {}", path, key); if (lastModifies.containsKey(path) && configFile.lastModified() <= lastModifies.get(path)) { LOGGER.debug("update - no newer version available for {} for key {}", path, key); continue; } else { lastModifies.put(path, configFile.lastModified()); } inputStream = configFile.getInputStream(); try { configNode = mapper.readTree(inputStream); } finally { inputStream.close(); } if (configNode != null) { configure(key, configNode); } } } @SuppressWarnings({ "unchecked", "rawtypes" }) protected void configure(String host, JsonNode configNode) throws Exception { Map<String, ConfigParser<? extends WebResource>> parsers; ConfigParser<? extends WebResource> parser; Entry<String, JsonNode> field; Iterator<Entry<String, JsonNode>> fields; Map<String, ? extends WebResource> resources; List<ResourceConfig> resourceConfigs; Map<String, Map<String, ? extends WebResource>> resourceStack = new HashMap<>(); ResourceConfig config; fields = configNode.fields(); parsers = configParserProvider.getConfigParsers(Thread.currentThread().getContextClassLoader()); LOGGER.debug("update - scanning with parsers={}", parsers); resourceConfigs = configs.get(host); if (resourceConfigs == null) { resourceConfigs = new ArrayList<>(); configs.put(host, resourceConfigs); } while (fields.hasNext()) { field = fields.next(); parser = parsers.get(field.getKey()); LOGGER.debug("update - got parser for field={}: {}", field.getKey(), parser); config = new ResourceConfig(); if (parser != null) { resources = parser.instanceWebResources(mapper, field.getValue()); resourceStack.put(field.getKey(), resources); if (parser.getConfigParserSettings() != null) { config.setName(field.getKey()); config.setResources(resources); config.setSettings(parser.getConfigParserSettings()); resourceConfigs.add(config); } } } fields = configNode.fields(); while (fields.hasNext()) { field = fields.next(); parser = parsers.get(field.getKey()); if (parser != null) { parser.configureWebResources(mapper, field.getValue(), (Map) resourceStack.get(field.getKey()), (Map) resourceStack); } } LOGGER.debug("update - added config with host={}: {}", host, resourceConfigs); } }