All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.marklogic.client.example.extension.BatchManager Maven / Gradle / Ivy

/*
 * Copyright 2012-2015 MarkLogic Corporation
 *
 * 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 com.marklogic.client.example.extension;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentLinkedQueue;

import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import com.marklogic.client.DatabaseClient;
import com.marklogic.client.FailedRequestException;
import com.marklogic.client.MarkLogicIOException;
import com.marklogic.client.document.DocumentDescriptor;
import com.marklogic.client.document.DocumentManager;
import com.marklogic.client.extensions.ResourceManager;
import com.marklogic.client.extensions.ResourceServices.ServiceResult;
import com.marklogic.client.extensions.ResourceServices.ServiceResultIterator;
import com.marklogic.client.io.BaseHandle;
import com.marklogic.client.io.DOMHandle;
import com.marklogic.client.io.DocumentMetadataHandle;
import com.marklogic.client.io.Format;
import com.marklogic.client.io.StringHandle;
import com.marklogic.client.io.marker.AbstractReadHandle;
import com.marklogic.client.io.marker.AbstractWriteHandle;
import com.marklogic.client.io.marker.XMLReadHandle;
import com.marklogic.client.util.RequestParameters;

/**
 * BatchManager provides an extension for executing a batch of document requests.
 */
public class BatchManager extends ResourceManager {
	public class BatchRequest {
		private LinkedHashMap items = new LinkedHashMap();

		BatchRequest() {
			super();
		}

		public Set listCategories(DocumentManager.Metadata... categories) {
			if (categories == null || categories.length == 0) {
				return null;
			}

			Set catSet = new HashSet();
			for (DocumentManager.Metadata category: categories) {
				catSet.add(category);
			}

			return catSet;
		}

// TODO: DocumentDescriptor overloads
		public BatchRequest withDelete(String uri) {
			items.put(uri, new DeleteInput());
			return this;
		}
		public BatchRequest withRead(String uri, String mimetype) {
			return withRead(uri, (Set) null, mimetype);
		}
		public BatchRequest withRead(String uri, DocumentManager.Metadata category, String mimetype) {
			return withRead(uri, listCategories(category), mimetype);
		}
		public BatchRequest withRead(String uri, Set categories, String mimetype) {
			items.put(uri, new ReadInput().withMetadata(categories).withMimetype(mimetype));
			return this;
		}
		public BatchRequest withWrite(String uri, AbstractWriteHandle content) {
			return withWrite(uri, null, content);
		}
		// TODO: allow any metadata handle
		public BatchRequest withWrite(String uri, DocumentMetadataHandle metadata, AbstractWriteHandle content) {
			items.put(uri, new WriteInput().withMetadata(metadata).withContent(content));
			return this;
		}
	}

// TODO: assemble a single thread-safe queue instead of two separate queues
	static public class BatchResponse implements Iterator {
		boolean success = false;
		Iterator items;
		ServiceResultIterator results;
		BatchResponse() {
			super();
		}
		public boolean getSuccess() {
			return success;
		}
		@Override
		public boolean hasNext() {
			if (items == null)
				return false;
			return items.hasNext();
		}
		@Override
		public OutputItem next() {
			if (items == null)
				return null;
			OutputItem item = items.next();
			if (item.exceptionMimetype != null) {
				if (results == null || !results.hasNext()) {
					throw new IllegalStateException("unable to get exception for request");
				}
				item.exception = results.next();
			} else if (item instanceof ReadOutput) {
				ReadOutput ritem = (ReadOutput) item;
				if (ritem.metadataMimetype != null) {
					if (results == null || !results.hasNext()) {
						throw new IllegalStateException("unable to get metadata for read request");
					}
					ritem.metadata = results.next();
				}
				if (ritem.contentMimetype != null) {
					if (results == null || !results.hasNext()) {
						throw new IllegalStateException("unable to get content for read request");
					}
					ritem.content = results.next();
				}
			}
			if (results != null && !results.hasNext()) {
				results.close();
				results = null;
			}
			return item;
		}
		@Override
		public void remove() {
			throw new UnsupportedOperationException("cannot remove output item");
		}
		public void close() {
			if (results != null) {
				results.close();
				results = null;
			}
			items = null;
		}
		@Override
		protected void finalize() throws Throwable {
			close();
			super.finalize();
		}
	}

	class InputItem {
		DocumentDescriptor desc;
		InputItem withDescriptor(DocumentDescriptor desc) {
			this.desc = desc;
			return this;
		}
		DocumentDescriptor getDescriptor() {
			return desc;
		}
	}
	class DeleteInput extends InputItem {
	}
	class ReadInput extends InputItem {
		Set  categories;
		String mimetype;
		ReadInput withMetadata(Set  categories) {
			this.categories = categories;
			return this;
		}
		ReadInput withMimetype(String mimetype) {
			this.mimetype = mimetype;
			return this;
		}
	}
	class WriteInput extends InputItem {
		DocumentMetadataHandle metadata;
		AbstractWriteHandle    content;
		WriteInput withMetadata(DocumentMetadataHandle metadata) {
			this.metadata = metadata;
			return this;
		}
		WriteInput withContent(AbstractWriteHandle content) {
			if (content == null)
				return this;
			if (!(content instanceof BaseHandle)) {
				throw new MarkLogicIOException(
						"Write handle does not extend base handle:"+
						content.getClass().getName());
			}
			this.content = content;
			return this;
		}
		@SuppressWarnings("rawtypes")
		String getContentMimetype() {
			return ((BaseHandle) content).getMimetype();
		}
		DocumentMetadataHandle getMetadata() {
			return metadata;
		}
		AbstractWriteHandle getContent() {
			return content;
		}
	}

	/**
	 * Enumerates the operations supported on a document in the batch.
	 */
	public enum OperationType {
		DELETE, READ, WRITE;
	}
	abstract public class OutputItem {
		String uri;
//		DocumentDescriptor desc;
		boolean success = false;
		String exceptionMimetype;
		ServiceResult exception;
		OutputItem() {
			super();
		}
		abstract public OperationType getOperationType();
		public String getUri() {
			return uri;
		}
/*
		public DocumentDescriptor getDesc() {
			return desc;
		}
 */
		public boolean getSuccess() {
			return success;
		}
		public boolean hasException() {
			return (exception != null);
		}
		// TODO: expose throwable exception
		public  R getException(R handle) {
			if (exception == null)
				throw new IllegalStateException("could not get exception for result");
			return exception.getContent(handle);
		}
	}
	public class DeleteOutput extends OutputItem {
		DeleteOutput() {
			super();
		}
		public OperationType getOperationType() {
			return OperationType.DELETE;
		}
	}
	public class ReadOutput extends OutputItem {
		String        metadataMimetype;
		ServiceResult metadata;
		String        contentMimetype;
		ServiceResult content;
		ReadOutput() {
			super();
		}
		public OperationType getOperationType() {
			return OperationType.READ;
		}
		public boolean hasMetadata() {
			return (metadata != null);
		}
		public DocumentMetadataHandle getMetadata() {
			if (metadata == null)
				throw new IllegalStateException("could not get metadata for result");
			return metadata.getContent(new DocumentMetadataHandle());
		}
		public  R getMetadata(R handle) {
			if (metadata == null)
				throw new IllegalStateException("could not get metadata for result");
			return metadata.getContent(handle);
		}
		public boolean hasContent() {
			return (content != null);
		}
		public String getContentMimetype() {
			return contentMimetype;
		}
		public  R getContent(R handle) {
			if (content == null)
				throw new IllegalStateException("could not get content for result");
			return content.getContent(handle);
		}
	}
	public class WriteOutput extends OutputItem {
		WriteOutput() {
			super();
		}
		public OperationType getOperationType() {
			return OperationType.WRITE;
		}
	}

	static final public String NAME = "docbatch";

	public BatchManager(DatabaseClient client) {
		super();
		client.init(NAME, this);
	}
	public BatchRequest newBatchRequest() {
		return new BatchRequest();
	}
	public BatchResponse apply(BatchRequest request) {
		if (request == null)
			return null;

		StringHandle requestManifest  = new StringHandle();

		ArrayList requestHandles = new ArrayList();
		requestHandles.add(requestManifest);

		StringBuilder manifestBuilder = new StringBuilder();
		manifestBuilder.append("\n");
		manifestBuilder.append("\n");

		ArrayList readMimetypes = new ArrayList();
		// read the response manifest first
		readMimetypes.add("application/xml");
		for (Map.Entry entry: request.items.entrySet()) {
			String    uri  = entry.getKey();
			InputItem item = entry.getValue();

			if (item instanceof DeleteInput) {
				manifestBuilder.append("\n");

				manifestBuilder.append("");
				manifestBuilder.append(uri);
				manifestBuilder.append("\n");

				manifestBuilder.append("\n");
			} else if (item instanceof ReadInput) {
				ReadInput ritem = (ReadInput) item;
				manifestBuilder.append("\n");

				manifestBuilder.append("");
				manifestBuilder.append(uri);
				manifestBuilder.append("\n");

				if (ritem.categories != null && ritem.categories.size() > 0) {
					StringBuilder categoryBuilder = new StringBuilder();
					categoryBuilder.append("\n");

					for (DocumentManager.Metadata category: ritem.categories) {
						categoryBuilder.append("\n");
					}

					categoryBuilder.append("\n");

					manifestBuilder.append(categoryBuilder.toString());
				}

				if (ritem.mimetype != null) {
					readMimetypes.add(ritem.mimetype);

					manifestBuilder.append("");
					manifestBuilder.append(ritem.mimetype);
					manifestBuilder.append("\n");
				}
				
				manifestBuilder.append("\n");
			} else if (item instanceof WriteInput) {
				WriteInput witem = (WriteInput) item;
				manifestBuilder.append("\n");

				manifestBuilder.append("");
				manifestBuilder.append(uri);
				manifestBuilder.append("\n");

				if (witem.content != null) {
					manifestBuilder.append("");
					manifestBuilder.append(witem.getContentMimetype());
					manifestBuilder.append("\n");

					requestHandles.add(witem.content);
				}

				manifestBuilder.append("\n");
			} 
		}

		manifestBuilder.append("\n");
		requestManifest.set(manifestBuilder.toString());
		requestManifest.setFormat(Format.XML);

		String[] requestMimetypes = new String[readMimetypes.size()];

		ServiceResultIterator resultItr = getServices().post(
				new RequestParameters(),
				requestHandles.toArray(new AbstractWriteHandle[requestHandles.size()]),
				requestMimetypes
				);

		if (!resultItr.hasNext())
			throw new FailedRequestException("Could not executed batch request");
		
		DOMHandle responseManifest = resultItr.next().getContent(new DOMHandle());

		List items = new ArrayList();

		boolean requestSuccess = true;

		NodeList responseItems = responseManifest.get().getDocumentElement().getChildNodes();
		int      responseCount = responseItems.getLength();
		for (int i=0; i < responseCount; i++) {
			Node responseNode = responseItems.item(i);
			if (responseNode.getNodeType() != Node.ELEMENT_NODE)
				continue;
			Element responseItem = (Element) responseNode;

			String  itemUri       = null;
			boolean itemSuccess   = false;
			String  itemMetadata  = null;
			String  itemContent   = null;
			String  itemException = null;

			NodeList fieldItems = responseItem.getChildNodes();
			int      fieldCount = fieldItems.getLength();
			for (int j=0; j < fieldCount; j++) {
				Node fieldNode = fieldItems.item(j);
				if (fieldNode.getNodeType() != Node.ELEMENT_NODE)
					continue;
				Element fieldItem = (Element) fieldNode;
				String  fieldName = fieldItem.getLocalName();

				if ("uri".equals(fieldName)) {
					itemUri       = fieldItem.getTextContent();
				} else if ("request-succeeded".equals(fieldName)) {
					itemSuccess   = "true".equals(fieldItem.getTextContent());
				} else if ("metadata-mimetype".equals(fieldName)) {
					itemMetadata  = fieldItem.getTextContent();
				} else if ("content-mimetype".equals(fieldName)) {
					itemContent   = fieldItem.getTextContent();
				} else if ("request-failure".equals(fieldName)) {
					itemException = fieldItem.getTextContent();
				} else {
					// TODO: warn
				}
			}

			if (requestSuccess && !itemSuccess)
				requestSuccess = false;

			String responseName = responseItem.getLocalName();
			if ("delete-response".equals(responseName)) {
				DeleteOutput deleteOutput = new DeleteOutput();
				deleteOutput.uri = itemUri;
				deleteOutput.success = itemSuccess;
				if (itemException != null) {
					deleteOutput.exceptionMimetype = itemException;
				}
				items.add(deleteOutput);
			} else if ("get-response".equals(responseName)) {
				ReadOutput readOutput = new ReadOutput();
				readOutput.uri = itemUri;
				readOutput.success = itemSuccess;
				if (itemException != null) {
					readOutput.exceptionMimetype = itemException;
				} else {
					if (itemMetadata != null) {
						readOutput.metadataMimetype = itemMetadata;
					}
					if (itemContent != null) {
						// TODO: set format
						readOutput.contentMimetype = itemContent;
					}
				}
				items.add(readOutput);
			} else if ("put-response".equals(responseName)) {
				WriteOutput writeOutput = new WriteOutput();
				writeOutput.uri = itemUri;
				writeOutput.success = itemSuccess;
				if (itemException != null) {
					writeOutput.exceptionMimetype = itemException;
				}
				items.add(writeOutput);
			}
		}

		BatchResponse response = new BatchResponse();
		response.success = requestSuccess;
		response.items   = new ConcurrentLinkedQueue(items).iterator();
		response.results = resultItr;

		return response;
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy