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