001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: package org.apache.jetspeed.locator;
018:
019: import java.io.File;
020: import java.io.FileNotFoundException;
021: import java.util.Collections;
022: import java.util.HashMap;
023: import java.util.Iterator;
024: import java.util.List;
025: import java.util.Map;
026: import java.util.StringTokenizer;
027:
028: import org.apache.commons.logging.Log;
029: import org.apache.commons.logging.LogFactory;
030:
031: /**
032: * Jetspeed's default implementation of a template locator.
033: *
034: * @author <a href="mailto:taylor@apache.org">David Sean Taylor</a>
035: * @version $Id: JetspeedTemplateLocator.java 587334 2007-10-23 00:30:49Z taylor $
036: */
037: public class JetspeedTemplateLocator implements TemplateLocator {
038: private final static Log log = LogFactory
039: .getLog(JetspeedTemplateLocator.class);
040:
041: private static final String PATH_SEPARATOR = "/";
042:
043: /** the template root directories, all application root relative */
044: private List roots;
045:
046: /** Root of the application running this locator */
047: private String appRoot;
048:
049: /** the Template class is factory created */
050: private Class templateClass = JetspeedTemplateDescriptor.class;
051:
052: /** the TemplateLocator class is factory created */
053: private Class locatorClass = JetspeedLocatorDescriptor.class;
054:
055: /** the default locator type */
056: private String defaultLocatorType = "layout";
057:
058: /** template name cache used to speed up searches for templates */
059: private Map templateMap = null;
060:
061: /** use the name cache when looking up a template */
062: private boolean useNameCache = true;
063:
064: private JetspeedTemplateLocator() {
065: // need to know roots
066: }
067:
068: /**
069: * Minimal assembly with a list of resource directory roots.
070: *
071: * @param roots A list of resource root directories where templates are located.
072: * @param appRoot Root from where this application runs
073: */
074: public JetspeedTemplateLocator(List roots, String appRoot)
075: throws FileNotFoundException {
076: this .appRoot = appRoot;
077: log.info("Locator application root "
078: + new File(appRoot).getAbsolutePath());
079: this .roots = roots;
080: Iterator itr = roots.iterator();
081: while (itr.hasNext()) {
082: String path = (String) itr.next();
083: File checkFile = new File(path);
084: if (!checkFile.exists()) {
085: throw new FileNotFoundException(
086: "Locator resource root "
087: + checkFile.getAbsolutePath()
088: + " does not exist.");
089: }
090: }
091: }
092:
093: /**
094: * Construct with a root list and a default locator type.
095: *
096: * @param roots A list of resource root directories where templates are located.
097: * @param defaultLocatorType Under root directories, subdirectories represent locator types.
098: * A locator type represents a classification of templates.
099: * Any value is allowed. Use locator types to group templates together.
100: */
101: public JetspeedTemplateLocator(List roots,
102: String defaultLocatorType, String appRoot)
103: throws FileNotFoundException {
104: this (roots, appRoot);
105: this .defaultLocatorType = defaultLocatorType;
106: }
107:
108: /**
109: * Assemble with list resource directory roots and OM classes and a defaultLocatorType.
110: *
111: * @param roots A list of resource root directories where templates are located.
112: * @param omClasses Template replacable object model implementations for Template and TemplateLocator.
113: * Required order, with second optional: [ <code>Template</code>, <code>TemplateLocator</code> implementations.
114: * @param defaultLocatorType Under root directories, subdirectories represent locator types.
115: * A locator type represents a classification of templates.
116: * Any value is allowed. Use locator types to group templates together.
117: */
118: public JetspeedTemplateLocator(List roots, List omClasses,
119: String defaultLocatorType, String appRoot)
120: throws FileNotFoundException {
121: this (roots, defaultLocatorType, appRoot);
122:
123: if (omClasses.size() > 0) {
124: this .templateClass = (Class) omClasses.get(0);
125: if (omClasses.size() > 1) {
126: this .locatorClass = (Class) omClasses.get(1);
127: }
128: }
129: }
130:
131: public TemplateDescriptor locateTemplate(LocatorDescriptor locator) {
132: for (int ix = 0; ix < roots.size(); ix++) {
133: TemplateDescriptor template = locateTemplate(locator,
134: (String) roots.get(ix), this .useNameCache);
135: if (null == template) {
136: // Try to locate it directly on file system, perhaps it was recently added
137: template = locateTemplate(locator, (String) roots
138: .get(ix), false);
139: if (null != template) {
140: // add it to the map
141: templateMap.put(template.getAbsolutePath(), null);
142: }
143: }
144: if (template != null) {
145: return template;
146: }
147: }
148: return null;
149: }
150:
151: /**
152: * General template location algorithm. Starts with the most specific resource,
153: * including mediatype + nls specification, and fallsback to least specific.
154: *
155: * @param locator The template locator
156: * @param root The root directory to search
157: *
158: * @return TemplateDescriptor the exact path to the template, or null if not found.
159: */
160: private TemplateDescriptor locateTemplate(
161: LocatorDescriptor locator, String root, boolean useCache) {
162: String templateName = locator.getName();
163: String path = locator.toPath();
164:
165: String realPath = null;
166: String workingPath = null;
167:
168: int lastSeperator;
169: while (path != null
170: && (lastSeperator = path.lastIndexOf(PATH_SEPARATOR)) > 0) {
171: path = path.substring(0, lastSeperator);
172:
173: workingPath = path + PATH_SEPARATOR + templateName;
174: realPath = root + workingPath;
175:
176: // the current template exists, return the corresponding path
177: if (templateExists(realPath, useCache)) {
178: if (log.isDebugEnabled()) {
179: log.debug("TemplateLocator: template exists: "
180: + realPath + " returning " + workingPath);
181: }
182: int appRootLength = appRoot.length();
183: // remove the application root path from the reall path to
184: // give us a app relative path
185: String appRelativePath = realPath.substring(
186: appRootLength, realPath.length());
187: // return createTemplateFromPath(path, templateName, realPath, "/WEB-INF/templates" + workingPath);
188: return createTemplateFromPath(path, templateName,
189: realPath, appRelativePath);
190: }
191: }
192: return null;
193: }
194:
195: /**
196: * Checks for the existence of a template resource given a key.
197: * The key are absolute paths to the templates, and are cached
198: * in a template cache for performance.
199: *
200: * @param key The absolute path to the template resource.
201: *
202: * @return True when the template is found, otherwise false.
203: */
204: public boolean templateExists(String templateKey, boolean useCache) {
205: if (null == templateKey) {
206: return false;
207: }
208: if (useCache == true) {
209: return templateMap.containsKey(templateKey);
210: }
211: return (new File(templateKey).exists());
212: }
213:
214: public boolean templateExists(String templateKey) {
215: return templateExists(templateKey, this .useNameCache);
216: }
217:
218: public LocatorDescriptor createFromString(String path)
219: throws TemplateLocatorException {
220: LocatorDescriptor locator = createLocatorDescriptor(this .defaultLocatorType);
221: StringTokenizer tok = new StringTokenizer(path, "/");
222: while (tok.hasMoreTokens()) {
223: String name = tok.nextToken();
224: if (name.equals(LocatorDescriptor.PARAM_TYPE)
225: && tok.hasMoreTokens()) {
226: locator.setType(tok.nextToken());
227: } else if (name.equals(LocatorDescriptor.PARAM_MEDIA_TYPE)
228: && tok.hasMoreTokens()) {
229: locator.setMediaType(tok.nextToken());
230: } else if (name.equals(LocatorDescriptor.PARAM_LANGUAGE)
231: && tok.hasMoreTokens()) {
232: locator.setLanguage(tok.nextToken());
233: } else if (name.equals(LocatorDescriptor.PARAM_COUNTRY)
234: && tok.hasMoreTokens()) {
235: locator.setCountry(tok.nextToken());
236: }
237:
238: else if (name.equals(LocatorDescriptor.PARAM_NAME)
239: && tok.hasMoreTokens()) {
240: locator.setName(tok.nextToken());
241: }
242: }
243: return locator;
244: }
245:
246: /**
247: * Given a path, name and realPath creates a new template object
248: *
249: * @param path the relative path to the template
250: * @param name the template name
251: * @param realPath the real path on the file system
252: * @return newly created TemplateDescriptor
253: */
254: private TemplateDescriptor createTemplateFromPath(String path,
255: String name, String realPath, String relativePath) {
256: TemplateDescriptor template = this .createTemplate();
257: template.setAbsolutePath(realPath);
258: if (relativePath.indexOf("/") != 0) {
259: relativePath = "/" + relativePath;
260: }
261: template.setAppRelativePath(relativePath);
262: template.setName(name);
263: StringTokenizer tok = new StringTokenizer(path, "/");
264: int count = 0;
265: while (tok.hasMoreTokens()) {
266: String token = tok.nextToken();
267: switch (count) {
268: case 0:
269: template.setType(token);
270: break;
271: case 1:
272: template.setMediaType(token);
273: break;
274: case 2:
275: template.setLanguage(token);
276: break;
277: case 3:
278: template.setCountry(token);
279: break;
280: }
281: count++;
282: }
283: return template;
284: }
285:
286: public LocatorDescriptor createLocatorDescriptor(String type)
287: throws TemplateLocatorException {
288: LocatorDescriptor locator = null;
289:
290: try {
291: locator = (LocatorDescriptor) locatorClass.newInstance();
292: locator.setType(type);
293: } catch (Exception e) {
294: throw new TemplateLocatorException(
295: "Failed instantiate a Template Locator implementation object: ",
296: e);
297: }
298: return locator;
299: }
300:
301: private TemplateDescriptor createTemplate() {
302: TemplateDescriptor template = null;
303:
304: try {
305: template = (TemplateDescriptor) templateClass.newInstance();
306: } catch (Exception e) {
307: log.error("Failed to create template", e);
308: template = new JetspeedTemplateDescriptor();
309: }
310: return template;
311: }
312:
313: public void start() {
314: this .templateMap = Collections.synchronizedMap(new HashMap());
315:
316: for (int ix = 0; ix < roots.size(); ix++) {
317: String templateRoot = (String) roots.get(ix);
318:
319: if (!templateRoot.endsWith(PATH_SEPARATOR)) {
320: templateRoot = templateRoot + PATH_SEPARATOR;
321: }
322:
323: loadNameCache(templateRoot, "");
324: }
325: }
326:
327: public void stop() {
328: }
329:
330: public Iterator query(LocatorDescriptor locator) {
331: return null; // TODO: implement this
332: }
333:
334: /**
335: * Loads the template name cache map to accelerate template searches.
336: *
337: * @param path The template
338: * @param name just the name of the resource
339: */
340: private void loadNameCache(String path, String name) {
341: File file = new File(path);
342: if (file.isFile()) {
343: // add it to the map
344: templateMap.put(path, null);
345: } else {
346: if (file.isDirectory()) {
347: if (!path.endsWith(File.separator)) {
348: path += File.separator;
349: }
350:
351: String list[] = file.list();
352:
353: // Process all files recursivly
354: for (int ix = 0; list != null && ix < list.length; ix++) {
355: loadNameCache(path + list[ix], list[ix]);
356: }
357: }
358: }
359: }
360: }
|