/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt * or http://forgerock.org/license/CDDLv1.0.html. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at legal-notices/CDDLv1_0.txt. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: * Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END * * * Copyright 2006-2009 Sun Microsystems, Inc. * Portions Copyright 2015 ForgeRock AS */ package org.opends.server.tools.makeldif; import java.io.ByteArrayOutputStream; import java.io.InputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; import org.opends.server.types.LDIFExportConfig; import org.opends.server.util.LDIFException; import org.opends.server.util.LDIFWriter; /** * This class creates an input stream that can be used to read entries generated * by MakeLDIF as if they were being read from another source like a file. It * has a fixed-size queue that dictates how many entries may be held in memory * at any given time. */ public class MakeLDIFInputStream extends InputStream implements EntryWriter { /** Indicates whether all of the entries have been generated. */ private boolean allGenerated; /** Indicates whether this input stream has been closed. */ private boolean closed; /** * The byte array output stream that will be used to convert entries to byte * arrays with their LDIF representations. */ private ByteArrayOutputStream entryOutputStream; /** * The byte array that will hold the LDIF representation of the next entry to * be read. */ private ByteBuffer entryBytes; /** The IOException that should be thrown the next time a read is requested. */ private IOException ioException; /** The LDIF writer that will be used to write the entries to LDIF. */ private LDIFWriter ldifWriter; /** The queue used to hold generated entries until they can be read. */ private LinkedBlockingQueue<TemplateEntry> entryQueue; /** The background thread being used to actually generate the entries. */ private MakeLDIFInputStreamThread generatorThread; /** The template file to use to generate the entries. */ private TemplateFile templateFile; /** * Creates a new MakeLDIF input stream that will generate entries based on the * provided template file. * * @param templateFile The template file to use to generate the entries. */ public MakeLDIFInputStream(TemplateFile templateFile) { this.templateFile = templateFile; allGenerated = false; closed = false; entryQueue = new LinkedBlockingQueue<>(10); ioException = null; entryBytes = null; entryOutputStream = new ByteArrayOutputStream(8192); LDIFExportConfig exportConfig = new LDIFExportConfig(entryOutputStream); try { ldifWriter = new LDIFWriter(exportConfig); } catch (IOException ioe) { // This should never happen. ioException = ioe; } generatorThread = new MakeLDIFInputStreamThread(this, templateFile); generatorThread.start(); } /** * Closes this input stream so that no more data may be read from it. */ public void close() { closed = true; ioException = null; } /** * Reads a single byte of data from this input stream. * * @return The byte read from the input stream, or -1 if the end of the * stream has been reached. * * @throws IOException If a problem has occurred while generating data for * use by this input stream. */ public int read() throws IOException { if (closed) { return -1; } else if (ioException != null) { throw ioException; } if ((entryBytes == null || !entryBytes.hasRemaining()) && !getNextEntry()) { closed = true; return -1; } return 0xFF & entryBytes.get(); } /** * Reads data from this input stream. * * @param b The array into which the data should be read. * @param off The position in the array at which point the data read may be * placed. * @param len The maximum number of bytes that may be read into the * provided array. * * @return The number of bytes read from the input stream into the provided * array, or -1 if the end of the stream has been reached. * * @throws IOException If a problem has occurred while generating data for * use by this input stream. */ public int read(byte[] b, int off, int len) throws IOException { if (closed) { return -1; } else if (ioException != null) { throw ioException; } if ((entryBytes == null || !entryBytes.hasRemaining()) && !getNextEntry()) { closed = true; return -1; } int bytesRead = Math.min(len, entryBytes.remaining()); entryBytes.get(b, off, bytesRead); return bytesRead; } /** {@inheritDoc} */ public boolean writeEntry(TemplateEntry entry) throws IOException, MakeLDIFException { while (! closed) { try { if (entryQueue.offer(entry, 500, TimeUnit.MILLISECONDS)) { return true; } } catch (InterruptedException ie) {} } return false; } /** {@inheritDoc} */ public void closeEntryWriter() { allGenerated = true; } /** * Sets the I/O exception that should be thrown on any subsequent calls to * <CODE>available</CODE> or <CODE>read</CODE>. * * @param ioException The I/O exception that should be thrown. */ void setIOException(IOException ioException) { this.ioException = ioException; } /** * Retrieves the next entry and puts it in the entry byte buffer. * * @return <CODE>true</CODE> if the next entry is available, or * <CODE>false</CODE> if there are no more entries or if the input * stream has been closed. */ private boolean getNextEntry() { TemplateEntry entry = entryQueue.poll(); while (entry == null) { if (closed) { return false; } else if (allGenerated) { entry = entryQueue.poll(); if (entry == null) { return false; } } else { try { entry = entryQueue.poll(500, TimeUnit.MILLISECONDS); } catch (InterruptedException ie) {} } } try { entryOutputStream.reset(); ldifWriter.writeTemplateEntry(entry); ldifWriter.flush(); entryBytes = ByteBuffer.wrap(entryOutputStream.toByteArray()); } catch (LDIFException le) { // This should never happen. ioException = new IOException(le.getMessage()); return false; } catch (IOException ioe) { // Neither should this. ioException = ioe; return false; } return true; } }