001: /*
002: * Copyright 2003 The Apache Software Foundation.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package velosurf.web;
018:
019: import java.io.InputStream;
020: import java.util.Map;
021: import java.util.HashMap;
022: import java.net.URL;
023:
024: import javax.servlet.ServletContext;
025: import javax.servlet.http.HttpSession;
026:
027: import org.apache.velocity.tools.view.context.ViewContext;
028:
029: import velosurf.context.DBReference;
030: import velosurf.sql.Database;
031: import velosurf.util.Logger;
032: import velosurf.util.ServletLogWriter;
033: import velosurf.util.ToolFinder;
034: import velosurf.util.XIncludeResolver;
035: import velosurf.util.UserContext;
036: import velosurf.web.l10n.Localizer;
037:
038: /** <p>This class is a tool meant to be referenced in toolbox.xml</p>
039: * <p>It can be used in any scope you want (application/session/request), depending on the behaviour you need for the refinement and ordering mechanisms (which will follow the same scope).
040: * The initialization itself is very fast once all static initialization has been done, so there is no performance bottleneck when using request scope.</p>
041: *<p>Since version 1.0rc1, you can have several instances of VelosurfTool, each with a distinct configuration file.
042: * This can be useful to have one instance per schema, or one instance per database if dealing with several databases.</p>
043: * <p>For this to work, you have to use the 1.3 version of velocity-tools (not yet released at the time I'm writing this,
044: * so you need to grab it from the Velocity subversion repository) and give each instance the pathname of its configuration file
045: * via the 'config' parameter in the toolbox.xml file, like this :</p>
046: * <pre>
047: *
048: * <!-- first instance -->
049: * <tool>
050: * <key>db1</key>
051: * <scope>request</scope>
052: * <class>velosurf.tools.VelosurfTool</scope>
053: * <parameter name="config" value="WEB-INF/db1.xml" />
054: * </tool>
055: *
056: * <!-- second instance -->
057: * <tool>
058: * <key>db2</key>
059: * <scope>request</scope>
060: * <class>velosurf.tools.VelosurfTool</scope>
061: * <parameter name="config" value="WEB-INF/db2.xml" />
062: * </tool>
063: *
064: *</pre>
065: *
066: * @author <a href=mailto:claude.brisson@gmail.com>Claude Brisson</a>
067: *
068: */
069: public class VelosurfTool extends DBReference {
070: /** build a new VelosurfTool.
071: */
072: public VelosurfTool() {
073: }
074:
075: /**
076: * Initialize this instance using the given ViewContext.
077: *
078: * @param viewContext initialization data
079: */
080: public void init(Object viewContext) throws Exception {
081: // get servlet context
082: ViewContext viewctx = null;
083: ServletContext ctx = null;
084: if (viewContext instanceof ServletContext) {
085: ctx = (ServletContext) viewContext;
086: } else if (viewContext instanceof ViewContext) {
087: viewctx = (ViewContext) viewContext;
088: ctx = viewctx.getServletContext();
089: } else {
090: Logger
091: .error("Error: Initialization: no valid initialization data found!");
092: System.err
093: .println("Error: Initialization: no valid initialization data found!");
094: }
095:
096: if (!Logger.isInitialized() && ctx != null) {
097: Logger.setWriter(new ServletLogWriter(ctx));
098: }
099:
100: // get config file
101: if (configFile == null) { // if not already given by configure()
102: configFile = findConfigFile(ctx);
103: }
104: UserContext userContext = null;
105:
106: /* user context */
107: if (viewctx != null) {
108: HttpSession session = viewctx.getRequest()
109: .getSession(false);
110: if (session != null) {
111: synchronized (session) {
112: userContext = (UserContext) session
113: .getAttribute(UserContext.USER_CONTEXT_KEY);
114: if (userContext == null) {
115: /* check in the database if already connected */
116: Database db = dbMap.get(configFile);
117: if (db != null) {
118: userContext = db.getUserContext();
119: } else {
120: userContext = new UserContext();
121: }
122: session.setAttribute(
123: UserContext.USER_CONTEXT_KEY,
124: userContext);
125: if (fetchLocalizer) {
126: Localizer localizer = ToolFinder
127: .findSessionTool(session,
128: Localizer.class);
129: if (localizer != null) {
130: userContext.setLocalizer(localizer);
131: } else {
132: // don't search for it again
133: fetchLocalizer = false;
134: userContext.setLocale(viewctx
135: .getRequest().getLocale());
136: }
137: } else {
138: userContext.setLocale(viewctx.getRequest()
139: .getLocale());
140: }
141: }
142: }
143: session
144: .setAttribute(VelosurfTool.class.getName(),
145: this );
146:
147: }
148: }
149:
150: /* initialize with a new or existing connection */
151: Database db = getConnection(configFile, ctx);
152: /* FIXME: null check? anyway this is a RuntimeException... */
153: db.setUserContext(userContext);
154: super .init(db);
155: }
156:
157: /**
158: * Tries to find the configuration file.
159: * @param ctx servelt context
160: * @return found config file or null
161: */
162: static String findConfigFile(ServletContext ctx) {
163: /* tries with a servlet context parameter */
164: String configFile = ctx
165: .getInitParameter(WEBAPP_CONFIG_FILE_KEY);
166: if (configFile != null) {
167: // Logger.warn("Use of the "+WEBAPP_CONFIG_FILE_KEY+" servlet context parameter is deprecated.");
168: // Logger.warn("Consider moving this parameter to toolbox.xml.");
169: return configFile;
170: }
171: configFile = ctx.getInitParameter(WEBAPP_CONFIG_FILE_KEY2);
172: if (configFile != null) {
173: // Logger.warn("Use of the "+WEBAPP_CONFIG_FILE_KEY+" servlet context parameter is deprecated.");
174: // Logger.warn("Consider moving this parameter to toolbox.xml.");
175: return configFile;
176: }
177:
178: // else try default
179: InputStream check = ctx
180: .getResourceAsStream(DEFAULT_CONFIG_FILE);
181: if (check == null) {
182: check = ctx.getResourceAsStream(OLD_DEFAULT_CONFIG_FILE);
183: if (check == null) {
184: throw new RuntimeException(
185: "Velosurf config file not found! Please specify it using the servlet context or the toolbox parameters.");
186: } else {
187: configFile = OLD_DEFAULT_CONFIG_FILE;
188: }
189: } else {
190: configFile = DEFAULT_CONFIG_FILE;
191: }
192: return configFile;
193: }
194:
195: /** initialization from a servlet context
196: *
197: */
198: protected void initialize(ServletContext ctx) {
199:
200: }
201:
202: /** key used in the deployment descriptor (web.xml) to set the name of the config file.
203: */
204: private static final String WEBAPP_CONFIG_FILE_KEY = "velosurf.config";
205:
206: /** alternate webapp key
207: */
208: private static final String WEBAPP_CONFIG_FILE_KEY2 = "velosurf.configuration";
209:
210: /** key used in the toolbox (toolbox.xml) to set the name of the config file.
211: */
212: private static final String TOOLBOX_CONFIG_FILE_KEY = "config";
213:
214: /** alternate key for the toolbox
215: */
216: private static final String TOOLBOX_CONFIG_FILE_KEY2 = "configuration";
217:
218: /** default database config file.
219: */
220: private static final String DEFAULT_CONFIG_FILE = "/WEB-INF/model.xml";
221:
222: /** old default database config file.
223: */
224: private static final String OLD_DEFAULT_CONFIG_FILE = "/WEB-INF/velosurf.xml";
225:
226: /** path to the config file.
227: */
228: private String configFile = null;
229:
230: /** database connections.
231: */
232: private static Map<String, Database> dbMap = new HashMap<String, Database>();
233:
234: /** configure.
235: *
236: * @param map parameters
237: */
238: public void configure(Map<String, String> map) {
239: configFile = map.get(TOOLBOX_CONFIG_FILE_KEY);
240: if (configFile == null) {
241: configFile = map.get(TOOLBOX_CONFIG_FILE_KEY2);
242: }
243: }
244:
245: /** return the existing Database for the specified config file, or null
246: * if it isn't already open.
247: * @param configFile
248: * @return a Database
249: */
250: protected static Database getConnection(String configFile) {
251: if (!configFile.startsWith("/"))
252: configFile = "/" + configFile;
253: return dbMap.get(configFile);
254: }
255:
256: /** return a db reference on the existing Database for the specified config file, or null
257: * if it isn't already open.
258: * @param configFile
259: * @return a DBReference
260: */
261: public static DBReference getInstance(String configFile) {
262: if (!configFile.startsWith("/"))
263: configFile = "/" + configFile;
264: Database db = dbMap.get(configFile);
265: return db == null ? null : new DBReference(db);
266: }
267:
268: /** return the existing Database for the specified config file and servlet context,
269: * or null if an error occurs.
270: * @param configFile
271: * @return a Database
272: */
273: protected static Database getConnection(String configFile,
274: ServletContext servletContext) {
275: if (!configFile.startsWith("/")) {
276: configFile = "/" + configFile;
277: }
278: Database db = (Database) dbMap.get(configFile);
279: if (db == null) {
280: try {
281: Logger.info("Using config file '" + configFile + "'");
282: InputStream is = servletContext
283: .getResourceAsStream(configFile);
284: if (is == null) {
285: Logger.error("Could not read config file "
286: + configFile);
287: return null;
288: }
289: /* calculate the base directory, for XInclude */
290: /* Velosurf won't like '/' in windows names or '\' in linux ones... Does Java anyway? */
291: String base = null;
292: configFile = configFile.replace('\\', '/');
293: int i = configFile.lastIndexOf('/');
294: if (i == -1) {
295: base = ".";
296: } else {
297: base = configFile.substring(0, i);
298: }
299: db = Database.getInstance(is, new XIncludeResolver(
300: base, servletContext));
301: dbMap.put(configFile, db);
302: } catch (Throwable e) {
303: Logger.error("Could not get a connection!");
304: Logger.log(e);
305: }
306: }
307: return db;
308: }
309:
310: /** return a db reference on the existing Database for the specified config file and servlet context,
311: * or null if an error occurs.
312: * @param configFile
313: * @return a DBReference
314: */
315: public static DBReference getInstance(String configFile,
316: ServletContext servletContext) {
317: Database db = getConnection(configFile, servletContext);
318: return db == null ? null : new DBReference(db);
319: }
320:
321: /** return the existing Database for the default config file, or null
322: * if it does not already exist.
323: * @return a Database
324: */
325: protected static Database getDefaultConnection() {
326: return (Database) dbMap.get(DEFAULT_CONFIG_FILE);
327: }
328:
329: /** return a db reference the existing Database for the default config file, or null
330: * if it does not already exist.
331: * @return a DBReference
332: */
333: public static DBReference getDefaultInstance() {
334: Database db = getDefaultConnection();
335: return db == null ? null : new DBReference(db);
336: }
337:
338: /** return the existing Database for the default config file and servlet context,
339: * or null if an error occurs.
340: * @return a Database
341: */
342: protected static Database getDefaultConnection(
343: ServletContext servletContext) {
344: return getConnection(DEFAULT_CONFIG_FILE, servletContext);
345: }
346:
347: /** return a db reference on the existing Database for the default config file
348: * or null if an error occurs.
349: * @return a Database
350: */
351: public static DBReference getDefaultInstance(
352: ServletContext servletContext) {
353: String configFile = findConfigFile(servletContext);
354: if (configFile == null) {
355: throw new RuntimeException(
356: "VelosurfTool.getDefaultInstance: Configuration file not found! Please add a 'velosurf.config' servlet context parameter.");
357: }
358: Database db = getConnection(configFile, servletContext);
359: return db == null ? null : new DBReference(db);
360: }
361:
362: /**
363: * do we need to try to fetch the localizer object?
364: * True initially, false after one unsuccessful try.
365: */
366: private static boolean fetchLocalizer = true;
367: }
|