001    /**
002     *  Licensed to the Apache Software Foundation (ASF) under one or more
003     *  contributor license agreements.  See the NOTICE file distributed with
004     *  this work for additional information regarding copyright ownership.
005     *  The ASF licenses this file to You under the Apache License, Version 2.0
006     *  (the "License"); you may not use this file except in compliance with
007     *  the License.  You may obtain a copy of the License at
008     *
009     *     http://www.apache.org/licenses/LICENSE-2.0
010     *
011     *  Unless required by applicable law or agreed to in writing, software
012     *  distributed under the License is distributed on an "AS IS" BASIS,
013     *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     *  See the License for the specific language governing permissions and
015     *  limitations under the License.
016     */
017    package org.apache.geronimo.deployment.remote;
018    
019    import javax.servlet.http.HttpServlet;
020    import javax.servlet.http.HttpServletRequest;
021    import javax.servlet.http.HttpServletResponse;
022    import javax.servlet.ServletException;
023    import java.io.DataOutputStream;
024    import java.io.DataInputStream;
025    import java.io.File;
026    import java.io.IOException;
027    import java.io.BufferedOutputStream;
028    import java.io.FileOutputStream;
029    
030    /**
031     * A servlet that accepts file uploads.  It takes only POST requests, which should
032     * contain a Java "DataOutput" formatted stream from RemoteDeployUtil containing:
033     *
034     * RemoteDeployer data stream format:
035     *   0) an int, the version of this datastream format - REMOTE_DEPLOY_REQUEST_VER
036     *   1) an int, the number of files being uploaded
037     *   2) for each file:
038     *     2.0) a UTF String, the filename of the file being uploaded
039     *     2.1) a long, the length of the file in bytes
040     *     2.2) byte[], byte count equal to the number above for the file
041     *
042     * RemoteDeployer response stream format:
043     *   It returns a serialized stream containing:
044     *   0) an int, the version of this datastream format - REMOTE_DEPLOY_RESPONSE_VER
045     *   1) a UTF string, the status (should be "OK")
046     *   2) an int, the number of files received
047     *   3) for each file:
048     *     3.1) a UTF String, the path to the file as saved to the server's filesystem
049     *
050     *   The file positions in the response will be the same as in the request.
051     *   That is, a name for upload file #2 will be in response position #2.
052     *
053     * @version $Rev: 706640 $ $Date: 2008-10-21 14:44:05 +0000 (Tue, 21 Oct 2008) $
054     */
055    public class FileUploadServlet extends HttpServlet {
056    
057        /** Note:  The below versions should be kept in sync with those in RemoteDeployUtil.java **/
058        // Starting RemoteDeploy datastream versions
059        public static final int REMOTE_DEPLOY_REQUEST_VER_0 = 0;
060        public static final int REMOTE_DEPLOY_RESPONSE_VER_0 = 0;
061        // Current RemoteDeploy datastream versions
062        public static final int REMOTE_DEPLOY_REQUEST_VER = 0;
063        public static final int REMOTE_DEPLOY_RESPONSE_VER = 0;
064    
065    
066        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
067            int fileCount = 0, filesCreated = 0;
068            String names[] = null;
069            String status = "OK";
070    
071            /* --------------------
072             * RemoteDeploy Request
073             * --------------------
074             *
075             * Note:  The below code has to match RemoteDeployUtil.java
076             *
077             * RemoteDeployer data stream format:
078             *   0) an int, the version of this datastream format - REMOTE_DEPLOY_REQUEST_VER
079             *   1) an int, the number of files being uploaded
080             *   2) for each file:
081             *     2.0) a UTF String, the filename of the file being uploaded
082             *     2.1) a long, the length of the file in bytes
083             *     2.2) byte[], byte count equal to the number above for the file
084             */
085            DataInputStream in = null;
086            try {
087                String fileName;
088                in = new DataInputStream(request.getInputStream());
089                // 0) an int, the version of this datastream format - REMOTE_DEPLOY_REQUEST_VER
090                int reqVer = in.readInt();
091                // whenever we update the stream version, the next line needs to
092                // be changed to just - (reqVer >= REMOTE_DEPLOY_REQUEST_VER_0)
093                // but until then, be more restrictive so we can handle old deployers
094                // that don't send a version as the first thing, but a file count instead...
095                if ((reqVer >= REMOTE_DEPLOY_REQUEST_VER_0) && (reqVer <= REMOTE_DEPLOY_REQUEST_VER)) {
096                    // 1) an int, the number of files being uploaded
097                    fileCount = in.readInt();
098                    names = new String[fileCount];
099                    // 2) for each file:
100                    for(int i=0; i<fileCount; i++) {
101                        // 2.0) a UTF String, the filename of the file being uploaded
102                        fileName = in.readUTF();
103                        // 2.1) a long, the length of the file in bytes
104                        long length = in.readLong();
105                        // create the local temp file
106                        //File temp = File.createTempFile("remote-deploy", "");
107                        // Note: Doing this because WAR files have to be their original names to
108                        // handle the case where no web.xml or context root was provided
109                        File temp = new File(System.getProperty("java.io.tmpdir"), fileName.trim());
110                        temp.createNewFile();
111                        temp.deleteOnExit();
112                        names[i] = temp.getAbsolutePath();
113                        // 2.2) raw bytes, equal to the number above for the file
114                        readToFile(in, temp, length);
115                        filesCreated++;
116                    }
117                }
118            } catch (IOException e) {
119                status = "ERROR: "+e.getMessage();
120            } finally {
121                if (in != null) {
122                    in.close();
123                    in = null;
124                }
125            }
126    
127            /* ---------------------
128             * RemoteDeploy Response
129             * ---------------------
130             *
131             * Note:  The below code has to match RemoteDeployUtil.java
132             *
133             * RemoteDeployer response stream format:
134             *   It returns a serialized stream containing:
135             *   0) an int, the version of this datastream format - REMOTE_DEPLOY_RESPONSE_VER
136             *   1) a UTF string, the status (should be "OK")
137             *   2) an int, the number of files received
138             *   3) for each file:
139             *     3.1) a UTF String, the path to the file as saved to the server's filesystem
140             *   x) new data would be added here
141             *
142             *   The file positions in the response will be the same as in the request.
143             *   That is, a name for upload file #2 will be in response position #2.
144             */
145            DataOutputStream out = null;
146            try {
147                out = new DataOutputStream(response.getOutputStream());
148                // 0) an int, the version of this datastream format - REMOTE_DEPLOY_RESPONSE_VER
149                out.writeInt(REMOTE_DEPLOY_RESPONSE_VER);
150                // 1) a UTF string, the status (should be "OK")
151                out.writeUTF(status);
152                if (filesCreated == fileCount) {
153                    // 2) an int, the number of files received
154                    out.writeInt(fileCount);
155                    // 3) for each file:
156                    for (int i = 0; i < names.length; i++) {
157                        // 3.1) a UTF String, the path to the file as saved to the server's filesystem
158                        out.writeUTF(names[i]);
159                    }
160                    // x) new data would be added here
161                    // only send newer data depending on the REQUEST_VER that came in
162                } else {
163                    // error occurred, so don't send back any filenames, just a zero count
164                    // 2) an int, the number of files received
165                    out.writeInt(0);
166                }
167            } finally {
168                if (out != null) {
169                    out.flush();
170                    out.close();
171                    out = null;
172                }
173            }
174        }
175    
176        private static void readToFile(DataInputStream in, File temp, long length) throws IOException {
177            BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(temp));
178            int read;
179            long total;
180            try {
181                byte[] buf = new byte[8192];
182                total = 0;
183                while((read = in.read(buf, 0, (int)Math.min(buf.length, length - total))) > -1) {
184                    out.write(buf, 0, read);
185                    total += read;
186                    if(total == length) {
187                        break;
188                    }
189                }
190            } finally {
191                try {out.flush();} catch (IOException e) {}
192                out.close();
193                out = null;
194            }
195            if(total != length) {
196                throw new IOException("Unable to read entire upload file ("+total+"B expecting "+length+"B)");
197            }
198        }
199    }
200