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 2008 Sun Microsystems, Inc. 015 * Portions Copyright 2014-2015 ForgeRock AS. 016 */ 017package org.opends.server.protocols.internal; 018 019 020 021import java.io.IOException; 022import java.io.InputStream; 023import java.util.concurrent.ArrayBlockingQueue; 024 025import org.forgerock.opendj.io.ASN1; 026import org.forgerock.opendj.io.ASN1Writer; 027import org.opends.server.protocols.ldap.LDAPMessage; 028import org.forgerock.opendj.ldap.ByteSequenceReader; 029import org.forgerock.opendj.ldap.ByteStringBuilder; 030 031 032/** 033 * This class provides an implementation of a 034 * {@code java.io.InputStream} that can be used to facilitate internal 035 * communication with the Directory Server. On the backend, this 036 * input stream will be populated by ASN.1 elements encoded from LDAP 037 * messages created from internal operation responses. 038 */ 039@org.opends.server.types.PublicAPI( 040 stability=org.opends.server.types.StabilityLevel.UNCOMMITTED, 041 mayInstantiate=false, 042 mayExtend=false, 043 mayInvoke=true) 044public final class InternalLDAPInputStream 045 extends InputStream 046{ 047 /** 048 * The queue of LDAP messages providing the data to be made 049 * available to the client. 050 */ 051 private final ArrayBlockingQueue<LDAPMessage> messageQueue; 052 053 /** Indicates whether this stream has been closed. */ 054 private boolean closed; 055 056 /** The byte buffer with partial data to be written to the client. */ 057 private final ByteStringBuilder messageBuffer; 058 059 /** The byte buffer reader. */ 060 private final ByteSequenceReader messageReader; 061 062 /** The byte buffer writer. */ 063 private final ASN1Writer writer; 064 065 /** The internal LDAP socket serviced by this input stream. */ 066 private final InternalLDAPSocket socket; 067 068 069 070 /** 071 * Creates a new internal LDAP input stream that will service the 072 * provided internal LDAP socket. 073 * 074 * @param socket The internal LDAP socket serviced by this 075 * internal LDAP input stream. 076 */ 077 public InternalLDAPInputStream(InternalLDAPSocket socket) 078 { 079 this.socket = socket; 080 this.messageQueue = new ArrayBlockingQueue<>(10); 081 this.messageBuffer = new ByteStringBuilder(); 082 this.messageReader = messageBuffer.asReader(); 083 this.writer = ASN1.getWriter(messageBuffer); 084 this.closed = false; 085 } 086 087 088 089 /** 090 * Adds the provided LDAP message to the set of messages to be 091 * returned to the client. Note that this may block if there is 092 * already a significant backlog of messages to be returned. 093 * 094 * @param message The message to add to the set of messages to be 095 * returned to the client. 096 */ 097 @org.opends.server.types.PublicAPI( 098 stability=org.opends.server.types.StabilityLevel.PRIVATE, 099 mayInstantiate=false, 100 mayExtend=false, 101 mayInvoke=false) 102 void addLDAPMessage(LDAPMessage message) 103 { 104 // If the stream is closed, then simply drop the message. 105 if (closed) 106 { 107 return; 108 } 109 110 try 111 { 112 messageQueue.put(message); 113 return; 114 } 115 catch (Exception e) 116 { 117 // This shouldn't happen, but if it does then try three more 118 // times before giving up and dropping the message. 119 for (int i=0; i < 3; i++) 120 { 121 try 122 { 123 messageQueue.put(message); 124 break; 125 } catch (Exception e2) {} 126 } 127 128 return; 129 } 130 } 131 132 133 134 /** 135 * Retrieves the number of bytes that can be read (or skipped over) 136 * from this input stream without blocking. 137 * 138 * @return The number of bytes that can be read (or skipped over) 139 * from this input stream without blocking. 140 * @throws IOException if an I/O error occurs. 141 */ 142 @Override 143 public synchronized int available() throws IOException 144 { 145 if (messageReader.remaining() < 1) 146 { 147 LDAPMessage message = messageQueue.poll(); 148 if (message == null || message instanceof NullLDAPMessage) 149 { 150 if (message != null) 151 { 152 messageQueue.clear(); 153 closed = true; 154 } 155 156 return 0; 157 } 158 else 159 { 160 messageBuffer.clear(); 161 messageReader.rewind(); 162 message.write(writer); 163 } 164 } 165 166 return messageReader.remaining(); 167 } 168 169 170 171 /** 172 * Closes this input stream. This will add a special marker 173 * element to the message queue indicating that the end of the 174 * stream has been reached. If the queue is full, then it will be 175 * cleared before adding the marker element. 176 */ 177 @Override 178 public void close() 179 { 180 socket.close(); 181 } 182 183 184 185 /** 186 * Closes this input stream through an internal mechanism that will 187 * not cause an infinite recursion loop by trying to also close the 188 * input stream. 189 */ 190 @org.opends.server.types.PublicAPI( 191 stability=org.opends.server.types.StabilityLevel.PRIVATE, 192 mayInstantiate=false, 193 mayExtend=false, 194 mayInvoke=false) 195 void closeInternal() 196 { 197 if (closed) 198 { 199 return; 200 } 201 202 closed = true; 203 NullLDAPMessage nullMessage = new NullLDAPMessage(); 204 205 while (! messageQueue.offer(nullMessage)) 206 { 207 messageQueue.clear(); 208 } 209 } 210 211 212 213 /** 214 * Marks the current position in the input stream. This will not 215 * have any effect, as this input stream implementation does not 216 * support marking. 217 * 218 * @param readLimit The maximum limit of bytes that can be read 219 * before the mark position becomes invalid. 220 */ 221 @Override 222 public void mark(int readLimit) 223 { 224 // No implementation is required. 225 } 226 227 228 229 /** 230 * Indicates whether this input stream implementation supports the 231 * use of the {@code mark} and {@code reset} methods. This 232 * implementation does not support that functionality. 233 * 234 * @return {@code false} because this implementation does not 235 * support the use of the {@code mark} and {@code reset} 236 * methods. 237 */ 238 @Override 239 public boolean markSupported() 240 { 241 return false; 242 } 243 244 245 246 /** 247 * Reads the next byte of data from the input stream, blocking if 248 * necessary until there is data available. 249 * 250 * @return The next byte of data read from the input stream, or -1 251 * if the end of the input stream has been reached. 252 * 253 * @throws IOException If a problem occurs while trying to read 254 * data from the stream. 255 */ 256 @Override 257 public synchronized int read() 258 throws IOException 259 { 260 if (messageReader.remaining() < 1) 261 { 262 LDAPMessage message; 263 try 264 { 265 message = messageQueue.take(); 266 } 267 catch(InterruptedException ie) 268 { 269 // Probably because a shutdown was started. EOF 270 message = new NullLDAPMessage(); 271 } 272 273 if (message == null || message instanceof NullLDAPMessage) 274 { 275 if (message instanceof NullLDAPMessage) 276 { 277 messageQueue.clear(); 278 closed = true; 279 return -1; 280 } 281 282 return 0; 283 } 284 else 285 { 286 messageBuffer.clear(); 287 messageReader.rewind(); 288 message.write(writer); 289 } 290 } 291 292 return 0xFF & messageReader.readByte(); 293 } 294 295 296 297 /** 298 * Reads some number of bytes from the input stream, blocking if 299 * necessary until there is data available, and adds them to the 300 * provided array starting at position 0. 301 * 302 * @param b The array to which the data is to be written. 303 * 304 * @return The number of bytes actually written into the 305 * provided array, or -1 if the end of the stream has been 306 * reached. 307 * 308 * @throws IOException If a problem occurs while trying to read 309 * data from the stream. 310 */ 311 @Override 312 public int read(byte[] b) 313 throws IOException 314 { 315 return read(b, 0, b.length); 316 } 317 318 319 320 /** 321 * Reads some number of bytes from the input stream, blocking if 322 * necessary until there is data available, and adds them to the 323 * provided array starting at the specified position. 324 * 325 * @param b The array to which the data is to be written. 326 * @param off The offset in the array at which to start writing 327 * data. 328 * @param len The maximum number of bytes that may be added to the 329 * array. 330 * 331 * @return The number of bytes actually written into the 332 * provided array, or -1 if the end of the stream has been 333 * reached. 334 * 335 * @throws IOException If a problem occurs while trying to read 336 * data from the stream. 337 */ 338 @Override 339 public synchronized int read(byte[] b, int off, int len) 340 throws IOException 341 { 342 if (messageReader.remaining() < 1) 343 { 344 LDAPMessage message; 345 try 346 { 347 message = messageQueue.take(); 348 } 349 catch(InterruptedException ie) 350 { 351 // Probably because a shutdown was started. EOF 352 message = new NullLDAPMessage(); 353 } 354 355 if (message == null || message instanceof NullLDAPMessage) 356 { 357 if (message instanceof NullLDAPMessage) 358 { 359 messageQueue.clear(); 360 closed = true; 361 return -1; 362 } 363 364 return 0; 365 } 366 else 367 { 368 messageBuffer.clear(); 369 messageReader.rewind(); 370 message.write(writer); 371 } 372 } 373 374 int actualLen = Math.min(len, messageReader.remaining()); 375 messageReader.readBytes(b, off, actualLen); 376 return actualLen; 377 } 378 379 380 381 /** 382 * Repositions this stream to the position at the time that the 383 * {@code mark} method was called on this stream. This will not 384 * have any effect, as this input stream implementation does not 385 * support marking. 386 */ 387 @Override 388 public void reset() 389 { 390 // No implementation is required. 391 } 392 393 394 395 /** 396 * Skips over and discards up to the specified number of bytes of 397 * data from this input stream. This implementation will always 398 * skip the requested number of bytes unless the end of the stream 399 * is reached. 400 * 401 * @param n The maximum number of bytes to skip. 402 * 403 * @return The number of bytes actually skipped. 404 * 405 * @throws IOException If a problem occurs while trying to read 406 * data from the input stream. 407 */ 408 @Override 409 public synchronized long skip(long n) 410 throws IOException 411 { 412 byte[] b; 413 if (n > 8192) 414 { 415 b = new byte[8192]; 416 } 417 else 418 { 419 b = new byte[(int) n]; 420 } 421 422 long totalBytesRead = 0L; 423 while (totalBytesRead < n) 424 { 425 int maxLen = (int) Math.min((n - totalBytesRead), b.length); 426 427 int bytesRead = read(b, 0, maxLen); 428 if (bytesRead < 0) 429 { 430 if (totalBytesRead > 0) 431 { 432 return totalBytesRead; 433 } 434 else 435 { 436 return bytesRead; 437 } 438 } 439 else 440 { 441 totalBytesRead += bytesRead; 442 } 443 } 444 445 return totalBytesRead; 446 } 447 448 449 450 /** 451 * Retrieves a string representation of this internal LDAP socket. 452 * 453 * @return A string representation of this internal LDAP socket. 454 */ 455 @Override 456 public String toString() 457 { 458 return getClass().getSimpleName(); 459 } 460}