/* (c) 2014 - 2016 Open Source Geospatial Foundation - all rights reserved * (c) 2001 - 2013 OpenPlans * This code is licensed under the GPL 2.0 license, available at the root * application directory. */ package org.geoserver.web; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.util.Arrays; import java.util.Enumeration; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Properties; import java.util.logging.Level; import java.util.logging.Logger; import java.util.regex.Pattern; import org.apache.commons.io.FilenameUtils; import org.apache.wicket.core.util.resource.locator.IResourceNameIterator; import org.apache.wicket.core.util.resource.locator.ResourceNameIterator; import org.apache.wicket.core.util.resource.locator.ResourceStreamLocator; import org.apache.wicket.util.resource.AbstractResourceStream; import org.apache.wicket.util.resource.IResourceStream; import org.apache.wicket.util.resource.ResourceStreamNotFoundException; import org.geotools.util.logging.Logging; /** * A custom resource stream locator which supports loading i18n properties files on a single file * per module basis. It also works around https://issues.apache.org/jira/browse/WICKET-2534. * <p> * This class also tries to optimize resource lookups (which are slower with the wicket 7 upgrade) by skipping the * mechanism that looks up static files (markup, css, etc...) with the locale specific prefixes. * </p> */ public class GeoServerResourceStreamLocator extends ResourceStreamLocator { public static Logger LOGGER = Logging.getLogger("org.geoserver.web"); static Pattern GS_PROPERTIES = Pattern.compile("GeoServerApplication.*.properties"); static Pattern GS_LOCAL_I18N = Pattern.compile("org/geoserver/.*(\\.properties|\\.xml)]"); @SuppressWarnings( { "serial" }) @Override public IResourceStream locate(Class<?> clazz, String path) { int i = path.lastIndexOf("/"); if (i != -1) { String p = path.substring(i + 1); if (GS_PROPERTIES.matcher(p).matches()) { try { // process the classpath for property files Enumeration<URL> urls = getClass().getClassLoader().getResources(p); // build up a single properties file Properties properties = new Properties(); while (urls.hasMoreElements()) { URL url = urls.nextElement(); InputStream in = url.openStream(); properties.load(in); in.close(); } // transform the properties to a stream final ByteArrayOutputStream out = new ByteArrayOutputStream(); properties.store(out, ""); return new AbstractResourceStream() { public InputStream getInputStream() throws ResourceStreamNotFoundException { return new ByteArrayInputStream(out.toByteArray()); } public void close() throws IOException { out.close(); } }; } catch (IOException e) { LOGGER.log(Level.WARNING, "", e); } } else if (GS_LOCAL_I18N.matcher(path).matches()) { return null; } else if (path.matches("org/geoserver/.*" + clazz.getName() + ".*_.*.html")) { return null; } } return super.locate(clazz, path); } static Map<String,List<String>> PREFIXES = new HashMap<>(); static { PREFIXES.put("html", Arrays.asList("html")); PREFIXES.put("css", Arrays.asList("css")); PREFIXES.put("png", Arrays.asList("png")); PREFIXES.put("js", Arrays.asList("js")); PREFIXES.put("ico", Arrays.asList("ico")); } @Override public IResourceNameIterator newResourceNameIterator( String path, Locale locale, String style, String variation, String extension, boolean strict) { Iterable<String> extensions = null; // if the resource under the geoserver or wicket namespace? if (path.startsWith("org/geoserver") || path.startsWith("org/apache/wicket")) { String ext = extension; if (ext == null) { // no extension passed in, strip it from the path ext = FilenameUtils.getExtension(path); } if (ext != null) { // we have an extension, look it up in the whitelist extensions = PREFIXES.get(ext); } } if (extensions != null) { // ensure the path doesn't contain the extension, sometimes this method is called with extension == null, // in which case the extension is usually in the path path = FilenameUtils.getPathNoEndSeparator(path) + "/" + FilenameUtils.getBaseName(path); return new ResourceNameIterator(path, style, variation, null, extensions, false); } // couldn't optimize, just pass through to parent return super.newResourceNameIterator(path, locale, style, variation, extension, strict); } }