opens an Recorder for read. If no recorder with the given ID exists recorder with no data is returned

@param messageSequenceId
@param xaDataLogger
@return
@throws IOException
@throws InterruptedException
554699
static PhynixxXADataRecorder openRecorderForWrite(long messageSequenceId, XADataLogger xaDataLogger, IXADataRecorderLifecycleListener dataRecorderLifycycleListner) {
        PhynixxXADataRecorder dataRecorder = new PhynixxXADataRecorder(messageSequenceId, xaDataLogger, dataRecorderLifycycleListner);
        try {
            dataRecorder.dataLogger.prepareForWrite(dataRecorder.getXADataRecorderId());
            return dataRecorder;
        } catch (Exception e) {
            throw new DelegatedRuntimeException(e);
        }
    }
242620
@Override
    public void openLog() throws LogCorruptedException, LogAllocationException, InternalLogException,
                    LogIncompatibleException
    {
        if (tc.isEntryEnabled())
            Tr.entry(tc, "openLog", this);

        _recoveryLog.openLog();

        if (tc.isEntryEnabled())
            Tr.exit(tc, "openLog");
    }
1202333
public synchronized void startLogSegment(RequestInfo reqInfo, long txid)
      throws IOException {
    assert fjm != null;
    checkJournalStorageFormatted();
    checkRequest(reqInfo);
    
    if (curSegment != null) {
      LOG.warn("Client is requesting a new log segment " + txid + 
          " though we are already writing " + curSegment + ". " +
          "Aborting the current segment in order to begin the new one.");
      // The writer may have lost a connection to us and is now
      // re-connecting after the connection came back.
      // We should abort our own old segment.
      abortCurSegment();
    }

    // Paranoid sanity check: we should never overwrite a finalized log file.
    // Additionally, if it's in-progress, it should have at most 1 transaction.
    // This can happen if the writer crashes exactly at the start of a segment.
    EditLogFile existing = fjm.getLogFile(txid);
    if (existing != null) {
      if (!existing.isInProgress()) {
        throw new IllegalStateException("Already have a finalized segment " +
            existing + " beginning at " + txid);
      }
      
      // If it's in-progress, it should only contain one transaction,
      // because the "startLogSegment" transaction is written alone at the
      // start of each segment. 
      existing.validateLog();
      if (existing.getLastTxId() != existing.getFirstTxId()) {
        throw new IllegalStateException("The log file " +
            existing + " seems to contain valid transactions");
      }
    }
    
    long curLastWriterEpoch = lastWriterEpoch.get();
    if (curLastWriterEpoch != reqInfo.getEpoch()) {
      LOG.info("Updating lastWriterEpoch from " + curLastWriterEpoch +
          " to " + reqInfo.getEpoch() + " for client " +
          Server.getRemoteIp());
      lastWriterEpoch.set(reqInfo.getEpoch());
      metrics.lastWriterEpoch.set(reqInfo.getEpoch());
    }

    // The fact that we are starting a segment at this txid indicates
    // that any previous recovery for this same segment was aborted.
    // Otherwise, no writer would have started writing. So, we can
    // remove the record of the older segment here.
    purgePaxosDecision(txid);
    
    curSegment = fjm.startLogSegment(txid);
    curSegmentTxId = txid;
    nextTxId = txid;
    // the layout version has only been written
    // plus OP_INVALID
    currentSegmentWrittenBytes = 5L;
    
    // inform the syncer service that we might have some work to do
    if (journalNode != null) {
      journalNode.addSyncTask(this, curSegmentTxId);
    }
  }
1974
public static void interruptedException(String msg, InterruptedException e)
      throws IOException {
    Thread.currentThread().interrupt();
    LOG.error(msg, e);
    throw new IOException(msg, e);
  }
99766
private void init()
        throws IOException, InterruptedException {
        nakedDone = false;

        // initialize the total length
        float recToFragRatio = conf.getFloat(RECORD_TO_FRAGMENT_RATIO,
            getDefaultRatio());
        length = mlSplit.getLength() * recToFragRatio;

        // generate the query
        String queryText;
        long start = mlSplit.getStart() + 1 + count;
        long end = mlSplit.isLastSplit() ? Long.MAX_VALUE : start
            + mlSplit.getLength() - count - 1;

        String src = conf.get(MarkLogicConstants.DOCUMENT_SELECTOR);
        redactionRuleCol = conf.getStrings(REDACTION_RULE_COLLECTION);
        Collection<String> nsCol = null;
        if (src != null) {
            nsCol = conf.getStringCollection(
                    MarkLogicConstants.PATH_NAMESPACE);
        } else {
            src = "fn:collection()";
        }
        ctsQuery = conf.get(MarkLogicConstants.QUERY_FILTER);
        StringBuilder buf = new StringBuilder();
        if (ctsQuery != null) {
            buildSearchQuery(src, ctsQuery, nsCol, buf);
        } else {
            buildDocExprQuery(src, nsCol, null, buf);
        }   
        src = buf.toString();
        buf = new StringBuilder();
        buf.append("xquery version \"1.0-ml\"; \n");
        buf.append("import module namespace hadoop = ");
        buf.append("\"http://marklogic.com/xdmp/hadoop\" at ");
        buf.append("\"/MarkLogic/hadoop.xqy\";\n");
        if (redactionRuleCol != null) {
            buf.append(
                "import module namespace rdt = \"http://marklogic.com/xdmp/redaction\" at \"/MarkLogic/redaction.xqy\";\n");
        }
        buf.append(
                "declare namespace mlmr=\"http://marklogic.com/hadoop\";\n");
        buf.append("declare option xdmp:output \"indent=no\";\n");
        buf.append("declare option xdmp:output \"indent-untyped=no\";\n");
        buf.append(
                "declare variable $mlmr:splitstart as xs:integer external;\n");
        buf.append(
                "declare variable $mlmr:splitend as xs:integer external;\n");
        buf.append("let $cols := ");
        buf.append(src);
        buf.append("\nlet $all-meta :=");
        buf.append("\nfor $doc in $cols");
        buf.append("\nlet $uri := fn:base-uri($doc)\n return (");

        buf.append("'META',");
        buf.append("$uri,");
        buf.append("if(fn:empty($doc/node())) then 0 ");
        buf.append("else if (fn:count($doc/node())>1) then \"element\" ");
        buf.append("else xdmp:node-kind($doc/node())");
        if (copyCollection || copyPermission || copyProperties || copyQuality) {
            buf.append(",");
            if (copyCollection) {
                buf.append("xdmp:document-get-collections($uri),\n");
            }
            if (copyPermission) {
                buf.append("let $list := xdmp:document-get-permissions($uri)\n");
                buf.append("return hadoop:get-permissions($list),");
            }
            // if copy-quality, else + 0
            if (copyQuality) {
                buf.append("xdmp:document-get-quality($uri),\n");
            } else {
                buf.append("0,");
            }
            // if copy-metadata
            if (copyMetadata) {
                buf.append(
                    "(let $f := fn:function-lookup(xs:QName('xdmp:document-get-metadata'),1)\n"
                    + "return if (exists($f)) then $f($uri) else ()),\n");
            }
            // if copy-properties, else + (),\n
            if (copyProperties) {
                buf.append("xdmp:document-properties($uri)/prop:properties,\n");
            }
        } else {
            buf.append(",0,"); // quality
            buf.append("(),\n");//properties
        }
        // end-of-record marker
        buf.append("0");       
        buf.append(" )\n");
        buf.append("return ($all-meta");
        buf.append(",'EOM',$cols)");

        queryText = buf.toString();
        if (LOG.isDebugEnabled()) {
            LOG.debug(queryText);
        }

        // set up a connection to the server
        while (retry < maxRetries) {
        try {
            if (retry == 1) {
                LOG.info("Retrying connect");
            }
            String curForestName = "";
            String curHostName = "";
            if (curForest == -1) {
                curForestName = mlSplit.getForestId().toString();
                curHostName = hostNames[0];
            } else {
                curForestName = replicas.get(curForest).getForest();
                curHostName = replicas.get(curForest).getHostName();
            }
            ContentSource cs = InternalUtilities.getInputContentSource(conf,
                    curHostName);
            session = cs.newSession("#"+curForestName);
            if (LOG.isDebugEnabled()) {
                LOG.debug("Connect to forest "
                    + curForestName + " on "
                    + session.getConnectionUri().getHost());
            }

            AdhocQuery aquery = session.newAdhocQuery(queryText);
            aquery.setNewIntegerVariable(MR_NAMESPACE, SPLIT_START_VARNAME,
                start);
            aquery.setNewIntegerVariable(MR_NAMESPACE, SPLIT_END_VARNAME, end);
            RequestOptions options = new RequestOptions();
            options.setCacheResult(false);
            String ts = conf.get(INPUT_QUERY_TIMESTAMP);
            if (ts != null) {
                options.setEffectivePointInTime(new BigInteger(ts));
                if (LOG.isDebugEnabled()) {
                    LOG.debug("Query timestamp: " + ts);
                }
            } 
            aquery.setOptions(options);
            result = session.submitRequest(aquery);
            
            initMetadataMap();
        } catch (XccConfigException e) {
            LOG.error("XccConfigException:" + e);
            throw new IOException(e);
        } catch (QueryException e) {
            LOG.error("QueryException:" + e);
            LOG.debug("Query: " + queryText);
            throw new IOException(e);
        } catch (Exception e) {
            LOG.error("Exception:" + e.getMessage());
            if (curForest != -1) {
                if (++retry < maxRetries) {
                    // failover
                    try {
                        Thread.sleep(sleepTime);
                    } catch (Exception e2) {
                    }
                    sleepTime = Math.min(sleepTime * 2,maxSleepTime);

                    curForest = (curForest+1)%replicas.size();
                    continue;
                }
                LOG.info("Retry limit exceeded");
            }
            throw new IOException(e);
        }
        break;
        }
    }
360187
public ApacheHttpdLogfileRecordReader createRecordReader() {
        try {
            return new ApacheHttpdLogfileRecordReader(getLogFormat(), getRequestedFields(), getTypeRemappings(), getAdditionalDissectors());
        } catch (IOException e) {
            return null;
        }
    }
93713
@Override
    public boolean sequenceForReplay(VoltMessage message)
    {
        boolean canDeliver = false;
        long sequenceWithUniqueId = Long.MIN_VALUE;

        boolean commandLog = (message instanceof TransactionInfoBaseMessage &&
                (((TransactionInfoBaseMessage)message).isForReplay()));

        boolean sentinel = message instanceof MultiPartitionParticipantMessage;

        boolean replay = commandLog || sentinel;
        boolean sequenceForReplay = m_isLeader && replay;

        if (replay) {
            sequenceWithUniqueId = ((TransactionInfoBaseMessage)message).getUniqueId();
        }

        if (sequenceForReplay) {
            InitiateResponseMessage dupe = m_replaySequencer.dedupe(sequenceWithUniqueId,
                    (TransactionInfoBaseMessage) message);
            if (dupe != null) {
                // Duplicate initiate task message, send response
                m_mailbox.send(dupe.getInitiatorHSId(), dupe);
            }
            else if (!m_replaySequencer.offer(sequenceWithUniqueId, (TransactionInfoBaseMessage) message)) {
                canDeliver = true;
            }
            else {
                deliverReadyTxns();
            }

            // If it's a DR sentinel, send an acknowledgement
            if (sentinel && !commandLog) {
                MultiPartitionParticipantMessage mppm = (MultiPartitionParticipantMessage) message;
                final InitiateResponseMessage response = new InitiateResponseMessage(mppm);
                ClientResponseImpl clientResponse =
                        new ClientResponseImpl(ClientResponseImpl.UNEXPECTED_FAILURE,
                                new VoltTable[0], ClientResponseImpl.IGNORED_TRANSACTION);
                response.setResults(clientResponse);
                m_mailbox.send(response.getInitiatorHSId(), response);
            }
        }
        else {
            if (replay) {
                // Update last seen and last polled uniqueId for replicas
                m_replaySequencer.updateLastSeenUniqueId(sequenceWithUniqueId,
                        (TransactionInfoBaseMessage) message);
                m_replaySequencer.updateLastPolledUniqueId(sequenceWithUniqueId,
                        (TransactionInfoBaseMessage) message);
            }

            canDeliver = true;
        }

        return canDeliver;
    }
198690
@SuppressWarnings("rawtypes")
    public void pushMessage(IPipe pipe, IMessage message) throws IOException {
        if (message instanceof RTMPMessage) {
            final IRTMPEvent msg = ((RTMPMessage) message).getBody();
            // get the type
            byte dataType = msg.getDataType();
            // get the timestamp
            int timestamp = msg.getTimestamp();
            log.trace("Data type: {} timestamp: {}", dataType, timestamp);
            // if writes are delayed, queue the data and sort it by time
            if (queue == null) {
                // if we plan to use a queue, create one
                queue = new PriorityQueue<QueuedMediaData>(queueThreshold <= 0 ? 11 : queueThreshold);
            }
            QueuedMediaData queued = null;
            if (msg instanceof IStreamData) {
                if (log.isTraceEnabled()) {
                    log.trace("Stream data, body saved. Data type: {} class type: {}", dataType, msg.getClass().getName());
                }
                // ensure that our first video frame written is a key frame
                if (msg instanceof VideoData) {
                    log.debug("pushMessage video - waitForVideoKeyframe: {} gotVideoKeyframe: {}", waitForVideoKeyframe, gotVideoKeyframe);
                    if (!gotVideoKeyframe) {
                        VideoData video = (VideoData) msg;
                        if (video.getFrameType() == FrameType.KEYFRAME) {
                            log.debug("Got our first keyframe");
                            gotVideoKeyframe = true;
                        }
                        if (waitForVideoKeyframe && !gotVideoKeyframe) {
                            // skip this frame bail out
                            log.debug("Skipping video data since keyframe has not been written yet");
                            return;
                        }
                    }
                }
                queued = new QueuedMediaData(timestamp, dataType, (IStreamData) msg);
            } else {
                // XXX what type of message are we saving that has no body data??
                if (log.isTraceEnabled()) {
                    log.trace("Non-stream data, body not saved. Data type: {} class type: {}", dataType, msg.getClass().getName());
                }
                queued = new QueuedMediaData(timestamp, dataType);
            }
            if (queued != null) {
                writeLock.lock();
                try {
                    // add to the queue
                    queue.add(queued);
                } finally {
                    writeLock.unlock();
                }
            }
            int queueSize = 0;
            readLock.lock();
            try {
                queueSize = queue.size();
            } finally {
                readLock.unlock();
            }
            // initialize a writer
            if (writer == null) {
                init();
                if (msg instanceof VideoData) {
                    writeQueuedDataSlice(createTimestampLimitedSlice(msg.getTimestamp()));
                } else if (queueThreshold >= 0 && queueSize >= queueThreshold) {
                    writeQueuedDataSlice(createFixedLengthSlice(queueThreshold / (100 / percentage)));
                }
            }
        } else if (message instanceof ResetMessage) {
            startTimestamp = -1;
        } else if (log.isDebugEnabled()) {
            log.debug("Ignoring pushed message: {}", message);
        }
    }
1114134
@Override
	public synchronized RecordAlert read(int alertId) throws DatabaseException {
        try {
			psRead.setInt(1, alertId);
			try (ResultSet rs = psRead.executeQuery()) {
				RecordAlert ra = build(rs);
				return ra;
			}
		} catch (Exception e) {
			throw new DatabaseException(e);
		}
    }
1296025
@Override
  protected void deliverLog(String msg)
  {
    synchronized (_os) {
      try {
        _os.print(msg);
      } catch (Exception e) {
        e.printStackTrace();
      }
    }
  }
990681
public synchronized LogEntries getSessionLog(SessionId sessionId) throws IOException {
    List<LogEntry> entries = new ArrayList<>();
    for (LogRecord record : records(sessionId)) {
      if (record.getLevel().intValue() >= serverLogLevel.intValue())
        entries.add(new LogEntry(record.getLevel(), record.getMillis(), record.getMessage()));
    }
    return new LogEntries(entries);
  }
563135
public void addLog(final byte iOperation, final int iTxId, final int iClusterId, final long iClusterOffset,
      final byte iRecordType, final int iRecordVersion, final byte[] iRecordContent, int dataSegmentId) throws IOException {

    final int contentSize = iRecordContent != null ? iRecordContent.length : 0;
    final int size = OFFSET_RECORD_CONTENT + contentSize;

    lock.acquireExclusiveLock();
    try {

      int offset = file.allocateSpace(size);

      file.writeByte(offset, STATUS_COMMITTING);
      offset += OBinaryProtocol.SIZE_BYTE;

      file.writeByte(offset, iOperation);
      offset += OBinaryProtocol.SIZE_BYTE;

      file.writeInt(offset, iTxId);
      offset += OBinaryProtocol.SIZE_INT;

      file.writeShort(offset, (short) iClusterId);
      offset += OBinaryProtocol.SIZE_SHORT;

      file.writeLong(offset, iClusterOffset);
      offset += OBinaryProtocol.SIZE_LONG;

      file.writeByte(offset, iRecordType);
      offset += OBinaryProtocol.SIZE_BYTE;

      file.writeInt(offset, iRecordVersion);
      offset += OBinaryProtocol.SIZE_INT;

      file.writeInt(offset, dataSegmentId);
      offset += OBinaryProtocol.SIZE_INT;

      file.writeInt(offset, contentSize);
      offset += OBinaryProtocol.SIZE_INT;

      file.write(offset, iRecordContent);
      offset += contentSize;

      if (synchEnabled)
        file.synch();

    } finally {
      lock.releaseExclusiveLock();
    }
  }
1308033
<E> Observable<E> addLogging(@NonNull Observable<E> obs, @NonNull Logger log, final String msg) {
        return obs.doOnNext(r -> log(log, (ComapiResult) r, msg))
                .doOnError(t -> log(log, t, msg));
    }
1330369
@Handler
    public void onRead(Input<ByteBuffer> event)
            throws InterruptedException {
        for (IOSubchannel channel : event.channels(IOSubchannel.class)) {
            ManagedBuffer<ByteBuffer> out = channel.byteBufferPool().acquire();
            out.backingBuffer().put(event.buffer().backingBuffer());
            channel.respond(Output.fromSink(out, event.isEndOfRecord()));
        }
    }
1346264
synchronized boolean markReadMessageForId(String messageId, String userId){
        if(messageId == null || userId == null) return false;

        final String tName = Table.INBOX_MESSAGES.getName();
        try{
            final SQLiteDatabase db = dbHelper.getWritableDatabase();
            ContentValues cv = new ContentValues();
            cv.put(IS_READ,1);
            db.update(Table.INBOX_MESSAGES.getName(), cv,_ID + " = ? AND " + USER_ID + " = ?",new String[]{messageId,userId});
            return true;
        }catch (final SQLiteException e){
            getConfigLogger().verbose("Error removing stale records from " + tName, e);
            return false;
        } finally {
            dbHelper.close();
        }
    }
648546
@Override
    public List<Message> getNextMessagesToDeliver() throws InterruptedException {
        LinkedList<Message> toDeliver = new LinkedList<>();
        synchronized (deliverySet) {
            while (deliverySet.isEmpty() || !deliverySet.first().isReadyToDeliver()) {
                deliverySet.wait();
            }

            Iterator<MessageInfo> iterator = deliverySet.iterator();

            while (iterator.hasNext()) {
                MessageInfo messageInfo = iterator.next();
                if (messageInfo.isReadyToDeliver()) {
                    toDeliver.add(messageInfo.getMessage());
                    iterator.remove();
                } else {
                    break;
                }
            }
        }
        return toDeliver;
    }
439661
@Override
    void open(final WorkQueue<ClasspathEntryWorkUnit> workQueue, final LogNode log) throws InterruptedException {
        if (!scanSpec.scanJars) {
            if (log != null) {
                log.log("Skipping classpath element, since jar scanning is disabled: " + rawPath);
            }
            skipClasspathElement = true;
            return;
        }
        final LogNode subLog = log == null ? null : log.log("Opening jar: " + rawPath);
        final int plingIdx = rawPath.indexOf('!');
        final String outermostZipFilePathResolved = FastPathResolver.resolve(FileUtils.CURR_DIR_PATH,
                plingIdx < 0 ? rawPath : rawPath.substring(0, plingIdx));
        if (!scanSpec.jarWhiteBlackList.isWhitelistedAndNotBlacklisted(outermostZipFilePathResolved)) {
            if (subLog != null) {
                subLog.log("Skipping jarfile that is blacklisted or not whitelisted: " + rawPath);
            }
            skipClasspathElement = true;
            return;
        }

        try {
            // Get LogicalZipFile for innermost nested jarfile
            Entry<LogicalZipFile, String> logicalZipFileAndPackageRoot;
            try {
                logicalZipFileAndPackageRoot = nestedJarHandler.nestedPathToLogicalZipFileAndPackageRootMap
                        .get(rawPath, subLog);
            } catch (final NullSingletonException e) {
                // Generally thrown on the second and subsequent attempt to call .get(), after the first failed
                throw new IOException("Could not get logical zipfile " + rawPath + " : " + e);
            }
            logicalZipFile = logicalZipFileAndPackageRoot.getKey();
            if (logicalZipFile == null) {
                // Should not happen, but this keeps lgtm static analysis happy
                throw new IOException("Logical zipfile was null");
            }

            // Get the normalized path of the logical zipfile
            zipFilePath = FastPathResolver.resolve(FileUtils.CURR_DIR_PATH, logicalZipFile.getPath());

            // Get package root of jarfile 
            final String packageRoot = logicalZipFileAndPackageRoot.getValue();
            if (!packageRoot.isEmpty()) {
                packageRootPrefix = packageRoot + "/";
            }
        } catch (final IOException | IllegalArgumentException e) {
            if (subLog != null) {
                subLog.log("Could not open jarfile " + rawPath + " : " + e);
            }
            skipClasspathElement = true;
            return;
        }

        if (!scanSpec.enableSystemJarsAndModules && logicalZipFile.isJREJar) {
            // Found a blacklisted JRE jar that was not caught by filtering for rt.jar in ClasspathFinder
            // (the isJREJar value was set by detecting JRE headers in the jar's manifest file)
            if (subLog != null) {
                subLog.log("Ignoring JRE jar: " + rawPath);
            }
            skipClasspathElement = true;
            return;
        }

        if (!logicalZipFile.isWhitelistedAndNotBlacklisted(scanSpec.jarWhiteBlackList)) {
            if (subLog != null) {
                subLog.log("Skipping jarfile that is blacklisted or not whitelisted: " + rawPath);
            }
            skipClasspathElement = true;
            return;
        }

        // Automatically add any nested "lib/" dirs to classpath, since not all classloaders return them
        // as classpath elements
        int childClasspathEntryIdx = 0;
        if (scanSpec.scanNestedJars) {
            for (final FastZipEntry zipEntry : logicalZipFile.entries) {
                for (final String libDirPrefix : ClassLoaderHandlerRegistry.AUTOMATIC_LIB_DIR_PREFIXES) {
                    if (zipEntry.entryNameUnversioned.startsWith(libDirPrefix)
                            && zipEntry.entryNameUnversioned.endsWith(".jar")) {
                        final String entryPath = zipEntry.getPath();
                        if (subLog != null) {
                            subLog.log("Found nested lib jar: " + entryPath);
                        }
                        workQueue.addWorkUnit(new ClasspathEntryWorkUnit(
                                /* rawClasspathEntry = */ new SimpleEntry<>(entryPath, classLoader),
                                /* parentClasspathElement = */ this,
                                /* orderWithinParentClasspathElement = */
                                childClasspathEntryIdx++));
                        break;
                    }
                }
            }
        }

        // Don't add child classpath elements that are identical to this classpath element, or that are duplicates
        final Set<String> scheduledChildClasspathElements = new HashSet<>();
        scheduledChildClasspathElements.add(rawPath);

        // Create child classpath elements from values obtained from Class-Path entry in manifest, resolving
        // the paths relative to the dir or parent jarfile that the jarfile is contained in
        if (logicalZipFile.classPathManifestEntryValue != null) {
            // Get parent dir of logical zipfile within grandparent slice,
            // e.g. for a zipfile slice path of "/path/to/jar1.jar!/lib/jar2.jar", this is "lib",
            // or for "/path/to/jar1.jar", this is "/path/to", or "" if the jar is in the toplevel dir.
            final String jarParentDir = FileUtils
                    .getParentDirPath(logicalZipFile.getPathWithinParentZipFileSlice());
            // Add paths in manifest file's "Class-Path" entry to the classpath, resolving paths relative to
            // the parent directory or jar
            for (final String childClassPathEltPathRelative : logicalZipFile.classPathManifestEntryValue
                    .split(" ")) {
                if (!childClassPathEltPathRelative.isEmpty()) {
                    // Resolve Class-Path entry relative to containing dir
                    String childClassPathEltPath = FastPathResolver.resolve(jarParentDir,
                            childClassPathEltPathRelative);
                    // If this is a nested jar, prepend outer jar prefix
                    final ZipFileSlice parentZipFileSlice = logicalZipFile.getParentZipFileSlice();
                    if (parentZipFileSlice != null) {
                        childClassPathEltPath = parentZipFileSlice.getPath()
                                + (childClassPathEltPath.startsWith("/") ? "!" : "!/") + childClassPathEltPath;
                    }
                    // Only add child classpath elements once
                    if (scheduledChildClasspathElements.add(childClassPathEltPath)) {
                        // Schedule child classpath element for scanning
                        workQueue.addWorkUnit( //
                                new ClasspathEntryWorkUnit(
                                        /* rawClasspathEntry = */ new SimpleEntry<>(childClassPathEltPath,
                                                classLoader),
                                        /* parentClasspathElement = */ this,
                                        /* orderWithinParentClasspathElement = */
                                        childClasspathEntryIdx++));
                    }
                }
            }
        }
        // Add paths in an OSGi bundle jar manifest's "Bundle-ClassPath" entry to the classpath, resolving 
        // the paths relative to the root of the jarfile
        if (logicalZipFile.bundleClassPathManifestEntryValue != null) {
            final String zipFilePathPrefix = zipFilePath + "!/";
            // Class-Path is split on " ", but Bundle-ClassPath is split on ","
            for (String childBundlePath : logicalZipFile.bundleClassPathManifestEntryValue.split(",")) {
                // Assume that Bundle-ClassPath paths have to be given relative to jarfile root
                while (childBundlePath.startsWith("/")) {
                    childBundlePath = childBundlePath.substring(1);
                }
                // Currently the position of "." relative to child classpath entries is ignored (the
                // Bundle-ClassPath path is treated as if "." is in the first position, since child
                // classpath entries are always added to the classpath after the parent classpath
                // entry that they were obtained from).
                if (!childBundlePath.isEmpty() && !childBundlePath.equals(".")) {
                    // Resolve Bundle-ClassPath entry within jar
                    final String childClassPathEltPath = zipFilePathPrefix
                            + FileUtils.sanitizeEntryPath(childBundlePath, /* removeInitialSlash = */ true);
                    // Only add child classpath elements once
                    if (scheduledChildClasspathElements.add(childClassPathEltPath)) {
                        // Schedule child classpath element for scanning
                        workQueue.addWorkUnit(new ClasspathEntryWorkUnit(
                                /* rawClasspathEntry = */ //
                                new SimpleEntry<>(childClassPathEltPath, classLoader),
                                /* parentClasspathElement = */ this,
                                /* orderWithinParentClasspathElement = */
                                childClasspathEntryIdx++));
                    }
                }
            }
        }
    }