/* Copyright (c) 2001 - 2007 TOPP - www.openplans.org. All rights reserved.
* 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.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.JarURLConnection;
import java.net.URI;
import java.net.URL;
import java.net.URLConnection;
import java.util.Enumeration;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Pattern;
import org.apache.wicket.Application;
import org.apache.wicket.protocol.http.WebApplication;
import org.apache.wicket.resource.PropertiesFactory.IPropertiesLoader;
import org.apache.wicket.util.resource.AbstractResourceStream;
import org.apache.wicket.util.resource.IFixedLocationResourceStream;
import org.apache.wicket.util.resource.IResourceStream;
import org.apache.wicket.util.resource.ResourceStreamNotFoundException;
import org.apache.wicket.util.resource.locator.ResourceStreamLocator;
import org.apache.wicket.util.time.Time;
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
*/
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( { "unchecked", "serial" })
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);
}
/**
* Search the the resource my means of the various classloaders available
*
* @param clazz
* @param path
* @return resource stream
*/
protected IResourceStream locateByClassLoader(final Class clazz, final String path) {
ClassLoader classLoader = null;
if (clazz != null) {
classLoader = clazz.getClassLoader();
}
if (classLoader == null) {
// use context classloader when no specific classloader is set
// (package resources for instance)
classLoader = Thread.currentThread().getContextClassLoader();
}
if (classLoader == null) {
// use Wicket classloader when no specific classloader is set
classLoader = getClass().getClassLoader();
}
// Try loading path using classloader
final URL url = classLoader.getResource(path);
if (url != null) {
return new UrlResourceStream(url);
}
return null;
}
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*/
/**
* This is a modified version of Wicket own {@link UrlResourceStream} that has
* been modified to work around https://issues.apache.org/jira/browse/WICKET-2534 without
* having to upgrade to Wicket 1.4
*
* @see org.apache.wicket.util.resource.IResourceStream
* @see org.apache.wicket.util.watch.IModifiable
* @author Jonathan Locke
*/
static class UrlResourceStream extends AbstractResourceStream implements
IFixedLocationResourceStream {
private static final long serialVersionUID = 1L;
/** Resource stream. */
private transient InputStream inputStream;
/** The URL to this resource. */
private final URL url;
/**
* the handle to the file if it is a file resource
*/
private File file;
/** Length of stream. */
private int contentLength;
/** Content type for stream. */
private String contentType;
/** Last known time the stream was last modified. */
private long lastModified;
/**
* Construct.
*
* @param url
* URL of resource
*/
public UrlResourceStream(final URL url) {
// Save URL
this.url = url;
URLConnection connection = null;
try {
connection = url.openConnection();
contentLength = connection.getContentLength();
contentType = connection.getContentType();
try {
file = new File(new URI(url.toExternalForm()));
} catch (Exception ex) {
LOGGER.fine("cannot convert url: " + url + " to file (" + ex.getMessage()
+ "), falling back to the inputstream for polling");
}
if (file != null && !file.exists()) {
file = null;
}
} catch (IOException ex) {
// It should be impossible to get here or the original URL
// couldn't have been constructed. But we re-throw with details
// anyway.
final IllegalArgumentException illegalArgumentException = new IllegalArgumentException(
"Invalid URL parameter " + url);
illegalArgumentException.initCause(ex);
throw illegalArgumentException;
} finally {
// if applicable, disconnect
if (connection != null) {
if (connection instanceof HttpURLConnection) {
((HttpURLConnection) connection).disconnect();
} else {
try {
connection.getInputStream().close();
} catch (Exception ex) {
// ignore
}
}
}
}
}
/**
* Closes this resource.
*
* @throws IOException
*/
public void close() throws IOException {
if (inputStream != null) {
inputStream.close();
inputStream = null;
}
}
/**
* @return The content type of this resource, such as "image/jpeg" or "text/html"
*/
public String getContentType() {
testContentType();
return contentType;
}
/**
* Method to test the content type on null or unknown. if this is the case the content type
* is tried to be resolved throw the servlet context
*/
private void testContentType() {
if (contentType == null || contentType.indexOf("unknown") != -1) {
Application application = Application.get();
if (application instanceof WebApplication) {
// TODO Post 1.2: General: For non webapplication another method
// should be implemented (getMimeType on application?)
contentType = ((WebApplication) application).getServletContext().getMimeType(
url.getFile());
if (contentType == null) {
contentType = URLConnection.getFileNameMap().getContentTypeFor(
url.getFile());
}
} else {
contentType = URLConnection.getFileNameMap().getContentTypeFor(url.getFile());
}
}
}
/**
* @return A readable input stream for this resource.
* @throws ResourceStreamNotFoundException
*/
public InputStream getInputStream() throws ResourceStreamNotFoundException {
if (inputStream == null) {
try {
inputStream = url.openStream();
} catch (IOException e) {
throw new ResourceStreamNotFoundException("Resource " + url
+ " could not be opened", e);
}
}
return inputStream;
}
/**
* @return The URL to this resource (if any)
*/
public URL getURL() {
return url;
}
/**
* @see org.apache.wicket.util.watch.IModifiable#lastModifiedTime()
* @return The last time this resource was modified
*/
public Time lastModifiedTime() {
if (file != null) {
long lastModified = file.lastModified();
if (lastModified != this.lastModified) {
this.lastModified = lastModified;
contentLength = (int) file.length();
}
} else {
URLConnection urlConnection = null;
boolean close = false;
try {
urlConnection = url.openConnection();
long lastModified = this.lastModified;
if (urlConnection instanceof JarURLConnection) {
JarURLConnection jarUrlConnection = (JarURLConnection) urlConnection;
URL jarFileUrl = jarUrlConnection.getJarFileURL();
URLConnection jarFileConnection = jarFileUrl.openConnection();
try {
lastModified = jarFileConnection.getLastModified();
} finally {
jarFileConnection.getInputStream().close();
}
} else {
close = true;
lastModified = urlConnection.getLastModified();
}
// update the last modified time.
if (lastModified != this.lastModified) {
this.lastModified = lastModified;
close = true;
contentLength = urlConnection.getContentLength();
}
} catch (IOException e) {
if (url.toString().indexOf(".jar!") >= 0) {
if (LOGGER.isLoggable(Level.FINE)) {
LOGGER
.fine("getLastModified for " + url + " failed: "
+ e.getMessage());
}
} else {
LOGGER.severe("getLastModified for " + url + " failed: " + e.getMessage());
}
} finally {
// if applicable, disconnect
if (urlConnection != null) {
if (urlConnection instanceof HttpURLConnection) {
((HttpURLConnection) urlConnection).disconnect();
} else if (close) {
try {
urlConnection.getInputStream().close();
} catch (Exception ex) {
// ignore
}
}
}
}
}
return Time.milliseconds(lastModified);
}
/**
* @see java.lang.Object#toString()
*/
public String toString() {
return url.toString();
}
/**
* @see org.apache.wicket.util.resource.IResourceStream#length()
*/
public long length() {
return contentLength;
}
/**
* @see org.apache.wicket.util.resource.IFixedLocationResourceStream#locationAsString()
*/
public String locationAsString() {
return url.toExternalForm();
}
}
}