001/* 002 * CDDL HEADER START 003 * 004 * The contents of this file are subject to the terms of the 005 * Common Development and Distribution License, Version 1.0 only 006 * (the "License"). You may not use this file except in compliance 007 * with the License. 008 * 009 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt 010 * or http://forgerock.org/license/CDDLv1.0.html. 011 * See the License for the specific language governing permissions 012 * and limitations under the License. 013 * 014 * When distributing Covered Code, include this CDDL HEADER in each 015 * file and include the License file at legal-notices/CDDLv1_0.txt. 016 * If applicable, add the following below this CDDL HEADER, with the 017 * fields enclosed by brackets "[]" replaced with your own identifying 018 * information: 019 * Portions Copyright [yyyy] [name of copyright owner] 020 * 021 * CDDL HEADER END 022 * 023 * 024 * Copyright 2006-2009 Sun Microsystems, Inc. 025 * Portions Copyright 2015 ForgeRock AS 026 */ 027package org.opends.server.tools.makeldif; 028 029 030 031import java.io.ByteArrayOutputStream; 032import java.io.InputStream; 033import java.io.IOException; 034import java.nio.ByteBuffer; 035import java.util.concurrent.LinkedBlockingQueue; 036import java.util.concurrent.TimeUnit; 037 038import org.opends.server.types.LDIFExportConfig; 039import org.opends.server.util.LDIFException; 040import org.opends.server.util.LDIFWriter; 041 042 043 044/** 045 * This class creates an input stream that can be used to read entries generated 046 * by MakeLDIF as if they were being read from another source like a file. It 047 * has a fixed-size queue that dictates how many entries may be held in memory 048 * at any given time. 049 */ 050public class MakeLDIFInputStream 051 extends InputStream 052 implements EntryWriter 053{ 054 /** Indicates whether all of the entries have been generated. */ 055 private boolean allGenerated; 056 057 /** Indicates whether this input stream has been closed. */ 058 private boolean closed; 059 060 /** 061 * The byte array output stream that will be used to convert entries to byte 062 * arrays with their LDIF representations. 063 */ 064 private ByteArrayOutputStream entryOutputStream; 065 066 /** 067 * The byte array that will hold the LDIF representation of the next entry to 068 * be read. 069 */ 070 private ByteBuffer entryBytes; 071 072 /** The IOException that should be thrown the next time a read is requested. */ 073 private IOException ioException; 074 075 /** The LDIF writer that will be used to write the entries to LDIF. */ 076 private LDIFWriter ldifWriter; 077 078 /** The queue used to hold generated entries until they can be read. */ 079 private LinkedBlockingQueue<TemplateEntry> entryQueue; 080 081 /** The background thread being used to actually generate the entries. */ 082 private MakeLDIFInputStreamThread generatorThread; 083 084 /** The template file to use to generate the entries. */ 085 private TemplateFile templateFile; 086 087 088 089 /** 090 * Creates a new MakeLDIF input stream that will generate entries based on the 091 * provided template file. 092 * 093 * @param templateFile The template file to use to generate the entries. 094 */ 095 public MakeLDIFInputStream(TemplateFile templateFile) 096 { 097 this.templateFile = templateFile; 098 099 allGenerated = false; 100 closed = false; 101 entryQueue = new LinkedBlockingQueue<>(10); 102 ioException = null; 103 entryBytes = null; 104 105 entryOutputStream = new ByteArrayOutputStream(8192); 106 LDIFExportConfig exportConfig = new LDIFExportConfig(entryOutputStream); 107 108 try 109 { 110 ldifWriter = new LDIFWriter(exportConfig); 111 } 112 catch (IOException ioe) 113 { 114 // This should never happen. 115 ioException = ioe; 116 } 117 118 generatorThread = new MakeLDIFInputStreamThread(this, templateFile); 119 generatorThread.start(); 120 } 121 122 123 124 /** 125 * Closes this input stream so that no more data may be read from it. 126 */ 127 public void close() 128 { 129 closed = true; 130 ioException = null; 131 } 132 133 134 135 /** 136 * Reads a single byte of data from this input stream. 137 * 138 * @return The byte read from the input stream, or -1 if the end of the 139 * stream has been reached. 140 * 141 * @throws IOException If a problem has occurred while generating data for 142 * use by this input stream. 143 */ 144 public int read() 145 throws IOException 146 { 147 if (closed) 148 { 149 return -1; 150 } 151 else if (ioException != null) 152 { 153 throw ioException; 154 } 155 156 if ((entryBytes == null || !entryBytes.hasRemaining()) 157 && !getNextEntry()) 158 { 159 closed = true; 160 return -1; 161 } 162 163 return 0xFF & entryBytes.get(); 164 } 165 166 167 168 /** 169 * Reads data from this input stream. 170 * 171 * @param b The array into which the data should be read. 172 * @param off The position in the array at which point the data read may be 173 * placed. 174 * @param len The maximum number of bytes that may be read into the 175 * provided array. 176 * 177 * @return The number of bytes read from the input stream into the provided 178 * array, or -1 if the end of the stream has been reached. 179 * 180 * @throws IOException If a problem has occurred while generating data for 181 * use by this input stream. 182 */ 183 public int read(byte[] b, int off, int len) 184 throws IOException 185 { 186 if (closed) 187 { 188 return -1; 189 } 190 else if (ioException != null) 191 { 192 throw ioException; 193 } 194 195 if ((entryBytes == null || !entryBytes.hasRemaining()) 196 && !getNextEntry()) 197 { 198 closed = true; 199 return -1; 200 } 201 202 int bytesRead = Math.min(len, entryBytes.remaining()); 203 entryBytes.get(b, off, bytesRead); 204 return bytesRead; 205 } 206 207 208 209 /** {@inheritDoc} */ 210 public boolean writeEntry(TemplateEntry entry) 211 throws IOException, MakeLDIFException 212 { 213 while (! closed) 214 { 215 try 216 { 217 if (entryQueue.offer(entry, 500, TimeUnit.MILLISECONDS)) 218 { 219 return true; 220 } 221 } catch (InterruptedException ie) {} 222 } 223 224 return false; 225 } 226 227 228 229 /** {@inheritDoc} */ 230 public void closeEntryWriter() 231 { 232 allGenerated = true; 233 } 234 235 236 237 /** 238 * Sets the I/O exception that should be thrown on any subsequent calls to 239 * <CODE>available</CODE> or <CODE>read</CODE>. 240 * 241 * @param ioException The I/O exception that should be thrown. 242 */ 243 void setIOException(IOException ioException) 244 { 245 this.ioException = ioException; 246 } 247 248 249 250 /** 251 * Retrieves the next entry and puts it in the entry byte buffer. 252 * 253 * @return <CODE>true</CODE> if the next entry is available, or 254 * <CODE>false</CODE> if there are no more entries or if the input 255 * stream has been closed. 256 */ 257 private boolean getNextEntry() 258 { 259 TemplateEntry entry = entryQueue.poll(); 260 while (entry == null) 261 { 262 if (closed) 263 { 264 return false; 265 } 266 else if (allGenerated) 267 { 268 entry = entryQueue.poll(); 269 if (entry == null) 270 { 271 return false; 272 } 273 } 274 else 275 { 276 try 277 { 278 entry = entryQueue.poll(500, TimeUnit.MILLISECONDS); 279 } catch (InterruptedException ie) {} 280 } 281 } 282 283 try 284 { 285 entryOutputStream.reset(); 286 ldifWriter.writeTemplateEntry(entry); 287 ldifWriter.flush(); 288 entryBytes = ByteBuffer.wrap(entryOutputStream.toByteArray()); 289 } 290 catch (LDIFException le) 291 { 292 // This should never happen. 293 ioException = new IOException(le.getMessage()); 294 return false; 295 } 296 catch (IOException ioe) 297 { 298 // Neither should this. 299 ioException = ioe; 300 return false; 301 } 302 303 return true; 304 } 305} 306