View Javadoc

1   package org.apache.velocity.runtime.resource.loader;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.io.InputStream;
23  import java.io.IOException;
24  import java.lang.reflect.Method;
25  import java.net.URL;
26  import java.net.URLConnection;
27  import java.util.HashMap;
28  import org.apache.commons.collections.ExtendedProperties;
29  import org.apache.velocity.exception.VelocityException;
30  import org.apache.velocity.exception.ResourceNotFoundException;
31  import org.apache.velocity.runtime.resource.Resource;
32  import org.apache.commons.lang.StringUtils;
33  
34  /**
35   * This is a simple URL-based loader.
36   *
37   * @author <a href="mailto:geirm@apache.org">Geir Magnusson Jr.</a>
38   * @author <a href="mailto:nbubna@apache.org">Nathan Bubna</a>
39   * @version $Id: URLResourceLoader.java 191743 2005-06-21 23:22:20Z dlr $
40   * @since 1.5
41   */
42  public class URLResourceLoader extends ResourceLoader
43  {
44      private String[] roots = null;
45      protected HashMap templateRoots = null;
46      private int timeout = -1;
47      private Method[] timeoutMethods;
48  
49      /**
50       * @see org.apache.velocity.runtime.resource.loader.ResourceLoader#init(org.apache.commons.collections.ExtendedProperties)
51       */
52      public void init(ExtendedProperties configuration)
53      {
54          log.trace("URLResourceLoader : initialization starting.");
55  
56          roots = configuration.getStringArray("root");
57          if (log.isDebugEnabled())
58          {
59              for (int i=0; i < roots.length; i++)
60              {
61                  log.debug("URLResourceLoader : adding root '" + roots[i] + "'");
62              }
63          }
64  
65          timeout = configuration.getInt("timeout", -1);
66          if (timeout > 0)
67          {
68              try
69              {
70                  Class[] types = new Class[] { Integer.TYPE };
71                  Method conn = URLConnection.class.getMethod("setConnectTimeout", types);
72                  Method read = URLConnection.class.getMethod("setReadTimeout", types);
73                  timeoutMethods = new Method[] { conn, read };
74                  log.debug("URLResourceLoader : timeout set to "+timeout);
75              }
76              catch (NoSuchMethodException nsme)
77              {
78                  log.debug("URLResourceLoader : Java 1.5+ is required to customize timeout!", nsme);
79                  timeout = -1;
80              }
81          }
82  
83          // init the template paths map
84          templateRoots = new HashMap();
85  
86          log.trace("URLResourceLoader : initialization complete.");
87      }
88  
89      /**
90       * Get an InputStream so that the Runtime can build a
91       * template with it.
92       *
93       * @param name name of template to fetch bytestream of
94       * @return InputStream containing the template
95       * @throws ResourceNotFoundException if template not found
96       *         in the file template path.
97       */
98      public synchronized InputStream getResourceStream(String name)
99          throws ResourceNotFoundException
100     {
101         if (StringUtils.isEmpty(name))
102         {
103             throw new ResourceNotFoundException("URLResourceLoader : No template name provided");
104         }
105 
106         InputStream inputStream = null;
107         Exception exception = null;
108         for(int i=0; i < roots.length; i++)
109         {
110             try
111             {
112                 URL u = new URL(roots[i] + name);
113                 URLConnection conn = u.openConnection();
114                 tryToSetTimeout(conn);
115                 inputStream = conn.getInputStream();
116 
117                 if (inputStream != null)
118                 {
119                     if (log.isDebugEnabled()) log.debug("URLResourceLoader: Found '"+name+"' at '"+roots[i]+"'");
120 
121                     // save this root for later re-use
122                     templateRoots.put(name, roots[i]);
123                     break;
124                 }
125             }
126             catch(IOException ioe)
127             {
128                 if (log.isDebugEnabled()) log.debug("URLResourceLoader: Exception when looking for '"+name+"' at '"+roots[i]+"'", ioe);
129 
130                 // only save the first one for later throwing
131                 if (exception == null)
132                 {
133                     exception = ioe;
134                 }
135             }
136         }
137 
138         // if we never found the template
139         if (inputStream == null)
140         {
141             String msg;
142             if (exception == null)
143             {
144                 msg = "URLResourceLoader : Resource '" + name + "' not found.";
145             }
146             else
147             {
148                 msg = exception.getMessage();
149             }
150             // convert to a general Velocity ResourceNotFoundException
151             throw new ResourceNotFoundException(msg);
152         }
153 
154         return inputStream;
155     }
156 
157     /**
158      * Checks to see if a resource has been deleted, moved or modified.
159      *
160      * @param resource Resource  The resource to check for modification
161      * @return boolean  True if the resource has been modified, moved, or unreachable
162      */
163     public boolean isSourceModified(Resource resource)
164     {
165         long fileLastModified = getLastModified(resource);
166         // if the file is unreachable or otherwise changed
167         if (fileLastModified == 0 ||
168             fileLastModified != resource.getLastModified())
169         {
170             return true;
171         }
172         return false;
173     }
174 
175     /**
176      * Checks to see when a resource was last modified
177      *
178      * @param resource Resource the resource to check
179      * @return long The time when the resource was last modified or 0 if the file can't be reached
180      */
181     public long getLastModified(Resource resource)
182     {
183         // get the previously used root
184         String name = resource.getName();
185         String root = (String)templateRoots.get(name);
186 
187         try
188         {
189             // get a connection to the URL
190             URL u = new URL(root + name);
191             URLConnection conn = u.openConnection();
192             tryToSetTimeout(conn);
193             return conn.getLastModified();
194         }
195         catch (IOException ioe)
196         {
197             // the file is not reachable at its previous address
198             String msg = "URLResourceLoader: '"+name+"' is no longer reachable at '"+root+"'";
199             log.error(msg, ioe);
200             throw new ResourceNotFoundException(msg, ioe);
201         }
202     }
203 
204     /**
205      * Returns the current, custom timeout setting. If negative, there is no custom timeout.
206      * @since 1.6
207      */
208     public int getTimeout()
209     {
210         return timeout;
211     }
212 
213     private void tryToSetTimeout(URLConnection conn)
214     {
215         if (timeout > 0)
216         {
217             Object[] arg = new Object[] { new Integer(timeout) };
218             try
219             {
220                 timeoutMethods[0].invoke(conn, arg);
221                 timeoutMethods[1].invoke(conn, arg);
222             }
223             catch (Exception e)
224             {
225                 String msg = "Unexpected exception while setting connection timeout for "+conn;
226                 log.error(msg, e);
227                 throw new VelocityException(msg, e);
228             }
229         }
230     }
231 
232 }