/*
 * Copyright (c) 2015 JRuby.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v20.html
 *
 * Contributors:
 *    JRuby - initial API and implementation and/or initial documentation
 */
package org.jruby.util.io;

import org.jruby.RubyInstanceConfig;
import org.jruby.javasupport.Java;

import java.io.ByteArrayInputStream;
import java.io.FilterInputStream;
import java.io.FilterOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;

Helper that attempts to improve Channels' static helpers.
Author:kares
/** * Helper that attempts to improve Channels' static helpers. * @author kares */
public abstract class ChannelHelper { private static final Field filterInField; private static final Field filterOutField; private ChannelHelper() { /* */ } static { Field _filterInField = null, _filterOutField = null; try { _filterInField = FilterInputStream.class.getDeclaredField("in"); } catch (Exception e) { } try { _filterOutField = FilterOutputStream.class.getDeclaredField("out"); } catch (Exception e) { } filterInField = _filterInField; filterOutField = _filterOutField; } public static ReadableByteChannel readableChannel(final InputStream inputStream) { if ( inputStream instanceof ByteArrayInputStream ) { if ( SeekableByteChannelImpl.USABLE ) { return new SeekableByteChannelImpl((ByteArrayInputStream) inputStream); } } return Channels.newChannel(inputStream); } public static WritableByteChannel writableChannel(final OutputStream ouputStream) { return Channels.newChannel(ouputStream); }
Unwrap all filtering streams between the given stream and its actual unfiltered stream. This is primarily to unwrap streams that have buffers that would interfere with interactivity.
Params:
  • filteredStream – The stream to unwrap
Returns:An unwrapped stream, presumably unbuffered
/** * Unwrap all filtering streams between the given stream and its actual * unfiltered stream. This is primarily to unwrap streams that have * buffers that would interfere with interactivity. * * @param filteredStream The stream to unwrap * @return An unwrapped stream, presumably unbuffered */
public static OutputStream unwrapBufferedStream(OutputStream filteredStream) { if (RubyInstanceConfig.NO_UNWRAP_PROCESS_STREAMS) return filteredStream; return unwrapFilterOutputStream(filteredStream); }
Unwrap all filtering streams between the given stream and its actual unfiltered stream. This is primarily to unwrap streams that have buffers that would interfere with interactivity.
Params:
  • filteredStream – The stream to unwrap
Returns:An unwrapped stream, presumably unbuffered
/** * Unwrap all filtering streams between the given stream and its actual * unfiltered stream. This is primarily to unwrap streams that have * buffers that would interfere with interactivity. * * @param filteredStream The stream to unwrap * @return An unwrapped stream, presumably unbuffered */
public static InputStream unwrapBufferedStream(InputStream filteredStream) { if (RubyInstanceConfig.NO_UNWRAP_PROCESS_STREAMS) return filteredStream; // Java 7+ uses a stream that drains the child on exit, which when // unwrapped breaks because the channel gets drained prematurely. if (filteredStream.getClass().getName().indexOf("ProcessPipeInputStream") != 1) { return filteredStream; } return unwrapFilterInputStream((FilterInputStream)filteredStream); }
Unwrap the given stream to its first non-FilterOutputStream. If the stream is not a FilterOutputStream it is returned immediately. Note that this version is used when you are absolutely sure you want to unwrap; the unwrapBufferedStream version will perform checks for certain types of process-related streams that should not be unwrapped (Java 7+ Process, e.g.).
Params:
  • filteredStream – a stream to be unwrapped, if it is a FilterOutputStream
Returns:the deeped non-FilterOutputStream stream, or filterOutputStream if it is not a FilterOutputStream to begin with.
/** * Unwrap the given stream to its first non-FilterOutputStream. If the stream is not * a FilterOutputStream it is returned immediately. * * Note that this version is used when you are absolutely sure you want to unwrap; * the unwrapBufferedStream version will perform checks for certain types of * process-related streams that should not be unwrapped (Java 7+ Process, e.g.). * * @param filteredStream a stream to be unwrapped, if it is a FilterOutputStream * @return the deeped non-FilterOutputStream stream, or filterOutputStream if it is * not a FilterOutputStream to begin with. */
public static OutputStream unwrapFilterOutputStream(OutputStream filteredStream) { if (filterOutField != null) { while (filteredStream instanceof FilterOutputStream) { try { OutputStream tmpStream = Java.trySetAccessible(filterOutField) ? (OutputStream) filterOutField.get(filteredStream) : null; // try to unwrap as a Drip stream if (!(tmpStream instanceof FilterOutputStream)) { // try to get stream out of drip stream OutputStream dripStream = unwrapDripStream(tmpStream); if (dripStream != null) { // got it, use it for the next cycle tmpStream = dripStream; } } filteredStream = tmpStream; } catch (Exception e) { break; // break out if we've dug as deep as we can } } } return filteredStream; }
Unwrap the given stream to its first non-FilterInputStream. If the stream is not a FilterInputStream it is returned immediately. Note that this version is used when you are absolutely sure you want to unwrap; the unwrapBufferedStream version will perform checks for certain types of process-related streams that should not be unwrapped (Java 7+ Process, e.g.).
Params:
  • filteredStream – a stream to be unwrapped, if it is a FilterInputStream
Returns:the deeped non-FilterInputStream stream, or filterInputStream if it is not a FilterInputStream to begin with.
/** * Unwrap the given stream to its first non-FilterInputStream. If the stream is not * a FilterInputStream it is returned immediately. * * Note that this version is used when you are absolutely sure you want to unwrap; * the unwrapBufferedStream version will perform checks for certain types of * process-related streams that should not be unwrapped (Java 7+ Process, e.g.). * * @param filteredStream a stream to be unwrapped, if it is a FilterInputStream * @return the deeped non-FilterInputStream stream, or filterInputStream if it is * not a FilterInputStream to begin with. */
public static InputStream unwrapFilterInputStream(InputStream filteredStream) { if (filterInField != null) { while (filteredStream instanceof FilterInputStream) { try { InputStream tmpStream = Java.trySetAccessible(filterInField) ? (InputStream) filterInField.get(filteredStream) : null; // could not acquire if (tmpStream == null) break; // try to unwrap result as a Drip stream if (!(tmpStream instanceof FilterInputStream)) { // try to get stream out of drip stream InputStream dripStream = unwrapDripStream(tmpStream); if (dripStream != null) { // got it, use it for the next cycle tmpStream = dripStream; } } filteredStream = tmpStream; } catch (Exception e) { break; // break out if we've dug as deep as we can } } } return filteredStream; } private static OutputStream unwrapDripStream(OutputStream stream) { if (isDripSwitchable(stream)) { try { Field out = stream.getClass().getDeclaredField("out"); return Java.trySetAccessible(out) ? (OutputStream) out.get(stream) : null; } catch (Exception e) { } } return null; } private static InputStream unwrapDripStream(InputStream stream) { if (isDripSwitchable(stream)) { try { Field in = stream.getClass().getDeclaredField("in"); return Java.trySetAccessible(in) ? (InputStream) in.get(stream) : null; } catch (Exception e) { } } return null; } private static boolean isDripSwitchable(Object stream) { return stream.getClass().getName().startsWith("org.flatland.drip.Switchable"); } }