View Javadoc

1   /****************************************************************
2    * Licensed to the Apache Software Foundation (ASF) under one   *
3    * or more contributor license agreements.  See the NOTICE file *
4    * distributed with this work for additional information        *
5    * regarding copyright ownership.  The ASF licenses this file   *
6    * to you under the Apache License, Version 2.0 (the            *
7    * "License"); you may not use this file except in compliance   *
8    * with the License.  You may obtain a copy of the License at   *
9    *                                                              *
10   *   http://www.apache.org/licenses/LICENSE-2.0                 *
11   *                                                              *
12   * Unless required by applicable law or agreed to in writing,   *
13   * software distributed under the License is distributed on an  *
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
15   * KIND, either express or implied.  See the License for the    *
16   * specific language governing permissions and limitations      *
17   * under the License.                                           *
18   ****************************************************************/
19  
20  package org.apache.james.mpt;
21  
22  import java.nio.ByteBuffer;
23  import java.nio.channels.SocketChannel;
24  import java.nio.charset.Charset;
25  
26  final class ExternalSession implements Session {
27  
28      /** Number of milliseconds to sleep after empty read */
29      private static final int SHORT_WAIT_FOR_INPUT = 10;
30  
31      private static final byte[] CRLF = { '\r', '\n' };
32  
33      private final SocketChannel socket;
34  
35      private final Monitor monitor;
36  
37      private final ByteBuffer readBuffer;
38  
39      private final Charset ascii;
40  
41      private final ByteBuffer lineEndBuffer;
42  
43      private boolean first = true;
44  
45      private final String shabang;
46      
47      public ExternalSession(final SocketChannel socket, final Monitor monitor, String shabang) {
48          this(socket, monitor, shabang, false);
49      }
50  
51      public ExternalSession(final SocketChannel socket, final Monitor monitor, String shabang, boolean debug) {
52          super();
53          this.socket = socket;
54          this.monitor = monitor;
55          readBuffer = ByteBuffer.allocateDirect(2048);
56          ascii = Charset.forName("US-ASCII");
57          lineEndBuffer = ByteBuffer.wrap(CRLF);
58          this.shabang = shabang;
59      }
60  
61      public String readLine() throws Exception {
62          StringBuffer buffer = new StringBuffer();
63          readlineInto(buffer);
64          final String result;
65          if (first && shabang != null) {
66              // fake shabang
67              monitor.note("<-" + buffer.toString());
68              result = shabang;
69              first = false;
70          } else {
71              result = buffer.toString();
72              monitor.note("<-" + result);
73          }
74          return result;
75      }
76  
77      private void readlineInto(StringBuffer buffer) throws Exception {
78          monitor.debug("[Reading line]");
79          readBuffer.flip();
80          while (oneFromLine(buffer))
81              ;
82  //      May have partial read
83          readBuffer.compact();
84          monitor.debug("[Done]");
85      }
86  
87      private boolean oneFromLine(StringBuffer buffer) throws Exception {
88          final boolean result;
89          if (readBuffer.hasRemaining()) {
90              char next = (char) readBuffer.get();
91              if (next == '\n') {
92                  monitor.debug("[LF]");
93  //              Reached end of the line
94                  result = false;
95              } else if (next == '\r') {
96  //              CRLF line endings so drop
97                  monitor.debug("[CR]");
98                  result = true;
99              } else {
100 //              Load buffer
101                 monitor.debug(next);
102                 buffer.append(next);
103                 result = true;
104             }
105         } else {
106             monitor.debug("[Reading into buffer]");
107             readBuffer.clear();
108             while (socket.read(readBuffer) == 0) {
109 //              No response yet
110 //              Wait a little while
111                 Thread.sleep(SHORT_WAIT_FOR_INPUT);
112             }
113 //          Reset for transfer into string buffer
114             readBuffer.flip();
115             monitor.debug("[Done]");
116             result = true;
117         }
118         return result;
119     }
120 
121     public void start() throws Exception {
122         while (!socket.finishConnect()) {
123             monitor.note("connecting...");
124             Thread.sleep(10);
125         }
126     }
127 
128     public void stop() throws Exception {
129         monitor.note("closing");
130         socket.close();
131     }
132 
133     public void writeLine(String line) throws Exception {
134         monitor.note("-> " + line);
135         monitor.debug("[Writing line]");
136         ByteBuffer writeBuffer = ascii.encode(line);
137         while (writeBuffer.hasRemaining()) {
138             socket.write(writeBuffer);
139         }
140         lineEndBuffer.rewind();
141         while (lineEndBuffer.hasRemaining()) {
142             socket.write(lineEndBuffer);
143         }
144         monitor.debug("[Done]");
145     }
146 
147     /**
148      * Constructs a <code>String</code> with all attributes
149      * in name = value format.
150      *
151      * @return a <code>String</code> representation 
152      * of this object.
153      */
154     public String toString()
155     {
156         final String TAB = " ";
157         
158         String result =  "External ( "
159             + "socket = " + this.socket + TAB
160             + "monitor = " + this.monitor + TAB
161             + "readBuffer = " + this.readBuffer + TAB
162             + "ascii = " + this.ascii + TAB
163             + "lineEndBuffer = " + this.lineEndBuffer + TAB
164             + "first = " + this.first + TAB
165             + "shabang = " + this.shabang + TAB
166             + " )";
167     
168         return result;
169     }
170     
171     
172 }