view software/eXist/mpdl-modules/src/de/mpg/mpiwg/berlin/mpdl/xmlrpc/MpdlXmlRpcInterfaceImpl.java @ 0:408254cf2f1d

Erstellung
author Josef Willenborg <jwillenborg@mpiwg-berlin.mpg.de>
date Wed, 24 Nov 2010 17:24:23 +0100
parents
children
line wrap: on
line source

package de.mpg.mpiwg.berlin.mpdl.xmlrpc;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.List;

import org.apache.log4j.Logger;
import org.apache.xmlrpc.XmlRpcException;
import org.apache.xmlrpc.client.XmlRpcClient;
import org.apache.xmlrpc.client.XmlRpcClientConfigImpl;

import de.mpg.mpiwg.berlin.mpdl.exception.ApplicationException;

public class MpdlXmlRpcInterfaceImpl implements MpdlXmlRpcInterface {
  private static MpdlXmlRpcInterfaceImpl instance;
  private static Logger LOGGER = Logger.getLogger(MpdlXmlRpcInterfaceImpl.class); // Logs to EXIST_HOME/webapp/WEB-INF/logs/exist.log
  private String serverName = "localhost";
  private int port = 8090;
  private String xmlRpcUri = "http://" + serverName + ":" + port + "/exist/xmlrpc";
  private XmlRpcClient xmlClient = null;
  private String userName = "admin";
  private String pw = "";

  private MpdlXmlRpcInterfaceImpl() {
  }
  
  public static MpdlXmlRpcInterfaceImpl getInstance(String serverName, int port, String userName, String pw) throws ApplicationException {
    if (instance == null) {
      instance = new MpdlXmlRpcInterfaceImpl();
	  }
    instance.serverName = serverName;
    instance.port = port;
    instance.userName = userName;
    instance.pw = pw;
    instance.xmlRpcUri = "http://" + serverName + ":" + port + "/exist/xmlrpc";
    instance.init();
	  return instance;
  }

  public String[] getDocumentNames(String collection) throws ApplicationException {
    String[] documentNames = null;
    try {
      Object[] params = new Object[1];
      params[0] = collection;
      HashMap collectionDesc = (HashMap) xmlClient.execute("getCollectionDesc", params);
      Object[] documents = (Object[]) collectionDesc.get("documents");
      documentNames = new String[documents.length];
      for (int i=0; i < documents.length; i++) {
        HashMap doc = (HashMap) documents[i];
        String docName = (String) doc.get("name");
        documentNames[i] = collection + "/" + docName;
      }
    } catch (XmlRpcException e) {
      logError(e);
      throw new ApplicationException(e);
    }
    return documentNames;
  }

  public boolean readDocumentIntoLocalFile(String collection, String documentName, String localFileName) throws ApplicationException {
    boolean success = false;
    OutputStream out = null;
    try {
      out = new BufferedOutputStream(new FileOutputStream(localFileName));
      Hashtable options = new Hashtable();
      options.put("indent", "yes");  
      options.put("encoding", "UTF-8");  
      options.put("expand-xincludes", "yes");  
      options.put("highlight-matches", "elements");  
      // get the document from eXist 
      Object[] params = new Object[2];
      params[0] = collection + "/" + documentName;
      params[1] = options;  
      HashMap result = (HashMap) xmlClient.execute("getDocumentData", params);
      byte[] data = (byte[]) result.get("data");
      String handle = (String) result.get("handle");
      Integer offset = (Integer) result.get("offset");
      out.write(data); // the first chunk of data
      // if there are more than one chunk of data
      while (offset != 0) {
        Object[] paramsGetNextChunk = new Object[2];
        paramsGetNextChunk[0] = handle;
        paramsGetNextChunk[1] = offset;  
        HashMap resultChunk = (HashMap) xmlClient.execute("getNextChunk", paramsGetNextChunk);
        data = (byte[]) resultChunk.get("data");
        out.write(data);
        offset = (Integer) resultChunk.get("offset");
      }
      out.flush();
      success = true;
    } catch (XmlRpcException e) {
      logError(e);
      throw new ApplicationException(e);
    } catch (IOException e) {
      logError(e);
      throw new ApplicationException(e);
    } finally {
      try { 
        if (out != null)
          out.close();
      } catch (Exception e) {
        // nothing: always close the stream at the end of the method
      }
    }
    return success;
  }  

  public boolean saveDocument(String collection, String documentName, String localFile) throws ApplicationException {
    boolean success = false;
    String mimeType = getMimeType(localFile);  // e.g. text/xml or application/xml ...
    try {
      testFile(localFile);
      // first: create an empty file on server
      byte[] dummyChunk = new byte[0];
      Object[] paramsUpload1 = new Object[2];
      paramsUpload1[0] = dummyChunk;
      paramsUpload1[1] = 0;
      String tmpFileNameOnServer = (String) xmlClient.execute("upload", paramsUpload1);  
      // second: upload the whole file in chunks to server file; normally needs 3 seconds for 20 MB
      int chunkSize = 20000 * 1024;  // copies data from a file in 20 MB chunks to server file so that not too much RAM is consumed on server 
      InputStream localFileInputStream = new BufferedInputStream(new FileInputStream(localFile));
      byte[] chunk = new byte[chunkSize];
      while ((chunk = readBytes(localFileInputStream, chunkSize)) != null) {
        int realChunkSize = chunk.length;  // is smaller than chunkSize if last chunk is read
        Object[] paramsUpload2 = new Object[3];
        paramsUpload2[0] = tmpFileNameOnServer;
        paramsUpload2[1] = chunk;
        paramsUpload2[2] = realChunkSize;
        String uploadResult = (String) xmlClient.execute("upload", paramsUpload2);
      }
      // third: parseLocal
      String docName = collection + "/" + documentName;
      Object[] paramsParseLocal = new Object[4];
      paramsParseLocal[0] = tmpFileNameOnServer;
      paramsParseLocal[1] = docName;
      paramsParseLocal[2] = true;  // overwrites an existing file of the same name if it exists
      paramsParseLocal[3] = mimeType; // mime type of the document   
      success = (Boolean) xmlClient.execute("parseLocal", paramsParseLocal);
    } catch (XmlRpcException e) {
      logError(e);
      throw new ApplicationException(e);
    } catch (FileNotFoundException e) {
      logError(e);
      throw new ApplicationException(e);
    }
    return success;
  }

  public boolean saveDocuments(String collection, String localFileDirectory) throws ApplicationException {
    boolean success = true;
    testFile(localFileDirectory); // directory exists ?
    File localFileDir = new File(localFileDirectory);
    File[] files = localFileDir.listFiles();
    for (int i=0; i < files.length; i++) {
      File f = files[i];
      String fileNameWithoutPath = f.getName();
      String localFileName = f.getPath();
      boolean localSuccess = saveDocument(collection, fileNameWithoutPath, localFileName);
      if (! localSuccess)
        success = false;
    }
    return success;
  }

  public boolean saveDocuments(String collection, String localFileDirectory, String fileExtension) throws ApplicationException {
    boolean success = true;
    testFile(localFileDirectory); // directory exists ?
    File localFileDir = new File(localFileDirectory);
    FilenameFilter filter = new FilenameFilterExtension(fileExtension);
    File[] files = localFileDir.listFiles(filter);
    for (int i=0; i < files.length; i++) {
      File f = files[i];
      String fileNameWithoutPath = f.getName();
      String localFileName = f.getPath();
      boolean localSuccess = saveDocument(collection, fileNameWithoutPath, localFileName);
      if (! localSuccess)
        success = false;
    }
    return success;
  }

  public boolean deleteDocument(String collection, String documentName) throws ApplicationException {
    boolean success = false;
    String[] docDesc = getDocumentDescription(collection, documentName);
    if (docDesc == null) {  // document does not exist
      System.out.println("Warning: Could not delete: " + collection + "/" + documentName + " because it does not exist");
    } else {
      success = deleteDocument(collection + "/" + documentName);
    }
    return success;
  }

  public boolean deleteDocuments(String collection) throws ApplicationException {
    boolean success = true;
    String[] documentNames = getDocumentNames(collection);
    for (int i=0; i < documentNames.length; i++) {
      String documentName = documentNames[i];
      boolean localSuccess = deleteDocument(documentName);
      if (! localSuccess)
        success = false;
    }
    return success;
  }

  public boolean createCollection(String collection) throws ApplicationException {
    boolean success = false;
    try {
      Object[] params = new Object[1];
      params[0] = collection;
      success = (Boolean) xmlClient.execute("createCollection", params);  
    } catch (XmlRpcException e) {
      logError(e);
      throw new ApplicationException(e);
    }
    return success;
  }

  public boolean deleteCollection(String collection) throws ApplicationException {
    boolean success = false;
    try {
      Object[] params = new Object[1];
      params[0] = collection;
      success = (Boolean) xmlClient.execute("removeCollection", params);  
    } catch (XmlRpcException e) {
      logError(e);
      throw new ApplicationException(e);
    }
    return success;
  }

  private void init() throws ApplicationException {
    try {
      XmlRpcClientConfigImpl config = new XmlRpcClientConfigImpl();
      config.setServerURL(new URL(xmlRpcUri));
      config.setBasicUserName(userName);
      config.setBasicPassword(pw);
      xmlClient = new XmlRpcClient();
      xmlClient.setConfig(config);
    } catch (MalformedURLException e) {
      logError(e);
      throw new ApplicationException(e);
    }    
  }
  
  private void logError(Exception e) {
    LOGGER.error(e.getMessage());
    LOGGER.error(e.getStackTrace());
  }
  
  private String[] getDocumentDescription(String collection, String documentName) throws ApplicationException {
    String[] documentDesc = new String[3];
    try {
      Object[] params = new Object[1];
      params[0] = collection + "/" + documentName;
      HashMap docDesc = (HashMap) xmlClient.execute("describeResource", params);
      if (docDesc == null || docDesc.isEmpty())
        return null;
      String name = (String) docDesc.get("name");
      String owner = (String) docDesc.get("owner");
      String group = (String) docDesc.get("group");
      documentDesc[0] = name;
      documentDesc[1] = owner;
      documentDesc[2] = group;
    } catch (XmlRpcException e) {
      logError(e);
      throw new ApplicationException(e);
    }
    return documentDesc;
  }
  
  private boolean deleteDocument(String fullDocumentName) throws ApplicationException {
    boolean success = false;
    try {
      Object[] params = new Object[1];
      params[0] = fullDocumentName;
      success = (Boolean) xmlClient.execute("remove", params);  
    } catch (XmlRpcException e) {
      logError(e);
      throw new ApplicationException(e);
    }
    return success;
  }

  private void testFile(String fileName) throws ApplicationException {
    File file = new File(fileName);
    boolean fileExists = file.exists();
    if (! fileExists) {
      throw new ApplicationException("File: " + fileName + " does not exist");
    }
  }
  
  /**
   *  Reads a chunk of data of an input stream.
   *  Does not close the stream until last bytes are read
   *  @in in the input stream to be read
   *  @chunkSize chunkSize length of the chunk which is read
   *  @return byte[] of bytes read
   */
  private byte[] readBytes(InputStream in, int chunkSize) throws ApplicationException {
    byte[] resultBytes = new byte[chunkSize];
    try {
      int len = in.read(resultBytes, 0, chunkSize);
      if (len == -1) {
        try { in.close(); } catch (Exception e) { }  // close the stream if end of file is reached
        resultBytes = null;
      } else if (len < chunkSize && len != chunkSize) {  // if read chunk is last chunk of the file it delivers this chunk 
        byte[] tmp = new byte[len];
        System.arraycopy(resultBytes, 0, tmp, 0, len);
        resultBytes = tmp;
      }
    } catch (FileNotFoundException e) {
      logError(e);
      throw new ApplicationException(e);
    } catch (IOException e) {
      logError(e);
      throw new ApplicationException(e);
    } 
    return resultBytes;  
  }

  /**
   *  Reads a file storing intermediate data into an array.
   *  @file file the file to be read
   *  @return byte[] of file content
   */
  private byte[] readAllBytes(String file) throws ApplicationException {
    InputStream in = null;
    byte[] out = new byte[0]; 
    try {
      in = new BufferedInputStream(new FileInputStream(file));
      // the length of a buffer can vary
      int bufLen = 20000*1024;
      byte[] buf = new byte[bufLen];
      byte[] tmp = null;
      int len = 0;
      while((len = in.read(buf, 0, bufLen)) != -1) {
        // extend array
        tmp = new byte[out.length + len];
        System.arraycopy(out, 0, tmp, 0, out.length);
        System.arraycopy(buf, 0, tmp, out.length, len);
        out = tmp;
        tmp = null;            
      }
    } catch (FileNotFoundException e) {
      logError(e);
      throw new ApplicationException(e);
    } catch (IOException e) {
      logError(e);
      throw new ApplicationException(e);
    } finally {
      // always close the stream 
      if (in != null) try { in.close(); } catch (Exception e) { }
    }
    return out;  
  }

  private String getMimeType(String fileName) throws ApplicationException {
    String mimeType = null;
    File file = new File(fileName);
    try {
      URI uri = file.toURI();
      URL url = uri.toURL();
      URLConnection urlConnection = url.openConnection();
      mimeType = urlConnection.getContentType();
    } catch (MalformedURLException e) {
      logError(e);
      throw new ApplicationException(e);
    } catch (IOException e) {
      logError(e);
      throw new ApplicationException(e);
    }
    return mimeType;
  }
  
  /**
   *  Reads a file storing intermediate data into an array.
   *  @file file the file to be read
   *  @return byte array of the file content
   *  TODO test this method if it is really faster
   */
  private byte[] readAllBytesFast(String file) throws ApplicationException {
    InputStream in = null;
    byte[] buf = null; 
    int bufLen = 20000*1024;
    try {
      in = new BufferedInputStream(new FileInputStream(file));
      buf = new byte[bufLen];
      byte[] tmp = null;
      int len    = 0;
      List data  = new ArrayList(24); // keeps peaces of data
      while((len = in.read(buf, 0, bufLen)) != -1){
        tmp = new byte[len];
        System.arraycopy(buf, 0, tmp, 0, len); // still need to do copy 
        data.add(tmp);
      }
      /* This part os optional. This method could return a List data
         for further processing, etc. */
      len = 0;
      if (data.size() == 1) return (byte[]) data.get(0);
      for (int i=0;i<data.size();i++) len += ((byte[]) data.get(i)).length;
      buf = new byte[len]; // final output buffer 
      len = 0;
      for (int i=0;i<data.size();i++){ // fill with data 
        tmp = (byte[]) data.get(i);
        System.arraycopy(tmp,0,buf,len,tmp.length);
        len += tmp.length;
      } 
    } catch (FileNotFoundException e) {
      logError(e);
      throw new ApplicationException(e);
    } catch (IOException e) {
      logError(e);
      throw new ApplicationException(e);
    } finally {
      if (in != null) try { in.close(); } catch (Exception e) {}
    }
    return buf;  
  }
}