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 }