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:$ $Date:$
047: */
048: public class FileUploadServletClient implements FileUploadClient {
049: private static final Log log = LogFactory
050: .getLog(FileUploadServletClient.class);
051:
052: /** Note: The below versions should be kept in sync with those in FileUploadServlet.java **/
053: // Starting RemoteDeploy datastream versions
054: public static final int REMOTE_DEPLOY_REQUEST_VER_0 = 0;
055: public static final int REMOTE_DEPLOY_RESPONSE_VER_0 = 0;
056: // Current RemoteDeploy datastream versions
057: public static final int REMOTE_DEPLOY_REQUEST_VER = 0;
058: public static final int REMOTE_DEPLOY_RESPONSE_VER = 0;
059:
060: public URL getRemoteDeployUploadURL(Kernel kernel) {
061: AbstractName deployerName = getDeployerName(kernel);
062: String remoteDeployUpload;
063: try {
064: remoteDeployUpload = (String) kernel.getAttribute(
065: deployerName, "remoteDeployUploadURL");
066: return new URL(remoteDeployUpload);
067: } catch (Exception e) {
068: throw new AssertionError(e);
069: }
070: }
071:
072: protected AbstractName getDeployerName(Kernel kernel) {
073: Set<AbstractName> deployerNames = kernel
074: .listGBeans(new AbstractNameQuery(
075: "org.apache.geronimo.deployment.Deployer"));
076: if (1 != deployerNames.size()) {
077: throw new IllegalStateException(
078: "No Deployer GBean present in running Geronimo server. "
079: + "This usually indicates a serious problem with the configuration of "
080: + "your running Geronimo server. If "
081: + "the deployer is present but not started, the workaround is to run "
082: + "a deploy command like 'start geronimo/geronimo-gbean-deployer/1.0/car'. "
083: + "If the deployer service is not present at all (it was undeployed) then "
084: + "you need to either re-install Geronimo or get a deployment plan for the "
085: + "runtime deployer and distribute it while the server is not running and "
086: + "then start the server with a command like the above. For help on this, "
087: + "write to user@geronimo.apache.org and include the contents of your "
088: + "var/config/config.xml file.");
089: }
090: return deployerNames.iterator().next();
091: }
092:
093: public void uploadFilesToServer(URL uploadURL, String username,
094: String password, File[] files, FileUploadProgress progress) {
095: if (files == null) {
096: return;
097: }
098:
099: List valid = new LinkedList();
100: for (int i = 0; i < files.length; i++) {
101: if (files[i] == null) {
102: continue;
103: }
104: File file = files[i];
105: if (!file.exists() || !file.canRead()) {
106: continue;
107: }
108: valid.add(new Integer(i));
109: }
110:
111: if (valid.size() > 0) {
112: progress.updateStatus("Uploading " + valid.size()
113: + " file(s) to server");
114: if (log.isDebugEnabled()) {
115: log.debug("Uploading " + valid.size()
116: + " file(s) to server");
117: }
118: try {
119: URLConnection con = connectToServer(uploadURL,
120: username, password);
121: writeRequest(con, files, valid, progress);
122: readResponse(con, files, valid, progress);
123: } catch (Exception e) {
124: progress.fail(e);
125: }
126: }
127: }
128:
129: protected void readResponse(URLConnection con, File[] files,
130: List valid, FileUploadProgress progress) throws IOException {
131: /* ---------------------
132: * RemoteDeploy Response
133: * ---------------------
134: *
135: * Note: The below code has to match FileUploadServlet.java
136: *
137: * RemoteDeployer response stream format:
138: * It returns a serialized stream containing:
139: * 0) an int, the version of this datastream format - REMOTE_DEPLOY_RESPONSE_VER
140: * 1) a UTF string, the status (should be "OK")
141: * 2) an int, the number of files received
142: * 3) for each file:
143: * 3.1) a UTF String, the path to the file as saved to the server's filesystem
144: * x) new data would be added here
145: *
146: * The file positions in the response will be the same as in the request.
147: * That is, a name for upload file #2 will be in response position #2.
148: */
149: DataInputStream in = new DataInputStream(
150: new BufferedInputStream(con.getInputStream()));
151: // 0) an int, the version of this datastream format - REMOTE_DEPLOY_RESPONSE_VER
152: int rspVer = in.readInt();
153: // whenever we update the stream version, the next line needs to
154: // be changed to just - (rspVer >= REMOTE_DEPLOY_RESPONSE_VER_0)
155: // but until then, be more restrictive so we can handle old servers
156: // that don't send a version as the first thing, but UTF instead...
157: if ((rspVer >= REMOTE_DEPLOY_RESPONSE_VER_0)
158: && (rspVer <= REMOTE_DEPLOY_RESPONSE_VER)) {
159: // 1) a UTF string, the status (should be "OK")
160: String status = in.readUTF();
161: if (!status.equals("OK")) {
162: progress
163: .fail("Unable to upload files to server. Server returned status="
164: + status);
165: log
166: .error("Unable to upload files to server. Server returned status="
167: + status);
168: return;
169: }
170: progress
171: .updateStatus("File upload complete (Server status="
172: + status + ")");
173: if (log.isDebugEnabled()) {
174: log.debug("File upload complete (Server status="
175: + status + ")");
176: }
177: // 2) an int, the number of files received
178: int count = in.readInt();
179: if (count != valid.size()) {
180: progress.fail("Server only received " + count + " of "
181: + valid.size() + " files");
182: log.warn("Server only received " + count + " of "
183: + valid.size() + " files");
184: }
185: // 3) for each file:
186: for (Iterator it = valid.iterator(); it.hasNext();) {
187: Integer index = (Integer) it.next();
188: // 3.1) a UTF String, the path to the file as saved to the server's filesystem
189: String serverFileName = in.readUTF();
190: if (serverFileName != null) {
191: files[index.intValue()] = new File(serverFileName);
192: } else {
193: log
194: .error("Received an invalid filename from the server");
195: files[index.intValue()] = null;
196: }
197: if (log.isDebugEnabled()) {
198: log.debug("Server created file=" + serverFileName);
199: }
200: }
201: // x) new data would be added here
202: if (rspVer > REMOTE_DEPLOY_RESPONSE_VER_0) {
203: // additions in later datastream versions would be handled here
204:
205: if (rspVer > REMOTE_DEPLOY_RESPONSE_VER) {
206: // if the server is sending a newer version than we know about
207: // just ignore it and warn the user about the mismatch
208: log
209: .warn("Received a newer server response ("
210: + rspVer
211: + ") than expected ("
212: + REMOTE_DEPLOY_RESPONSE_VER
213: + "). Ignoring any additional server response data.");
214: }
215: }
216: } else {
217: // should never happen, but handle it anyway
218: progress.fail("Received unknown server response version="
219: + rspVer);
220: log.warn("Received unknown server response version="
221: + rspVer);
222: }
223: in.close();
224: progress
225: .updateStatus("File(s) transferred to server. Resuming deployment operation.");
226: }
227:
228: protected void writeRequest(URLConnection con, File[] files,
229: List valid, FileUploadProgress progress)
230: throws IOException, FileNotFoundException {
231: /* --------------------
232: * RemoteDeploy Request
233: * --------------------
234: *
235: * Note: The below code has to match FileUploadServlet.java
236: *
237: * RemoteDeployer data stream format:
238: * 0) an int, the version of this datastream format - REMOTE_DEPLOY_REQUEST_VER
239: * 1) an int, the number of files being uploaded
240: * 2) for each file:
241: * 2.0) a UTF String, the filename of the file being uploaded
242: * 2.1) a long, the length of the file in bytes
243: * 2.2) byte[], byte count equal to the number above for the file
244: */
245: DataOutputStream out = new DataOutputStream(
246: new BufferedOutputStream(con.getOutputStream()));
247: // 0) an int, the version of this datastream format - REMOTE_DEPLOY_REQUEST_VER
248: out.writeInt(REMOTE_DEPLOY_REQUEST_VER);
249: // 1) an int, the number of files being uploaded
250: out.writeInt(valid.size());
251: byte[] buf = new byte[1024];
252: int size;
253: long total, length, threshold, next;
254: // 2) for each file:
255: for (Iterator it = valid.iterator(); it.hasNext();) {
256: Integer index = (Integer) it.next();
257: File file = files[index.intValue()];
258: // 2.0) a UTF String, the filename of the file being uploaded
259: out.writeUTF(file.getName().trim());
260: // 2.1) a long, the length of the file in bytes
261: out.writeLong(length = file.length());
262: threshold = Math.max(length / 100, (long) 10240);
263: next = threshold;
264: BufferedInputStream in = new BufferedInputStream(
265: new FileInputStream(file));
266: if (log.isDebugEnabled()) {
267: log.debug("Uploading " + file.getName());
268: }
269: total = 0;
270: // 2.2) raw bytes, equal to the number above for the file
271: while ((size = in.read(buf)) > -1) {
272: out.write(buf, 0, size);
273: total += size;
274: if (total > next) {
275: progress.updateStatus("Uploading " + file.getName()
276: + ": " + (total / 1024) + " KB");
277: while (total > next)
278: next += threshold;
279: }
280: }
281: in.close();
282: }
283: out.flush();
284: out.close();
285: }
286:
287: protected URLConnection connectToServer(URL url, String username,
288: String password) throws IOException {
289: URLConnection con = url.openConnection();
290: String auth = username + ":" + password;
291: byte[] data = auth.getBytes();
292: String s = new String(Base64.encode(data));
293: while (s.length() % 4 != 0)
294: s += "=";
295: con.setRequestProperty("Authorization", "Basic " + s);
296: con.setDoInput(true);
297: con.setDoOutput(true);
298: con.connect();
299: return con;
300: }
301:
302: }
|