001: /*
002: *
003: *
004: * Copyright 1990-2007 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License version
009: * 2 only, as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful, but
012: * WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * General Public License version 2 for more details (a copy is
015: * included at /legal/license.txt).
016: *
017: * You should have received a copy of the GNU General Public License
018: * version 2 along with this work; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA
021: *
022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
023: * Clara, CA 95054 or visit www.sun.com if you need additional
024: * information or have any questions.
025: */
026: package com.sun.kvem.jsr082.obex;
027:
028: import javax.obex.Operation;
029: import javax.obex.HeaderSet;
030: import javax.obex.ResponseCodes;
031: import java.io.InputStream;
032: import java.io.OutputStream;
033: import java.io.IOException;
034: import java.io.DataInputStream;
035: import java.io.DataOutputStream;
036:
037: /**
038: * The class implements server side of put/get operation.
039: */
040: final class ServerOperation implements Operation {
041: // Debug information, should be false for RR
042: private static final boolean DEBUG = false;
043:
044: private Object lock = new Object();
045: private HeaderSetImpl recvHeaders;
046: private ServerConnectionImpl stream;
047: private int opcode;
048: private boolean isGet;
049:
050: private boolean isAborted;
051: private boolean requestEnd;
052:
053: private boolean inputStreamOpened = false;
054: private boolean outputStreamOpened = false;
055: private boolean inputStreamEof;
056:
057: private OperationInputStream is = new OperationInputStream();
058: private OperationOutputStream os = new OperationOutputStream();
059:
060: private byte[] head = ObexPacketStream.PACKET_CONTINUE;
061:
062: /** Constructor for get operation. */
063: ServerOperation(ServerConnectionImpl stream) throws IOException {
064: this .stream = stream;
065: opcode = ObexPacketStream.OPCODE_GET;
066: isGet = true;
067: recvHeaders = new HeaderSetImpl(HeaderSetImpl.OWNER_SERVER);
068: int mode = waitForData(stream, recvHeaders,
069: ObexPacketStream.OPCODE_GET);
070: switch (mode) {
071: case 0:
072: isAborted = true;
073: stream.operationClosed = true;
074: break;
075: case 2:
076: requestEnd = true;
077: // no data was received
078: inputStreamEof = true;
079: stream.packetBegin(head);
080: // Response packets can contains both TARGET and CONN ID headers
081: stream
082: .packetAddConnectionID(stream.getConnectionID(),
083: null);
084: stream.packetAddAuthResponses();
085: stream.packetAddHeaders(null);
086: break;
087: }
088: }
089:
090: /** Constructor for put operation. */
091: ServerOperation(ServerConnectionImpl stream,
092: HeaderSetImpl recvHeaders) {
093: // data parsing mode already on.
094: this .stream = stream;
095: opcode = ObexPacketStream.OPCODE_PUT;
096: isGet = false;
097: this .recvHeaders = recvHeaders;
098:
099: }
100:
101: public DataOutputStream openDataOutputStream() throws IOException {
102: return new DataOutputStream(openOutputStream());
103: }
104:
105: public OutputStream openOutputStream() throws IOException {
106: if (DEBUG) {
107: System.out.println("server: openOutputStream()");
108: }
109: synchronized (lock) {
110: if (stream.operationClosed) {
111: throw new IOException("operation closed");
112: }
113: if (outputStreamOpened) {
114: throw new IOException(
115: "no more output streams available");
116: }
117: if (!requestEnd) {
118: throw new IOException("input data not read out");
119: }
120: outputStreamOpened = true;
121: return os;
122: }
123: }
124:
125: public DataInputStream openDataInputStream() throws IOException {
126: return new DataInputStream(openInputStream());
127: }
128:
129: public InputStream openInputStream() throws IOException {
130: if (DEBUG) {
131: System.out.println("server: openInputStream()");
132: }
133: synchronized (lock) {
134: if (stream.operationClosed) {
135: throw new IOException("operation closed");
136: }
137: if (inputStreamOpened) {
138: throw new IOException("no more input streams available");
139: }
140: inputStreamOpened = true;
141: return is;
142: }
143: }
144:
145: public void abort() throws IOException {
146: // forbidden on server
147: throw new IOException("not permitted");
148: }
149:
150: public HeaderSet getReceivedHeaders() throws IOException {
151: synchronized (lock) {
152: if (stream.operationClosed) {
153: throw new IOException("operation closed");
154: }
155: return new HeaderSetImpl(recvHeaders);
156: }
157: }
158:
159: public int getResponseCode() throws IOException {
160: // forbidden on server
161: throw new IOException("not permitted");
162: }
163:
164: public String getEncoding() {
165: return null; // acording to docs
166: }
167:
168: public long getLength() {
169: Long res = (Long) recvHeaders.getHeader(HeaderSetImpl.LENGTH);
170: if (res == null) {
171: return -1;
172: }
173: return res.longValue();
174: }
175:
176: public String getType() {
177: return (String) recvHeaders.getHeader(HeaderSetImpl.TYPE);
178: }
179:
180: public void close() {
181: stream.operationClosed = true;
182: }
183:
184: /**
185: * Called by ServerRequestHandler to finish any activity remaining
186: * and to return errorcode to client.
187: */
188: void destroy(int status) {
189: if (DEBUG) {
190: System.out.println("server: destroy()");
191: }
192: try {
193: outputStreamOpened = false;
194: if (!requestEnd) {
195: stream.packetBegin(head);
196: stream.packetAddConnectionID(stream.getConnectionID(),
197: null);
198: stream.packetAddAuthResponses();
199: }
200: stream.packetAddHeaders(null);
201: close();
202: if (isAborted)
203: status = ResponseCodes.OBEX_HTTP_OK;
204: stream.setPacketType(status);
205: stream.packetEnd(); // send final packet
206: } catch (Throwable t) {
207: // ignore
208: }
209: // remaining headers will be lost
210: stream.queuedHeaders.removeAllElements();
211: }
212:
213: public void sendHeaders(HeaderSet headers) throws IOException {
214: if (DEBUG) {
215: System.out.println("server: sendHeaders()");
216: }
217: synchronized (lock) {
218: if (stream.operationClosed) {
219: throw new IOException("operation closed");
220: }
221: if (headers == null) {
222: throw new NullPointerException("null headerset");
223: }
224: if (!(headers instanceof HeaderSetImpl)) {
225: throw new IllegalArgumentException(
226: "wrong headerset class");
227: }
228: HeaderSetImpl headersImpl = (HeaderSetImpl) headers;
229: if (!headersImpl.isSendable()) {
230: throw new IllegalArgumentException(
231: "not created with createHeaderSet");
232: }
233:
234: stream.packetAddHeaders(headersImpl);
235:
236: if (requestEnd && isGet) {
237: while (!stream.queuedHeaders.isEmpty()) {
238: packetExchange();
239: }
240: }
241: }
242: }
243:
244: private void packetExchange() throws IOException {
245: if (DEBUG) {
246: System.out.println("server: packetExchange()");
247: }
248: if (stream.operationHeadersOverflow) {
249: throw new IOException(
250: "operation terminated, too long headers");
251: }
252: if (!requestEnd) {
253: // reading out input stream
254: requestEnd = stream.packetType == (opcode | ObexPacketStream.OPCODE_FINAL);
255:
256: // inordenary case: EOF-DATA but no final bit
257: if (stream.isEof && !requestEnd) {
258: while (recvHeaders.packetType == ObexPacketStream.OPCODE_PUT) {
259: // not final - waiting for final, data not allowed
260: // after EOFB
261: stream.sendPacket(head, stream.getConnectionID(),
262: null, false);
263: stream.recvPacket();
264: stream.parsePacketHeaders(recvHeaders, 3);
265: }
266:
267: if (recvHeaders.packetType == ObexPacketStream.OPCODE_ABORT) {
268: stream.operationClosed = true;
269: isAborted = true;
270: throw new IOException("operation aborted");
271: }
272:
273: if (stream.packetType != (opcode | ObexPacketStream.OPCODE_FINAL)) {
274: stream.operationClosed = true;
275: stream.brokenLink();
276: throw new IOException("protocol error");
277: }
278: }
279: if (requestEnd) {
280: // switch to requestEnd packetExchange mode
281: stream.packetBegin(head);
282: stream.packetAddConnectionID(stream.getConnectionID(),
283: null);
284: stream.packetAddAuthResponses();
285: stream.packetAddHeaders(null);
286: return;
287: }
288: // stream.parseEnd();
289: stream.sendPacket(ObexPacketStream.PACKET_CONTINUE, stream
290: .getConnectionID(), null, false);
291: stream.recvPacket();
292:
293: if (stream.packetType == ObexPacketStream.OPCODE_ABORT) {
294: stream.parsePacketHeaders(recvHeaders, 3);
295: isAborted = true;
296: stream.operationClosed = true;
297: throw new IOException("operation aborted");
298: }
299:
300: if ((stream.packetType & ~ObexPacketStream.OPCODE_FINAL) != opcode) {
301: stream.operationClosed = true;
302: stream.brokenLink();
303: throw new IOException("protocol error");
304: }
305: stream.parsePacketDataBegin(recvHeaders, 3);
306: return;
307: }
308: stream.packetEnd();
309: stream.recvPacket();
310: stream.parsePacketHeaders(recvHeaders, 3);
311:
312: if (stream.packetType == ObexPacketStream.OPCODE_ABORT) {
313: // prepare response packet
314: stream.packetBegin(ObexPacketStream.PACKET_SUCCESS);
315: stream
316: .packetAddConnectionID(stream.getConnectionID(),
317: null);
318: stream.packetAddAuthResponses();
319: stream.packetAddHeaders(null);
320:
321: isAborted = true;
322: stream.operationClosed = true;
323: throw new IOException("operation aborted");
324: }
325:
326: if (stream.packetType == ObexPacketStream.OPCODE_DISCONNECT) {
327: stream.sendPacket(ObexPacketStream.PACKET_SUCCESS, stream
328: .getConnectionID(), null, false);
329:
330: stream.close();
331: return;
332: }
333:
334: if (stream.packetType != (opcode | ObexPacketStream.OPCODE_FINAL)) {
335: stream.operationClosed = true;
336: stream.brokenLink();
337: throw new IOException("protocol error");
338: }
339:
340: stream.packetBegin(head);
341: stream.packetAddConnectionID(stream.getConnectionID(), null);
342: stream.packetAddAuthResponses();
343: stream.packetAddHeaders(null);
344: }
345:
346: static int waitForData(ServerConnectionImpl stream,
347: HeaderSetImpl inputHeaderSet, int op) throws IOException {
348: if (DEBUG) {
349: System.out.println("server: waitForData()");
350: }
351:
352: // check of errorcode should be done before after data parsing
353: stream.parsePacketDataBegin(inputHeaderSet, 3);
354:
355: // special request to check data availability
356: int hasData = stream
357: .parsePacketData(inputHeaderSet, null, 0, 0);
358:
359: // waiting for data or final bit or abort
360: while (true) {
361: if (stream.packetType == ObexPacketStream.OPCODE_ABORT) {
362: return 0;
363: }
364:
365: if (hasData == 1 || stream.isEof) {
366: return 1; // has data
367: }
368:
369: if (stream.packetType == (op | ObexPacketStream.OPCODE_FINAL)) {
370: return 2; // final
371: }
372:
373: if (stream.packetType != op) {
374: stream.brokenLink();
375: throw new IOException("protocol error");
376: }
377:
378: stream.sendPacket(ObexPacketStream.PACKET_CONTINUE, stream
379: .getConnectionID(), null, false);
380: stream.recvPacket();
381:
382: // check of errorcode should be done before after data parsing
383: stream.parsePacketDataBegin(inputHeaderSet, 3);
384:
385: // special request to check data availability
386: hasData = stream
387: .parsePacketData(inputHeaderSet, null, 0, 0);
388: }
389: }
390:
391: private class OperationInputStream extends InputStream {
392: OperationInputStream() {
393: }
394:
395: public int read() throws IOException {
396: byte[] b = new byte[1];
397: int len = read(b, 0, 1);
398: if (len == -1) {
399: return -1;
400: }
401: return b[0] & 0xFF;
402: }
403:
404: public int read(byte[] b, int offset, int len)
405: throws IOException {
406: if (DEBUG) {
407: // System.out.println("server: is.read()");
408: }
409: synchronized (lock) {
410: if (!inputStreamOpened) {
411: throw new IOException("operation finished");
412: }
413: // Nullpointer check is here
414: if (len < 0 || offset < 0 || offset + len > b.length) {
415: throw new ArrayIndexOutOfBoundsException();
416: }
417:
418: if (len == 0) {
419: return 0;
420: }
421:
422: if (inputStreamEof) {
423: return -1;
424: }
425:
426: int result = 0;
427: while (true) {
428: int rd = stream.parsePacketData(recvHeaders, b,
429: offset, len);
430: if (rd != 0) {
431: offset += rd;
432: len -= rd;
433: result += rd;
434: if (len == 0)
435: return result;
436: }
437:
438: // need more data, packet is finished
439: packetExchange();
440: if (stream.isEof) {
441: inputStreamEof = true;
442: return (result == 0) ? -1 : result;
443: }
444: }
445: }
446: }
447:
448: public void close() throws IOException {
449: if (DEBUG) {
450: System.out.println("server: is.close()");
451: }
452: // errorcode unknown yet,
453: // ServerRequestHandler will send errorcode packet
454: synchronized (lock) {
455: inputStreamOpened = false;
456: inputStreamEof = false;
457: }
458: }
459: }
460:
461: private class OperationOutputStream extends OutputStream {
462: OperationOutputStream() {
463: }
464:
465: public void write(int b) throws IOException {
466: write(new byte[] { (byte) b }, 0, 1);
467: }
468:
469: public void write(byte[] b, int offset, int len)
470: throws IOException {
471: if (DEBUG) {
472: // System.out.println("server: os.write()");
473: }
474: synchronized (lock) {
475: if (!outputStreamOpened) {
476: throw new IOException("operation finished");
477: }
478: if (len < 0 || offset < 0 || offset + len > b.length) {
479: throw new ArrayIndexOutOfBoundsException();
480: }
481: while (len > 0) {
482: int wr = stream.packetAddData(b, offset, len);
483: if (wr != len) {
484: packetExchange();
485: }
486: len -= wr;
487: offset += wr;
488: }
489: }
490: }
491:
492: public void flush() throws IOException {
493: if (DEBUG) {
494: System.out.println("server: os.flush()");
495: }
496: synchronized (lock) {
497: if (!outputStreamOpened) {
498: throw new IOException("operation finished");
499: }
500: packetExchange();
501: }
502: }
503:
504: public void close() throws IOException {
505: if (DEBUG) {
506: System.out.println("server: os.close()");
507: }
508:
509: synchronized (lock) {
510: if (outputStreamOpened) {
511: outputStreamOpened = false;
512: boolean res = stream.packetEOFBody();
513: if (!res) { // error adding EOFB previous packet too long
514: packetExchange();
515: stream.packetEOFBody();
516: }
517: }
518: // Unknown errorcode yet, not sending: stream.packetEnd();
519: }
520: }
521: }
522: }
|