001/*
002 * The contents of this file are subject to the license and copyright
003 * detailed in the LICENSE and NOTICE files at the root of the source
004 * tree.
005 */
006
007package org.fcrepo.persistence.ocfl.impl;
008
009import io.micrometer.core.instrument.Metrics;
010import io.micrometer.core.instrument.Timer;
011import org.fcrepo.common.lang.CheckedRunnable;
012import org.fcrepo.common.metrics.MetricsHelper;
013import org.fcrepo.persistence.api.exceptions.PersistentItemNotFoundException;
014import org.fcrepo.persistence.api.exceptions.PersistentStorageException;
015import org.fcrepo.storage.ocfl.CommitType;
016import org.fcrepo.storage.ocfl.OcflObjectSession;
017import org.fcrepo.storage.ocfl.OcflVersionInfo;
018import org.fcrepo.storage.ocfl.ResourceContent;
019import org.fcrepo.storage.ocfl.ResourceHeaders;
020import org.fcrepo.storage.ocfl.exception.NotFoundException;
021
022import java.io.InputStream;
023import java.time.OffsetDateTime;
024import java.util.List;
025import java.util.concurrent.Callable;
026import java.util.stream.Stream;
027
028/**
029 * Wrapper around an OcflObjectSession to convert exceptions into fcrepo exceptions and time operations
030 *
031 * @author pwinckles
032 */
033public class FcrepoOcflObjectSessionWrapper implements OcflObjectSession {
034
035    private final OcflObjectSession inner;
036
037    private static final String METRIC_NAME = "fcrepo.storage.ocfl.object";
038    private static final String OPERATION = "operation";
039    private static final Timer writeTimer = Metrics.timer(METRIC_NAME, OPERATION, "write");
040    private static final Timer writeHeadersTimer = Metrics.timer(METRIC_NAME, OPERATION, "writeHeaders");
041    private static final Timer deleteContentTimer = Metrics.timer(METRIC_NAME, OPERATION, "deleteContent");
042    private static final Timer deleteResourceTimer = Metrics.timer(METRIC_NAME, OPERATION, "deleteResource");
043    private static final Timer containsResourceTimer = Metrics.timer(METRIC_NAME, OPERATION, "containsResource");
044    private static final Timer readHeadersTimer = Metrics.timer(METRIC_NAME, OPERATION, "readHeaders");
045    private static final Timer readContentTimer = Metrics.timer(METRIC_NAME, OPERATION, "readContent");
046    private static final Timer listVersionsTimer = Metrics.timer(METRIC_NAME, OPERATION, "listVersions");
047    private static final Timer commitTimer = Metrics.timer(METRIC_NAME, OPERATION, "commit");
048
049    /**
050     * @param inner the session to wrap
051     */
052    public FcrepoOcflObjectSessionWrapper(final OcflObjectSession inner) {
053        this.inner = inner;
054    }
055
056    @Override
057    public String sessionId() {
058        return inner.sessionId();
059    }
060
061    @Override
062    public String ocflObjectId() {
063        return inner.ocflObjectId();
064    }
065
066    @Override
067    public ResourceHeaders writeResource(final ResourceHeaders headers, final InputStream content) {
068        return MetricsHelper.time(writeTimer, () -> {
069            return exec(() -> inner.writeResource(headers, content));
070        });
071    }
072
073    @Override
074    public void writeHeaders(final ResourceHeaders headers) {
075        writeHeadersTimer.record(() -> {
076            exec(() -> inner.writeHeaders(headers));
077        });
078    }
079
080    @Override
081    public void deleteContentFile(final ResourceHeaders headers) {
082        deleteContentTimer.record(() -> {
083            exec(() -> inner.deleteContentFile(headers));
084        });
085    }
086
087    @Override
088    public void deleteResource(final String resourceId) {
089        deleteResourceTimer.record(() -> {
090            exec(() -> inner.deleteResource(resourceId));
091        });
092    }
093
094    @Override
095    public boolean containsResource(final String resourceId) {
096        return MetricsHelper.time(containsResourceTimer, () -> {
097            return exec(() -> inner.containsResource(resourceId));
098        });
099    }
100
101    @Override
102    public ResourceHeaders readHeaders(final String resourceId) {
103        return MetricsHelper.time(readHeadersTimer, () -> {
104            return exec(() -> inner.readHeaders(resourceId));
105        });
106    }
107
108    @Override
109    public ResourceHeaders readHeaders(final String resourceId, final String versionNumber) {
110        return MetricsHelper.time(readHeadersTimer, () -> {
111            return exec(() -> inner.readHeaders(resourceId, versionNumber));
112        });
113    }
114
115    @Override
116    public ResourceContent readContent(final String resourceId) {
117        return MetricsHelper.time(readContentTimer, () -> {
118            return exec(() -> inner.readContent(resourceId));
119        });
120    }
121
122    @Override
123    public ResourceContent readContent(final String resourceId, final String versionNumber) {
124        return MetricsHelper.time(readContentTimer, () -> {
125            return exec(() -> inner.readContent(resourceId, versionNumber));
126        });
127    }
128
129    @Override
130    public List<OcflVersionInfo> listVersions(final String resourceId) {
131        return MetricsHelper.time(listVersionsTimer, () -> {
132            return exec(() -> inner.listVersions(resourceId));
133        });
134    }
135
136    @Override
137    public Stream<ResourceHeaders> streamResourceHeaders() {
138        return exec(inner::streamResourceHeaders);
139    }
140
141    @Override
142    public void versionCreationTimestamp(final OffsetDateTime timestamp) {
143        inner.versionCreationTimestamp(timestamp);
144    }
145
146    @Override
147    public void versionAuthor(final String name, final String address) {
148        inner.versionAuthor(name, address);
149    }
150
151    @Override
152    public void versionMessage(final String message) {
153        inner.versionMessage(message);
154    }
155
156    @Override
157    public void commitType(final CommitType commitType) {
158        inner.commitType(commitType);
159    }
160
161    @Override
162    public void commit() {
163        commitTimer.record(() -> {
164            exec(inner::commit);
165        });
166    }
167
168    @Override
169    public void rollback() {
170        exec(inner::rollback);
171    }
172
173    @Override
174    public void abort() {
175        exec(inner::abort);
176    }
177
178    @Override
179    public boolean isOpen() {
180        return inner.isOpen();
181    }
182
183    @Override
184    public void close() {
185        exec(inner::close);
186    }
187
188    private <T> T exec(final Callable<T> callable) throws PersistentStorageException {
189        try {
190            return callable.call();
191        } catch (final NotFoundException e) {
192            throw new PersistentItemNotFoundException(e.getMessage(), e);
193        } catch (final Exception e) {
194            throw new PersistentStorageException(e.getMessage(), e);
195        }
196    }
197
198    private void exec(final CheckedRunnable runnable) throws PersistentStorageException {
199        try {
200            runnable.run();
201        } catch (final NotFoundException e) {
202            throw new PersistentItemNotFoundException(e.getMessage(), e);
203        } catch (final Exception e) {
204            throw new PersistentStorageException(e.getMessage(), e);
205        }
206    }
207
208}