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