001: /* ====================================================================
002: * The Jcorporate Apache Style Software License, Version 1.2 05-07-2002
003: *
004: * Copyright (c) 1995-2002 Jcorporate Ltd. All rights reserved.
005: *
006: * Redistribution and use in source and binary forms, with or without
007: * modification, are permitted provided that the following conditions
008: * are met:
009: *
010: * 1. Redistributions of source code must retain the above copyright
011: * notice, this list of conditions and the following disclaimer.
012: *
013: * 2. Redistributions in binary form must reproduce the above copyright
014: * notice, this list of conditions and the following disclaimer in
015: * the documentation and/or other materials provided with the
016: * distribution.
017: *
018: * 3. The end-user documentation included with the redistribution,
019: * if any, must include the following acknowledgment:
020: * "This product includes software developed by Jcorporate Ltd.
021: * (http://www.jcorporate.com/)."
022: * Alternately, this acknowledgment may appear in the software itself,
023: * if and wherever such third-party acknowledgments normally appear.
024: *
025: * 4. "Jcorporate" and product names such as "Expresso" must
026: * not be used to endorse or promote products derived from this
027: * software without prior written permission. For written permission,
028: * please contact info@jcorporate.com.
029: *
030: * 5. Products derived from this software may not be called "Expresso",
031: * or other Jcorporate product names; nor may "Expresso" or other
032: * Jcorporate product names appear in their name, without prior
033: * written permission of Jcorporate Ltd.
034: *
035: * 6. No product derived from this software may compete in the same
036: * market space, i.e. framework, without prior written permission
037: * of Jcorporate Ltd. For written permission, please contact
038: * partners@jcorporate.com.
039: *
040: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
041: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
042: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
043: * DISCLAIMED. IN NO EVENT SHALL JCORPORATE LTD OR ITS CONTRIBUTORS
044: * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
045: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
046: * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
047: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
048: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
049: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
050: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
051: * SUCH DAMAGE.
052: * ====================================================================
053: *
054: * This software consists of voluntary contributions made by many
055: * individuals on behalf of the Jcorporate Ltd. Contributions back
056: * to the project(s) are encouraged when you make modifications.
057: * Please send them to support@jcorporate.com. For more information
058: * on Jcorporate Ltd. and its products, please see
059: * <http://www.jcorporate.com/>.
060: *
061: * Portions of this software are based upon other open source
062: * products and are subject to their respective licenses.
063: */
064:
065: package com.jcorporate.expresso.ext.xml.dbobj;
066:
067: import com.jcorporate.expresso.core.db.DBException;
068: import com.jcorporate.expresso.core.dbobj.DBObject;
069: import com.jcorporate.expresso.core.dbobj.SecuredDBObject;
070: import com.jcorporate.expresso.core.dbobj.ValidValue;
071: import com.jcorporate.expresso.core.security.User;
072: import org.apache.commons.collections.LRUMap;
073: import org.apache.oro.text.regex.MalformedPatternException;
074: import org.apache.oro.text.regex.Pattern;
075: import org.apache.oro.text.regex.PatternCompiler;
076: import org.apache.oro.text.regex.PatternMatcher;
077: import org.apache.oro.text.regex.Perl5Compiler;
078: import org.apache.oro.text.regex.Perl5Matcher;
079:
080: import java.util.Enumeration;
081: import java.util.Iterator;
082: import java.util.Map;
083: import java.util.Vector;
084:
085: //Object cache to hold compiled regular expressions
086:
087: /**
088: * <p>Entries here define the mapping between a specific user and browser type and
089: * Controller combination to the correct XSL stlyesheet to be used to transform the
090: * outputs of this controller. This allows different users (by group) to see different
091: * representations of a Controller's output, allowing for personalization, and
092: * different browsers to get different XSL transformations as well. As a single
093: * combination of user/browser/controller might match more than one stylesheet
094: * potentially, there is a "sequence" in these combinations. The stylesheet with the
095: * lowest sequence that matches all of the other criteria is selected.</p>
096: *
097: * @author Michael Nash
098: */
099: public class ControllerXSLMap extends SecuredDBObject {
100: private static final String this Class = ControllerXSLMap.class
101: .getName()
102: + ".";
103:
104: /**
105: * Used to hold compiled regular expressions in an attempt to speed
106: * up pattern matching.
107: */
108: protected static Map regExpCache = null;
109:
110: /**
111: * Used for regular expression compilation
112: */
113: protected static PatternCompiler compiler = new Perl5Compiler();
114:
115: /**
116: * Used to match compiled patterns against strings
117: */
118: protected static PatternMatcher matcher = new Perl5Matcher();
119:
120: /**
121: * Constructor
122: */
123: public ControllerXSLMap() throws DBException {
124: super ();
125: } /* ControllerXSLMap() */
126:
127: /**
128: * Normal DBObject Constructor
129: *
130: * @param uid The user id of the person to have the permissions of this DBOBject
131: */
132: public ControllerXSLMap(int uid) throws DBException {
133: super (uid);
134: } /* ControllerXSLMap(String) */
135:
136: /**
137: * Creates the object cache based upon the size of the controllerXSL map.
138: *
139: * @param mapList the ControllerXSLMap dbobject
140: */
141: private synchronized void createObjectCache(ControllerXSLMap mapList) {
142: try {
143: if (regExpCache == null) {
144: int numMatches = mapList.count();
145:
146: //Let's limit the size of the cache in case it's really
147: //ridiculous
148: if (numMatches > 60) {
149: numMatches = 60;
150: }
151:
152: regExpCache = new LRUMap(numMatches);
153: }
154: } catch (DBException dbe) {
155: regExpCache = new LRUMap(60);
156: }
157: }
158:
159: /**
160: * Get's the regular expression patter to match against.
161: *
162: * @param controllerClass the classname of the controller to get the appropriate
163: * pattern match
164: * @return a precompiled Regular Expression pattern
165: * @throws DBException if the mattern match is bad.
166: */
167: private synchronized Pattern getPattern(String controllerClass)
168: throws DBException {
169: Pattern p = (Pattern) regExpCache.get(controllerClass);
170:
171: if (p == null) {
172: try {
173: p = compiler.compile(controllerClass,
174: Perl5Compiler.READ_ONLY_MASK);
175: regExpCache.put(controllerClass, p);
176: } catch (MalformedPatternException mpe) {
177: throw new DBException(this Class + "getPattern(String)",
178: mpe);
179: }
180: }
181:
182: return p;
183: }
184:
185: /**
186: * wlo: This is a copy of getMatch(String, String, String). I changed
187: * the parameter for the UserAgent from 'String userAgentString' to
188: * 'int userAgentId' because this is what actually is passed by the
189: * XMLViewHandler (see changes there).
190: * <p/>
191: * Figure out which XSL map entry should match, given a username, trx class name
192: * and User-Agent string & set this db object to the appropriate object.
193: *
194: * @param userName the username of the currently logged in user.
195: * @param controllerClassParam the Controller classname to match against
196: * @param userAgentId the id of the User Agent as for the User Agent table
197: * @return true if we have found a match.
198: * @throws DBException if there's an error communicating with the underlying table
199: */
200: public synchronized boolean getMatch(String userName,
201: String controllerClassParam, int userAgentId)
202: throws DBException {
203:
204: /* wlo: no need to get UserAgent object unless we
205: want to verify the passed userAgentId which
206: is not necessary in my opinion */
207: // UserAgent ua = new UserAgent();
208: // ua.setDBName(getDBName());
209: // ua.setField("UserAgent", userAgentId);
210: // ua.retrieve();
211: /* Now get the list of groups that the user is in */
212: User oneUser = new User();
213: oneUser.setDataContext(getDataContext());
214: /* wlo: use special setter method instead of generic setField() method */
215: //oneUser.setField("LoginName", userName);
216: oneUser.setLoginName(userName);
217: /* wlo: retrieve only works with key fields */
218: //oneUser.retrieve();
219: oneUser.find();
220:
221: Vector groupList = oneUser.getGroups();
222: ControllerXSLMap oneMap = null;
223: ControllerXSLMap mapList = new ControllerXSLMap();
224: /* wlo: since we get the UserAgent id as parameter, we
225: can use it here */
226: //mapList.setField("UserAgent", ua.getField("UserAgent"));
227: mapList.setField("UserAgent", userAgentId);
228: mapList.setDataContext(getDataContext());
229: createObjectCache(mapList);
230:
231: for (Iterator i = mapList.searchAndRetrieveList("MatchSeq")
232: .iterator(); i.hasNext();) {
233: oneMap = (ControllerXSLMap) i.next();
234:
235: String oneGroup = null;
236:
237: for (Enumeration eg = groupList.elements(); eg
238: .hasMoreElements();) {
239: oneGroup = (String) eg.nextElement();
240:
241: if (oneGroup.equals(oneMap.getField("GroupName"))) {
242: String controllerClass = oneMap
243: .getField("ControllerClass");
244: Pattern compiledRegExp = getPattern(controllerClass);
245:
246: boolean result;
247: synchronized (matcher) {
248: result = matcher.matches(controllerClassParam,
249: compiledRegExp);
250:
251: }
252: if (result) {
253: /* wlo: oneMap.getField("MapNumber") to oneMap.getField("MatchNumber")
254: This is only a guess since I did not spent much thougts on
255: the idea behind MatchSeq, MatchNumber and so on. */
256: setField("MatchNumber", oneMap
257: .getField("MatchNumber"));
258: retrieve();
259:
260: return true;
261: }
262: /* if we match the transaction expression */
263:
264: } /* if we match the group expression */
265:
266: } /* for each group the user belongs to */
267:
268: }
269:
270: return false;
271: } /* getMatch(String, String, String) */
272:
273: /**
274: * Figure out which XSL map entry should match, given a username, trx class name
275: * and User-Agent string & set this db object to the appropriate object.
276: *
277: * @param userName the name of the user logged in
278: * @param controllerClassName the classname of the controller to check against.
279: * @param userAgentString the useragent string to check against.
280: * @return true if we have found a match
281: * @throws DBException if there's an error communicating with the underlying
282: * database
283: */
284: public synchronized boolean getMatch(String userName,
285: String controllerClassName, String userAgentString)
286: throws DBException {
287: /* Determine which UserAgent identifier/code we match */
288: UserAgent ua = new UserAgent();
289: ua.setDataContext(getDataContext());
290: ua.getMatch(userAgentString);
291:
292: /* Now get the list of groups that the user is in */
293: User oneUser = new User();
294: oneUser.setDataContext(getDataContext());
295: oneUser.setLoginName(userName);
296: //oneUser.setField("UserName", userName);
297: if (!oneUser.find()) {
298: throw new DBException("User not found: '" + userName + "'");
299: }
300:
301: Vector groupList = oneUser.getGroups();
302: ControllerXSLMap oneMap = null;
303: ControllerXSLMap mapList = new ControllerXSLMap();
304: mapList.setField("UserAgent", ua.getField("UserAgent"));
305: mapList.setDataContext(getDataContext());
306: createObjectCache(mapList);
307:
308: for (Iterator i = mapList.searchAndRetrieveList("MatchSeq")
309: .iterator(); i.hasNext();) {
310: oneMap = (ControllerXSLMap) i.next();
311:
312: String oneGroup = null;
313:
314: for (Enumeration eg = groupList.elements(); eg
315: .hasMoreElements();) {
316: oneGroup = (String) eg.nextElement();
317:
318: if (oneGroup.equals(oneMap.getField("GroupName"))) {
319: String controllerClass = oneMap
320: .getField("ControllerClass");
321: Pattern compiledRegExp = getPattern(controllerClass);
322:
323: boolean result;
324: synchronized (matcher) {
325: result = matcher.matches(controllerClassName,
326: compiledRegExp);
327: }
328: if (result) {
329: setField("MatchNumber", oneMap
330: .getField("MapNumber"));
331: retrieve();
332:
333: return true;
334: } /* if we match the transaction expression */
335:
336: } /* if we match the group expression */
337:
338: } /* for each group the user belongs to */
339:
340: }
341:
342: return false;
343: } /* getMatch(String, String, String) */
344:
345: /**
346: * Standard method to return a new CacheSync object
347: *
348: * @return DBObject A new CacheSync object
349: * @throws DBException If the new object cannot be created
350: */
351: public DBObject getThisDBObj() throws DBException {
352: return (DBObject) new ControllerXSLMap();
353: } /* getThisDBObj() */
354:
355: /**
356: * Override the method getValues to provide specific values for our
357: * multi-valued fields
358: *
359: * @param fieldName Fielname to retrieve values for
360: * @return Vector of ValidValue Value/description pairs for this field
361: * @throws DBException If the values cannot be retrieved
362: */
363: public synchronized Vector getValidValues(String fieldName)
364: throws DBException {
365: if (fieldName.equals("Protocol")) {
366: Vector myValues = new Vector(4);
367: myValues.addElement(new ValidValue("http", "HTTP"));
368: myValues.addElement(new ValidValue("jobqueue",
369: "Via Job Queue"));
370:
371: return myValues;
372: }
373:
374: return super .getValidValues(fieldName);
375: } /* getValidValues(String) */
376:
377: /**
378: * Define the table and fields for this object
379: *
380: * @throws DBException If the fields cannot be set up
381: */
382: public void setupFields() throws DBException {
383: setTargetTable("CTLXSLMAP");
384: setDescription("Controller/XSL Mapping Entries");
385: setCharset("ISO-8859-1");
386: addField("MatchNumber", "int", 0, false, "Match Number");
387: addField("ControllerClass", "text", 0, false,
388: "Controller Class Pattern");
389: addField("MatchSeq", "int", 0, false, "Match Sequence");
390: addField("GroupName", "varchar", 10, false, "User Group");
391: addField("XSLFileName", "text", 0, false, "XSL File Name");
392: addField("UserAgent", "int", 0, false, "User-Agent/Browser");
393: addField("Param", "text", 0, false, "Additional Parameters");
394: addKey("MatchNumber");
395: } /* setupFields() */
396:
397: } /* ControllerXSLMap */
398:
399: /* ControllerXSLMap */
|