1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 package no.sesat.search.view.velocity;
24
25 import java.util.HashMap;
26 import java.util.Map;
27 import java.util.Properties;
28 import java.util.concurrent.locks.ReentrantReadWriteLock;
29
30 import javax.xml.parsers.DocumentBuilder;
31
32 import no.geodata.maputil.CoordHelper;
33 import no.schibstedsok.commons.ioc.BaseContext;
34 import no.schibstedsok.commons.ioc.ContextWrapper;
35 import no.sesat.search.InfrastructureException;
36 import no.sesat.search.result.Boomerang;
37 import no.sesat.search.result.Decoder;
38 import no.sesat.search.site.Site;
39 import no.sesat.search.site.SiteContext;
40 import no.sesat.search.site.SiteKeyedFactory;
41 import no.sesat.search.site.config.BytecodeLoader;
42 import no.sesat.search.site.config.DocumentLoader;
43 import no.sesat.search.site.config.PropertiesLoader;
44 import no.sesat.search.site.config.ResourceContext;
45 import no.sesat.search.site.config.SiteClassLoaderFactory;
46 import no.sesat.search.site.config.SiteConfiguration;
47 import no.sesat.search.site.config.Spi;
48 import no.sesat.search.site.config.UrlResourceLoader;
49
50 import no.sesat.search.view.navigation.NavigationHelper;
51 import org.apache.log4j.Level;
52 import org.apache.log4j.Logger;
53 import org.apache.velocity.Template;
54 import org.apache.velocity.VelocityContext;
55 import org.apache.velocity.app.Velocity;
56 import org.apache.velocity.app.VelocityEngine;
57 import org.apache.velocity.exception.ParseErrorException;
58 import org.apache.velocity.exception.ResourceNotFoundException;
59 import org.apache.velocity.runtime.RuntimeConstants;
60 import org.apache.velocity.tools.generic.DateTool;
61 import org.apache.velocity.tools.generic.MathTool;
62 import org.apache.velocity.tools.generic.NumberTool;
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79 public final class VelocityEngineFactory implements SiteKeyedFactory{
80
81
82
83
84 public interface Context extends SiteContext, ResourceContext {
85 }
86
87
88
89 private static final Logger LOG = Logger.getLogger(VelocityEngineFactory.class);
90
91 private static final String INFO_TEMPLATE_NOT_FOUND = "Could not find template ";
92 private static final String ERR_IN_TEMPLATE = "Error parsing template ";
93 private static final String ERR_GETTING_TEMPLATE = "Error getting template ";
94
95 private static final String VELOCITY_LOGGER = "org.apache.velocity";
96
97 private static final Map<Site,VelocityEngineFactory> INSTANCES = new HashMap<Site,VelocityEngineFactory>();
98 private static final ReentrantReadWriteLock INSTANCES_LOCK = new ReentrantReadWriteLock();
99
100 private static final String LOGSYSTEM_CLASS = "org.apache.velocity.runtime.log.Log4JLogChute";
101 private static final String LOG_NAME = "runtime.log.logsystem.log4j.logger";
102
103 private static final boolean VELOCITY_DEBUG = Boolean.getBoolean("VELOCITY_DEBUG");
104
105
106
107 private final VelocityEngine engine;
108
109
110
111
112
113
114
115
116
117
118
119
120
121 public static Template getTemplate(
122 final VelocityEngine engine,
123 final Site site,
124 final String templateName) throws ResourceNotFoundException{
125
126 final String templateUrl = site.getTemplateDir() + '/' + templateName + ".vm";
127
128 try {
129 return engine.getTemplate(templateUrl);
130
131
132 } catch (ParseErrorException ex) {
133 LOG.error(ERR_IN_TEMPLATE + templateUrl, ex);
134 throw new InfrastructureException(ex);
135
136 } catch (ResourceNotFoundException ex) {
137
138 throw ex;
139
140 } catch (Exception ex) {
141 LOG.error(ERR_GETTING_TEMPLATE + templateUrl, ex);
142 throw new InfrastructureException(ex);
143 }
144 }
145
146 public static VelocityContext newContextInstance(final VelocityEngine engine){
147
148 final VelocityContext context = new VelocityContext();
149
150
151 context.put("coordHelper", new CoordHelper());
152
153 context.put("decoder", new Decoder());
154
155 context.put("math", new MathTool());
156
157 context.put("number", new NumberTool());
158
159 context.put("date", new DateTool());
160
161 context.put("navigationHelper", new NavigationHelper());
162
163 context.put("boomerang", new Boomerang());
164
165 return context;
166 }
167
168
169
170
171
172
173
174 public static VelocityEngineFactory instanceOf(final Context cxt) {
175
176 final Site site = cxt.getSite();
177
178 VelocityEngineFactory instance = null;
179
180 try {
181 INSTANCES_LOCK.readLock().lock();
182 instance = INSTANCES.get(site);
183 } finally {
184 INSTANCES_LOCK.readLock().unlock();
185 }
186
187 if(!VELOCITY_DEBUG) {
188 if (instance == null) {
189 instance = new VelocityEngineFactory(cxt);
190 }
191 } else {
192 instance = new VelocityEngineFactory(cxt);
193 }
194
195 return instance;
196 }
197
198
199
200
201
202
203
204 public static VelocityEngineFactory valueOf(final Site site) {
205
206
207 final VelocityEngineFactory instance = VelocityEngineFactory.instanceOf(new VelocityEngineFactory.Context() {
208 public Site getSite() {
209 return site;
210 }
211 public PropertiesLoader newPropertiesLoader(
212 final SiteContext siteCxt,
213 final String resource,
214 final Properties properties) {
215
216 return UrlResourceLoader.newPropertiesLoader(siteCxt, resource, properties);
217 }
218 public DocumentLoader newDocumentLoader(
219 final SiteContext siteCxt,
220 final String resource,
221 final DocumentBuilder builder) {
222
223 return UrlResourceLoader.newDocumentLoader(siteCxt, resource, builder);
224 }
225 public BytecodeLoader newBytecodeLoader(final SiteContext site, final String name, final String jar) {
226 return UrlResourceLoader.newBytecodeLoader(site, name, jar);
227 }
228
229 });
230 return instance;
231 }
232
233
234
235
236
237 private VelocityEngineFactory(final Context cxt) {
238
239
240 try{
241 INSTANCES_LOCK.writeLock().lock();
242
243 final Site site = cxt.getSite();
244
245 final SiteConfiguration siteConf = SiteConfiguration.instanceOf(ContextWrapper.wrap(
246 SiteConfiguration.Context.class,
247 cxt));
248
249 final StringBuilder directives = new StringBuilder();
250
251 for(int i=0; i < 10; ++i){
252 final String d = siteConf.getProperty("velocity.directives." + i);
253
254 if(null != d && d.length() > 0){
255
256 directives.append(d + ',');
257 }
258 }
259
260
261 directives.setLength(directives.length()-1);
262
263 final Logger logger = Logger.getLogger(VELOCITY_LOGGER);
264
265 engine = new VelocityEngine(){
266
267
268
269
270 @Override
271 public Template getTemplate(final String name)
272 throws ResourceNotFoundException, ParseErrorException, Exception {
273
274 final Level level = logger.getLevel();
275 logger.setLevel(Level.FATAL);
276
277 final Template retValue = super.getTemplate(name);
278
279 logger.setLevel(level);
280 return retValue;
281 }
282
283 };
284
285
286 final ClassLoader origLoader = Thread.currentThread().getContextClassLoader();
287 try{
288
289 engine.setProperty(RuntimeConstants.RUNTIME_LOG_LOGSYSTEM_CLASS, LOGSYSTEM_CLASS);
290 engine.setProperty(LOG_NAME, logger.getName());
291 engine.setProperty(Velocity.RESOURCE_LOADER, "url");
292 engine.setProperty("url.resource.loader.class", URLVelocityTemplateLoader.class.getName());
293
294 if(VELOCITY_DEBUG) {
295 engine.setProperty("url.resource.loader.cache", "false");
296 engine.setProperty("velocimacro.library.autoreload", "true");
297 } else {
298 engine.setProperty("url.resource.loader.cache", "true");
299 engine.setProperty("url.resource.loader.modificationCheckInterval", "60");
300 engine.setProperty(Velocity.RESOURCE_MANAGER_CLASS, QuickResourceManagerImpl.class.getName());
301 engine.setProperty(Velocity.RESOURCE_MANAGER_CACHE_CLASS, QuickResourceCacheImpl.class.getName());
302 engine.setProperty(Velocity.RESOURCE_MANAGER_DEFAULTCACHE_SIZE, "0");
303
304
305 engine.setProperty(QuickResourceCacheImpl.INITIAL_SIZE_PROPERTY, 1000);
306 }
307
308 engine.setProperty(Site.NAME_KEY, site);
309
310 engine.setProperty("input.encoding", "UTF-8");
311 engine.setProperty("output.encoding", "UTF-8");
312 engine.setProperty("userdirective", directives.toString());
313 engine.setProperty(
314 "velocimacro.library",
315 site.getTemplateDir() + "/VM_sesat_library.vm,"
316 + site.getTemplateDir() + "/VM_global_library.vm,"
317 + site.getTemplateDir() + "/VM_site_library.vm,"
318 + site.getTemplateDir() + "/VM_map_library.vm");
319
320 final SiteClassLoaderFactory.Context classContext = ContextWrapper.wrap(
321 SiteClassLoaderFactory.Context.class,
322 new BaseContext() {
323 public Site getSite(){
324 return site;
325 }
326 public Spi getSpi() {
327 return Spi.VELOCITY_DIRECTIVES;
328 }
329 },
330 cxt);
331
332 final ClassLoader ctrlClassLoader = SiteClassLoaderFactory.instanceOf(classContext).getClassLoader();
333 Thread.currentThread().setContextClassLoader(ctrlClassLoader);
334 engine.init();
335
336 }catch (Exception e) {
337 throw new InfrastructureException(e);
338 }finally{
339 Thread.currentThread().setContextClassLoader(origLoader);
340 }
341
342 INSTANCES.put(site, this);
343 }finally{
344 INSTANCES_LOCK.writeLock().unlock();
345 }
346 }
347
348
349
350 public VelocityEngine getEngine() {
351 return engine;
352 }
353
354 public boolean remove(final Site site) {
355
356 try{
357 INSTANCES_LOCK.writeLock().lock();
358 return null != INSTANCES.remove(site);
359 }finally{
360 INSTANCES_LOCK.writeLock().unlock();
361 }
362 }
363
364
365
366
367
368
369
370
371
372 }