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 invalidateCache(final String resourceId) {
158        inner.invalidateCache(resourceId);
159    }
160
161    @Override
162    public void commitType(final CommitType commitType) {
163        inner.commitType(commitType);
164    }
165
166    @Override
167    public void commit() {
168        commitTimer.record(() -> {
169            exec(inner::commit);
170        });
171    }
172
173    @Override
174    public void rollback() {
175        exec(inner::rollback);
176    }
177
178    @Override
179    public void abort() {
180        exec(inner::abort);
181    }
182
183    @Override
184    public boolean isOpen() {
185        return inner.isOpen();
186    }
187
188    @Override
189    public void close() {
190        exec(inner::close);
191    }
192
193    private <T> T exec(final Callable<T> callable) throws PersistentStorageException {
194        try {
195            return callable.call();
196        } catch (final NotFoundException e) {
197            throw new PersistentItemNotFoundException(e.getMessage(), e);
198        } catch (final Exception e) {
199            throw new PersistentStorageException(e.getMessage(), e);
200        }
201    }
202
203    private void exec(final CheckedRunnable runnable) throws PersistentStorageException {
204        try {
205            runnable.run();
206        } catch (final NotFoundException e) {
207            throw new PersistentItemNotFoundException(e.getMessage(), e);
208        } catch (final Exception e) {
209            throw new PersistentStorageException(e.getMessage(), e);
210        }
211    }
212
213}