001: /*
002: * This file is part of the QuickServer library
003: * Copyright (C) 2003-2005 QuickServer.org
004: *
005: * Use, modification, copying and distribution of this software is subject to
006: * the terms and conditions of the GNU Lesser General Public License.
007: * You should have received a copy of the GNU LGP License along with this
008: * library; if not, you can download a copy from <http://www.quickserver.org/>.
009: *
010: * For questions, suggestions, bug-reports, enhancement-requests etc.
011: * visit http://www.quickserver.org
012: *
013: */
014:
015: package filesrv;
016:
017: import org.quickserver.net.*;
018: import org.quickserver.net.server.*;
019: import org.quickserver.util.MyString;
020:
021: import org.quickserver.util.pool.PoolableObject;
022: import org.apache.commons.pool.BasePoolableObjectFactory;
023: import org.apache.commons.pool.PoolableObjectFactory;
024:
025: import java.io.*;
026: import java.net.*;
027: import java.util.*;
028: import java.util.logging.*;
029: import java.nio.*;
030: import java.nio.channels.*;
031:
032: /**
033: * FileServer Example
034: * @author Akshathkumar Shetty
035: */
036: public class Data implements ClientData, PoolableObject {
037: public static String serverIP = null;
038: private static Logger logger = Logger.getLogger(Data.class
039: .getName());
040: static {
041: try {
042: serverIP = java.net.InetAddress.getLocalHost()
043: .getHostName();
044: } catch (UnknownHostException e) {
045: serverIP = "UnknownHost";
046: }
047: }
048: private static String userRootHome = "dist";
049:
050: public static void setUserRootHome(String name) {
051: userRootHome = name;
052: }
053:
054: private HashMap httpHeader;
055: private String user_root = userRootHome;
056: private ByteBuffer wrapedByteBuffer;
057: private ByteBuffer pooledByteBuffer;
058: private boolean sendFile = false;
059: private FileChannel fileChannel;
060:
061: private long startRange = 0;
062: private long endRange = -1;
063: private long fileLength = -1;
064: private boolean wroteFileHttpHeader;
065: private boolean closeConWhenDone;
066:
067: private String nonBlockingWriteDesc = null;
068:
069: public boolean isHeaderReady() {
070: return httpHeader != null;
071: }
072:
073: public void initHeader(String data) {
074: httpHeader = new HashMap();
075: int i = data.lastIndexOf(" HTTP/");
076: httpHeader.put("FILE", data.substring(4, i));
077: }
078:
079: public boolean addHeader(String data) {
080: if (data.startsWith("GET /")) {
081: initHeader(data);
082: return false;
083: }
084:
085: if (data.length() == 0)
086: return true;
087: int i = data.indexOf(": ");
088: if (i == -1) {
089: logger.warning("Got unknown header: " + data);
090: return false;
091: }
092:
093: httpHeader.put(data.substring(0, i).toUpperCase(), data
094: .substring(i + 2));
095: return false;
096: }
097:
098: public File getFile() throws BadRequestException {
099: String reqDir = (String) httpHeader.get("FILE");
100: if (reqDir == null)
101: throw new BadRequestException("No File Requested!");
102: if (reqDir.length() == 0)
103: reqDir = File.separator;
104: return new File(user_root + reqDir);
105: }
106:
107: public boolean isDirList() throws BadRequestException {
108: File file = getFile();
109: if (file.canRead() == false) {
110: logger.finest("File : " + file.getAbsolutePath());
111: throw new BadRequestException("File Not Found: "
112: + (String) httpHeader.get("FILE"));
113: }
114: if (file.isDirectory()) {
115: String reqDir = (String) httpHeader.get("FILE");
116: if (reqDir.charAt(reqDir.length() - 1) != '/') {
117: httpHeader.put("FILE", reqDir + "/");
118: }
119: }
120: return file.isDirectory();
121: }
122:
123: public String getDirList() throws BadRequestException {
124: File file = getFile();
125: File list[] = file.listFiles();
126: String loc = (String) httpHeader.get("FILE");
127:
128: StringBuffer content = new StringBuffer();
129: content.append("<html>\r\n<head>\r\n<title>" + serverIP + " - "
130: + loc + "</title>\r\n</head>\r\n<body>\r\n");
131: content.append("<H3>Filesrv Server - File List for " + serverIP
132: + " - " + loc + "</H3>\r\n<hr/>\r\n");
133: content.append("<blockquote>\r\n<table width=\"80%\">");
134:
135: if (loc.equals("/") == false) {
136: content.append("\r\n<tr>\r\n<td colspan=\"5\">");
137: String parent = file.getParent();
138: parent = parent.replace('\\', '/');
139: //parent = parent.replaceAll("\\("+user_root+"\\)", "");
140: parent = MyString.replaceAll(parent, user_root, "");
141: if (parent.equals(""))
142: parent = "/";
143: content.append("[<a href=\"" + parent
144: + "\">To Parent Directory</a>]"); //to fix
145: content.append("\r\n</td>\r\n</tr>");
146: }
147:
148: for (int i = 0; i < list.length; i++) {
149: content.append("\r\n<tr>");
150:
151: content.append("\r\n<td align=\"right\">");
152: content.append(new java.util.Date(list[i].lastModified()));
153: content.append("</td>");
154:
155: content.append("\r\n<td> </td>");
156:
157: content.append("\r\n<td align=\"right\">");
158: if (list[i].isDirectory()) {
159: content.append(" <dir>");
160: } else {
161: content.append(" " + list[i].length());
162: }
163: content.append("</td>");
164:
165: content.append("\r\n<td> </td>");
166:
167: content.append("\r\n<td><a href=\"");
168: content.append(loc + list[i].getName());//to fix
169: content.append("\">");
170: content.append(list[i].getName());
171: content.append("</a></td>");
172:
173: content.append("\r\n</tr>");
174: }
175: content.append("\r\n</table>\r\n</blockquote>");
176: content.append("\r\n<hr/>\r\n</body>\r\n</html>");
177: return content.toString();
178: }
179:
180: private String makeFileResponseHeader(ClientHandler handler) {
181: String range = (String) httpHeader.get("RANGE");
182:
183: StringBuffer sb = new StringBuffer();
184:
185: if (range == null || range.startsWith("bytes=") == false) {
186: sb.append("HTTP/1.1 200 OK\r\n");
187: sb.append("Content-Length: " + fileLength).append("\r\n");
188: startRange = 0;
189: endRange = fileLength - 1;
190: } else {
191: try {
192: range = range.substring(6); //skip bytes=
193: int i = range.indexOf("-");
194: if (i == -1)
195: i = range.length();
196: startRange = Integer.parseInt(range.substring(0, i));
197: i++;
198: if (i < range.length()) {
199: endRange = Integer.parseInt(range.substring(i));
200: } else {
201: endRange = fileLength - 1;
202: }
203: } catch (Exception e) {
204: logger.finest("IGNORE Error: " + e);
205: }
206: sb.append("HTTP/1.1 206 Partial content\r\n");
207: sb.append("Content-Range: bytes " + startRange + "-"
208: + endRange + "/" + fileLength + "\r\n");
209:
210: }
211: sb.append("Server: ").append(handler.getServer().getName())
212: .append("\r\n");
213: sb.append("Content-Type: application/octet-stream").append(
214: "\r\n");
215: sb.append("\r\n");
216:
217: return sb.toString();
218: }
219:
220: public void sendFileNonBlocking(ClientHandler handler)
221: throws IOException, BadRequestException {
222: fileLength = getFile().length();
223: String header = makeFileResponseHeader(handler);
224: logger.fine("Will Send: \n" + header);
225:
226: makeNonBlockingWrite(handler, header.getBytes(), true,
227: "Sending HTTP header for file.", false);
228: }
229:
230: public void makeNonBlockingWrite(ClientHandler handler,
231: byte data[], boolean sendFileFlag, String desc,
232: boolean closeConWhenDone) throws IOException {
233: if (wrapedByteBuffer != null) {
234: //this client must have sent another req. with out waiting for res.. let close him..
235: throw new IOException(
236: "The old data was still not fully written sorry!");
237: }
238: wrapedByteBuffer = ByteBuffer.wrap(data);
239: sendFile = sendFileFlag;
240: nonBlockingWriteDesc = desc;
241: this .closeConWhenDone = closeConWhenDone;
242: handler.registerForWrite();
243: }
244:
245: public void sendFileBlocking(ClientHandler handler)
246: throws IOException, BadRequestException {
247: File file = getFile();
248:
249: fileLength = file.length();
250: String header = makeFileResponseHeader(handler);
251:
252: FileInputStream in = null;
253: byte buffer[] = new byte[1024 * 1024];
254: try {
255: in = new FileInputStream(file);
256: if (startRange > 0) {
257: logger.finest("Will skip: " + startRange);
258: in.skip(startRange);
259: }
260: int i = 0;
261:
262: i = in.read(buffer);
263: logger.finest("Sending HTTP header for file.");
264: handler.sendClientBytes(header);
265:
266: handler.setDataMode(DataMode.BINARY, DataType.OUT);
267: logger.finest("Sending file data: " + file);
268: long remain = fileLength - startRange;
269: logger.finest("Remain: " + remain + ", startRange: "
270: + startRange + ", i: " + i);
271:
272: while (i != -1 && remain != 0) {
273: if (i > remain)
274: i = (int) remain;
275:
276: handler.sendClientBinary(buffer, 0, i);
277: startRange += i;
278:
279: remain = fileLength - startRange;
280: i = in.read(buffer);
281: logger.finest("Remain: " + remain + ", startRange: "
282: + startRange + ", i: " + i);
283: Thread.currentThread().yield();
284: }
285: logger.fine("Sent the file!");
286: } catch (Exception er) {
287: logger.info("Error sending file: " + er);
288: } finally {
289: if (in != null)
290: in.close();
291: handler.setDataMode(DataMode.BYTE, DataType.OUT);
292: }
293:
294: if (false) {
295: handler.closeConnection();
296: } else {
297: //so that we can take more req.
298: clean();
299: }
300: }
301:
302: public void writeData(ClientHandler handler) throws Exception {
303: if (wrapedByteBuffer != null
304: && wrapedByteBuffer.remaining() != 0) {
305: logger.finest(nonBlockingWriteDesc);
306: int written = handler.getSocketChannel().write(
307: wrapedByteBuffer);
308: logger.finest("Written " + written + " Bytes");
309: if (wrapedByteBuffer.remaining() != 0) {
310: handler.registerForWrite();
311: return;
312: }
313: wroteFileHttpHeader = true;
314: if (sendFile == false) {
315: wrapedByteBuffer = null;
316: if (closeConWhenDone) {
317: handler.closeConnection();
318: }
319: return;
320: }
321: }
322:
323: if (sendFile == true) {
324: if (fileChannel == null) {
325: File file = getFile();
326: logger.finest("Sending file data: " + file);
327: FileInputStream fin = new FileInputStream(file);
328: fileChannel = fin.getChannel();
329: fileChannel.position(startRange);
330: }
331:
332: if (pooledByteBuffer == null) {
333: pooledByteBuffer = (ByteBuffer) handler.getServer()
334: .getByteBufferPool().borrowObject();
335: }
336:
337: if (pooledByteBuffer.hasRemaining()) {
338: int ret = -1;
339: long remain = fileLength - startRange;
340: logger.finest("Remain: " + remain);
341:
342: if (pooledByteBuffer.remaining() > remain) {
343: pooledByteBuffer.limit((int) (pooledByteBuffer
344: .position() + remain));
345: }
346:
347: ret = fileChannel.read(pooledByteBuffer);
348:
349: logger.finest("Read " + ret + " Bytes from file");
350: if (ret < 0 || remain == 0) {//EOF
351: fileChannel.close();
352: //fileChannel = null;
353: } else {
354: startRange += ret;
355: }
356: }
357: pooledByteBuffer.flip();
358:
359: if (pooledByteBuffer.hasRemaining()) {
360: int written = handler.getSocketChannel().write(
361: pooledByteBuffer);
362: logger
363: .finest("Written " + written
364: + " Bytes to socket");
365: }
366:
367: long remain = fileLength - startRange;
368: if (remain == 0 && pooledByteBuffer.hasRemaining() == false) {
369: fileChannel.close();
370: }
371:
372: if (pooledByteBuffer.hasRemaining() == false
373: && fileChannel.isOpen() == false) {
374: sendFile = false;
375: logger.finest("Sent the file!");
376:
377: if (false) {
378: handler.closeConnection();
379: } else {
380: //so that we can take more req.
381: clean();
382: }
383: return; //work done
384: }
385: pooledByteBuffer.compact(); //In case of partial write
386: handler.registerForWrite();
387: } //end of sendFile
388: }
389:
390: public void cleanPooledByteBuffer(QuickServer quickserver) {
391: if (pooledByteBuffer != null) {
392: try {
393: quickserver.getByteBufferPool().returnObject(
394: pooledByteBuffer);
395: } catch (Exception er) {
396: logger
397: .warning("Could not return ByteBuffer back to pool: "
398: + er);
399: }
400: pooledByteBuffer = null;
401: }
402: }
403:
404: public boolean getWroteFileHttpHeader() {
405: return wroteFileHttpHeader;
406: }
407:
408: //--- pool --
409: public void clean() {
410: httpHeader = null;
411: user_root = userRootHome;
412: wrapedByteBuffer = null;
413: if (pooledByteBuffer != null) {
414: pooledByteBuffer.clear();
415: }
416: sendFile = false;
417: if (fileChannel != null) {
418: try {
419: fileChannel.close();
420: } catch (Exception er) {
421: }
422: fileChannel = null;
423: }
424: startRange = 0;
425: endRange = -1;
426: fileLength = -1;
427: wroteFileHttpHeader = false;
428:
429: nonBlockingWriteDesc = null;
430: closeConWhenDone = false;
431: }
432:
433: public boolean isPoolable() {
434: return true;
435: }
436:
437: public PoolableObjectFactory getPoolableObjectFactory() {
438: return new BasePoolableObjectFactory() {
439: public Object makeObject() {
440: return new Data();
441: }
442:
443: public void passivateObject(Object obj) {
444: Data d = (Data) obj;
445: d.clean();
446: }
447:
448: public void destroyObject(Object obj) {
449: if (obj == null)
450: return;
451: passivateObject(obj);
452: obj = null;
453: }
454:
455: public boolean validateObject(Object obj) {
456: if (obj == null)
457: return false;
458: else
459: return true;
460: }
461: };
462: }
463: }
|