/**
* 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.xcc;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringWriter;
import java.io.Writer;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.nio.charset.CharacterCodingException;
import java.nio.charset.CharsetDecoder;
import com.marklogic.ps.Utilities;
import com.marklogic.recordloader.ContentInterface;
import com.marklogic.recordloader.FatalException;
import com.marklogic.recordloader.LoaderException;
import com.marklogic.xcc.Request;
import com.marklogic.xcc.Session;
import com.marklogic.xcc.exceptions.RequestException;
import com.marklogic.xcc.types.ValueType;
/**
* @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!
*
*/
public class XccModuleContent extends XccAbstractContent implements
ContentInterface {
protected String xml = null;
protected Request request = 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 skipExistingUntilFirstMiss;
protected boolean errorExisting;
protected CharsetDecoder decoder;
protected long quality;
/**
* @param _session
* @param _uri
* @param _moduleUri
* @param _executeRoles
* @param _insertRoles
* @param _readRoles
* @param _updateRoles
* @param _collections
* @param _language
* @param _namespace
* @param _skipExisting
* @param _errorExisting
* @param _placeKeys
* @param _decoder
* @param _quality
*/
public XccModuleContent(Session _session, String _uri,
String _moduleUri, String[] _executeRoles,
String[] _insertRoles, String[] _readRoles,
String[] _updateRoles, String[] _collections,
String _language, String _namespace, boolean _skipExisting,
boolean _skipExistingUntilFirstMiss, boolean _errorExisting,
BigInteger[] _placeKeys, CharsetDecoder _decoder,
long _quality) {
session = _session;
uri = _uri;
if (null == _moduleUri) {
throw new FatalException("module URI cannot be null");
}
request = session.newModuleInvoke(_moduleUri);
executeRoles = _executeRoles;
insertRoles = _insertRoles;
readRoles = _readRoles;
updateRoles = _updateRoles;
collections = _collections;
language = _language;
quality = _quality;
namespace = _namespace;
skipExisting = _skipExisting;
skipExistingUntilFirstMiss = _skipExistingUntilFirstMiss;
errorExisting = _errorExisting;
decoder = _decoder;
if (null == _placeKeys) {
placeKeys = new String[0];
} else {
placeKeys = new String[_placeKeys.length];
for (int i = 0; i < _placeKeys.length; i++) {
placeKeys[i] = "" + _placeKeys[i];
}
}
}
/*
* (non-Javadoc)
*
* @see com.marklogic.recordloader.ContentInterface#insert()
*/
public void insert() throws LoaderException {
if (null == uri) {
throw new NullPointerException("URI cannot be null");
}
if (null == session) {
throw new NullPointerException("Session cannot be null");
}
if (null == request) {
throw new NullPointerException("Request cannot be null");
}
try {
request.setNewStringVariable("URI", uri);
request.setNewStringVariable("XML-STRING", xml);
request.setNewStringVariable("NAMESPACE", namespace);
request.setNewStringVariable("LANGUAGE",
(null == language) ? "" : language);
request.setNewStringVariable("ROLES-EXECUTE", Utilities
.joinSsv(executeRoles));
request.setNewStringVariable("ROLES-INSERT", Utilities
.joinSsv(insertRoles));
request.setNewStringVariable("ROLES-READ", Utilities
.joinSsv(readRoles));
request.setNewStringVariable("ROLES-UPDATE", Utilities
.joinSsv(updateRoles));
request.setNewStringVariable("COLLECTIONS", Utilities
.joinCsv(collections));
request.setNewVariable("SKIP-EXISTING", ValueType.XS_BOOLEAN,
skipExisting);
request.setNewVariable("SKIP-EXISTING-UNTIL-FIRST-MISS",
ValueType.XS_BOOLEAN, skipExisting);
request.setNewVariable("ERROR-EXISTING",
ValueType.XS_BOOLEAN, errorExisting);
// apparently it is ok if OUTPUT_FORESTS are empty (tested 4.1-1)
request.setNewStringVariable("FORESTS", Utilities
.joinCsv(placeKeys));
request.setNewIntegerVariable("QUALITY", quality);
// ignore results
// TODO use results to handle skipExistingUntilFirstMiss - how to
// feed back to config?
session.submitRequest(request);
// synchronized (monitor) {
// logger.info("resetting "
// + Configuration.SKIP_EXISTING_KEY + " at "
// + uri);
// config.setSkipExisting(false);
// config.configureThrottling();
// monitor.resetTimer("skipped");
// }
} catch (RequestException e) {
throw new LoaderException(e);
}
}
/*
* (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 {
// override super(), so that we don't check the database.
// checking the database does not work, as modules can rewrite uris.
return false;
}
}