io.sentry.instrumentation.file.SentryFileInputStream Maven / Gradle / Ivy
package io.sentry.instrumentation.file;
import io.sentry.IScopes;
import io.sentry.ISpan;
import io.sentry.ScopesAdapter;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.concurrent.atomic.AtomicInteger;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* An implementation of {@link java.io.FileInputStream} that creates a {@link io.sentry.ISpan} for
* reading operation with filename and byte count set as description
*
* Note, that span is started when this InputStream is instantiated via constructor and finishes
* when the {@link java.io.FileInputStream#close()} is called.
*/
public final class SentryFileInputStream extends FileInputStream {
private final @NotNull FileInputStream delegate;
private final @NotNull FileIOSpanManager spanManager;
public SentryFileInputStream(final @Nullable String name) throws FileNotFoundException {
this(name != null ? new File(name) : null, ScopesAdapter.getInstance());
}
public SentryFileInputStream(final @Nullable File file) throws FileNotFoundException {
this(file, ScopesAdapter.getInstance());
}
public SentryFileInputStream(final @NotNull FileDescriptor fdObj) {
this(fdObj, ScopesAdapter.getInstance());
}
SentryFileInputStream(final @Nullable File file, final @NotNull IScopes scopes)
throws FileNotFoundException {
this(init(file, null, scopes));
}
SentryFileInputStream(final @NotNull FileDescriptor fdObj, final @NotNull IScopes scopes) {
this(init(fdObj, null, scopes), fdObj);
}
private SentryFileInputStream(
final @NotNull FileInputStreamInitData data, final @NotNull FileDescriptor fd) {
super(fd);
spanManager = new FileIOSpanManager(data.span, data.file, data.options);
delegate = data.delegate;
}
private SentryFileInputStream(final @NotNull FileInputStreamInitData data)
throws FileNotFoundException {
super(getFileDescriptor(data.delegate));
spanManager = new FileIOSpanManager(data.span, data.file, data.options);
delegate = data.delegate;
}
private static FileInputStreamInitData init(
final @Nullable File file, @Nullable FileInputStream delegate, final @NotNull IScopes scopes)
throws FileNotFoundException {
final ISpan span = FileIOSpanManager.startSpan(scopes, "file.read");
if (delegate == null) {
delegate = new FileInputStream(file);
}
return new FileInputStreamInitData(file, span, delegate, scopes.getOptions());
}
private static FileInputStreamInitData init(
final @NotNull FileDescriptor fd,
@Nullable FileInputStream delegate,
final @NotNull IScopes scopes) {
final ISpan span = FileIOSpanManager.startSpan(scopes, "file.read");
if (delegate == null) {
delegate = new FileInputStream(fd);
}
return new FileInputStreamInitData(null, span, delegate, scopes.getOptions());
}
@Override
public int read() throws IOException {
// this is the only case, when the read() operation returns the byte value, and not the count
// hence we need this special handling
AtomicInteger result = new AtomicInteger(0);
spanManager.performIO(
() -> {
final int res = delegate.read();
result.set(res);
return res != -1 ? 1 : 0;
});
return result.get();
}
@Override
public int read(final byte @NotNull [] b) throws IOException {
return spanManager.performIO(() -> delegate.read(b));
}
@Override
public int read(final byte @NotNull [] b, final int off, final int len) throws IOException {
return spanManager.performIO(() -> delegate.read(b, off, len));
}
@Override
public long skip(final long n) throws IOException {
return spanManager.performIO(() -> delegate.skip(n));
}
@Override
public void close() throws IOException {
spanManager.finish(delegate);
}
private static FileDescriptor getFileDescriptor(final @NotNull FileInputStream stream)
throws FileNotFoundException {
try {
return stream.getFD();
} catch (IOException error) {
throw new FileNotFoundException("No file descriptor");
}
}
public static final class Factory {
public static FileInputStream create(
final @NotNull FileInputStream delegate, final @Nullable String name)
throws FileNotFoundException {
return new SentryFileInputStream(
init(name != null ? new File(name) : null, delegate, ScopesAdapter.getInstance()));
}
public static FileInputStream create(
final @NotNull FileInputStream delegate, final @Nullable File file)
throws FileNotFoundException {
return new SentryFileInputStream(init(file, delegate, ScopesAdapter.getInstance()));
}
public static FileInputStream create(
final @NotNull FileInputStream delegate, final @NotNull FileDescriptor descriptor) {
return new SentryFileInputStream(
init(descriptor, delegate, ScopesAdapter.getInstance()), descriptor);
}
static FileInputStream create(
final @NotNull FileInputStream delegate,
final @Nullable File file,
final @NotNull IScopes scopes)
throws FileNotFoundException {
return new SentryFileInputStream(init(file, delegate, scopes));
}
}
}