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

com.unboundid.util.StreamFileValuePatternComponent Maven / Gradle / Ivy

/*
 * Copyright 2018-2021 Ping Identity Corporation
 * All Rights Reserved.
 */
/*
 * Copyright 2018-2021 Ping Identity Corporation
 *
 * Licensed under 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.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*
 * Copyright (C) 2018-2021 Ping Identity Corporation
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License (GPLv2 only)
 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
 * as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, see .
 */
package com.unboundid.util;



import java.io.File;
import java.io.IOException;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;

import com.unboundid.ldap.sdk.LDAPException;
import com.unboundid.ldap.sdk.LDAPRuntimeException;
import com.unboundid.ldap.sdk.ResultCode;

import static com.unboundid.util.UtilityMessages.*;



/**
 * This class defines a stream file value pattern component, which may be used
 * to provide string values read from a specified local file.  Values will only
 * be accessed in sequential order, and only a relatively small amount of data
 * will be held in memory at any given time, so this may be a suitable option
 * for dealing with very large files.
 */
final class StreamFileValuePatternComponent
      extends ValuePatternComponent
{
  /**
   * The serial version UID for this serializable class.
   */
  private static final long serialVersionUID = -4557045230341165225L;



  // A value that tracks the position at which the next line of data should be
  // read from the file.
  @NotNull private final AtomicLong nextReadPosition;

  // A reference that holds this thread and makes it available to the associated
  // StreamFileValuePatternComponent.
  @NotNull private final AtomicReference
       threadRef;

  // The file from which the data will be read.
  @NotNull private final File file;

  // The queue that will be used to hold the lines of data read from the file.
  @NotNull private final LinkedBlockingQueue lineQueue;

  // The maximum length of time in milliseconds that an attempt to offer a
  // string to the queue will be allowed to block before the associated reader
  // thread will exit.
  private final long maxOfferBlockTimeMillis;



  /**
   * Creates a new stream file value pattern component that will read data from
   * the specified file.  It will use a queue size of 1000 strings and a maximum
   * block time of 60,000 milliseconds (1 minute).
   *
   * @param  path  The path to the file from which data is to be read.
   *
   * @throws  IOException  If a problem is encountered while trying to open the
   *                       specified file for reading.
   */
  StreamFileValuePatternComponent(@NotNull final String path)
       throws IOException
  {
    this(path, 10_000, 60_000L);
  }



  /**
   * Creates a new stream file value pattern component that will read data from
   * the specified file.
   *
   * @param  path                     The path to the file from which data is to
   *                                  be read.  It must not be {@code null}, and
   *                                  it must reference a file that exists.
   * @param  queueSize                The maximum number of lines read from the
   *                                  file that should be held in memory at any
   *                                  given time.  It must be greater than zero.
   * @param  maxOfferBlockTimeMillis  The maximum length of time in milliseconds
   *                                  that an attempt to offer a string into the
   *                                  queue will be allowed to block before the
   *                                  associated reader thread will exit.  It
   *                                  must be greater than zero.
   *
   * @throws  IOException  If a problem is encountered while trying to open the
   *                       specified file for reading.
   */
  StreamFileValuePatternComponent(@NotNull final String path,
                                  final int queueSize,
                                  final long maxOfferBlockTimeMillis)
       throws IOException
  {
    Validator.ensureNotNull(path);
    Validator.ensureTrue(queueSize > 0);
    Validator.ensureTrue(maxOfferBlockTimeMillis > 0L);

    this.maxOfferBlockTimeMillis = maxOfferBlockTimeMillis;

    file = new File(path);
    if (! file.exists())
    {
      throw new IOException(ERR_STREAM_FILE_VALUE_PATTERN_PATH_MISSING.get(
           file.getAbsolutePath()));
    }

    if (! file.isFile())
    {
      throw new IOException(ERR_STREAM_FILE_VALUE_PATTERN_PATH_NOT_FILE.get(
           file.getAbsolutePath()));
    }

    if (file.length() <= 0)
    {
      throw new IOException(ERR_STREAM_FILE_VALUE_PATTERN_FILE_EMPTY.get(
           file.getAbsolutePath()));
    }

    lineQueue = new LinkedBlockingQueue<>(queueSize);
    nextReadPosition = new AtomicLong(0L);
    threadRef = new AtomicReference<>();

    final StreamFileValuePatternReaderThread readerThread =
         new StreamFileValuePatternReaderThread(file, lineQueue,
              maxOfferBlockTimeMillis, nextReadPosition, threadRef);
    threadRef.set(readerThread);
    readerThread.start();
  }



  /**
   * {@inheritDoc}
   */
  @Override()
  void append(@NotNull final StringBuilder buffer)
  {
    String line = lineQueue.poll();
    if (line != null)
    {
      buffer.append(line);
      return;
    }

    while (true)
    {
      try
      {
        StreamFileValuePatternReaderThread readerThread;
        synchronized (this)
        {
          readerThread = threadRef.get();
          if (readerThread == null)
          {
            readerThread = new StreamFileValuePatternReaderThread(file,
                 lineQueue, maxOfferBlockTimeMillis, nextReadPosition,
                 threadRef);
            threadRef.set(readerThread);
            readerThread.start();
          }
        }

        line = lineQueue.poll(10L, TimeUnit.MILLISECONDS);
        if (line != null)
        {
          buffer.append(line);
          return;
        }
      }
      catch (final Exception e)
      {
        Debug.debugException(e);
        throw new LDAPRuntimeException(
             new LDAPException(ResultCode.LOCAL_ERROR,
                  ERR_STREAM_FILE_VALUE_PATTERN_ERROR_GETTING_NEXT_VALUE.get(
                       file.getAbsolutePath(),
                       StaticUtils.getExceptionMessage(e)),
                  e));
      }
    }
  }



  /**
   * {@inheritDoc}
   */
  @Override()
  boolean supportsBackReference()
  {
    return true;
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy