/**
* Copyright (c) 2008-2010 Mark Logic Corporation. 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.
*
* The use of the Apache License does not indicate that this project is
* affiliated with the Apache Software Foundation.
*/
package com.marklogic.recordloader.http;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.ByteBuffer;
import java.nio.charset.CharacterCodingException;
import java.nio.charset.CharsetDecoder;
import java.util.Arrays;
import com.marklogic.recordloader.AbstractContent;
import com.marklogic.recordloader.ContentInterface;
import com.marklogic.recordloader.LoaderException;
/**
* @author Michael Blakeley, michael.blakeley@marklogic.com
*
* This implementation passes the XML source to a defined module. As
* such, it cannot handle documents larger than available memory.
*
* Also, this class can only handle XML, and possibly text: no binaries!
*
*/
/**
* @author Michael Blakeley, Mark Logic Corporation
*
*/
public class HttpModuleContent extends AbstractContent implements
ContentInterface {
protected String xml = null;
protected String[] executeRoles;
protected String[] insertRoles;
protected String[] readRoles;
protected String[] updateRoles;
protected String[] collections;
protected String[] placeKeys;
protected String language;
protected String namespace;
protected boolean skipExisting;
protected boolean errorExisting;
protected CharsetDecoder decoder;
protected String uri;
private URL connectionUrl;
/**
* @param _connectionUrl
* @param _uri
* @param _executeRoles
* @param _insertRoles
* @param _readRoles
* @param _updateRoles
* @param _collections
* @param _language
* @param _namespace
* @param _skipExisting
* @param _errorExisting
* @param _placeKeys
* @param _decoder
*/
public HttpModuleContent(URL _connectionUrl, String _uri,
String[] _executeRoles, String[] _insertRoles,
String[] _readRoles, String[] _updateRoles,
String[] _collections, String _language, String _namespace,
boolean _skipExisting, boolean _errorExisting,
String[] _placeKeys, CharsetDecoder _decoder) {
connectionUrl = _connectionUrl;
uri = _uri;
executeRoles = _executeRoles;
insertRoles = _insertRoles;
readRoles = _readRoles;
updateRoles = _updateRoles;
collections = _collections;
language = _language;
namespace = _namespace;
skipExisting = _skipExisting;
errorExisting = _errorExisting;
decoder = _decoder;
if (null == _placeKeys) {
placeKeys = new String[0];
} else {
placeKeys = Arrays.copyOf(_placeKeys, _placeKeys.length);
}
}
/*
* (non-Javadoc)
*
* @see com.marklogic.recordloader.ContentInterface#insert()
*/
public void insert() throws LoaderException {
if (null == uri) {
throw new NullPointerException("URI cannot be null");
}
StringBuilder body = new StringBuilder();
try {
append(body, "URI", uri);
append(body, "XML-STRING", xml);
append(body, "NAMESPACE", namespace);
append(body, "LANGUAGE", (null == language) ? "" : language);
append(body, "ROLES-EXECUTE", executeRoles);
append(body, "ROLES-INSERT", insertRoles);
append(body, "ROLES-READ", readRoles);
append(body, "ROLES-UPDATE", updateRoles);
append(body, "COLLECTIONS", collections);
// TODO skip existing until first miss
append(body, "SKIP-EXISTING", Boolean.toString(skipExisting));
append(body, "ERROR-EXISTING", Boolean
.toString(errorExisting));
append(body, "FORESTS", placeKeys);
HttpURLConnection conn = (HttpURLConnection) connectionUrl
.openConnection();
conn.setUseCaches(false);
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Type",
"application/x-www-form-urlencoded");
// use keep-alive to reduce open sockets
conn.setRequestProperty("Connection", "keep-alive");
conn.setDoOutput(true);
OutputStreamWriter osw = new OutputStreamWriter(conn
.getOutputStream());
osw.write(body.toString());
osw.flush();
BufferedReader in = new BufferedReader(new InputStreamReader(
conn.getInputStream()));
String line;
while ((line = in.readLine()) != null) {
System.out.println(line);
}
in.close();
return;
} catch (IOException e) {
throw new LoaderException(e);
}
}
/**
* @param _query
* @param _key
* @param _value
* @throws UnsupportedEncodingException
*/
private void append(StringBuilder _query, String _key, String _value)
throws UnsupportedEncodingException {
append(_query, _key, new String[] { _value });
}
/**
* @param _query
* @param _key
* @param _value
* @throws UnsupportedEncodingException
*/
private void append(StringBuilder _query, String _key, String[] _value)
throws UnsupportedEncodingException {
if (null == _value || 1 > _value.length) {
return;
}
for (int i = 0; i < _value.length; i++) {
if (_query.length() > 0) {
_query.append("&");
}
_query.append(_key).append("=").append(
URLEncoder.encode(_value[i], "UTF-8"));
}
}
/*
* (non-Javadoc)
*
* @see
* com.marklogic.recordloader.ContentInterface#setProducer(com.marklogic
* .recordloader.Producer)
*/
public void setInputStream(InputStream _producer)
throws LoaderException {
if (null == uri) {
throw new LoaderException("URI cannot be null");
}
// by now, the producer is in the configured output encoding,
// so no decoder is needed
Reader reader = new InputStreamReader(_producer);
Writer writer = new StringWriter();
char[] buf = new char[32 * 1024];
int count = -1;
try {
// read the entire doc - this won't scale past the VM size
while ((count = reader.read(buf)) > -1) {
writer.write(buf, 0, count);
}
writer.flush();
xml = writer.toString();
writer.close();
reader.close();
} catch (IOException e) {
throw new LoaderException(e);
}
}
/*
* (non-Javadoc)
*
* @see com.marklogic.recordloader.ContentInterface#setXml(java.lang.String)
*/
public void setBytes(byte[] _xml) throws LoaderException {
// ModuleContent only works with strings
// TODO support text? binary?
try {
// use the correct decoder
xml = decoder.decode(ByteBuffer.wrap(_xml)).toString();
} catch (CharacterCodingException e) {
throw new LoaderException(uri, e);
}
}
@SuppressWarnings("unused")
public boolean checkDocumentUri(String _uri) throws LoaderException {
// checking the database does not work, as modules can rewrite uris.
// instead, we provide the value of SKIP_EXISTING to the module.
// given the information, it can do as it likes.
return false;
}
}