KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > hudson > model > DirectoryBrowserSupport


1 package hudson.model;
2
3 import hudson.FilePath;
4 import hudson.FilePath.FileCallable;
5 import hudson.remoting.VirtualChannel;
6 import org.kohsuke.stapler.StaplerRequest;
7 import org.kohsuke.stapler.StaplerResponse;
8
9 import javax.servlet.ServletException JavaDoc;
10 import javax.servlet.http.HttpServletResponse JavaDoc;
11 import java.io.File JavaDoc;
12 import java.io.FilenameFilter JavaDoc;
13 import java.io.IOException JavaDoc;
14 import java.io.InputStream JavaDoc;
15 import java.io.Serializable JavaDoc;
16 import java.net.URLEncoder JavaDoc;
17 import java.util.ArrayList JavaDoc;
18 import java.util.Arrays JavaDoc;
19 import java.util.Collections JavaDoc;
20 import java.util.Comparator JavaDoc;
21 import java.util.List JavaDoc;
22 import java.util.StringTokenizer JavaDoc;
23
24 /**
25  * Has convenience methods to serve file system.
26  *
27  * <p>
28  * This object can be used in a mix-in style to provide a directory browsing capability
29  * to a {@link ModelObject}.
30  *
31  * @author Kohsuke Kawaguchi
32  */

33 public final class DirectoryBrowserSupport {
34
35     public final ModelObject owner;
36
37     public DirectoryBrowserSupport(ModelObject owner) {
38         this.owner = owner;
39     }
40
41     /**
42      * Serves a file from the file system (Maps the URL to a directory in a file system.)
43      *
44      * @param icon
45      * The icon file name, like "folder-open.gif"
46      * @param serveDirIndex
47      * True to generate the directory index.
48      * False to serve "index.html"
49      */

50     public final void serveFile(StaplerRequest req, StaplerResponse rsp, FilePath root, String JavaDoc icon, boolean serveDirIndex) throws IOException JavaDoc, ServletException JavaDoc, InterruptedException JavaDoc {
51         if(req.getQueryString()!=null) {
52             req.setCharacterEncoding("UTF-8");
53             String JavaDoc path = req.getParameter("path");
54             if(path!=null) {
55                 rsp.sendRedirect(URLEncoder.encode(path,"UTF-8"));
56                 return;
57             }
58         }
59
60         String JavaDoc path = req.getRestOfPath();
61
62         if(path.length()==0)
63             path = "/";
64
65         if(path.indexOf("..")!=-1 || path.length()<1) {
66             // don't serve anything other than files in the artifacts dir
67
rsp.sendError(HttpServletResponse.SC_BAD_REQUEST);
68             return;
69         }
70
71         FilePath f = new FilePath(root,path.substring(1));
72
73         boolean isFingerprint=false;
74         if(f.getName().equals("*fingerprint*")) {
75             f = f.getParent();
76             isFingerprint = true;
77         }
78
79         if(f.isDirectory()) {
80             if(!req.getRequestURL().toString().endsWith("/")) {
81                 rsp.sendRedirect2(req.getRequestURL().append('/').toString());
82                 return;
83             }
84
85             if(serveDirIndex) {
86                 req.setAttribute("it",this);
87                 List JavaDoc<Path> parentPaths = buildParentPath(path);
88                 req.setAttribute("parentPath",parentPaths);
89                 req.setAttribute("topPath",
90                     parentPaths.isEmpty() ? "." : repeat("../",parentPaths.size()));
91                 req.setAttribute("files", f.act(new ChildPathBuilder()));
92                 req.setAttribute("icon",icon);
93                 req.setAttribute("path",path);
94                 req.getView(this,"dir.jelly").forward(req,rsp);
95                 return;
96             } else {
97                 f = f.child("index.html");
98             }
99         }
100
101         if(!f.exists()) {
102             rsp.sendError(HttpServletResponse.SC_NOT_FOUND);
103             return;
104         }
105
106         if(isFingerprint) {
107             rsp.forward(Hudson.getInstance().getFingerprint(f.digest()),"/",req);
108         } else {
109             ContentInfo ci = f.act(new ContentInfo());
110
111             InputStream JavaDoc in = f.read();
112             rsp.serveFile(req, in, ci.lastModified, -1, ci.contentLength, f.getName() );
113             in.close();
114         }
115     }
116
117     private static final class ContentInfo implements FileCallable<ContentInfo> {
118         int contentLength;
119         long lastModified;
120
121         public ContentInfo invoke(File JavaDoc f, VirtualChannel channel) throws IOException JavaDoc {
122             contentLength = (int) f.length();
123             lastModified = f.lastModified();
124             return this;
125         }
126
127         private static final long serialVersionUID = 1L;
128     }
129
130     /**
131      * Builds a list of {@link Path} that represents ancestors
132      * from a string like "/foo/bar/zot".
133      */

134     private List JavaDoc<Path> buildParentPath(String JavaDoc pathList) {
135         List JavaDoc<Path> r = new ArrayList JavaDoc<Path>();
136         StringTokenizer JavaDoc tokens = new StringTokenizer JavaDoc(pathList, "/");
137         int total = tokens.countTokens();
138         int current=1;
139         while(tokens.hasMoreTokens()) {
140             String JavaDoc token = tokens.nextToken();
141             r.add(new Path(repeat("../",total-current),token,true,0));
142             current++;
143         }
144         return r;
145     }
146
147     private static String JavaDoc repeat(String JavaDoc s,int times) {
148         StringBuffer JavaDoc buf = new StringBuffer JavaDoc(s.length()*times);
149         for(int i=0; i<times; i++ )
150             buf.append(s);
151         return buf.toString();
152     }
153
154     /**
155      * Represents information about one file or folder.
156      */

157     public static final class Path implements Serializable JavaDoc {
158         /**
159          * Relative URL to this path from the current page.
160          */

161         private final String JavaDoc href;
162         /**
163          * PluginName of this path. Just the file name portion.
164          */

165         private final String JavaDoc title;
166
167         private final boolean isFolder;
168
169         /**
170          * File size, or null if this is not a file.
171          */

172         private final long size;
173
174         public Path(String JavaDoc href, String JavaDoc title, boolean isFolder, long size) {
175             this.href = href;
176             this.title = title;
177             this.isFolder = isFolder;
178             this.size = size;
179         }
180
181         public boolean isFolder() {
182             return isFolder;
183         }
184
185         public String JavaDoc getHref() {
186             return href;
187         }
188
189         public String JavaDoc getTitle() {
190             return title;
191         }
192
193         public String JavaDoc getIconName() {
194             return isFolder?"folder.gif":"text.gif";
195         }
196
197         public long getSize() {
198             return size;
199         }
200
201         private static final long serialVersionUID = 1L;
202     }
203
204
205
206     private static final class FileComparator implements Comparator JavaDoc<File JavaDoc> {
207         public int compare(File JavaDoc lhs, File JavaDoc rhs) {
208             // directories first, files next
209
int r = dirRank(lhs)-dirRank(rhs);
210             if(r!=0) return r;
211             // otherwise alphabetical
212
return lhs.getName().compareTo(rhs.getName());
213         }
214
215         private int dirRank(File JavaDoc f) {
216             if(f.isDirectory()) return 0;
217             else return 1;
218         }
219     }
220
221     /**
222      * Builds a list of list of {@link Path}. The inner
223      * list of {@link Path} represents one child item to be shown
224      * (this mechanism is used to skip empty intermediate directory.)
225      */

226     private static final class ChildPathBuilder implements FileCallable<List JavaDoc<List JavaDoc<Path>>> {
227         public List JavaDoc<List JavaDoc<Path>> invoke(File JavaDoc cur, VirtualChannel channel) throws IOException JavaDoc {
228             List JavaDoc<List JavaDoc<Path>> r = new ArrayList JavaDoc<List JavaDoc<Path>>();
229
230             File JavaDoc[] files = cur.listFiles();
231             Arrays.sort(files,new FileComparator());
232
233             for( File JavaDoc f : files ) {
234                 Path p = new Path(f.getName(),f.getName(),f.isDirectory(),f.length());
235                 if(!f.isDirectory()) {
236                     r.add(Collections.singletonList(p));
237                 } else {
238                     // find all empty intermediate directory
239
List JavaDoc<Path> l = new ArrayList JavaDoc<Path>();
240                     l.add(p);
241                     String JavaDoc relPath = f.getName();
242                     while(true) {
243                         // files that don't start with '.' qualify for 'meaningful files', nor SCM related files
244
File JavaDoc[] sub = f.listFiles(new FilenameFilter JavaDoc() {
245                             public boolean accept(File JavaDoc dir, String JavaDoc name) {
246                                 return !name.startsWith(".") && !name.equals("CVS") && !name.equals(".svn");
247                             }
248                         });
249                         if(sub.length!=1 || !sub[0].isDirectory())
250                             break;
251                         f = sub[0];
252                         relPath += '/'+f.getName();
253                         l.add(new Path(relPath,f.getName(),true,0));
254                     }
255                     r.add(l);
256                 }
257             }
258
259             return r;
260         }
261
262         private static final long serialVersionUID = 1L;
263     }
264 }
265
Popular Tags