I've been asked to provide more information, so here is combination of the two messages I posted with some more commentary and attachments. It pertains to Tomcat-3.2.1 and looks to be the same in 3.2.2.b4. I'm running Apache 1.3.17 on Win 2K Professional. I'm also using mod_jk I have some client code that sends a jar file to the servlet. The jar file was getting corrupted. After much digging, I found a CVS commit to Ajp13ConnectorRequest.java that mentioned a problem like this with the doRead() method. It turns out the the same applies to the doRead(byte[], int, int) method. The same problem exists in the Ajp12ConnectionHandler for that byte array read. Single byte reads for both protocols work just fine. I'm including the diffs for these classes to show what I'm talking about. I finally got out from under some work and was able to make some test code. I'm attaching the client and servlet code. The code transfers a couple parameters, then a binary file (I was using a .jar). If you call the client with "BinTestClient localhost something.jar b", it uses byte-by-byte read on the server to spool the file to a temp file. If you call the client without the 'b', it uses the byte-array read that I was complaining about. Transfer a file, then try "jar tvf testXXXX.jar" to see if it works. I uses a jar that contains .jpg images and when using the byte array read method, it creats a corrupt jar file. If I apply my fix to the Ajp13ConnectorRequest class, it works fine. (I tried a jar that contained class files and it worked anyway...) I'd like for someone else to try this out to make sure I didn't screw something up. The code seems pretty simple. I discovered this when using JarIn/OutputStream to transfer data from client to servlet.I've seen this type of thing in Java before when writing code that talks to hardware (such as touchscreen driver and scanner drivers). David
Index: Ajp13ConnectorRequest.java =================================================================== RCS file: /home/cvspublic/jakarta-tomcat/src/share/org/apache/tomcat/service/connector/Attic/Ajp13ConnectorRequest.java,v retrieving revision 1.5.2.7 diff -r1.5.2.7 Ajp13ConnectorRequest.java 274c274,277 < System.arraycopy(bodyBuff, pos, b, off, c); --- > //System.arraycopy(bodyBuff, pos, b, off, c); > for (int i=pos, j=off, d=c; d > 0; i++, j++, d--) { > b[j] = (byte)(((char)bodyBuff[i])&0xff); > } What I've done here is to replace the array copy with a loop that does the appropriate data conversion. Index: Ajp12ConnectionHandler.java =================================================================== RCS file: /home/cvspublic/jakarta-tomcat/src/share/org/apache/tomcat/service/connector/Attic/Ajp12ConnectionHandler.java,v retrieving revision 1.28.2.4 diff -r1.28.2.4 Ajp12ConnectionHandler.java 542a543,549 > public int read(byte b[], int off, int len) throws IOException { > int ret = super.read(b, off, len); > for (int i=0, j=off; i<len; i++, j++) { > b[j] = (byte)(((char)b[j])&0xff); > } > return ret; > } In this case, I over-rode the read method to convert the data after calling the super.read
import java.io.DataOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.InputStream; import java.io.OutputStream; import java.net.URL; import java.net.URLConnection; // args[0] = hostname // args[1] = jarfile // args[2] = 'b' for single byte read. public class BinTestClient { public static void main(String [] args) { try { URL url = new URL("http://"+args[0]+"/examples/BinTest"); URLConnection connection = (URLConnection)url.openConnection(); connection.setDoOutput(true); connection.setUseCaches(false); DataOutputStream output = new DataOutputStream(connection.getOutputStream()); File jarFile = new File(args[1]); if (jarFile.exists()) { output.writeUTF(""+jarFile.length()); } if (args.length > 2 && args[2] != null && args[2].trim().equals("b")) output.writeChar('b'); else output.writeChar(' '); InputStream istr = new FileInputStream(jarFile); byte [] buf = new byte[8192]; int count = istr.read(buf); while (count != -1) { if (count > 0) output.write(buf, 0, count); count = istr.read(buf); } istr.close(); output.flush(); output.close(); istr = connection.getInputStream(); istr.read(); } catch (Exception ex) { ex.printStackTrace(); } } }
import java.io.DataInputStream; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.InputStream; import java.io.IOException; import java.io.OutputStream; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class BinTestServlet extends HttpServlet{ public void doPost (HttpServletRequest request, HttpServletResponse response) { try { DataInputStream istr = new DataInputStream(request.getInputStream()); long fileLen = Long.parseLong(istr.readUTF()); char mode = istr.readChar(); File tmp = File.createTempFile("test", ".jar"); OutputStream fstr = new FileOutputStream(tmp); if (mode == 'b') { System.out.println("Using byte-by-byte read"); for (int i=0; i<fileLen; i++) fstr.write(istr.read()); } else { System.out.println("Using byte-array read"); byte [] buf = new byte[8192]; int count = istr.read(buf); while (count != -1) { if (count > 0) fstr.write(buf, 0, count); count = istr.read(buf); } } fstr.flush(); fstr.close(); OutputStream ostr = response.getOutputStream(); ostr.write(1); // positive response } catch (Exception ex) { ex.printStackTrace(); } } }