PageRenderTime 66ms CodeModel.GetById 21ms RepoModel.GetById 2ms app.codeStats 0ms

/drools-core/src/main/java/org/drools/core/io/impl/UrlResource.java

https://gitlab.com/MichelZuniga/drools
Java | 432 lines | 314 code | 60 blank | 58 comment | 42 complexity | 6492d32a49f86c88b03c1a7835fe4336 MD5 | raw file
  1. /*
  2. * Copyright 2010 JBoss Inc
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package org.drools.core.io.impl;
  17. import java.io.Externalizable;
  18. import java.io.File;
  19. import java.io.FileInputStream;
  20. import java.io.FileNotFoundException;
  21. import java.io.FileOutputStream;
  22. import java.io.IOException;
  23. import java.io.InputStream;
  24. import java.io.InputStreamReader;
  25. import java.io.ObjectInput;
  26. import java.io.ObjectOutput;
  27. import java.io.Reader;
  28. import java.io.UnsupportedEncodingException;
  29. import java.net.HttpURLConnection;
  30. import java.net.MalformedURLException;
  31. import java.net.URL;
  32. import java.net.URLConnection;
  33. import java.net.URLEncoder;
  34. import java.util.ArrayList;
  35. import java.util.Collection;
  36. import java.util.List;
  37. import org.apache.commons.codec.binary.Base64;
  38. import org.drools.core.util.IoUtils;
  39. import org.drools.core.util.StringUtils;
  40. import org.drools.core.io.internal.InternalResource;
  41. import org.kie.api.io.Resource;
  42. import org.kie.api.io.ResourceType;
  43. /**
  44. * Borrowed gratuitously from Spring under ASL2.0.
  45. *
  46. * Added in local file cache ability for http and https urls.
  47. *
  48. * Set the system property: "drools.resource.urlcache" to a directory which can be written to and read from
  49. * as a cache - so remote resources will be cached with last known good copies.
  50. */
  51. public class UrlResource extends BaseResource
  52. implements
  53. InternalResource,
  54. Externalizable {
  55. private static final int DEFAULT_BUFFER_SIZE = 1024 * 4;
  56. public static final File CACHE_DIR = getCacheDir();
  57. private URL url;
  58. private long lastRead = -1;
  59. private static final String DROOLS_RESOURCE_URLCACHE = "drools.resource.urlcache";
  60. private String basicAuthentication = "disabled";
  61. private String username = "";
  62. private String password = "";
  63. private String encoding;
  64. private static final String DROOLS_RESOURCE_URLTIMEOUT = "drools.resource.urltimeout";
  65. private static final int DEFAULT_TIMEOUT = 10000; // 10 seconds
  66. private static final int TIMEOUT = initTimeout();
  67. public UrlResource() {
  68. }
  69. public UrlResource(URL url) {
  70. this.url = getCleanedUrl(url,
  71. url.toString());
  72. setSourcePath(this.url.getPath());
  73. setResourceType(ResourceType.determineResourceType(this.url.getPath()));
  74. }
  75. public UrlResource(URL url, String encoding) {
  76. this(url);
  77. this.encoding = encoding;
  78. }
  79. public UrlResource(String path) {
  80. try {
  81. this.url = getCleanedUrl(new URL(path),
  82. path);
  83. setSourcePath(this.url.getPath());
  84. setResourceType(ResourceType.determineResourceType(this.url.getPath()));
  85. } catch (MalformedURLException e) {
  86. throw new IllegalArgumentException("'" + path + "' path is malformed",
  87. e);
  88. }
  89. }
  90. public UrlResource(String path, String encoding) {
  91. this(path);
  92. this.encoding = encoding;
  93. }
  94. public void writeExternal(ObjectOutput out) throws IOException {
  95. super.writeExternal( out );
  96. out.writeObject(this.url);
  97. out.writeObject(this.encoding);
  98. }
  99. public void readExternal(ObjectInput in) throws IOException,
  100. ClassNotFoundException {
  101. super.readExternal( in );
  102. this.url = (URL) in.readObject();
  103. this.encoding = (String) in.readObject();
  104. }
  105. public String getEncoding() {
  106. return this.encoding;
  107. }
  108. public String getBasicAuthentication() {
  109. return basicAuthentication;
  110. }
  111. public void setBasicAuthentication(String basicAuthentication) {
  112. this.basicAuthentication = basicAuthentication;
  113. }
  114. public String getUsername() {
  115. return username;
  116. }
  117. public void setUsername(String username) {
  118. this.username = username;
  119. }
  120. public String getPassword() {
  121. return password;
  122. }
  123. public void setPassword(String password) {
  124. this.password = password;
  125. }
  126. /**
  127. * This implementation opens an InputStream for the given URL.
  128. * It sets the "UseCaches" flag to <code>false</code>,
  129. * mainly to avoid jar file locking on Windows.
  130. * @see java.net.URL#openConnection()
  131. * @see java.net.URLConnection#setUseCaches(boolean)
  132. * @see java.net.URLConnection#getInputStream()
  133. */
  134. public InputStream getInputStream() throws IOException {
  135. try {
  136. long lastMod = grabLastMod();
  137. if (lastMod == 0) {
  138. //we will try the cache...
  139. if (cacheFileExists())
  140. return fromCache();
  141. }
  142. if (lastMod > 0 && lastMod > lastRead) {
  143. if (CACHE_DIR != null && (url.getProtocol().equals("http") || url.getProtocol().equals("https"))) {
  144. //lets grab a copy and cache it in case we need it in future...
  145. cacheStream();
  146. lastMod = getCacheFile().lastModified();
  147. this.lastRead = lastMod;
  148. return fromCache();
  149. }
  150. }
  151. this.lastRead = lastMod;
  152. return grabStream();
  153. } catch (IOException e) {
  154. if (cacheFileExists()) {
  155. return fromCache();
  156. } else {
  157. throw e;
  158. }
  159. }
  160. }
  161. private boolean cacheFileExists() {
  162. return CACHE_DIR != null && getCacheFile().exists();
  163. }
  164. private InputStream fromCache() throws FileNotFoundException, UnsupportedEncodingException {
  165. File fi = getCacheFile();
  166. return new FileInputStream(fi);
  167. }
  168. private File getCacheFile() {
  169. try {
  170. return new File(CACHE_DIR, URLEncoder.encode(this.url.toString(), "UTF-8"));
  171. } catch (UnsupportedEncodingException e) {
  172. throw new RuntimeException(e);
  173. }
  174. }
  175. private File getTemproralCacheFile() {
  176. try {
  177. return new File(CACHE_DIR, URLEncoder.encode(this.url.toString(), "UTF-8") + "_tmp");
  178. } catch (UnsupportedEncodingException e) {
  179. throw new RuntimeException(e);
  180. }
  181. }
  182. /**
  183. * Save a copy in the local cache - in case remote source is not available in future.
  184. */
  185. private void cacheStream() {
  186. try {
  187. File fi = getTemproralCacheFile();
  188. if (fi.exists())
  189. fi.delete();
  190. FileOutputStream fout = new FileOutputStream(fi);
  191. InputStream in = grabStream();
  192. byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
  193. int n;
  194. while (-1 != (n = in.read(buffer))) {
  195. fout.write(buffer, 0, n);
  196. }
  197. fout.flush();
  198. fout.close();
  199. in.close();
  200. File cacheFile = getCacheFile();
  201. fi.renameTo(cacheFile);
  202. } catch (Exception e) {
  203. e.printStackTrace();
  204. }
  205. }
  206. private URLConnection openURLConnection(URL url) throws IOException {
  207. URLConnection con = url.openConnection();
  208. con.setConnectTimeout(TIMEOUT);
  209. con.setReadTimeout(TIMEOUT);
  210. return con;
  211. }
  212. private InputStream grabStream() throws IOException {
  213. URLConnection con = openURLConnection(this.url);
  214. con.setUseCaches(false);
  215. if (con instanceof HttpURLConnection) {
  216. if ("enabled".equalsIgnoreCase(basicAuthentication)) {
  217. String userpassword = username + ":" + password;
  218. byte[] authEncBytes = Base64.encodeBase64( userpassword.getBytes(IoUtils.UTF8_CHARSET) );
  219. ((HttpURLConnection) con).setRequestProperty("Authorization",
  220. "Basic " + new String(authEncBytes, IoUtils.UTF8_CHARSET));
  221. }
  222. }
  223. return con.getInputStream();
  224. }
  225. public Reader getReader() throws IOException {
  226. if (this.encoding != null) {
  227. return new InputStreamReader( getInputStream(), this.encoding );
  228. } else {
  229. return new InputStreamReader( getInputStream(), IoUtils.UTF8_CHARSET );
  230. }
  231. }
  232. /**
  233. * Determine a cleaned URL for the given original URL.
  234. * @param originalUrl the original URL
  235. * @param originalPath the original URL path
  236. * @return the cleaned URL
  237. */
  238. private URL getCleanedUrl(URL originalUrl,
  239. String originalPath) {
  240. try {
  241. return new URL(StringUtils.cleanPath(originalPath));
  242. } catch (MalformedURLException ex) {
  243. // Cleaned URL path cannot be converted to URL
  244. // -> take original URL.
  245. return originalUrl;
  246. }
  247. }
  248. public URL getURL() throws IOException {
  249. return this.url;
  250. }
  251. public boolean hasURL() {
  252. return true;
  253. }
  254. public File getFile() throws IOException {
  255. try {
  256. return new File(StringUtils.toURI(url.toString()).getSchemeSpecificPart());
  257. } catch (Exception e) {
  258. throw new RuntimeException("Unable to get File for url " + this.url, e);
  259. }
  260. }
  261. public long getLastModified() {
  262. try {
  263. long lm = grabLastMod();
  264. //try the cache.
  265. if (lm == 0 && cacheFileExists()) {
  266. //OK we will return it from the local cached copy, as remote one isn't available..
  267. return getCacheFile().lastModified();
  268. }
  269. return lm;
  270. } catch (IOException e) {
  271. //try the cache...
  272. if (cacheFileExists()) {
  273. //OK we will return it from the local cached copy, as remote one isn't available..
  274. return getCacheFile().lastModified();
  275. } else {
  276. throw new RuntimeException("Unable to get LastModified for ClasspathResource",
  277. e);
  278. }
  279. }
  280. }
  281. private long grabLastMod() throws IOException {
  282. // use File if possible, as http rounds milliseconds on some machines, this fine level of granularity is only really an issue for testing
  283. // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4504473
  284. if ("file".equals(url.getProtocol())) {
  285. File file = getFile();
  286. return file.lastModified();
  287. } else {
  288. URLConnection conn = openURLConnection(getURL());
  289. if (conn instanceof HttpURLConnection) {
  290. ((HttpURLConnection) conn).setRequestMethod("HEAD");
  291. if ("enabled".equalsIgnoreCase(basicAuthentication)) {
  292. String userpassword = username + ":" + password;
  293. byte[] authEncBytes = Base64.encodeBase64( userpassword.getBytes(IoUtils.UTF8_CHARSET) );
  294. ((HttpURLConnection) conn).setRequestProperty("Authorization",
  295. "Basic " + new String(authEncBytes, IoUtils.UTF8_CHARSET));
  296. }
  297. }
  298. long date = conn.getLastModified();
  299. if (date == 0) {
  300. try {
  301. date = Long.parseLong(conn.getHeaderField("lastModified"));
  302. } catch (Exception e) { /* well, we tried ... */
  303. }
  304. }
  305. return date;
  306. }
  307. }
  308. public long getLastRead() {
  309. return this.lastRead;
  310. }
  311. public boolean isDirectory() {
  312. try {
  313. URL url = getURL();
  314. if ("file".equals(url.getProtocol())) {
  315. File file = new File(StringUtils.toURI(url.toString()).getSchemeSpecificPart());
  316. return file.isDirectory();
  317. }
  318. } catch (Exception e) {
  319. // swallow as returned false
  320. }
  321. return false;
  322. }
  323. public Collection<Resource> listResources() {
  324. try {
  325. URL url = getURL();
  326. if ("file".equals(url.getProtocol())) {
  327. File dir = getFile();
  328. List<Resource> resources = new ArrayList<Resource>();
  329. for (File file : dir.listFiles()) {
  330. resources.add(new FileSystemResource(file));
  331. }
  332. return resources;
  333. }
  334. } catch (Exception e) {
  335. // swallow as we'll throw an exception anyway
  336. }
  337. throw new RuntimeException("This Resource cannot be listed, or is not a directory");
  338. }
  339. /**
  340. * This implementation compares the underlying URL references.
  341. */
  342. public boolean equals(Object obj) {
  343. if (obj == null) {
  344. return false;
  345. }
  346. return (obj == this || (obj instanceof UrlResource && this.url.equals(((UrlResource) obj).url)));
  347. }
  348. /**
  349. * This implementation returns the hash code of the underlying URL reference.
  350. */
  351. public int hashCode() {
  352. return this.url.hashCode();
  353. }
  354. public String toString() {
  355. return "UrlResource[path=" + this.url.toString() + "]";
  356. }
  357. private static File getCacheDir() {
  358. String root = System.getProperty(DROOLS_RESOURCE_URLCACHE, "NONE");
  359. if (root.equals("NONE")) {
  360. return null;
  361. } else {
  362. return new File(root);
  363. }
  364. }
  365. private static int initTimeout() {
  366. try {
  367. return Integer.parseInt(System.getProperty( DROOLS_RESOURCE_URLTIMEOUT ));
  368. } catch (Exception e) {
  369. return DEFAULT_TIMEOUT;
  370. }
  371. }
  372. }