001/*
002 * Licensed to DuraSpace under one or more contributor license agreements.
003 * See the NOTICE file distributed with this work for additional information
004 * regarding copyright ownership.
005 *
006 * DuraSpace licenses this file to you under the Apache License,
007 * Version 2.0 (the "License"); you may not use this file except in
008 * compliance with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.fcrepo.camel.integration;
019
020import static org.junit.Assert.assertNotNull;
021
022import java.util.HashMap;
023import java.util.Map;
024
025import org.apache.camel.EndpointInject;
026import org.apache.camel.Exchange;
027import org.apache.camel.Produce;
028import org.apache.camel.ProducerTemplate;
029import org.apache.camel.builder.RouteBuilder;
030import org.apache.camel.builder.xml.Namespaces;
031import org.apache.camel.builder.xml.XPathBuilder;
032import org.apache.camel.component.mock.MockEndpoint;
033import org.apache.camel.impl.JndiRegistry;
034import org.apache.camel.spring.spi.SpringTransactionPolicy;
035import org.apache.camel.test.junit4.CamelTestSupport;
036import org.apache.jena.vocabulary.RDF;
037import org.fcrepo.camel.FcrepoHeaders;
038import org.fcrepo.camel.FcrepoTransactionManager;
039import org.fcrepo.client.FcrepoOperationFailedException;
040import org.junit.Test;
041import org.junit.Before;
042import org.springframework.transaction.TransactionDefinition;
043import org.springframework.transaction.support.TransactionTemplate;
044
045/**
046 * Test adding a new resource with POST
047 * @author Aaron Coburn
048 * @since November 7, 2014
049 */
050public class FcrepoTransactionIT extends CamelTestSupport {
051
052    private static final String REPOSITORY = "http://fedora.info/definitions/v4/repository#";
053
054    private TransactionTemplate txTemplate;
055
056    private FcrepoTransactionManager txMgr;
057
058    @EndpointInject(uri = "mock:created")
059    protected MockEndpoint createdEndpoint;
060
061    @EndpointInject(uri = "mock:transactedput")
062    protected MockEndpoint midtransactionEndpoint;
063
064    @EndpointInject(uri = "mock:notfound")
065    protected MockEndpoint notfoundEndpoint;
066
067    @EndpointInject(uri = "mock:verified")
068    protected MockEndpoint verifiedEndpoint;
069
070    @EndpointInject(uri = "mock:transacted")
071    protected MockEndpoint transactedEndpoint;
072
073    @EndpointInject(uri = "mock:rollback")
074    protected MockEndpoint rollbackEndpoint;
075
076    @EndpointInject(uri = "mock:deleted")
077    protected MockEndpoint deletedEndpoint;
078
079    @EndpointInject(uri = "mock:missing")
080    protected MockEndpoint missingEndpoint;
081
082    @Produce(uri = "direct:create")
083    protected ProducerTemplate template;
084
085    @Before
086    public void setUp() throws Exception {
087        super.setUp();
088
089        txTemplate = new TransactionTemplate(txMgr);
090        txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
091        txTemplate.afterPropertiesSet();
092    }
093
094    @Override
095    protected JndiRegistry createRegistry() throws Exception {
096        final JndiRegistry reg = super.createRegistry();
097
098        txMgr = new FcrepoTransactionManager();
099        txMgr.setBaseUrl(FcrepoTestUtils.getFcrepoBaseUrl());
100        reg.bind("txManager", txMgr);
101
102        final SpringTransactionPolicy txPolicy = new SpringTransactionPolicy();
103        txPolicy.setTransactionManager(txMgr);
104        txPolicy.setPropagationBehaviorName("PROPAGATION_REQUIRED");
105        reg.bind("required", txPolicy);
106
107        return reg;
108    }
109
110    @Test
111    public void testTransaction() throws InterruptedException {
112        // Assertions
113        deletedEndpoint.expectedMessageCount(4);
114        deletedEndpoint.expectedHeaderReceived(Exchange.HTTP_RESPONSE_CODE, 204);
115
116        transactedEndpoint.expectedMessageCount(1);
117
118        verifiedEndpoint.expectedMessageCount(3);
119
120        midtransactionEndpoint.expectedMessageCount(3);
121        midtransactionEndpoint.expectedHeaderReceived(Exchange.HTTP_RESPONSE_CODE, 201);
122
123        notfoundEndpoint.expectedMessageCount(6);
124        notfoundEndpoint.expectedHeaderReceived(Exchange.HTTP_RESPONSE_CODE, 404);
125
126        // Start the transaction
127        final Map<String, Object> headers = new HashMap<>();
128        headers.put(Exchange.HTTP_METHOD, "POST");
129        headers.put(Exchange.CONTENT_TYPE, "text/turtle");
130
131        // Create the object
132        final String fullPath = template.requestBodyAndHeaders(
133                "direct:create", FcrepoTestUtils.getTurtleDocument(), headers, String.class);
134
135        assertNotNull(fullPath);
136
137        final String identifier = fullPath.replaceAll(FcrepoTestUtils.getFcrepoBaseUrl(), "");
138
139        // Test the creation of several objects
140        template.sendBodyAndHeader("direct:transact", null, "TestIdentifierBase", identifier);
141
142        // Test the object
143        template.sendBodyAndHeader("direct:verify", null, FcrepoHeaders.FCREPO_IDENTIFIER, identifier + "/one");
144        template.sendBodyAndHeader("direct:verify", null, FcrepoHeaders.FCREPO_IDENTIFIER, identifier + "/two");
145        template.sendBodyAndHeader("direct:verify", null, FcrepoHeaders.FCREPO_IDENTIFIER, identifier + "/three");
146
147        // Teardown
148        template.sendBodyAndHeader("direct:teardown", null, FcrepoHeaders.FCREPO_IDENTIFIER, identifier + "/one");
149        template.sendBodyAndHeader("direct:teardown", null, FcrepoHeaders.FCREPO_IDENTIFIER, identifier + "/two");
150        template.sendBodyAndHeader("direct:teardown", null, FcrepoHeaders.FCREPO_IDENTIFIER, identifier + "/three");
151        template.sendBodyAndHeader("direct:teardown", null, FcrepoHeaders.FCREPO_IDENTIFIER, identifier);
152
153        // Confirm assertions
154        verifiedEndpoint.assertIsSatisfied();
155        deletedEndpoint.assertIsSatisfied();
156        transactedEndpoint.assertIsSatisfied();
157        notfoundEndpoint.assertIsSatisfied();
158        midtransactionEndpoint.assertIsSatisfied();
159    }
160
161    @Test
162    public void testTransactionWithRollback() throws InterruptedException {
163        // Assertions
164        deletedEndpoint.expectedMessageCount(1);
165        deletedEndpoint.expectedHeaderReceived(Exchange.HTTP_RESPONSE_CODE, 204);
166
167        transactedEndpoint.expectedMessageCount(0);
168
169        verifiedEndpoint.expectedMessageCount(0);
170
171        midtransactionEndpoint.expectedMessageCount(2);
172        midtransactionEndpoint.expectedHeaderReceived(Exchange.HTTP_RESPONSE_CODE, 201);
173
174        notfoundEndpoint.expectedMessageCount(3);
175        notfoundEndpoint.expectedHeaderReceived(Exchange.HTTP_RESPONSE_CODE, 404);
176
177        // Start the transaction
178        final Map<String, Object> headers = new HashMap<>();
179        headers.put(Exchange.HTTP_METHOD, "POST");
180        headers.put(Exchange.CONTENT_TYPE, "text/turtle");
181
182        // Create the object
183        final String fullPath = template.requestBodyAndHeaders(
184                "direct:create", FcrepoTestUtils.getTurtleDocument(), headers, String.class);
185
186        assertNotNull(fullPath);
187
188        final String identifier = fullPath.replaceAll(FcrepoTestUtils.getFcrepoBaseUrl(), "");
189
190        // Test the creation of several objects
191        template.sendBodyAndHeader("direct:transactWithError", null, "TestIdentifierBase", identifier);
192
193        // Test the object
194        template.sendBodyAndHeader("direct:verifyMissing", null, FcrepoHeaders.FCREPO_IDENTIFIER, identifier + "/one");
195        template.sendBodyAndHeader("direct:verifyMissing", null, FcrepoHeaders.FCREPO_IDENTIFIER, identifier + "/two");
196
197        // Teardown
198        template.sendBodyAndHeader("direct:teardown", null, FcrepoHeaders.FCREPO_IDENTIFIER, identifier);
199
200        // Confirm assertions
201        verifiedEndpoint.assertIsSatisfied();
202        deletedEndpoint.assertIsSatisfied();
203        transactedEndpoint.assertIsSatisfied();
204        notfoundEndpoint.assertIsSatisfied();
205        midtransactionEndpoint.assertIsSatisfied();
206    }
207
208    @Override
209    protected RouteBuilder createRouteBuilder() {
210        return new RouteBuilder() {
211            @Override
212            public void configure() {
213                final String fcrepo_uri = FcrepoTestUtils.getFcrepoEndpointUri();
214                final String http4_uri = fcrepo_uri.replaceAll("fcrepo:", "http4:");
215
216                final Namespaces ns = new Namespaces("rdf", RDF.uri);
217
218                final XPathBuilder titleXpath = new XPathBuilder("/rdf:RDF/rdf:Description/dc:title/text()");
219                titleXpath.namespaces(ns);
220                titleXpath.namespace("dc", "http://purl.org/dc/elements/1.1/");
221
222                onException(FcrepoOperationFailedException.class)
223                    .handled(true)
224                    .to("mock:missing");
225
226                from("direct:create")
227                    .to(fcrepo_uri)
228                    .to("mock:created");
229
230                from("direct:transactWithError")
231                    .transacted("required")
232                    .setHeader(FcrepoHeaders.FCREPO_IDENTIFIER).simple("${headers.TestIdentifierBase}/one")
233                    .setHeader(Exchange.HTTP_METHOD).constant("PUT")
234                    .to(fcrepo_uri)
235                    .to("mock:transactedput")
236
237                    .setHeader(Exchange.HTTP_PATH).simple("/fcrepo/rest${headers.TestIdentifierBase}/one")
238                    .setHeader(Exchange.HTTP_METHOD).constant("GET")
239                    .to(http4_uri + "?throwExceptionOnFailure=false")
240                    .to("mock:notfound")
241
242                    .setHeader(FcrepoHeaders.FCREPO_IDENTIFIER).simple("${headers.TestIdentifierBase}/two")
243                    .setHeader(Exchange.HTTP_METHOD).constant("PUT")
244                    .to(fcrepo_uri)
245                    .to("mock:transactedput")
246
247                    .setHeader(Exchange.HTTP_PATH).simple("/fcrepo/rest${headers.TestIdentifierBase}/one")
248                    .setHeader(Exchange.HTTP_METHOD).constant("GET")
249                    .to(http4_uri + "?throwExceptionOnFailure=false")
250                    .to("mock:notfound")
251                    .setHeader(Exchange.HTTP_PATH).simple("/fcrepo/rest${headers.TestIdentifierBase}/two")
252                    .setHeader(Exchange.HTTP_METHOD).constant("GET")
253                    .to(http4_uri + "?throwExceptionOnFailure=false")
254                    .to("mock:notfound")
255
256                    // this should throw an error
257                    .setHeader(FcrepoHeaders.FCREPO_IDENTIFIER).simple("${headers.TestIdentifierBase}/foo/")
258                    .setHeader(Exchange.HTTP_METHOD).constant("POST")
259                    .to(fcrepo_uri)
260                    .to("mock:transactedput")
261
262                    // this should never be reached
263                    .to("mock:transacted");
264
265                from("direct:transact")
266                    .transacted("required")
267                    .setHeader(FcrepoHeaders.FCREPO_IDENTIFIER).simple("${headers.TestIdentifierBase}/one")
268                    .setHeader(Exchange.HTTP_METHOD).constant("PUT")
269                    .to(fcrepo_uri)
270                    .to("mock:transactedput")
271
272                    .setHeader(Exchange.HTTP_PATH).simple("/fcrepo/rest${headers.TestIdentifierBase}/one")
273                    .setHeader(Exchange.HTTP_METHOD).constant("GET")
274                    .to(http4_uri + "?throwExceptionOnFailure=false")
275                    .to("mock:notfound")
276
277                    .setHeader(FcrepoHeaders.FCREPO_IDENTIFIER).simple("${headers.TestIdentifierBase}/two")
278                    .setHeader(Exchange.HTTP_METHOD).constant("PUT")
279                    .to(fcrepo_uri)
280                    .to("mock:transactedput")
281
282                    .setHeader(Exchange.HTTP_PATH).simple("/fcrepo/rest${headers.TestIdentifierBase}/one")
283                    .setHeader(Exchange.HTTP_METHOD).constant("GET")
284                    .to(http4_uri + "?throwExceptionOnFailure=false")
285                    .to("mock:notfound")
286                    .setHeader(Exchange.HTTP_PATH).simple("/fcrepo/rest${headers.TestIdentifierBase}/two")
287                    .setHeader(Exchange.HTTP_METHOD).constant("GET")
288                    .to(http4_uri + "?throwExceptionOnFailure=false")
289                    .to("mock:notfound")
290
291                    .setHeader(FcrepoHeaders.FCREPO_IDENTIFIER).simple("${headers.TestIdentifierBase}/three")
292                    .setHeader(Exchange.HTTP_METHOD).constant("PUT")
293                    .to(fcrepo_uri)
294                    .to("mock:transactedput")
295
296                    .setHeader(Exchange.HTTP_PATH).simple("/fcrepo/rest${headers.TestIdentifierBase}/one")
297                    .setHeader(Exchange.HTTP_METHOD).constant("GET")
298                    .to(http4_uri + "?throwExceptionOnFailure=false")
299                    .to("mock:notfound")
300                    .setHeader(Exchange.HTTP_PATH).simple("/fcrepo/rest${headers.TestIdentifierBase}/two")
301                    .setHeader(Exchange.HTTP_METHOD).constant("GET")
302                    .to(http4_uri + "?throwExceptionOnFailure=false")
303                    .to("mock:notfound")
304
305                    .setHeader(Exchange.HTTP_PATH).simple("/fcrepo/rest${headers.TestIdentifierBase}/three")
306                    .setHeader(Exchange.HTTP_METHOD).constant("GET")
307                    .to(http4_uri + "?throwExceptionOnFailure=false")
308                    .to("mock:notfound")
309
310                    .to("mock:transacted");
311
312                from("direct:verify")
313                    .to(fcrepo_uri)
314                    .filter().xpath(
315                        "/rdf:RDF/rdf:Description/rdf:type" +
316                        "[@rdf:resource='" + REPOSITORY + "Resource']", ns)
317                    .to("mock:verified");
318
319                from("direct:verifyMissing")
320                    .to(fcrepo_uri + "?throwExceptionOnFailure=false")
321                    .filter(header(Exchange.HTTP_RESPONSE_CODE).isEqualTo("404"))
322                    .to("mock:notfound");
323
324                from("direct:teardown")
325                    .setHeader(Exchange.HTTP_METHOD).constant("DELETE")
326                    .to(fcrepo_uri)
327                    .to("mock:deleted");
328            }
329        };
330    }
331}