KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > tigris > scarab > om > Attachment


1 package org.tigris.scarab.om;
2
3 /* ================================================================
4  * Copyright (c) 2000-2002 CollabNet. All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions are
8  * met:
9  *
10  * 1. Redistributions of source code must retain the above copyright
11  * notice, this list of conditions and the following disclaimer.
12  *
13  * 2. Redistributions in binary form must reproduce the above copyright
14  * notice, this list of conditions and the following disclaimer in the
15  * documentation and/or other materials provided with the distribution.
16  *
17  * 3. The end-user documentation included with the redistribution, if
18  * any, must include the following acknowlegement: "This product includes
19  * software developed by Collab.Net <http://www.Collab.Net/>."
20  * Alternately, this acknowlegement may appear in the software itself, if
21  * and wherever such third-party acknowlegements normally appear.
22  *
23  * 4. The hosted project names must not be used to endorse or promote
24  * products derived from this software without prior written
25  * permission. For written permission, please contact info@collab.net.
26  *
27  * 5. Products derived from this software may not use the "Tigris" or
28  * "Scarab" names nor may "Tigris" or "Scarab" appear in their names without
29  * prior written permission of Collab.Net.
30  *
31  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
32  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
33  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
34  * IN NO EVENT SHALL COLLAB.NET OR ITS CONTRIBUTORS BE LIABLE FOR ANY
35  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
36  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
37  * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
38  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
39  * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
40  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
41  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
42  *
43  * ====================================================================
44  *
45  * This software consists of voluntary contributions made by many
46  * individuals on behalf of Collab.Net.
47  */

48
49 import java.io.File JavaDoc;
50 import java.io.BufferedInputStream JavaDoc;
51 import java.io.BufferedOutputStream JavaDoc;
52 import java.io.FileInputStream JavaDoc;
53 import java.io.FileOutputStream JavaDoc;
54
55 import java.sql.Connection JavaDoc;
56 import java.util.Date JavaDoc;
57 import java.util.List JavaDoc;
58
59 import org.apache.torque.TorqueException;
60 import org.apache.torque.om.Persistent;
61
62 import org.apache.turbine.Turbine;
63 import org.apache.torque.util.Criteria;
64
65 import org.apache.commons.configuration.Configuration;
66 import org.apache.commons.fileupload.FileItem;
67
68 import org.tigris.scarab.tools.localization.L10NKeySet;
69 import org.tigris.scarab.util.Log;
70 import org.tigris.scarab.util.ScarabConstants;
71 import org.tigris.scarab.util.ScarabException;
72 import org.tigris.scarab.util.word.SearchIndex;
73 import org.tigris.scarab.util.word.SearchFactory;
74
75 /**
76  * Attachments contain data associated with an issue. It used to be that
77  * an issue could have multiple attachments of a given type but only one
78  * value for a given Attribute. Attributes are now multi-valued, so the
79  * difference is blurred in some cases. A comment given as a reason for a
80  * modification to attribute values is considered an Attachment.
81  * Notes and urls are also considered attachments, though these two could
82  * probably be implemented as attributes (with some ui redesign).
83  * The obvious form of attachment is a file uploaded and associated with
84  * an issue, such as a screenshot showing an error or a patch.
85  *
86  * @author <a HREF="mailto:jmcnally@collab.net">John McNally</a>
87  * @author <a HREF="mailto:jon@collab.net">Jon S. Stevens</a>
88  * @version $Id: Attachment.java 9272 2004-11-23 08:38:37Z dep4b $
89  */

90 public class Attachment
91     extends BaseAttachment
92     implements Persistent
93 {
94     /** ObjectKey for a file type attachment */
95     public static final Integer JavaDoc FILE__PK = new Integer JavaDoc(1);
96     /** ObjectKey for a note/comment type attachment */
97     public static final Integer JavaDoc COMMENT__PK = new Integer JavaDoc(2);
98     /** ObjectKey for a url type attachment */
99     public static final Integer JavaDoc URL__PK = new Integer JavaDoc(3);
100     /** ObjectKey for a reason for modification type attachment */
101     public static final Integer JavaDoc MODIFICATION__PK = new Integer JavaDoc(4);
102     
103     /** Path to the base location for storing files */
104     private static String JavaDoc fileRepo = null;
105
106     /**
107      * The FileItem that is associated with the attachment as it is uploaded
108      * from an html form.
109      */

110     private FileItem fileItem;
111     private static Configuration configuration;
112     
113     /**
114      * Makes sure to only save the simple filename which is the part
115      * following the last path separator. This is appended as the last
116      * part of the the path returned by getRelativePath() following the
117      * It would generally be set to original filename as given on the
118      * client that uploaded the file. Spaces are replaced by underscores.
119      */

120     public void setFileName(String JavaDoc name)
121     {
122         if (name == null)
123         {
124             super.setFileName(null);
125         }
126         else
127         {
128             // look for both '/' and '\' as path separators
129
int start = name.lastIndexOf('/')+1;
130             if (start == 0)
131             {
132                 start = name.lastIndexOf('\\')+1;
133             }
134             // don't allow spaces
135
String JavaDoc tmpName = name.substring(start).replace(' ', '_');
136             tmpName = tmpName.replace('%', '_');
137             super.setFileName(tmpName);
138         }
139     }
140     
141     /**
142      * There is no reason to reconstruct the FileItem, always returns null.
143      * This is not used, but required by the bean introspector used by intake.
144      * @return value of file.
145      */

146     public FileItem getFile()
147     {
148         return fileItem;
149     }
150     
151     /**
152      * Set the value of file.
153      * @param v Value to assign to file.
154      */

155     public void setFile(FileItem v)
156     {
157         fileItem = v;
158         if (getMimeType() == null)
159         {
160             setMimeType(v.getContentType());
161         }
162         setFileName(v.getName());
163     }
164     
165     /**
166      * Populates fields for a text (non-file) type of attachment.
167      */

168     public void setTextFields(ScarabUser user, Issue issue, Integer JavaDoc typeId)
169         throws Exception JavaDoc
170     {
171         setIssue(issue);
172         setTypeId(typeId);
173         setMimeType("text/plain");
174         //setCreatedDate(new Date());
175
setCreatedBy(user.getUserId());
176     }
177
178     /**
179      * This is a little method that uses getData() to make a http url
180      * if it isn't already prefixed with "htt://"
181      */

182     public String JavaDoc doMakeURLFromData()
183     {
184         String JavaDoc url = getData();
185         if (AttachmentTypePeer.URL_PK.equals(getTypeId()))
186         {
187             int stop = Math.min(url.indexOf('/'), url.indexOf('?'));
188             String JavaDoc test = null;
189             if (stop > 0)
190             {
191                 test = url.substring(0, stop);
192             }
193             else
194             {
195                 test = url;
196             }
197             int colon = test.indexOf(':');
198             if (colon < 0)
199             {
200                 // add default http protocol
201
StringBuffer JavaDoc sb = new StringBuffer JavaDoc(url.length() + 7);
202                 sb.append("http://").append(url);
203                 url = sb.toString();
204             }
205         }
206         return url;
207     }
208
209     /**
210      * Calls super.save(Connection) and also checks for a FileItem. if one
211      * exists the file is moved to its final location.
212      *
213      * @param dbCon a <code>DBConnection</code> value
214      * @exception TorqueException if an error occurs
215      */

216     public void save(Connection JavaDoc dbCon)
217         throws TorqueException
218     {
219         if (getIssue().isNew())
220         {
221             throw new TorqueException("Cannot save an attachment before saving"
222                                       + " the issue to which it is attached."); //EXCEPTION
223
}
224         // It would be better (from an oo perspective) to do this whenever
225
// setData is called, but we can't be sure the typeId will be
226
// set prior to setting the url, so we will do the check here.
227
setData(doMakeURLFromData());
228
229         // need to handle the case where we don't want to be smart
230
// and just set the dates to be whatever we want them
231
// to be (xml import!).
232
if (isNew() && (getCreatedDate() == null && getModifiedDate() == null))
233         {
234             Date JavaDoc now = new Date JavaDoc();
235             setCreatedDate(now);
236             setModifiedDate(now);
237         }
238         else if (isModified())
239         {
240             setModifiedDate(new Date JavaDoc());
241         }
242         
243         super.save(dbCon);
244         
245         try
246         {
247             FileItem file = getFile();
248             if (file != null)
249             {
250                 File JavaDoc uploadFile =
251                     new File JavaDoc(getRepositoryDirectory(),getRelativePath());
252                 File JavaDoc parent = uploadFile.getParentFile();
253                 if (!parent.exists())
254                 {
255                     mkdirs(parent);
256                 }
257                 file.write(uploadFile);
258             }
259         }
260         catch (Exception JavaDoc e)
261         {
262             throw new TorqueException(e); //EXCEPTION
263
}
264         
265         /*
266          * index the text for searching.
267          */

268         if (AttachmentTypePeer.COMMENT_PK.equals(getTypeId()))
269         {
270             try
271             {
272                 SearchIndex searchIndex = SearchFactory.getInstance();
273                 if (searchIndex != null)
274                 {
275                     searchIndex.index(this);
276                 }
277             }
278             catch (Exception JavaDoc e)
279             {
280                 throw new TorqueException(e); //EXCEPTION
281
}
282         }
283     }
284
285     /**
286      * Delete the attachment file on disk
287      * @return true if the file was deleted, false otherwise
288      */

289     public boolean deletePhysicalAttachment()
290     throws Exception JavaDoc
291     {
292         File JavaDoc f = new File JavaDoc(getFullPath());
293         return f.delete();
294     }
295
296
297     
298     /**
299      * creates the directory given by path, if it does not already exist
300      */

301     private static synchronized void mkdirs(File JavaDoc path)
302     {
303         if (!path.exists())
304         {
305             path.mkdirs();
306         }
307     }
308
309     /**
310      * The path to an attachment file relative to the base file repository.
311      * Files are saved according to:
312      * <code>moduleId/(issue_IdCount/1000)/issueID_attID_filename</code>
313      * where moduleId and attId are primary keys of the related module and
314      * this attachment. issueID is the unique id generally used to specify
315      * the issue within the ui. issue_IdCount is the numerical suffix of
316      * the unique id. So if the pk of module PACS is 201 and this attachment
317      * pk is 123 the path would be: 201/0/PACS5_123_diff.txt or if the issue
318      * count were higher: 201/2/PACS2115_123_diff.txt. The first two
319      * directories are used to keep the number of files per directory
320      * reasonable while the issue unique id and the final textual filename
321      * allow someone browsing the file system to be better able to pick
322      * out relevant files.
323      */

324     public String JavaDoc getRelativePath()
325         throws ScarabException, Exception JavaDoc
326     {
327         if (isNew())
328         {
329             throw new ScarabException(L10NKeySet.ExceptionPathNotSet);
330         }
331         String JavaDoc path = null;
332         String JavaDoc filename = getFileName();
333         if (filename != null)
334         {
335             // moduleId/(issue_IdCount/1000)/issueID_attID_filename
336
StringBuffer JavaDoc sb = new StringBuffer JavaDoc(30 + filename.length());
337             Issue issue = getIssue();
338             sb.append("mod").append(issue.getModule().getQueryKey())
339                 .append(File.separator)
340                 .append(issue.getIdCount() / 1000)
341                 .append(File.separator)
342                 .append(issue.getUniqueId()).append('_')
343                 .append(getQueryKey()).append('_')
344                 .append(filename);
345             path = sb.toString();
346         }
347         return path;
348     }
349
350     /**
351      * @return Prepends the base repository path to the path returned
352      * by {@link #getRelativePath()}, returns <code>null</code> {@link
353      * #getRelativePath()} does.
354      */

355     public String JavaDoc getFullPath()
356         throws Exception JavaDoc
357     {
358         String JavaDoc path = null;
359         String JavaDoc relativePath = getRelativePath();
360         if (relativePath != null)
361         {
362             path = getRepositoryDirectory() + File.separator + relativePath;
363         }
364         return path;
365     }
366
367     /**
368      * Get the repository path info as given in the configuration. if the
369      * path begins with a '/', it is assumed to be absolute. Otherwise
370      * the path is constructed relative to the webapp directory.
371      */

372     public static String JavaDoc getRepositoryDirectory()
373         throws Exception JavaDoc
374     {
375         if (fileRepo == null)
376         {
377             String JavaDoc testPath = getConfiguration()
378                 .getString(ScarabConstants.ATTACHMENTS_REPO_KEY);
379
380             File JavaDoc testDir = new File JavaDoc(testPath);
381             if (testDir.isAbsolute())
382             {
383                 if (!testDir.exists())
384                 {
385                     mkdirs(testDir);
386                 }
387                 fileRepo = testPath;
388             }
389             else
390             {
391                 // test for existence within the webapp directory.
392
testPath = Turbine.getRealPath(testPath);
393                 testDir = new File JavaDoc(testPath);
394                 if (!testDir.exists())
395                 {
396                     mkdirs(testDir);
397                 }
398                 fileRepo = testPath;
399             }
400         }
401         return fileRepo;
402     }
403
404     private static Configuration getConfiguration() {
405         if(configuration==null){
406             configuration = Turbine.getConfiguration();
407         }
408         return configuration;
409     }
410     protected static void setConfiguration(Configuration configuration){
411         Attachment.configuration = configuration;
412     }
413
414     public void copyFileTo(String JavaDoc path)
415         throws Exception JavaDoc
416     {
417         copyFileFromTo(getFullPath(), path);
418     }
419
420     public void copyFileFromTo(String JavaDoc from, String JavaDoc path)
421         throws Exception JavaDoc
422     {
423         BufferedInputStream JavaDoc in = null;
424         BufferedOutputStream JavaDoc out = null;
425         try
426         {
427             File JavaDoc f = new File JavaDoc(path);
428             if (!f.getParentFile().exists())
429             {
430                 f.getParentFile().mkdirs();
431             }
432             
433             in = new BufferedInputStream JavaDoc(new FileInputStream JavaDoc(from));
434             out = new BufferedOutputStream JavaDoc(new FileOutputStream JavaDoc(f));
435             byte[] bytes = new byte[2048];
436             int nbrRead = 0;
437             while ((nbrRead = in.read(bytes)) != -1)
438             {
439                 out.write(bytes, 0, nbrRead);
440             }
441         }
442         finally
443         {
444             try
445             {
446                 in.close();
447             }
448             catch (Exception JavaDoc e)
449             {
450                 Log.get().debug(e.getMessage());
451             }
452             try
453             {
454                 out.close();
455             }
456             catch (Exception JavaDoc e)
457             {
458                 Log.get().debug(e.getMessage());
459             }
460         }
461     }
462
463     /**
464      * Makes a copy of this object.
465      * It creates a new object filling in the simple attributes.
466      */

467     public Attachment copy() throws TorqueException
468     {
469         Attachment copyObj = AttachmentManager.getInstance();
470         copyObj.setIssueId(getIssueId());
471         copyObj.setTypeId(getTypeId());
472         copyObj.setName(getName());
473         copyObj.setData(getData());
474         copyObj.setFileName(getFileName());
475         copyObj.setMimeType(getMimeType());
476         copyObj.setModifiedBy(getModifiedBy());
477         copyObj.setCreatedBy(getCreatedBy());
478         copyObj.setModifiedDate(getModifiedDate());
479         copyObj.setCreatedDate(getCreatedDate());
480         copyObj.setDeleted(getDeleted());
481         return copyObj;
482     }
483
484     /**
485      * Retrieves the Activity in which this attachment was created.
486      */

487     public Activity getActivity() throws Exception JavaDoc
488     {
489         Activity activity = null;
490         Criteria crit = new Criteria()
491             .add(ActivityPeer.ATTACHMENT_ID, getAttachmentId());
492         
493         List JavaDoc activities = ActivityPeer.doSelect(crit);
494         if (activities.size() > 0)
495         {
496             activity = (Activity)activities.get(0);
497         }
498         return activity;
499      }
500 }
501
Popular Tags