001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements. See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership. The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License. You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied. See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019
020 package org.apache.geronimo.deployment.plugin.remote;
021
022 import java.io.BufferedInputStream;
023 import java.io.BufferedOutputStream;
024 import java.io.DataInputStream;
025 import java.io.DataOutputStream;
026 import java.io.File;
027 import java.io.FileInputStream;
028 import java.io.FileNotFoundException;
029 import java.io.IOException;
030 import java.net.URL;
031 import java.net.URLConnection;
032 import java.util.Iterator;
033 import java.util.LinkedList;
034 import java.util.List;
035 import java.util.Set;
036
037 import org.apache.commons.logging.Log;
038 import org.apache.commons.logging.LogFactory;
039 import org.apache.geronimo.gbean.AbstractName;
040 import org.apache.geronimo.gbean.AbstractNameQuery;
041 import org.apache.geronimo.kernel.Kernel;
042 import org.apache.geronimo.crypto.encoders.Base64;
043
044 /**
045 *
046 * @version $Rev: 706640 $ $Date: 2008-10-21 14:44:05 +0000 (Tue, 21 Oct 2008) $
047 */
048 public class FileUploadServletClient implements FileUploadClient {
049 private static final Log log = LogFactory.getLog(FileUploadServletClient.class);
050
051 /** Note: The below versions should be kept in sync with those in FileUploadServlet.java **/
052 // Starting RemoteDeploy datastream versions
053 public static final int REMOTE_DEPLOY_REQUEST_VER_0 = 0;
054 public static final int REMOTE_DEPLOY_RESPONSE_VER_0 = 0;
055 // Current RemoteDeploy datastream versions
056 public static final int REMOTE_DEPLOY_REQUEST_VER = 0;
057 public static final int REMOTE_DEPLOY_RESPONSE_VER = 0;
058
059 public URL getRemoteDeployUploadURL(Kernel kernel) {
060 AbstractName deployerName = getDeployerName(kernel);
061 String remoteDeployUpload;
062 try {
063 remoteDeployUpload = (String) kernel.getAttribute(deployerName, "remoteDeployUploadURL");
064 return new URL(remoteDeployUpload);
065 } catch (Exception e) {
066 throw new AssertionError(e);
067 }
068 }
069
070 protected AbstractName getDeployerName(Kernel kernel) {
071 Set<AbstractName> deployerNames = kernel.listGBeans(new AbstractNameQuery("org.apache.geronimo.deployment.Deployer"));
072 if (1 != deployerNames.size()) {
073 throw new IllegalStateException("No Deployer GBean present in running Geronimo server. " +
074 "This usually indicates a serious problem with the configuration of " +
075 "your running Geronimo server. If " +
076 "the deployer is present but not started, the workaround is to run " +
077 "a deploy command like 'start geronimo/geronimo-gbean-deployer/1.0/car'. " +
078 "If the deployer service is not present at all (it was undeployed) then " +
079 "you need to either re-install Geronimo or get a deployment plan for the " +
080 "runtime deployer and distribute it while the server is not running and " +
081 "then start the server with a command like the above. For help on this, " +
082 "write to user@geronimo.apache.org and include the contents of your " +
083 "var/config/config.xml file.");
084 }
085 return deployerNames.iterator().next();
086 }
087
088 public void uploadFilesToServer(URL uploadURL,
089 String username,
090 String password,
091 File[] files,
092 FileUploadProgress progress) {
093 if(files == null) {
094 return;
095 }
096
097 List valid = new LinkedList();
098 for(int i=0; i<files.length; i++) {
099 if(files[i] == null) {
100 continue;
101 }
102 File file = files[i];
103 if(!file.exists() || !file.canRead()) {
104 continue;
105 }
106 valid.add(new Integer(i));
107 }
108
109 if(valid.size() > 0) {
110 progress.updateStatus("Uploading "+valid.size()+" file(s) to server");
111 if (log.isDebugEnabled()) {
112 log.debug("Uploading "+valid.size()+" file(s) to server");
113 }
114 try {
115 URLConnection con = connectToServer(uploadURL, username, password);
116 writeRequest(con, files, valid, progress);
117 readResponse(con, files, valid, progress);
118 } catch (Exception e) {
119 progress.fail(e);
120 }
121 }
122 }
123
124 protected void readResponse(URLConnection con, File[] files, List valid, FileUploadProgress progress)
125 throws IOException {
126 /* ---------------------
127 * RemoteDeploy Response
128 * ---------------------
129 *
130 * Note: The below code has to match FileUploadServlet.java
131 *
132 * RemoteDeployer response stream format:
133 * It returns a serialized stream containing:
134 * 0) an int, the version of this datastream format - REMOTE_DEPLOY_RESPONSE_VER
135 * 1) a UTF string, the status (should be "OK")
136 * 2) an int, the number of files received
137 * 3) for each file:
138 * 3.1) a UTF String, the path to the file as saved to the server's filesystem
139 * x) new data would be added here
140 *
141 * The file positions in the response will be the same as in the request.
142 * That is, a name for upload file #2 will be in response position #2.
143 */
144 DataInputStream in = new DataInputStream(new BufferedInputStream(con.getInputStream()));
145 // 0) an int, the version of this datastream format - REMOTE_DEPLOY_RESPONSE_VER
146 int rspVer = in.readInt();
147 // whenever we update the stream version, the next line needs to
148 // be changed to just - (rspVer >= REMOTE_DEPLOY_RESPONSE_VER_0)
149 // but until then, be more restrictive so we can handle old servers
150 // that don't send a version as the first thing, but UTF instead...
151 if ((rspVer >= REMOTE_DEPLOY_RESPONSE_VER_0) && (rspVer <= REMOTE_DEPLOY_RESPONSE_VER)) {
152 // 1) a UTF string, the status (should be "OK")
153 String status = in.readUTF();
154 if(!status.equals("OK")) {
155 progress.fail("Unable to upload files to server. Server returned status="+status);
156 log.error("Unable to upload files to server. Server returned status="+status);
157 return;
158 }
159 progress.updateStatus("File upload complete (Server status="+status+")");
160 if (log.isDebugEnabled()) {
161 log.debug("File upload complete (Server status="+status+")");
162 }
163 // 2) an int, the number of files received
164 int count = in.readInt();
165 if(count != valid.size()) {
166 progress.fail("Server only received "+count+" of "+valid.size()+" files");
167 log.warn("Server only received "+count+" of "+valid.size()+" files");
168 }
169 // 3) for each file:
170 for (Iterator it = valid.iterator(); it.hasNext();) {
171 Integer index = (Integer) it.next();
172 // 3.1) a UTF String, the path to the file as saved to the server's filesystem
173 String serverFileName = in.readUTF();
174 if (serverFileName != null) {
175 files[index.intValue()] = new File(serverFileName);
176 } else {
177 log.error("Received an invalid filename from the server");
178 files[index.intValue()] = null;
179 }
180 if (log.isDebugEnabled()) {
181 log.debug("Server created file="+serverFileName);
182 }
183 }
184 // x) new data would be added here
185 if (rspVer > REMOTE_DEPLOY_RESPONSE_VER_0) {
186 // additions in later datastream versions would be handled here
187
188 if (rspVer > REMOTE_DEPLOY_RESPONSE_VER) {
189 // if the server is sending a newer version than we know about
190 // just ignore it and warn the user about the mismatch
191 log.warn("Received a newer server response ("+rspVer+") than expected ("+REMOTE_DEPLOY_RESPONSE_VER+"). Ignoring any additional server response data.");
192 }
193 }
194 } else {
195 // should never happen, but handle it anyway
196 progress.fail("Received unknown server response version="+rspVer);
197 log.warn("Received unknown server response version="+rspVer);
198 }
199 in.close();
200 progress.updateStatus("File(s) transferred to server. Resuming deployment operation.");
201 }
202
203 protected void writeRequest(URLConnection con, File[] files, List valid, FileUploadProgress progress)
204 throws IOException, FileNotFoundException {
205 /* --------------------
206 * RemoteDeploy Request
207 * --------------------
208 *
209 * Note: The below code has to match FileUploadServlet.java
210 *
211 * RemoteDeployer data stream format:
212 * 0) an int, the version of this datastream format - REMOTE_DEPLOY_REQUEST_VER
213 * 1) an int, the number of files being uploaded
214 * 2) for each file:
215 * 2.0) a UTF String, the filename of the file being uploaded
216 * 2.1) a long, the length of the file in bytes
217 * 2.2) byte[], byte count equal to the number above for the file
218 */
219 DataOutputStream out = new DataOutputStream(new BufferedOutputStream(con.getOutputStream()));
220 // 0) an int, the version of this datastream format - REMOTE_DEPLOY_REQUEST_VER
221 out.writeInt(REMOTE_DEPLOY_REQUEST_VER);
222 // 1) an int, the number of files being uploaded
223 out.writeInt(valid.size());
224 byte[] buf = new byte[1024];
225 int size;
226 long total, length, threshold, next;
227 // 2) for each file:
228 for (Iterator it = valid.iterator(); it.hasNext();) {
229 Integer index = (Integer) it.next();
230 File file = files[index.intValue()];
231 // 2.0) a UTF String, the filename of the file being uploaded
232 out.writeUTF(file.getName().trim());
233 // 2.1) a long, the length of the file in bytes
234 out.writeLong(length = file.length());
235 threshold = Math.max(length / 100, (long)10240);
236 next = threshold;
237 BufferedInputStream in = new BufferedInputStream(new FileInputStream(file));
238 if (log.isDebugEnabled()) {
239 log.debug("Uploading "+file.getName());
240 }
241 total = 0;
242 // 2.2) raw bytes, equal to the number above for the file
243 while((size = in.read(buf)) > -1) {
244 out.write(buf, 0, size);
245 total += size;
246 if(total > next) {
247 progress.updateStatus("Uploading "+file.getName()+": "+(total/1024)+" KB");
248 while(total > next) next += threshold;
249 }
250 }
251 in.close();
252 }
253 out.flush();
254 out.close();
255 }
256
257 protected URLConnection connectToServer(URL url, String username, String password) throws IOException {
258 URLConnection con = url.openConnection();
259 String auth = username + ":" + password;
260 byte[] data = auth.getBytes();
261 String s = new String(Base64.encode(data));
262 while(s.length() % 4 != 0) s += "=";
263 con.setRequestProperty("Authorization", "Basic "+s);
264 con.setDoInput(true);
265 con.setDoOutput(true);
266 con.connect();
267 return con;
268 }
269
270 }