View Javadoc

1   /* Copyright (2007) Schibsted Søk AS
2    * This file is part of SESAT.
3    *
4    *   SESAT is free software: you can redistribute it and/or modify
5    *   it under the terms of the GNU Affero General Public License as published by
6    *   the Free Software Foundation, either version 3 of the License, or
7    *   (at your option) any later version.
8    *
9    *   SESAT is distributed in the hope that it will be useful,
10   *   but WITHOUT ANY WARRANTY; without even the implied warranty of
11   *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12   *   GNU Affero General Public License for more details.
13   *
14   *   You should have received a copy of the GNU Affero General Public License
15   *   along with SESAT.  If not, see <http://www.gnu.org/licenses/>.
16   */
17  package no.sesat.search.view.velocity;
18  
19  import java.io.IOException;
20  import java.net.URL;
21  import java.net.MalformedURLException;
22  import java.io.InputStream;
23  import java.util.regex.Matcher;
24  import no.sesat.search.http.HTTPClient;
25  import no.sesat.search.site.config.UrlResourceLoader;
26  import no.sesat.search.site.Site;
27  import org.apache.log4j.Logger;
28  import org.apache.velocity.runtime.resource.Resource;
29  import org.apache.velocity.runtime.resource.loader.ResourceLoader;
30  import org.apache.velocity.exception.ResourceNotFoundException;
31  import org.apache.commons.collections.ExtendedProperties;
32  
33  /** XXX This source file needs to be published to the internet as it is open-source code.
34   *
35   *
36   * This is a simple URL-based loader.
37   * ORIGINAL FROM http://svn.apache.org/repos/asf/jakarta/velocity/engine/trunk/whiteboard/geir/URLResourceLoader.java
38   *
39   * original version Id: URLResourceLoader.java,v 1.3 2004/03/19 17:13:40 dlr Exp
40   *
41   *
42   *
43   * MODIFIED TO SUIT SCHIBSTEDSØK's NEEDS.
44   * There was a choice here to implement all the URL handling stuff from scratch or to plug into the existing
45   * functionality found in no.schibstedsøk.front.search.configuration.loader
46   * Since this class is hidden between the velocity API it made more sense to go from scratch to best
47   * meet velocity's requirements...
48   *
49   *
50   * @version $Id: URLResourceLoader.java,v 1.3 2004/03/19 17:13:40 dlr Exp $
51   */
52  public class URLResourceLoader extends ResourceLoader {
53  
54      public interface Context{
55          boolean doesUrlExist(final URL url);
56          URL getURL(final String resource, final Site site);
57      }
58  
59      // Constants -----------------------------------------------------
60  
61      private static final Logger LOG = Logger.getLogger(URLResourceLoader.class);
62  
63      private static final String ERR_RESOURCE_NOT_FOUND = "Cannot find resource ";
64      private static final String DEBUG_LOOKING_FOR = "Looking for ";
65      private static final String DEBUG_EXISTS = "Positive HEAD on ";
66      private static final String DEBUG_FULL_URL_IS = "Real URL is ";
67      private static final String DEBUG_HOST_HEADER_IS = "URL's host-header is ";
68      private static final String DEBUG_DOESNT_EXIST = "Using fallback URL";
69  
70  
71      // Attributes ----------------------------------------------------
72  
73      private Site site;
74  
75  
76      // Static --------------------------------------------------------
77  
78      private static Context context = new DefaultContext();
79  
80      // Allows the tests to switch the Velocity ResourceLoader over to a file based one.
81      static void setContext(final Context context){
82          URLResourceLoader.context = context;
83      }
84  
85      // Constructors --------------------------------------------------
86  
87  
88      // Public --------------------------------------------------------
89  
90      /** {@inheritDoc}
91       */
92      public void init(final ExtendedProperties configuration) {
93          // the engine's properties actually come from the RuntimeServices *not* the ExtendedProperties
94          site = (Site)rsvc.getProperty(Site.NAME_KEY);
95      }
96  
97      /**
98       * Get an InputStream so that the Runtime can build a
99       * template with it.
100      *
101      * @param url  url of template to fetch bytestream of
102      * @return InputStream containing the template
103      * @throws ResourceNotFoundException if template not found
104      *         in the file template path.
105      */
106      public /*synchronized*/ InputStream getResourceStream(final String url) throws ResourceNotFoundException{
107 
108         LOG.trace("start getResourceStream( " + url + " )");
109         try{
110             synchronized( url.intern() ){
111                 return getStream(findUrl(url, site));
112             }
113 
114         }catch( IOException e ){
115             LOG.debug( ERR_RESOURCE_NOT_FOUND + url);
116             throw new ResourceNotFoundException( ERR_RESOURCE_NOT_FOUND + url );
117         }
118     }
119 
120     /** {@inheritDoc}
121      */
122     public boolean isSourceModified(Resource resource){
123 
124         final boolean result =  getLastModified(resource) > resource.getLastModified();
125         LOG.debug("isSourceModified( "+resource.getName()+" ): "+result);
126         return result;
127     }
128 
129     /** {@inheritDoc}
130      */
131     public long getLastModified(Resource resource){
132         try{
133             final URL url = findUrl(resource.getName(), site);
134             final HTTPClient client = HTTPClient.instance(url, "localhost");
135 
136             return client.getLastModified("");
137         }catch( ResourceNotFoundException e ){
138             LOG.error( ERR_RESOURCE_NOT_FOUND + resource.getName() );
139         }catch( IOException e ){
140             LOG.error( ERR_RESOURCE_NOT_FOUND + resource.getName() );
141         }
142         return 0;
143     }
144 
145 
146     // Package protected ---------------------------------------------
147 
148     // Protected -----------------------------------------------------
149 
150     // Private -------------------------------------------------------
151 
152     private static URL findUrl(final String url, final Site currentSite) throws ResourceNotFoundException{
153 
154         try{
155             LOG.trace(DEBUG_LOOKING_FOR + url );
156             return findUrlImpl(url, currentSite);
157 
158         }catch( IOException e ){
159             LOG.error( ERR_RESOURCE_NOT_FOUND + url, e );
160             throw new ResourceNotFoundException( ERR_RESOURCE_NOT_FOUND + url );
161         }
162     }
163 
164     private static URL findUrlImpl(final String url, final Site currentSite)
165             throws IOException, ResourceNotFoundException {
166 
167         final URL u = context.getURL(url, currentSite);
168 
169         if (context.doesUrlExist(u)) {
170             return u;
171         } else {
172             final Site parent = currentSite.getParent();
173 
174             if (null == parent) {
175                 throw new ResourceNotFoundException( ERR_RESOURCE_NOT_FOUND + url );
176             }
177 
178             if (LOG.isTraceEnabled()) {
179                 LOG.trace(DEBUG_DOESNT_EXIST + parent.getName());
180             }
181 
182             // Recursively look for the resource in ancestor sites.
183             return findUrlImpl(getFallbackURL(url, currentSite, parent), parent);
184         }
185     }
186 
187     private static String getFallbackURL(final String url, final Site currSite, final Site ancestorSite) {
188 
189         final String oldUrl = currSite.getName() + currSite.getConfigContext();
190         final String newUrl = ancestorSite.getName() + ancestorSite.getConfigContext();
191 
192         return url.replaceFirst(Matcher.quoteReplacement(oldUrl), newUrl);
193     }
194 
195 
196     private static InputStream getStream(final URL url) throws IOException{
197 
198         final HTTPClient client = HTTPClient.instance(url, "localhost");
199         try{
200             return client.getBufferedStream("");
201         }catch(IOException ioe){
202             throw client.interceptIOException(ioe);
203         }
204     }
205 
206 
207     // Inner classes -------------------------------------------------
208 
209     private static final class DefaultContext implements Context{
210         public boolean doesUrlExist(final URL url) {
211             return UrlResourceLoader.doesUrlExist(url);
212         }
213 
214         public URL getURL(final String resource, final Site site) {
215             try {
216                 return new URL(resource);
217             } catch (MalformedURLException e) {
218                 throw new ResourceNotFoundException(e);
219             }
220         }
221     }
222 }
223