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 readRangeTimer = Metrics.timer(METRIC_NAME, OPERATION, "readRange");
047    private static final Timer listVersionsTimer = Metrics.timer(METRIC_NAME, OPERATION, "listVersions");
048    private static final Timer commitTimer = Metrics.timer(METRIC_NAME, OPERATION, "commit");
049
050    /**
051     * @param inner the session to wrap
052     */
053    public FcrepoOcflObjectSessionWrapper(final OcflObjectSession inner) {
054        this.inner = inner;
055    }
056
057    @Override
058    public String sessionId() {
059        return inner.sessionId();
060    }
061
062    @Override
063    public String ocflObjectId() {
064        return inner.ocflObjectId();
065    }
066
067    @Override
068    public ResourceHeaders writeResource(final ResourceHeaders headers, final InputStream content) {
069        return MetricsHelper.time(writeTimer, () -> {
070            return exec(() -> inner.writeResource(headers, content));
071        });
072    }
073
074    @Override
075    public void writeHeaders(final ResourceHeaders headers) {
076        writeHeadersTimer.record(() -> {
077            exec(() -> inner.writeHeaders(headers));
078        });
079    }
080
081    @Override
082    public void deleteContentFile(final ResourceHeaders headers) {
083        deleteContentTimer.record(() -> {
084            exec(() -> inner.deleteContentFile(headers));
085        });
086    }
087
088    @Override
089    public void deleteResource(final String resourceId) {
090        deleteResourceTimer.record(() -> {
091            exec(() -> inner.deleteResource(resourceId));
092        });
093    }
094
095    @Override
096    public boolean containsResource(final String resourceId) {
097        return MetricsHelper.time(containsResourceTimer, () -> {
098            return exec(() -> inner.containsResource(resourceId));
099        });
100    }
101
102    @Override
103    public ResourceHeaders readHeaders(final String resourceId) {
104        return MetricsHelper.time(readHeadersTimer, () -> {
105            return exec(() -> inner.readHeaders(resourceId));
106        });
107    }
108
109    @Override
110    public ResourceHeaders readHeaders(final String resourceId, final String versionNumber) {
111        return MetricsHelper.time(readHeadersTimer, () -> {
112            return exec(() -> inner.readHeaders(resourceId, versionNumber));
113        });
114    }
115
116    @Override
117    public ResourceContent readContent(final String resourceId) {
118        return MetricsHelper.time(readContentTimer, () -> {
119            return exec(() -> inner.readContent(resourceId));
120        });
121    }
122
123    @Override
124    public ResourceContent readContent(final String resourceId, final String versionNumber) {
125        return MetricsHelper.time(readContentTimer, () -> {
126            return exec(() -> inner.readContent(resourceId, versionNumber));
127        });
128    }
129
130    @Override
131    public ResourceContent readRange(final String resourceId, final long start, final long end) {
132        return MetricsHelper.time(readRangeTimer, () ->
133                exec(() -> inner.readRange(resourceId, start, end)));
134    }
135
136    @Override
137    public ResourceContent readRange(final String resourceId, final String versionNumber,
138                                     final long start, final long end) {
139        return MetricsHelper.time(readRangeTimer, () ->
140                exec(() -> inner.readRange(resourceId, versionNumber, start, end)));
141    }
142
143    @Override
144    public List<OcflVersionInfo> listVersions(final String resourceId) {
145        return MetricsHelper.time(listVersionsTimer, () -> {
146            return exec(() -> inner.listVersions(resourceId));
147        });
148    }
149
150    @Override
151    public Stream<ResourceHeaders> streamResourceHeaders() {
152        return exec(inner::streamResourceHeaders);
153    }
154
155    @Override
156    public void versionCreationTimestamp(final OffsetDateTime timestamp) {
157        inner.versionCreationTimestamp(timestamp);
158    }
159
160    @Override
161    public void versionAuthor(final String name, final String address) {
162        inner.versionAuthor(name, address);
163    }
164
165    @Override
166    public void versionMessage(final String message) {
167        inner.versionMessage(message);
168    }
169
170    @Override
171    public void invalidateCache(final String resourceId) {
172        inner.invalidateCache(resourceId);
173    }
174
175    @Override
176    public void commitType(final CommitType commitType) {
177        inner.commitType(commitType);
178    }
179
180    @Override
181    public void commit() {
182        commitTimer.record(() -> {
183            exec(inner::commit);
184        });
185    }
186
187    @Override
188    public void rollback() {
189        exec(inner::rollback);
190    }
191
192    @Override
193    public void abort() {
194        exec(inner::abort);
195    }
196
197    @Override
198    public boolean isOpen() {
199        return inner.isOpen();
200    }
201
202    @Override
203    public void close() {
204        exec(inner::close);
205    }
206
207    private <T> T exec(final Callable<T> callable) throws PersistentStorageException {
208        try {
209            return callable.call();
210        } catch (final NotFoundException e) {
211            throw new PersistentItemNotFoundException(e.getMessage(), e);
212        } catch (final Exception e) {
213            throw new PersistentStorageException(e.getMessage(), e);
214        }
215    }
216
217    private void exec(final CheckedRunnable runnable) throws PersistentStorageException {
218        try {
219            runnable.run();
220        } catch (final NotFoundException e) {
221            throw new PersistentItemNotFoundException(e.getMessage(), e);
222        } catch (final Exception e) {
223            throw new PersistentStorageException(e.getMessage(), e);
224        }
225    }
226
227}