2016-02-01 2 views
2

Это продолжение к anonymous file streams reusing descriptorsInvalidate поток без закрытия

Согласно моему предыдущему вопросу, я не могу зависеть от кода, как это (бывает работать в JDK8, на данный момент):

RandomAccessFile r = new RandomAccessFile(...); 

FileInputStream f_1 = new FileInputStream(r.getFD()); 
         // some io, not shown 
       f_1 = null; 

       f_2 = new FileInputStream(r.getFD()); 
         // some io, not shown 
       f_2 = null; 

       f_3 = new FileInputStream(r.getFD()); 
        // some io, not shown 
       f_3 = null; 

Однако, чтобы предотвратить случайные ошибки и как форму самодокументации, я хотел бы аннулировать каждый поток файлов после того, как я закончил использовать его - без закрытия базового дескриптора файла.

Каждый FileInputStream должен быть независимым, с позиционированием, контролируемым RandomAccessFile. Я разделяю тот же FileDescriptor, чтобы предотвратить любые условия гонки, возникающие при открытии одного и того же пути несколько раз. Когда я закончил с одним FileInputStream, я хочу сделать его недействительным, чтобы не было возможности случайно прочитать его при использовании второго FileInputStream (что привело бы к тому, что второй FileInputStream пропустил данные).

Как это сделать?

нота:

  • в библиотеках, которые я использую требует compatibiity с java.io. *
  • если вы предлагаете библиотеку (я предпочитаю Java встроенную семантику, если это вообще возможно), его должен быть общедоступным (упакованным) для linux (основной мишень) и использоваться на окнах (экспериментальная цель)
  • , но поддержка окон не является обязательной.

Edit: в ответ на замечание, вот мой рабочий процесс:

RandomAccessFile r = new RandomAccessFile(String path, "r"); 

int header_read; 
int header_remaining = 4; // header length, initially 

byte[]  ba = new byte[header_remaining]; 
ByteBuffer bb = new ByteBuffer.allocate(header_remaining); 

while ((header_read = r.read(ba, 0, header_remaining) > 0) { 
    header_remaining -= header_read; 
    bb.put(ba, 0, header_read); 
} 

byte[] header = bb.array(); 

// process header, not shown 
// the RandomAccessFile above reads only a small amount, so buffering isn't required 

r.seek(0); 

FileInputStream f_1 = new FileInputStream(r.getFD()); 

Library1Result result1 = library1.Main.entry_point(f_1) 

// process result1, not shown 
// Library1 reads the InputStream in large chunks, so buffering isn't required 
// invalidate f_1 (this question) 

r.seek(0) 

int read; 
while ((read = r.read(byte[4096] buffer)) > 0 && library1.continue()) { 
    library2.process(buffer, read); 
} 

// the RandomAccessFile above is read in large chunks, so buffering isn't required 
// in a previous edit the RandomAccessFile was used to create a FileInputStream. Obviously that's not required, so ignore 

r.seek(0) 

Reader r_1 = new BufferedReader(new InputStreamReader(new FileInputStream(r.getFD()))); 

Library3Result result3 = library3.Main.entry_point(r_2) 

// process result3, not shown 
// I'm not sure how Library3 uses the reader, so I'm providing buffering 
// invalidate r_1 (this question) - bonus: frees the buffer 

r.seek(0); 

FileInputStream f_2 = new FileInputStream(r.getFD()); 

Library1Result result1 = library1.Main.entry_point(f_2) 

// process result1 (reassigned), not shown 
// Yes, I actually have to call 'library1.Main.entry_point' *again* - same comments apply as from before 
// invalidate f_2 (this question) 
// 
// I've been told to be careful when opening multiple streams from the same 
// descriptor if one is buffered. This is very vague. I assume because I only 
// ever use any stream once and exclusively, this code is safe. 
// 
+3

Это может помочь, если вы объясните причину, что нужно открывать отдельные потоки на одном FD. Я предполагаю, что вы хотите последовательно читать из файла несколько раз, но тогда почему вы просто не разделяете «RandomAccessFile» с соответствующим конфликтующим контролем? У этого есть запах кода об этом, или возможность быть [проблема XY] (http://xyproblem.info/). –

ответ

2

Чистым решением для Java может быть создание декодера пересылки, который проверяет каждый вызов метода, проверяется ли поток или нет. Для InputStream этот декоратор может выглядеть следующим образом:

public final class CheckedInputStream extends InputStream { 
    final InputStream delegate; 
    boolean validated; 

    public CheckedInputStream(InputStream stream) throws FileNotFoundException { 
    delegate = stream; 
    validated = true; 
    } 

    public void invalidate() { 
    validated = false; 
    } 

    void checkValidated() { 
    if (!validated) { 
     throw new IllegalStateException("Stream is invalidated."); 
    } 
    } 

    @Override 
    public int read() throws IOException { 
    checkValidated(); 
    return delegate.read(); 
    } 

    @Override 
    public int read(byte b[]) throws IOException { 
    checkValidated(); 
    return read(b, 0, b.length); 
    } 

    @Override 
    public int read(byte b[], int off, int len) throws IOException { 
    checkValidated(); 
    return delegate.read(b, off, len); 
    } 

    @Override 
    public long skip(long n) throws IOException { 
    checkValidated(); 
    return delegate.skip(n); 
    } 

    @Override 
    public int available() throws IOException { 
    checkValidated(); 
    return delegate.available(); 
    } 

    @Override 
    public void close() throws IOException { 
    checkValidated(); 
    delegate.close(); 
    } 

    @Override 
    public synchronized void mark(int readlimit) { 
    checkValidated(); 
    delegate.mark(readlimit); 
    } 

    @Override 
    public synchronized void reset() throws IOException { 
    checkValidated(); 
    delegate.reset(); 
    } 

    @Override 
    public boolean markSupported() { 
    checkValidated(); 
    return delegate.markSupported(); 
    } 
} 

Вы можете использовать его как:

CheckedInputStream f_1 = new CheckedInputStream(new FileInputStream(r.getFD())); 
         // some io, not shown 
        f_1.invalidate(); 

        f_1.read(); // throws IllegalStateException 
+0

Это очень хорошо - класс CheckedInputStream защищает меня от методов библиотеки, которые без ведома читают из переданного потока. Поэтому, если я создам поток # 2 из потока # 1, передайте поток # 2 в библиотеку # 1, найдите 0, прочитайте из потока # 1 и функцию библиотеки вызовов # 1 (которая неизвестна мне для чтения из потока № 2), второе чтение из потока # 1 будет пропускать данные. CheckedInputClass защищает меня от этого. Только два вопроса: – user19087

+0

Можно ли определить CheckedInputClass программно, а не вручную? Если бы это был python, я бы переопределял \ _ \ _ getattribute \ _ \ _, использовал метакласс или декоратор класса. Яблоки и апельсины Я знаю, но у java есть некоторые способности отражения. Если это не слишком запутанно, мне было бы интересно. – user19087

+0

Во-вторых: Почему компилятор байт-кода не предупреждает о том, что я не поймаю выброшенное IllegalStateException CheckedInputStream (т. Е. Ваш пример без try/catch)? – user19087

0

Юникс можно вообще избежать подобных проблем dup «ИНГ дескриптора файла.

Поскольку java не предлагает такую ​​функцию, одним из вариантов может быть родная библиотека, которая предоставляет это. jnr-posix делает это например. С другой стороны, jnr зависит от гораздо более реалистичных свойств jdk, чем ваш исходный вопрос.

+0

Я нацелен на программное обеспечение, которое обеспечивает экспериментальную сборку окон. Я обновил свой вопрос, чтобы отразить это. – user19087

+0

Я считаю, что это тоже работает на окнах – the8472

Смежные вопросы