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

java.fedora.server.journal.JournalReader Maven / Gradle / Ivy

Go to download

The Fedora Client is a Java Library that allows API access to a Fedora Repository. The client is typically one part of a full Fedora installation.

The newest version!
/*
 * -----------------------------------------------------------------------------
 *
 * 

License and Copyright: The contents of this file are subject to 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.fedora-commons.org/licenses.

* *

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 entire file consists of original code.

*

Copyright © 2008 Fedora Commons, Inc.
*

Copyright © 2002-2007 The Rector and Visitors of the University of * Virginia and Cornell University
* All rights reserved.

* * ----------------------------------------------------------------------------- */ package fedora.server.journal; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Map; import javax.xml.namespace.QName; import javax.xml.stream.XMLEventReader; import javax.xml.stream.XMLStreamException; import javax.xml.stream.events.StartElement; import javax.xml.stream.events.XMLEvent; import org.apache.log4j.Logger; import fedora.server.errors.ModuleInitializationException; import fedora.server.errors.ServerException; import fedora.server.journal.entry.ConsumerJournalEntry; import fedora.server.journal.entry.JournalEntryContext; import fedora.server.journal.helpers.DecodingBase64OutputStream; import fedora.server.journal.helpers.JournalHelper; import fedora.server.journal.recoverylog.JournalRecoveryLog; import fedora.server.journal.xmlhelpers.AbstractXmlReader; import fedora.server.journal.xmlhelpers.BindingMapXmlReader; import fedora.server.journal.xmlhelpers.ContextXmlReader; /** *

* Title: JournalReader.java *

*

* Description: The abstract base for all JournalReader classes. Each * child class is responsible for providing an XMLEventReader that is positioned * at the beginning of a JournalEntry tag. This class will read the entry and * leave the XMLEventReader positioned after the corresponding closing tag. *

* * @author [email protected] * @version $Id: JournalReader.java 6726 2008-03-03 20:04:05 +0000 (Mon, 03 Mar * 2008) birkland $ */ public abstract class JournalReader extends AbstractXmlReader implements JournalConstants { /** Logger for this class. */ private static final Logger LOG = Logger.getLogger(JournalReader.class.getName()); protected final Map parameters; protected final String role; protected final JournalRecoveryLog recoveryLog; protected final ServerInterface server; private boolean ignoreHashErrors; /** * Create an instance of the proper JournalReader child class, as determined * by the server parameters. */ public static JournalReader getInstance(Map parameters, String role, JournalRecoveryLog recoveryLog, ServerInterface server) throws ModuleInitializationException { try { Object journalReader = JournalHelper .createInstanceAccordingToParameter(PARAMETER_JOURNAL_READER_CLASSNAME, new Class[] { Map.class, String.class, JournalRecoveryLog.class, ServerInterface.class}, new Object[] { parameters, role, recoveryLog, server}, parameters); LOG.info("JournalReader is " + journalReader.toString()); return (JournalReader) journalReader; } catch (JournalException e) { String msg = "Can't create JournalReader"; LOG.error(msg, e); throw new ModuleInitializationException(msg, role, e); } } /** * Concrete sub-classes must implement this constructor. */ protected JournalReader(Map parameters, String role, JournalRecoveryLog recoveryLog, ServerInterface server) throws JournalException { this.parameters = parameters; this.role = role; this.recoveryLog = recoveryLog; this.server = server; parseParameters(); } private void parseParameters() throws JournalException { String ignore = parameters.get(PARAMETER_IGNORE_HASH); if (ignore == null) { ignoreHashErrors = false; } else if (ignore.equals(VALUE_FALSE)) { ignoreHashErrors = false; } else if (ignore.equals(VALUE_TRUE)) { ignoreHashErrors = true; } else { throw new JournalException("'" + PARAMETER_IGNORE_HASH + "' parameter must be '" + VALUE_FALSE + "'(default) or '" + VALUE_TRUE + "'"); } } /** * Concrete sub-classes should probably synchronize this method, since it * can be called either from the JournalConsumerThread or from the Server. */ public abstract void shutdown() throws JournalException; /** * Concrete sub-classes should insure that their XMLEventReader is * positioned at the beginning of a JournalEntry, and call * {@link #readJournalEntry(XMLEventReader)}. It is likely that this method * should be synchronized also, since it could be called from * JournalConsumerThread when the server calls {@link #shutdown()} */ public abstract ConsumerJournalEntry readJournalEntry() throws JournalException, XMLStreamException; /** * Compare the repository hash from the journal file with the current hash * obtained from the server. If they do not match, either throw an exception * or simply log it, depending on the parameters. This method must not be * called before the server has completed initialization. That's the only * way we can be confident that the DOManager is present, and ready to * create the repository has that we will compare to. */ protected void checkRepositoryHash(String hash) throws JournalException { if (!server.hasInitialized()) { throw new IllegalStateException("The repository has is not available until " + "the server is fully initialized."); } JournalException hashException = null; if (hash == null) { hashException = new JournalException("'" + QNAME_TAG_JOURNAL + "' tag must have a '" + QNAME_ATTR_REPOSITORY_HASH + "' attribute."); } else { try { String currentHash = server.getRepositoryHash(); if (hash.equals(currentHash)) { recoveryLog.log("Validated repository hash: '" + hash + "'."); } else { hashException = new JournalException("'" + QNAME_ATTR_REPOSITORY_HASH + "' attribute in '" + QNAME_TAG_JOURNAL + "' tag does not match current repository hash: '" + hash + "' vs. '" + currentHash + "'."); } } catch (ServerException e) { hashException = new JournalException(e); } } if (hashException != null) { if (ignoreHashErrors) { recoveryLog.log("WARNING: " + hashException.getMessage()); } else { throw hashException; } } } /** * Read a JournalEntry from the journal, to produce a * ConsumerJournalEntry instance. Concrete sub-classes should * insure that the XMLEventReader is positioned at the beginning of a * JournalEntry before calling this method. */ protected ConsumerJournalEntry readJournalEntry(XMLEventReader reader) throws JournalException, XMLStreamException { StartElement startTag = getJournalEntryStartTag(reader); String methodName = getRequiredAttributeValue(startTag, QNAME_ATTR_METHOD); JournalEntryContext context = new ContextXmlReader().readContext(reader); ConsumerJournalEntry cje = new ConsumerJournalEntry(methodName, context); readArguments(reader, cje); return cje; } /** * Get the next event and complain if it isn't a JournalEntry start tag. */ private StartElement getJournalEntryStartTag(XMLEventReader reader) throws XMLStreamException, JournalException { XMLEvent event = reader.nextTag(); if (!isStartTagEvent(event, QNAME_TAG_JOURNAL_ENTRY)) { throw getNotStartTagException(QNAME_TAG_JOURNAL_ENTRY, event); } return event.asStartElement(); } /** * Read arguments and add them to the event, until we hit the end tag for * the event. */ private void readArguments(XMLEventReader reader, ConsumerJournalEntry cje) throws XMLStreamException, JournalException { while (true) { XMLEvent nextTag = reader.nextTag(); if (isStartTagEvent(nextTag, QNAME_TAG_ARGUMENT)) { readArgument(nextTag, reader, cje); } else if (isEndTagEvent(nextTag, QNAME_TAG_JOURNAL_ENTRY)) { return; } else { throw getNotNextMemberOrEndOfGroupException(QNAME_TAG_JOURNAL_ENTRY, QNAME_TAG_ARGUMENT, nextTag); } } } private void readArgument(XMLEvent nextTag, XMLEventReader reader, ConsumerJournalEntry journalEntry) throws JournalException, XMLStreamException { StartElement element = nextTag.asStartElement(); String argName = getRequiredAttributeValue(element, QNAME_ATTR_NAME); String argType = getRequiredAttributeValue(element, QNAME_ATTR_TYPE); if (ARGUMENT_TYPE_NULL.equals(argType)) { readNullArgument(reader, journalEntry, argName); } else if (ARGUMENT_TYPE_STRING.equals(argType)) { readStringArgument(reader, journalEntry, argName); } else if (ARGUMENT_TYPE_STRINGARRAY.equals(argType)) { readStringArrayArgument(reader, journalEntry, argName); } else if (ARGUMENT_TYPE_INTEGER.equals(argType)) { readIntegerArgument(reader, journalEntry, argName); } else if (ARGUMENT_TYPE_BOOLEAN.equals(argType)) { readBooleanArgument(reader, journalEntry, argName); } else if (ARGUMENT_TYPE_DATE.equals(argType)) { readDateArgument(reader, journalEntry, argName); } else if (ARGUMENT_TYPE_STREAM.equals(argType)) { readStreamArgument(reader, journalEntry, argName); } else if (ARGUMENT_TYPE_BINDING_MAP.equals(argType)) { readBindingMapArgument(reader, journalEntry, argName); } else { throw new JournalException("Unknown argument type: name='" + argName + "', type='" + argType + "'"); } } private void readStringArgument(XMLEventReader reader, ConsumerJournalEntry journalEntry, String name) throws XMLStreamException, JournalException { String value = readCharactersUntilEndOfArgument(reader, QNAME_TAG_ARGUMENT, journalEntry.getMethodName(), name, ARGUMENT_TYPE_STRING); journalEntry.addArgument(name, value); } private void readStringArrayArgument(XMLEventReader reader, ConsumerJournalEntry journalEntry, String name) throws XMLStreamException, JournalException { List values = new ArrayList(); while (true) { XMLEvent event = reader.nextTag(); if (isStartTagEvent(event, QNAME_TAG_ARRAYELEMENT)) { values .add(readCharactersUntilEndOfArgument(reader, QNAME_TAG_ARRAYELEMENT, journalEntry .getMethodName(), name, ARGUMENT_TYPE_STRINGARRAY)); } else if (isEndTagEvent(event, QNAME_TAG_ARGUMENT)) { break; } else { throw getUnexpectedEventInArgumentException(name, ARGUMENT_TYPE_STRINGARRAY, journalEntry .getMethodName(), event); } } Object[] valuesArray = values.toArray(new String[values.size()]); journalEntry.addArgument(name, valuesArray); } private void readIntegerArgument(XMLEventReader reader, ConsumerJournalEntry journalEntry, String name) throws XMLStreamException, JournalException { XMLEvent chars = reader.nextEvent(); if (!chars.isCharacters()) { throw getUnexpectedEventInArgumentException(name, ARGUMENT_TYPE_INTEGER, journalEntry .getMethodName(), chars); } Integer integerValue = Integer.valueOf(chars.asCharacters().getData()); XMLEvent endTag = reader.nextEvent(); if (isEndTagEvent(endTag, QNAME_TAG_ARGUMENT)) { journalEntry.addArgument(name, integerValue); } else { throw getUnexpectedEventInArgumentException(name, ARGUMENT_TYPE_INTEGER, journalEntry .getMethodName(), endTag); } } private void readBooleanArgument(XMLEventReader reader, ConsumerJournalEntry journalEntry, String name) throws XMLStreamException, JournalException { XMLEvent chars = reader.nextEvent(); if (!chars.isCharacters()) { throw getUnexpectedEventInArgumentException(name, ARGUMENT_TYPE_BOOLEAN, journalEntry .getMethodName(), chars); } Boolean booleanValue = Boolean.valueOf(chars.asCharacters().getData()); XMLEvent endTag = reader.nextEvent(); if (isEndTagEvent(endTag, QNAME_TAG_ARGUMENT)) { journalEntry.addArgument(name, booleanValue); } else { throw getUnexpectedEventInArgumentException(name, ARGUMENT_TYPE_BOOLEAN, journalEntry .getMethodName(), endTag); } } private void readDateArgument(XMLEventReader reader, ConsumerJournalEntry journalEntry, String name) throws XMLStreamException, JournalException { XMLEvent chars = reader.nextEvent(); if (!chars.isCharacters()) { throw getUnexpectedEventInArgumentException(name, ARGUMENT_TYPE_BOOLEAN, journalEntry .getMethodName(), chars); } Date dateValue = JournalHelper.parseDate(chars.asCharacters().getData()); XMLEvent endTag = reader.nextEvent(); if (isEndTagEvent(endTag, QNAME_TAG_ARGUMENT)) { journalEntry.addArgument(name, dateValue); } else { throw getUnexpectedEventInArgumentException(name, ARGUMENT_TYPE_DATE, journalEntry .getMethodName(), endTag); } } /** * An InputStream argument appears as a Base64-encoded String. It must be * decoded and written to a temp file, so it can be presented to the * management method as an InputStream again. */ private void readStreamArgument(XMLEventReader reader, ConsumerJournalEntry journalEntry, String name) throws XMLStreamException, JournalException { try { File tempFile = JournalHelper.createTempFile(); DecodingBase64OutputStream decoder = new DecodingBase64OutputStream(new FileOutputStream(tempFile)); while (true) { XMLEvent event = reader.nextEvent(); if (event.isCharacters()) { decoder.write(event.asCharacters().getData()); } else if (isEndTagEvent(event, QNAME_TAG_ARGUMENT)) { break; } else { throw getUnexpectedEventInArgumentException(name, ARGUMENT_TYPE_STREAM, journalEntry .getMethodName(), event); } } decoder.close(); journalEntry.addArgument(name, tempFile); } catch (IOException e) { throw new JournalException("failed to write stream argument to temp file", e); } } private void readNullArgument(XMLEventReader reader, ConsumerJournalEntry journalEntry, String name) throws XMLStreamException, JournalException { XMLEvent endTag = reader.nextTag(); if (!isEndTagEvent(endTag, QNAME_TAG_ARGUMENT)) { throw getUnexpectedEventInArgumentException(name, ARGUMENT_TYPE_NULL, journalEntry .getMethodName(), endTag); } } private void readBindingMapArgument(XMLEventReader reader, ConsumerJournalEntry journalEntry, String name) throws XMLStreamException, JournalException { journalEntry.addArgument(name, new BindingMapXmlReader() .readBindingMap(reader)); XMLEvent endTag = reader.nextTag(); if (!isEndTagEvent(endTag, QNAME_TAG_ARGUMENT)) { throw getUnexpectedEventInArgumentException(name, ARGUMENT_TYPE_BINDING_MAP, journalEntry .getMethodName(), endTag); } } /** * Loop through a series of character events, accumulating the data into a * String. The character events should be terminated by an EndTagEvent with * the expected tag name. */ private String readCharactersUntilEndOfArgument(XMLEventReader reader, QName tagName, String methodName, String argumentName, String argumentType) throws XMLStreamException, JournalException { StringBuffer stringValue = new StringBuffer(); while (true) { XMLEvent event = reader.nextEvent(); if (event.isCharacters()) { stringValue.append(event.asCharacters().getData()); } else if (isEndTagEvent(event, tagName)) { break; } else { throw getUnexpectedEventInArgumentException(argumentName, argumentType, methodName, event); } } return stringValue.toString(); } /** * If we encounter an unexpected event when reading the a method argument, * create an exception with all of the pertinent information. */ private JournalException getUnexpectedEventInArgumentException(String name, String argumentType, String methodName, XMLEvent event) { return new JournalException("Unexpected event while processing '" + name + "' argument (type = '" + argumentType + "') for '" + methodName + "' method call: event is '" + event + "'"); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy