001/*
002 * Copyright 2019 DuraSpace, Inc.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.fcrepo.migration.handlers.ocfl;
018
019import edu.wisc.library.ocfl.api.MutableOcflRepository;
020import edu.wisc.library.ocfl.api.model.ObjectVersionId;
021import edu.wisc.library.ocfl.core.OcflRepositoryBuilder;
022import edu.wisc.library.ocfl.core.extension.storage.layout.config.HashedNTupleLayoutConfig;
023import edu.wisc.library.ocfl.core.path.mapper.LogicalPathMappers;
024import edu.wisc.library.ocfl.core.storage.filesystem.FileSystemOcflStorage;
025import org.apache.commons.io.IOUtils;
026import org.apache.commons.lang3.SystemUtils;
027import org.fcrepo.storage.ocfl.InteractionModel;
028import org.fcrepo.storage.ocfl.OcflObjectSession;
029import org.fcrepo.storage.ocfl.OcflObjectSessionFactory;
030import org.fcrepo.storage.ocfl.ResourceContent;
031import org.fcrepo.storage.ocfl.ResourceHeaders;
032import org.junit.Before;
033import org.junit.Rule;
034import org.junit.Test;
035import org.junit.rules.TemporaryFolder;
036
037import java.io.IOException;
038import java.nio.file.Files;
039import java.nio.file.Path;
040
041import static org.junit.Assert.assertEquals;
042import static org.junit.Assert.assertTrue;
043
044/**
045 * @author pwinckles
046 */
047public class PlainOcflObjectSessionTest {
048
049    @Rule
050    public TemporaryFolder tempDir = new TemporaryFolder();
051
052    private Path ocflRoot;
053    private Path staging;
054
055    private MutableOcflRepository ocflRepo;
056    private OcflObjectSessionFactory plainSessionFactory;
057
058    private static final String AG_ID = "info:fedora/foo";
059
060    @Before
061    public void setup() throws IOException {
062        ocflRoot = tempDir.newFolder("ocfl").toPath();
063        staging = tempDir.newFolder("staging").toPath();
064
065        final var logicalPathMapper = SystemUtils.IS_OS_WINDOWS ?
066                LogicalPathMappers.percentEncodingWindowsMapper() : LogicalPathMappers.percentEncodingLinuxMapper();
067
068        ocflRepo = new OcflRepositoryBuilder()
069                .defaultLayoutConfig(new HashedNTupleLayoutConfig())
070                .logicalPathMapper(logicalPathMapper)
071                .storage(FileSystemOcflStorage.builder().repositoryRoot(ocflRoot).build())
072                .workDir(staging)
073                .buildMutable();
074
075        plainSessionFactory = new PlainOcflObjectSessionFactory(ocflRepo, staging,
076                "testing", "fedoraAdmin", "info:fedora/fedoraAdmin", false);
077    }
078
079    @Test
080    public void writeBinary() throws IOException {
081        final var dsId = "bar";
082        final var resourceId = AG_ID + "/" + dsId;
083        final var content = binary(resourceId, "test");
084
085        final var session = newSession();
086
087        write(content, session);
088
089        session.commit();
090
091        assertEquals("test",
092                IOUtils.toString(ocflRepo.getObject(ObjectVersionId.head(AG_ID)).getFile(dsId).getStream()));
093    }
094
095    @Test
096    public void writeRdf() throws IOException {
097        final var dsId = "baz";
098        final var resourceId = AG_ID + "/" + dsId + "/fcr:metadata";
099        final var content = rdf(resourceId, "desc");
100
101        final var session = newSession();
102
103        write(content, session);
104
105        session.commit();
106
107        assertEquals("desc",
108                IOUtils.toString(ocflRepo.getObject(ObjectVersionId.head(AG_ID))
109                        .getFile(dsId + "~fcr-desc.nt").getStream()));
110    }
111
112    @Test
113    public void writeAg() throws IOException {
114        final var content = rdf(AG_ID, "ag");
115
116        final var session = newSession();
117
118        write(content, session);
119
120        session.commit();
121
122        assertEquals("ag",
123                IOUtils.toString(ocflRepo.getObject(ObjectVersionId.head(AG_ID))
124                        .getFile("fcr-container.nt").getStream()));
125    }
126
127    @Test
128    public void worksOnWindows() throws IOException {
129        final var dsId = "bar:baz";
130        final var resourceId = AG_ID + "/" + dsId;
131        final var content = binary(resourceId, "test");
132
133        final var session = newSession();
134
135        write(content, session);
136
137        session.commit();
138
139        assertEquals("test",
140                IOUtils.toString(ocflRepo.getObject(ObjectVersionId.head(AG_ID)).getFile(dsId).getStream()));
141    }
142
143    @Test
144    public void cleanupOnAbort() {
145        final var dsId = "bar:baz";
146        final var resourceId = AG_ID + "/" + dsId;
147        final var content = binary(resourceId, "test");
148
149        final var session = newSession();
150
151        write(content, session);
152
153        session.abort();
154
155        assertTrue(Files.notExists(staging.resolve(session.sessionId())));
156    }
157
158    private void write(final ResourceContent content, final OcflObjectSession session) {
159        session.writeResource(content.getHeaders(), content.getContentStream().get());
160    }
161
162    private OcflObjectSession newSession() {
163        return plainSessionFactory.newSession(AG_ID);
164    }
165
166    private ResourceContent ag(final String resourceId, final String content) {
167        final var headers = headers(resourceId);
168        headers.withInteractionModel(InteractionModel.BASIC_CONTAINER.getUri());
169        headers.withArchivalGroup(true);
170        return new ResourceContent(IOUtils.toInputStream(content), headers.build());
171    }
172
173    private ResourceContent binary(final String resourceId, final String content) {
174        final var headers = headers(resourceId);
175        headers.withInteractionModel(InteractionModel.NON_RDF.getUri());
176        return new ResourceContent(IOUtils.toInputStream(content), headers.build());
177    }
178
179    private ResourceContent rdf(final String resourceId, final String content) {
180        final var headers = headers(resourceId);
181        headers.withInteractionModel(InteractionModel.NON_RDF_DESCRIPTION.getUri());
182        return new ResourceContent(IOUtils.toInputStream(content), headers.build());
183    }
184
185    private ResourceHeaders.Builder headers(final String resourceId) {
186        final var headers = ResourceHeaders.builder();
187        headers.withId(resourceId);
188        headers.withParent(AG_ID);
189        return headers;
190    }
191
192}