/**
* $Id: $
* $Date: $
*
*/
package org.xmlsh.exist.util;
import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.ProtocolException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;
import javax.xml.transform.stream.StreamSource;
import net.sf.saxon.s9api.QName;
import net.sf.saxon.s9api.SaxonApiException;
import net.sf.saxon.s9api.XQueryCompiler;
import net.sf.saxon.s9api.XQueryEvaluator;
import net.sf.saxon.s9api.XQueryExecutable;
import net.sf.saxon.s9api.XdmAtomicValue;
import net.sf.saxon.s9api.XdmItem;
import net.sf.saxon.s9api.XdmNode;
import org.xmlsh.core.CoreException;
import org.xmlsh.core.IXdmItemOutputStream;
import org.xmlsh.core.InputPort;
import org.xmlsh.core.InvalidArgumentException;
import org.xmlsh.core.Namespaces;
import org.xmlsh.core.Options;
import org.xmlsh.core.OutputPort;
import org.xmlsh.core.StreamOutputPort;
import org.xmlsh.core.XValue;
import org.xmlsh.sh.shell.SerializeOpts;
import org.xmlsh.sh.shell.Shell;
import org.xmlsh.util.Base64Coder;
import org.xmlsh.util.Util;
public class ExistConnection {
private static final String kEXIST_NS = "http://exist.sourceforge.net/NS/exist";
private final static String kSER_NAMESPACE = "http://exist-db.org/xquery/types/serialized";
private final static String kSEQ_ELEMENT = "sequence";
private final static String kVALUE_ELEMENT = "value";
private final static String kATTR_TYPE = "type";
private final static String kATTR_ITEM_TYPE = "item-type";
private HttpURLConnection mHttp ;
private Shell mShell;
private Map<String,XValue> mVariables = new HashMap<String,XValue>();
private Map<String,String> mProperties = new HashMap<String,String>();
private SerializeOpts mSerializeOpts ;
private String mURI;
private Options mOptions;
protected Namespaces mNs = null ;
public ExistConnection(Shell shell , String uri , Options options ) throws MalformedURLException, IOException, InvalidArgumentException {
mURI = uri ;
mOptions = options;
mSerializeOpts = shell.getSerializeOpts(options) ;
parseSerializesOpts( options );
mShell = shell ;
}
private void parseSerializesOpts(Options options) {
if( options.hasOpt("text"))
mSerializeOpts.setContent_type("text/plain");
else
if( options.hasOpt("binary"))
mSerializeOpts.setContent_type("application/octet-stream");
else
if( options.hasOpt("xml"))
mSerializeOpts.setContent_type("text/xml");
else
if( options.hasOpt("xquery"))
mSerializeOpts.setContent_type("application/xquery");
else
if( options.hasOpt("contentType"))
mSerializeOpts.setContent_type( options.getOptString("contentType", mSerializeOpts.getContent_type()));
}
public void resolveNamespaces( Options opts )
{
/*
* Add namespaces
*/
if( !opts.hasOpt("nons"))
mNs = mShell.getEnv().getNamespaces();
if( opts.hasOpt("ns")){
Namespaces ns2 = new Namespaces();
if( mNs != null )
ns2.putAll(mNs);
// Add custom name spaces
for( XValue v : opts.getOpt("ns").getValues() )
ns2.declare(v);
mNs = ns2;
}
}
public void setVariable(String name, XValue value) {
mVariables.put(name,value);
}
public void setProperty( String name, String value)
{
mProperties.put(name, value);
}
public int query(String query,boolean bRaw, boolean bMeta, boolean bCache, int start, int max, String session, OutputPort out) throws XMLStreamException, SaxonApiException, IOException, CoreException, URISyntaxException {
String suri = mURI ;
// if( !mVariables.isEmpty())
// suri += "?" + encodeVariables();
// mShell.printErr(suri);
URL url = new URL( suri );
mHttp = (HttpURLConnection) url.openConnection();
setOptions( mHttp , mOptions );
resolveNamespaces( mOptions );
String body = formatQuery(query,bCache,start,max,session);
// mShell.printErr(body);
mHttp.setRequestMethod("POST");
mHttp.setDoInput(true);
mHttp.setDoOutput(true);
byte[] bodyBytes = body.getBytes(mSerializeOpts.getOutputXmlEncoding()); // Output encoding - returned from exist
mHttp.setRequestProperty("Content-Length", Long.toString(bodyBytes.length));
mHttp.setRequestProperty("Content-Type", "application/xml");
mHttp.connect();
int ret;
try {
OutputStream httpout = mHttp.getOutputStream();
httpout.write( bodyBytes );
httpout.flush();
httpout.close();
ret = mHttp.getResponseCode();
if( ret == 200 )
ret = 0;
if( ret == 0 ){
InputStream in = mHttp.getInputStream();
writeResult(in, out, bRaw, bMeta);
in.close();
}
} finally {
mHttp.disconnect();
}
return ret ;
}
private String encodeVariables() throws UnsupportedEncodingException {
StringBuffer encoded = new StringBuffer();
for( Entry<String,XValue> entry : mVariables.entrySet()){
if( encoded.length() > 0)
encoded.append('&');
encoded.append(entry.getKey());
encoded.append('=');
encoded.append(URLEncoder.encode(entry.getValue().toString(),"UTF-8"));
}
return encoded.toString();
}
private void writeResult(InputStream ins, OutputPort outp, boolean bRaw, boolean bMeta) throws IOException, CoreException, SaxonApiException, URISyntaxException {
if( bRaw ){
OutputStream os = outp.asOutputStream(mSerializeOpts);
Util.copyStream(ins, os);
os.close();
return ;
}
IXdmItemOutputStream outs = outp.asXdmItemOutputStream(mSerializeOpts);
// InputPort inp = new StreamInputPort(ins, null);
// Util.copyStream(ins, outp.asOutputStream(mSerializeOpts));
XQueryExecutable exe = getXQuery("result.xquery");
XQueryEvaluator eval = exe.load();
eval.setExternalVariable( new QName("meta"), new XValue(bMeta).asXdmValue());
eval.setSource( new StreamSource(ins));
boolean bAnyOut = false ;
boolean bFirst = true ;
boolean bString = false ;
for( XdmItem item : eval ){
bAnyOut = true ;
if( ! bFirst )
outp.writeSequenceSeperator(mSerializeOpts); // Thrashes variable output !
bFirst = false ;
if( item instanceof XdmNode ){
XdmNode node = (XdmNode) item ;
if( bString )
item = new XdmAtomicValue( node.getStringValue());
}
//processor.writeXdmValue(item, ser );
// Util.writeXdmValue(item, ser);
outs.write(item);
}
if( bAnyOut )
outp.writeSequenceTerminator(mSerializeOpts); // write "\n"
}
private XQueryExecutable getXQuery(String string) throws SaxonApiException, IOException, URISyntaxException {
XQueryCompiler mXQueryCompiler = Shell.getProcessor().newXQueryCompiler();
String resPath = "/org/xmlsh/exist/resources/" + string ;
URL url = mShell.getResource(resPath);
if( url == null )
throw new URISyntaxException(resPath, "Cannot locate resource" );
InputStream isQuery = url.openStream();
try {
if( url != null ){
URI uri = url.toURI();
mXQueryCompiler.setBaseURI( uri );
}
return mXQueryCompiler.compile( isQuery );
} finally {
isQuery.close();
}
}
private String formatQuery(String query, boolean bCache, int start, int max, String session) throws InvalidArgumentException, XMLStreamException, SaxonApiException {
ByteArrayOutputStream buf = new ByteArrayOutputStream();
OutputPort out = new StreamOutputPort(buf);
// Need to modify the serialization opts to get past eXists very pecurlar parser
// Some of the parsing code is whitespace sensitive
SerializeOpts ser = mSerializeOpts.clone();
ser.setIndent(false);
ser.setOmit_xml_declaration(false);
XMLStreamWriter w = out.asXMLStreamWriter(ser);
w.writeStartDocument();
w.setDefaultNamespace( kEXIST_NS);
w.writeStartElement(kEXIST_NS , "query");
w.writeAttribute("cache", bCache ? "yes" : "no");
w.writeAttribute("start", String.valueOf(start));
w.writeAttribute("max" , String.valueOf(max));
if( !Util.isBlank(session))
w.writeAttribute("session_id", session);
writeProperties(w);
// writeVariables(w);
w.writeStartElement(kEXIST_NS , "text");
w.writeCData(query);
w.writeEndElement();
w.writeEndElement();
w.writeEndDocument();
w.flush();
w.close();
return buf.toString();
}
private void writeVariables(XMLStreamWriter w) throws XMLStreamException {
if(! mVariables.isEmpty()){
w.writeStartElement( kEXIST_NS , "variables");
for( Entry<String,XValue> entry : mVariables.entrySet()){
w.writeStartElement( kEXIST_NS , "variable");
QName vqname = Util.resolveQName( entry.getKey() , mNs );
w.writeStartElement(kEXIST_NS,"qname");
w.writeStartElement(kEXIST_NS, "localname");
w.writeCharacters(vqname.getLocalName());
w.writeEndElement();
if( !Util.isBlank(vqname.getNamespaceURI())){
w.writeStartElement(kEXIST_NS, "namespace");
w.writeCharacters(vqname.getNamespaceURI());
w.writeEndElement();
}
if( !Util.isBlank(vqname.getPrefix())){
w.writeStartElement(kEXIST_NS, "prefix");
w.writeCharacters(vqname.getPrefix());
w.writeEndElement();
}
w.writeEndElement();
// Value
w.writeStartElement( "sx" , kSEQ_ELEMENT , kSER_NAMESPACE );
for( XdmItem item : entry.getValue().asXdmValue() ){
w.writeStartElement( "sx", kVALUE_ELEMENT, kSER_NAMESPACE );
w.writeAttribute( kATTR_TYPE, "xs:string");
w.writeCharacters(item.getStringValue());
w.writeEndElement();
}
w.writeEndElement();
w.writeEndElement();
}
w.writeEndElement();
}
}
private void writeProperties(XMLStreamWriter w) throws XMLStreamException {
if(! mProperties.isEmpty()){
w.writeStartElement( kEXIST_NS , "properties");
for( Entry<String,String> entry : mProperties.entrySet()){
w.writeStartElement( kEXIST_NS , "property");
w.writeAttribute( "name" , entry.getKey() );
w.writeAttribute( "value" , entry.getValue() );
w.writeEndElement();
}
w.writeEndElement();
}
}
public void close() {
mHttp = null ;
}
private void setOptions(HttpURLConnection http, Options opts) throws UnsupportedEncodingException {
if( opts.hasOpt("connectTimeout"))
http.setConnectTimeout( (int)(opts.getOptDouble("connectTimeout", 0) * 1000.) );
if( opts.hasOpt("readTimeout"))
http.setReadTimeout( (int) (opts.getOptDouble("readTimeout", 0) * 1000.));
if( opts.hasOpt("useCaches"))
http.setUseCaches( opts.getOpt("useCaches").getFlag());
if( opts.hasOpt("followRedirects"))
http.setInstanceFollowRedirects( opts.getOpt("followRedirects").getFlag());
if( opts.hasOpt("contentType"))
http.setRequestProperty("Content-Type", opts.getOptString("contentType", "application/xml"));
String user = opts.getOptString("user", mShell.getEnv().getVarString("EXIST_USER"));
String pass = opts.getOptString("password", mShell.getEnv().getVarString("EXIST_PASSWORD"));
if( user != null && pass != null ){
String up = user + ":" + pass ;
// Encode String
String encoding = new String(Base64Coder.encode (up.getBytes("US-ASCII")));
http.setRequestProperty ("Authorization", "Basic " + encoding);
}
}
public int put(InputPort in) throws IOException, CoreException {
String suri = mURI ;
URL url = new URL( suri );
mHttp = (HttpURLConnection) url.openConnection();
mHttp.setRequestMethod("PUT");
mHttp.setDoInput(true);
mHttp.setDoOutput(true);
mHttp.setRequestProperty("Content-Type", mSerializeOpts.getContent_type());
setOptions( mHttp , mOptions );
mHttp.connect();
int ret;
try {
OutputStream httpout = mHttp.getOutputStream();
InputStream is = in.asInputStream(mSerializeOpts);
Util.copyStream(is, httpout);
is.close();
httpout.flush();
httpout.close();
ret = mHttp.getResponseCode();
if( ret == 200 )
ret = 0;
} finally {
mHttp.disconnect();
}
return ret ;
}
public int get(OutputPort out) throws IOException {
String suri = mURI + "?" + getParams();
URL url = new URL( suri );
mHttp = (HttpURLConnection) url.openConnection();
mHttp.setRequestMethod("GET");
mHttp.setDoInput(true);
mHttp.setDoOutput(false);
mHttp.setRequestProperty("Content-Type", mSerializeOpts.getContent_type());
setOptions( mHttp , mOptions );
mHttp.connect();
int ret;
try {
InputStream is = mHttp.getInputStream();
OutputStream os = out.asOutputStream(mSerializeOpts);
Util.copyStream(is, os);
is.close();
os.flush();
ret = mHttp.getResponseCode();
if( ret == 200 )
ret = 0;
} finally {
mHttp.disconnect();
}
return ret ;
}
private String getParams( ) {
StringBuffer sb = new StringBuffer( );
addParam( sb , "_xsl" , "no");
addParam( sb , "_indent" , mSerializeOpts.isIndent() ? "yes" : "no");
addParam( sb , "_encoding", mSerializeOpts.getOutputXmlEncoding() );
addParam( sb , "_source" , "yes");
return sb.toString();
}
private String getInvokeParams( ) {
StringBuffer sb = new StringBuffer( );
addParam( sb , "_xsl" , "no");
addParam( sb , "_indent" , mSerializeOpts.isIndent() ? "yes" : "no");
addParam( sb , "_encoding", mSerializeOpts.getOutputXmlEncoding() );
addParam( sb , "_wrap", "yes");
return sb.toString();
}
private void addParam( StringBuffer sb , String name, String value)
{
if( sb.length() > 0)
sb.append("&");
sb.append(name);
sb.append('=');
sb.append( Util.urlEncode(value));
}
public int invoke(InputPort in, OutputPort out, boolean bRaw) throws IOException, CoreException, SaxonApiException, URISyntaxException {
boolean bPost = (in != null );
// if POST then dont add query parameters - doesnt work
String suri = bPost ? mURI : (mURI + "?" + getInvokeParams());
URL url = new URL( suri );
mHttp = (HttpURLConnection) url.openConnection();
setOptions( mHttp , mOptions );
resolveNamespaces( mOptions );
if( bPost ) {
mHttp.setRequestMethod("POST");
// POST implies RAW - eXists problem wont wrap results for POST
bRaw = true ;
}
else
mHttp.setRequestMethod("GET");
mHttp.setDoInput(true);
mHttp.setDoOutput(bPost );
mHttp.connect();
int ret;
try {
if( bPost ){
OutputStream httpout = mHttp.getOutputStream();
InputStream is = in.asInputStream(mSerializeOpts);
Util.copyStream(is, httpout);
is.close();
httpout.flush();
httpout.close();
}
ret = mHttp.getResponseCode();
if( ret == 200 )
ret = 0;
if( ret == 0 ){
InputStream httpin = mHttp.getInputStream();
writeResult(httpin, out, bRaw, false);
httpin.close();
}
} finally {
mHttp.disconnect();
}
return ret ;
}
public int del() throws IOException {
String suri = mURI + "?" + getParams();
URL url = new URL( suri );
mHttp = (HttpURLConnection) url.openConnection();
mHttp.setRequestMethod("DELETE");
mHttp.setDoInput(true);
mHttp.setDoOutput(false);
setOptions( mHttp , mOptions );
mHttp.connect();
int ret;
try {
ret = mHttp.getResponseCode();
if( ret == 200 )
ret = 0;
} finally {
mHttp.disconnect();
}
return ret ;
}
}
//
//
//Copyright (C) 2008-2014 David A. Lee.
//
//The contents of this file are subject to the "Simplified BSD License" (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.opensource.org/licenses/bsd-license.php
//
//Software distributed under the License is distributed on an "AS IS" basis,
//WITHOUT WARRANTY OF ANY KIND, either express or implied.
//See the License for the specific language governing rights and limitations under the License.
//
//The Original Code is: all this file.
//
//The Initial Developer of the Original Code is David A. Lee
//
//Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
//
//Contributor(s): none.
//