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