Java tutorial
/* * Copyright 2010 JBoss Inc * * 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 org.drools.core.io.impl; import java.io.Externalizable; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.ObjectInput; import java.io.ObjectOutput; import java.io.Reader; import java.io.UnsupportedEncodingException; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; import java.net.URLConnection; import java.net.URLEncoder; import java.util.ArrayList; import java.util.Collection; import java.util.List; import org.apache.commons.codec.binary.Base64; import org.drools.core.util.IoUtils; import org.drools.core.util.StringUtils; import org.drools.core.io.internal.InternalResource; import org.kie.api.io.Resource; import org.kie.api.io.ResourceType; /** * Borrowed gratuitously from Spring under ASL2.0. * * Added in local file cache ability for http and https urls. * * Set the system property: "drools.resource.urlcache" to a directory which can be written to and read from * as a cache - so remote resources will be cached with last known good copies. */ public class UrlResource extends BaseResource implements InternalResource, Externalizable { private static final int DEFAULT_BUFFER_SIZE = 1024 * 4; public static final File CACHE_DIR = getCacheDir(); private URL url; private long lastRead = -1; private static final String DROOLS_RESOURCE_URLCACHE = "drools.resource.urlcache"; private String basicAuthentication = "disabled"; private String username = ""; private String password = ""; private String encoding; private static final String DROOLS_RESOURCE_URLTIMEOUT = "drools.resource.urltimeout"; private static final int DEFAULT_TIMEOUT = 10000; // 10 seconds private static final int TIMEOUT = initTimeout(); public UrlResource() { } public UrlResource(URL url) { this.url = getCleanedUrl(url, url.toString()); setSourcePath(this.url.getPath()); setResourceType(ResourceType.determineResourceType(this.url.getPath())); } public UrlResource(URL url, String encoding) { this(url); this.encoding = encoding; } public UrlResource(String path) { try { this.url = getCleanedUrl(new URL(path), path); setSourcePath(this.url.getPath()); setResourceType(ResourceType.determineResourceType(this.url.getPath())); } catch (MalformedURLException e) { throw new IllegalArgumentException("'" + path + "' path is malformed", e); } } public UrlResource(String path, String encoding) { this(path); this.encoding = encoding; } public void writeExternal(ObjectOutput out) throws IOException { super.writeExternal(out); out.writeObject(this.url); out.writeObject(this.encoding); } public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { super.readExternal(in); this.url = (URL) in.readObject(); this.encoding = (String) in.readObject(); } public String getEncoding() { return this.encoding; } public String getBasicAuthentication() { return basicAuthentication; } public void setBasicAuthentication(String basicAuthentication) { this.basicAuthentication = basicAuthentication; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } /** * This implementation opens an InputStream for the given URL. * It sets the "UseCaches" flag to <code>false</code>, * mainly to avoid jar file locking on Windows. * @see java.net.URL#openConnection() * @see java.net.URLConnection#setUseCaches(boolean) * @see java.net.URLConnection#getInputStream() */ public InputStream getInputStream() throws IOException { try { long lastMod = grabLastMod(); if (lastMod == 0) { //we will try the cache... if (cacheFileExists()) return fromCache(); } if (lastMod > 0 && lastMod > lastRead) { if (CACHE_DIR != null && (url.getProtocol().equals("http") || url.getProtocol().equals("https"))) { //lets grab a copy and cache it in case we need it in future... cacheStream(); lastMod = getCacheFile().lastModified(); this.lastRead = lastMod; return fromCache(); } } this.lastRead = lastMod; return grabStream(); } catch (IOException e) { if (cacheFileExists()) { return fromCache(); } else { throw e; } } } private boolean cacheFileExists() { return CACHE_DIR != null && getCacheFile().exists(); } private InputStream fromCache() throws FileNotFoundException, UnsupportedEncodingException { File fi = getCacheFile(); return new FileInputStream(fi); } private File getCacheFile() { try { return new File(CACHE_DIR, URLEncoder.encode(this.url.toString(), "UTF-8")); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } } private File getTemproralCacheFile() { try { return new File(CACHE_DIR, URLEncoder.encode(this.url.toString(), "UTF-8") + "_tmp"); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } } /** * Save a copy in the local cache - in case remote source is not available in future. */ private void cacheStream() { try { File fi = getTemproralCacheFile(); if (fi.exists()) fi.delete(); FileOutputStream fout = new FileOutputStream(fi); InputStream in = grabStream(); byte[] buffer = new byte[DEFAULT_BUFFER_SIZE]; int n; while (-1 != (n = in.read(buffer))) { fout.write(buffer, 0, n); } fout.flush(); fout.close(); in.close(); File cacheFile = getCacheFile(); fi.renameTo(cacheFile); } catch (Exception e) { e.printStackTrace(); } } private URLConnection openURLConnection(URL url) throws IOException { URLConnection con = url.openConnection(); con.setConnectTimeout(TIMEOUT); con.setReadTimeout(TIMEOUT); return con; } private InputStream grabStream() throws IOException { URLConnection con = openURLConnection(this.url); con.setUseCaches(false); if (con instanceof HttpURLConnection) { if ("enabled".equalsIgnoreCase(basicAuthentication)) { String userpassword = username + ":" + password; byte[] authEncBytes = Base64.encodeBase64(userpassword.getBytes(IoUtils.UTF8_CHARSET)); ((HttpURLConnection) con).setRequestProperty("Authorization", "Basic " + new String(authEncBytes, IoUtils.UTF8_CHARSET)); } } return con.getInputStream(); } public Reader getReader() throws IOException { if (this.encoding != null) { return new InputStreamReader(getInputStream(), this.encoding); } else { return new InputStreamReader(getInputStream(), IoUtils.UTF8_CHARSET); } } /** * Determine a cleaned URL for the given original URL. * @param originalUrl the original URL * @param originalPath the original URL path * @return the cleaned URL */ private URL getCleanedUrl(URL originalUrl, String originalPath) { try { return new URL(StringUtils.cleanPath(originalPath)); } catch (MalformedURLException ex) { // Cleaned URL path cannot be converted to URL // -> take original URL. return originalUrl; } } public URL getURL() throws IOException { return this.url; } public boolean hasURL() { return true; } public File getFile() throws IOException { try { return new File(StringUtils.toURI(url.toString()).getSchemeSpecificPart()); } catch (Exception e) { throw new RuntimeException("Unable to get File for url " + this.url, e); } } public long getLastModified() { try { long lm = grabLastMod(); //try the cache. if (lm == 0 && cacheFileExists()) { //OK we will return it from the local cached copy, as remote one isn't available.. return getCacheFile().lastModified(); } return lm; } catch (IOException e) { //try the cache... if (cacheFileExists()) { //OK we will return it from the local cached copy, as remote one isn't available.. return getCacheFile().lastModified(); } else { throw new RuntimeException("Unable to get LastModified for ClasspathResource", e); } } } private long grabLastMod() throws IOException { // use File if possible, as http rounds milliseconds on some machines, this fine level of granularity is only really an issue for testing // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4504473 if ("file".equals(url.getProtocol())) { File file = getFile(); return file.lastModified(); } else { URLConnection conn = openURLConnection(getURL()); if (conn instanceof HttpURLConnection) { ((HttpURLConnection) conn).setRequestMethod("HEAD"); if ("enabled".equalsIgnoreCase(basicAuthentication)) { String userpassword = username + ":" + password; byte[] authEncBytes = Base64.encodeBase64(userpassword.getBytes(IoUtils.UTF8_CHARSET)); ((HttpURLConnection) conn).setRequestProperty("Authorization", "Basic " + new String(authEncBytes, IoUtils.UTF8_CHARSET)); } } long date = conn.getLastModified(); if (date == 0) { try { date = Long.parseLong(conn.getHeaderField("lastModified")); } catch (Exception e) { /* well, we tried ... */ } } return date; } } public long getLastRead() { return this.lastRead; } public boolean isDirectory() { try { URL url = getURL(); if ("file".equals(url.getProtocol())) { File file = new File(StringUtils.toURI(url.toString()).getSchemeSpecificPart()); return file.isDirectory(); } } catch (Exception e) { // swallow as returned false } return false; } public Collection<Resource> listResources() { try { URL url = getURL(); if ("file".equals(url.getProtocol())) { File dir = getFile(); List<Resource> resources = new ArrayList<Resource>(); for (File file : dir.listFiles()) { resources.add(new FileSystemResource(file)); } return resources; } } catch (Exception e) { // swallow as we'll throw an exception anyway } throw new RuntimeException("This Resource cannot be listed, or is not a directory"); } /** * This implementation compares the underlying URL references. */ public boolean equals(Object obj) { if (obj == null) { return false; } return (obj == this || (obj instanceof UrlResource && this.url.equals(((UrlResource) obj).url))); } /** * This implementation returns the hash code of the underlying URL reference. */ public int hashCode() { return this.url.hashCode(); } public String toString() { return "UrlResource[path=" + this.url.toString() + "]"; } private static File getCacheDir() { String root = System.getProperty(DROOLS_RESOURCE_URLCACHE, "NONE"); if (root.equals("NONE")) { return null; } else { return new File(root); } } private static int initTimeout() { try { return Integer.parseInt(System.getProperty(DROOLS_RESOURCE_URLTIMEOUT)); } catch (Exception e) { return DEFAULT_TIMEOUT; } } }