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.services.dbobj;
066:
067: import com.jcorporate.expresso.core.controller.ControllerRequest;
068: import com.jcorporate.expresso.core.db.DBException;
069: import com.jcorporate.expresso.core.db.exception.DBRecordNotFoundException;
070: import com.jcorporate.expresso.core.dbobj.SecuredDBObject;
071: import com.jcorporate.expresso.core.dbobj.ValidValue;
072: import com.jcorporate.expresso.core.misc.ConfigManager;
073: import com.jcorporate.expresso.core.misc.ConfigurationException;
074: import com.jcorporate.expresso.core.misc.FileUtil;
075: import com.jcorporate.expresso.core.misc.StringUtil;
076: import com.jcorporate.expresso.kernel.util.FastStringBuffer;
077: import org.apache.log4j.Logger;
078:
079: import javax.activation.MimetypesFileTypeMap;
080: import java.io.IOException;
081: import java.lang.ref.SoftReference;
082: import java.util.ArrayList;
083: import java.util.Iterator;
084: import java.util.Vector;
085:
086: /**
087: * <p>This table provides for mapping between files and mime types. Here we
088: * try to put every mime type that we are aware of and provide the following
089: * services:</p>
090: * <ul>
091: * <li>Output Stream Content Type settings. Useful for content management, or file servers. Once
092: * the system knows what the mime type of the application is, it can be set for
093: * serving to downstream browsers.</li>
094: * <li>File to icon mapping. See this in action for the download controller. Useful
095: * for that fancy directory browsing where the appropriate icons appear for each
096: * data type</li>
097: * <li>javax.activation integration- MimeTypes extends the Activation
098: * <code>javax.activation.MimetypesFileTypeMap</code> object so that it recognizes
099: * all data stored in the MimeTypes table
100: * </li>
101: * </ul>
102: *
103: * @author Michael Rimov
104: * @version $Revision: 1.19 $ $Date: 2004/11/17 20:48:18 $
105: */
106: public class MimeTypes extends SecuredDBObject {
107:
108: /**
109: * Field Names for the MIMETYPES table
110: */
111: public static final String FLD_MIMENUMBER = "MimeNumber";
112: public static final String FLD_MIMETYPE = "MimeType";
113: public static final String FLD_DESCRIPTION = "MimeDescription";
114: public static final String FLD_MIMEICON = "MimeIcon";
115: public static final String FLD_FILE_EXTENSIONS = "MimeFileExtensions";
116:
117: private static String IMAGE_DIRECTORY = null;
118:
119: private static transient volatile SoftReference fileMap = null;
120:
121: private static transient Object fileMapLock = new Object();
122:
123: private static final Logger log = Logger
124: .getLogger("expresso.services.dbobj.MimeTypes");
125:
126: /**
127: * @see com.jcorporate.expresso.core.dbobj.SecuredDBObject
128: */
129: public MimeTypes()
130: throws com.jcorporate.expresso.core.db.DBException {
131: super ();
132: } /* MimeTypes() */
133:
134: /**
135: * @param uid The owning user id
136: */
137: public MimeTypes(int uid) throws DBException {
138: super (uid);
139: }
140:
141: /**
142: * For using DBObjects within Controllers. Initializes based upon the current
143: * user and the requested db. [Of course this can be modified later]
144: *
145: * @param request - The controller request handed to you by the framework.
146: */
147: public MimeTypes(ControllerRequest request) throws DBException {
148: super (request);
149: }
150:
151: /**
152: * @return Vector of ValidValue Value/Description pairs for mime types
153: * @throws DBException If the values cannot be retrieved
154: */
155: public Vector getValues() throws DBException {
156: return getValuesDefault("MimeNumber", "MimeType");
157: } /* getValues() */
158:
159: /**
160: * Useful method for unit testing to make sure that everything got added during
161: * setup as expected.
162: *
163: * @return int for what's expected in the database
164: */
165: public int getExpectedDefaultPopulation() {
166:
167: return new DefaultMimetypes().mimeTypes.length;
168: }
169:
170: /**
171: * Returns the valid values for any particular field
172: *
173: * @param fieldName the name of the field to retrieve
174: * @return a Vector of vaild value objects
175: * @throws DBException upon error
176: */
177: public synchronized Vector getValidValues(String fieldName)
178: throws DBException {
179: if (fieldName.equals(FLD_MIMEICON)) {
180: try {
181: Vector vv = new Vector();
182: if (IMAGE_DIRECTORY == null) {
183: FastStringBuffer fsb = FastStringBuffer
184: .getInstance();
185: // FastStringBuffer fsb = new FastStringBuffer(128);
186: try {
187: String webappDir = ConfigManager.getWebAppDir();
188: fsb.append(webappDir);
189: if (!webappDir.endsWith("/")) {
190: fsb.append("/");
191: }
192: fsb
193: .append(ConfigManager.getContext(
194: this .getDataContext())
195: .getExpressoDir());
196: fsb.append("/images/mimeTypeIcons");
197: IMAGE_DIRECTORY = fsb.toString();
198: } finally {
199: fsb.release();
200: fsb = null;
201: }
202: }
203:
204: Vector fileExtensions = new Vector();
205: fileExtensions.add("gif");
206: fileExtensions.add("png");
207: fileExtensions.add("jpg");
208:
209: Vector flist;
210: try {
211: flist = FileUtil.getFileListingRecursive(
212: IMAGE_DIRECTORY, null, "/", fileExtensions);
213: } catch (IOException e) {
214: throw new DBException(e);
215: }
216:
217: for (Iterator i = flist.iterator(); i.hasNext();) {
218: String oneFile = (String) i.next();
219: vv.add(new ValidValue(oneFile, oneFile));
220: }
221:
222: return vv;
223: } catch (ConfigurationException ce) {
224: throw new DBException(
225: "Configuration Exception in MimeTypes.getValidValues()",
226: ce);
227: }
228: } else {
229: return super .getValidValues(fieldName);
230: }
231: }
232:
233: /**
234: * Give it a file name and a data context and we'll get the
235: * MimeType object associated with it. Factory style method.
236: * <p/>
237: * <p> <b>DESIGN REQUEST</b> </p>
238: * <p/>
239: * <p>This method raises an exception if the MimeType is not
240: * found. It should instead return a <code>null</code>, because
241: * you cannot distinguished between a data base failure and db
242: * failure.
243: *
244: * @param fileName A name to retrieve the mime type. Note that this will be compared
245: * via lower case since thanks to Windows, you can have doc, DoC, DOC, dOC etc...
246: * which EXTREMELY complicates the matching process.
247: * @param dataContext the data context to get the underlying data from. May
248: * be null for <code>default</code> context
249: * @return a MimeType object the specified parameters
250: * @throws DBException if we can't find the appropriate object
251: * @throws DBException if a data access error occurs or if MIME Type does not in�the database
252: * @see #getMimeTypeFromMimeId
253: */
254: public static MimeTypes getMimeType(String fileName,
255: String dataContext) throws DBException {
256: MimetypesFileTypeMap map = getFileMap(dataContext);
257: String mimeString = map.getContentType(fileName.toLowerCase());
258: if (StringUtil.isBlankOrNull(mimeString)) {
259: throw new DBException(
260: "Unable to locate MIME type for filename: "
261: + fileName);
262: }
263:
264: MimeTypes mt = new MimeTypes(SecuredDBObject.SYSTEM_ACCOUNT);
265: if (StringUtil.isBlankOrNull(dataContext)) {
266: mt.setDataContext("default");
267: } else {
268: mt.setDataContext(dataContext);
269: }
270:
271: mt.setField(FLD_MIMETYPE, mimeString);
272: if (mt.find()) {
273: return mt;
274: } else {
275: throw new DBRecordNotFoundException(
276: "Unable to locate MIME Type: " + mimeString
277: + " in database.");
278: }
279: }
280:
281: /**
282: * Given a Expresso standard MimeTypes "MimeNumber" field and a
283: * data context and we'll get the MimeType object associated with
284: * it. Another factory style method.
285: *
286: * @param mimeNumber the mime number id from the MimeTypes data object
287: * @param dataContext the data context to get the underlying data from. May
288: * be null for <code>default</code> context
289: * @return a MimeType object the specified parameters, or
290: * <code>null</code> if no matching mime type object exists in the
291: * database.
292: * @throws DBException if we can't find the appropriate object
293: * @throws DBException if a data access error occurs.
294: * @see #getMimeTypeFromMimeId
295: */
296: public static MimeTypes getMimeTypeFromMimeId(int mimeNumber,
297: String dataContext) throws DBException {
298: MimeTypes mt = new MimeTypes(SecuredDBObject.SYSTEM_ACCOUNT);
299: if (StringUtil.isBlankOrNull(dataContext)) {
300: mt.setDataContext("default");
301: } else {
302: mt.setDataContext(dataContext);
303: }
304:
305: mt.setField(FLD_MIMENUMBER, mimeNumber);
306: if (mt.find()) {
307: return mt;
308: } else {
309: return null;
310: }
311: }
312:
313: /**
314: * <p>Use this function to get a <code>javax.activation.MimetypesFileTypeMap</code> object
315: * that is intantiated with the data stored in the MimeTypes table rather
316: * than the system default. Allows for centralized management of the system
317: * mime types rather than dealing with scattered configuration files. If there
318: * is an error reading the data, we simply instantiate a default <code>MimetypesFileTypeMap</code>
319: * object and return that instead.</p>
320: *
321: * @param dataContext the dbcontext to use or <code>null</code> if you want
322: * to just use the default context.
323: * @return an instantiated MimeTypesFileTypeMap
324: * @see javax.activation.MimetypesFileTypeMap
325: */
326: public synchronized static MimetypesFileTypeMap getFileMap(
327: String dataContext) {
328: synchronized (fileMapLock) {
329: if (fileMap == null || fileMap.get() == null) {
330: if (log.isDebugEnabled()) {
331: log.debug("Building MimetypesFileTypeMap");
332: }
333:
334: try {
335: MimeTypes mt = new MimeTypes(
336: SecuredDBObject.SYSTEM_ACCOUNT);
337: if (dataContext != null && dataContext.length() > 0) {
338: mt.setDataContext(dataContext);
339: }
340: MimetypesFileTypeMap mftm = new MimetypesFileTypeMap();
341:
342: ArrayList al = mt.searchAndRetrieveList();
343:
344: FastStringBuffer oneEntry = FastStringBuffer
345: .getInstance();
346: try {
347: for (Iterator i = al.iterator(); i.hasNext();) {
348: MimeTypes oneMime = (MimeTypes) i.next();
349: String extension = oneMime
350: .getField(FLD_FILE_EXTENSIONS);
351: //We don't add it to the list if we don't have
352: //a registered extension for it
353: if (extension != null
354: && extension.length() > 0) {
355: String aMimeType = oneMime
356: .getField(FLD_MIMETYPE);
357: if ("application/octet-stream"
358: .equals(aMimeType)) {
359: continue;
360: }
361: oneEntry.append(oneMime
362: .getField(FLD_MIMETYPE));
363: oneEntry.append(" ");
364: oneEntry.append(extension);
365:
366: String value = oneEntry.toString();
367: if (log.isDebugEnabled()) {
368: log
369: .debug("Adding mimetype mapping: "
370: + value);
371: }
372: mftm.addMimeTypes(value);
373: oneEntry.clear();
374: }
375: }
376: } finally {
377: oneEntry.release();
378: oneEntry = null;
379: }
380: fileMap = new SoftReference(mftm);
381: } catch (Exception e) {
382: log
383: .warn(
384: "Error creating MimetypesFileTypeMap. "
385: + "Returning default MimetypesFileTypeMap instead",
386: e);
387: return new MimetypesFileTypeMap();
388: }
389:
390: }
391: } //End Synchronize
392:
393: return (MimetypesFileTypeMap) fileMap.get();
394: }
395:
396: /**
397: * @throws DBException if an error occurs while populating the table.
398: * @see com.jcorporate.expresso.core.dbobj.DBObject#populateDefaultValues
399: * <p/>
400: * This function populates the mimeType table with a wide variety of
401: * basic MIME types.
402: */
403: public synchronized void populateDefaultValues() throws DBException {
404: String[][] mimeTypes = new DefaultMimetypes().mimeTypes;
405: int i;
406: int len = mimeTypes.length;
407:
408: for (i = 0; i < len; i++) {
409: this .clear();
410: try {
411: this .setField(FLD_MIMETYPE, mimeTypes[i][0]);
412:
413: if (!this .find()) {
414: setField(FLD_DESCRIPTION, mimeTypes[i][1]);
415: setField(FLD_MIMEICON, mimeTypes[i][2]);
416: setField(FLD_FILE_EXTENSIONS, mimeTypes[i][3]);
417: this .add();
418: }
419: } catch (DBException ex) {
420: log.error(
421: "MimeTypes.populateDefaultValues() Error adding mime type: "
422: + mimeTypes[i][0], ex);
423: }
424: }
425:
426: mimeTypes = null;
427:
428: //
429: //Ok, we just used a significant amount of memory that is getting freed
430: //time to schedule a GC to keep the throughput up.
431: //
432: System.gc();
433: } /* populateDefaultValues() */
434:
435: /**
436: * Returns the icon URL associated with this MimeType object
437: *
438: * @return the appropriate URL for this particular mime type.
439: */
440: public String getIconURL() {
441: try {
442: String iconName = StringUtil.notNull(this
443: .getField(FLD_MIMEICON));
444:
445: if (iconName.length() == 0) {
446: iconName = "Document.gif";
447: }
448:
449: FastStringBuffer fsb = FastStringBuffer.getInstance();
450: String returnValue = null;
451: try {
452: fsb.append(ConfigManager.getContext(
453: this .getDataContext()).getImages());
454: fsb.append("/mimeTypeIcons/");
455: fsb.append(iconName);
456: returnValue = fsb.toString();
457: } finally {
458: fsb.release();
459: fsb = null;
460: }
461:
462: return returnValue;
463: } catch (ConfigurationException ce) {
464: return "";
465: } catch (DBException dbe) {
466: return "";
467: }
468: }
469:
470: /**
471: * Table definition of the MimeTypes tables
472: */
473: protected void setupFields() throws DBException {
474: setTargetTable("MIMETYPES");
475: setDescription("DBmimeTypes");
476: setCharset("ISO-8859-1");
477: addField(FLD_MIMENUMBER, "auto-inc", 0, false, "mimeIdNumber");
478: addField(FLD_MIMETYPE, "varchar", 128, false, "mimeType");
479: addField(FLD_DESCRIPTION, "varchar", 128, true,
480: "mimeTypeDescrip");
481: addField(FLD_MIMEICON, "varchar", 60, true, "mimeTypeIcon");
482: addField(FLD_FILE_EXTENSIONS, "varchar", 60, true,
483: "File Extensions");
484: setStringFilter(FLD_MIMETYPE, "stripFilter");
485: setStringFilter(FLD_MIMEICON, "rawFilter");
486: addKey(FLD_MIMENUMBER);
487: setReadOnly(FLD_MIMENUMBER);
488: addIndex("MimeIndex", FLD_MIMETYPE, true);
489: setMultiValued(FLD_MIMEICON);
490: }
491:
492: /**
493: * As per usual add, but it resets the fileMap so future calls to it
494: * will show the appropriate changes
495: */
496: public synchronized void add()
497: throws com.jcorporate.expresso.core.db.DBException {
498: super .add();
499: clearFileMap();
500: }
501:
502: /**
503: * As per usual update, but it resets the fileMap so future calls to it
504: * will show the appropriate changes
505: */
506: public synchronized void update()
507: throws com.jcorporate.expresso.core.db.DBException {
508: super .update();
509: clearFileMap();
510: }
511:
512: /**
513: * As per usual delete, but it resets the fileMap so future calls to it
514: * will show the appropriate changes
515: */
516: public synchronized void delete()
517: throws com.jcorporate.expresso.core.db.DBException {
518: super .delete();
519: clearFileMap();
520: } /* setupFields() */
521:
522: public synchronized void clearFileMap() {
523: synchronized (fileMapLock) {
524: fileMap = null;
525: }
526: }
527:
528: }
529:
530: /* MimeTypes */
|