blob: 7ee254e3bae31cd8a4ce23f62fea3b07e41825a3 [file] [log] [blame]
// Copyright 2011 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package adaptorlib;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.Charset;
import java.util.logging.Logger;
import java.util.zip.GZIPOutputStream;
/** Takes an XML feed file for the GSA, sends it to GSA and
then reads reply from GSA. */
class GsaFeedFileSender {
private static final Logger log
= Logger.getLogger(GsaFeedFileSender.class.getName());
/** Indicates failure creating connection to GSA. */
static class FailedToConnect extends Exception {
private FailedToConnect(IOException e) {
super(e);
}
// TODO(pjo): Add corrective tips.
}
/** Indicates failure to send XML feed file to GSA. */
static class FailedWriting extends Exception {
private FailedWriting(IOException e) {
super(e);
}
// TODO(pjo): Add corrective tips.
}
/** Indicates failure to read response to sent XML feed file. */
static class FailedReadingReply extends Exception {
private FailedReadingReply(IOException e) {
super(e);
}
// TODO(pjo): Add corrective tips.
}
// All communications are expected to be tailored to GSA.
private final Charset encoding;
/** Whether to use HTTP or HTTPS to talk to the feedergate. */
private final boolean useHttps;
// Feed file XML will not contain "<<".
private static final String BOUNDARY = "<<";
// Another frequently used constant of sent message.
private static final String CRLF = "\r\n";
public GsaFeedFileSender(Charset encoding, boolean useHttps) {
this.encoding = encoding;
this.useHttps = useHttps;
}
// Get bytes of string in communication's encoding.
private byte[] toEncodedBytes(String s) {
return s.getBytes(encoding);
}
/** Helper method for creating a multipart/form-data HTTP post.
Creates a post parameter made of a name and value. */
private void buildPostParameter(StringBuilder sb, String name,
String mimetype, String value) {
sb.append("--").append(BOUNDARY).append(CRLF);
sb.append("Content-Disposition: form-data;");
sb.append(" name=\"").append(name).append("\"").append(CRLF);
sb.append("Content-Type: ").append(mimetype).append(CRLF);
sb.append(CRLF).append(value).append(CRLF);
}
private byte[] buildMessage(String datasource, String feedtype,
String xmlDocument) {
StringBuilder sb = new StringBuilder();
buildPostParameter(sb, "datasource", "text/plain", datasource);
buildPostParameter(sb, "feedtype", "text/plain", feedtype);
buildPostParameter(sb, "data", "text/xml", xmlDocument);
sb.append("--").append(BOUNDARY).append("--").append(CRLF);
return toEncodedBytes("" + sb);
}
/** Tries to get in touch with our GSA. */
private HttpURLConnection setupConnection(String gsaHost, int len,
boolean useCompression)
throws MalformedURLException, IOException {
URL feedUrl;
if (useHttps) {
feedUrl = new URL("https://" + gsaHost + ":19902/xmlfeed");
} else {
feedUrl = new URL("http://" + gsaHost + ":19900/xmlfeed");
}
HttpURLConnection uc = (HttpURLConnection) feedUrl.openConnection();
uc.setDoInput(true);
uc.setDoOutput(true);
if (useCompression) {
uc.setChunkedStreamingMode(0);
// GSA can handle gziped content, although there isn't a way to find out
// other than just trying
uc.setRequestProperty("Content-Encoding", "gzip");
} else {
uc.setFixedLengthStreamingMode(len);
}
uc.setRequestProperty("Content-Type",
"multipart/form-data; boundary=" + BOUNDARY);
return uc;
}
/** Put bytes onto output stream. */
private void writeToGsa(OutputStream outputStream, byte msgbytes[])
throws IOException {
try {
outputStream.write(msgbytes);
outputStream.flush();
} finally {
outputStream.close();
}
}
/** Get GSA's response. */
private String readGsaReply(HttpURLConnection uc) throws IOException {
StringBuilder buf = new StringBuilder();
BufferedReader br = null;
try {
InputStream inputStream = uc.getInputStream();
br = new BufferedReader(new InputStreamReader(inputStream, encoding));
String line;
while ((line = br.readLine()) != null) {
buf.append(line);
}
} finally {
try {
if (null != br) {
br.close();
}
} catch (IOException e) {
log.warning("failed to close buffered reader");
}
if (null != uc) {
uc.disconnect();
}
}
return buf.toString();
}
private void handleGsaReply(String reply) {
if ("Success".equals(reply)) {
log.info("success message received");
} else {
throw new IllegalStateException("GSA reply: " + reply);
}
/* TODO(pjo): Recognize additional replies.
if ("Error - Unauthorized Request".equals(reply)) {
// TODO(pjo): Improve message with Admin Console details.
throw new IllegalStateException("GSA is not configured "
+ "to accept feeds from this IP. Please add <IP> "
+ "to permitted machines.");
}
if ("Internal Error".equals(reply))
if ("".equals(reply))
*/
}
/**
* Sends XML with provided datasoruce name and feedtype "metadata-and-url".
* Datasource name is limited to [a-zA-Z0-9_].
*/
void sendMetadataAndUrl(String host, String datasource, String xmlString)
throws FailedToConnect, FailedWriting, FailedReadingReply {
// TODO(pjo): Check datasource characters for valid name.
String feedtype = "metadata-and-url";
byte msg[] = buildMessage(datasource, feedtype, xmlString);
// GSA only allows request content up to 1 MB to be compressed
boolean useCompression = msg.length < 1 * 1024 * 1024;
HttpURLConnection uc;
OutputStream outputStream;
try {
uc = setupConnection(host, msg.length, useCompression);
outputStream = uc.getOutputStream();
} catch (IOException ioe) {
throw new FailedToConnect(ioe);
}
try {
if (useCompression) {
// setupConnection set Content-Encoding: gzip
outputStream = new GZIPOutputStream(outputStream);
}
writeToGsa(outputStream, msg);
} catch (IOException ioe) {
throw new FailedWriting(ioe);
} finally {
try {
String reply = readGsaReply(uc);
handleGsaReply(reply);
} catch (IOException ioe) {
throw new FailedReadingReply(ioe);
}
}
}
}