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;
019
020import static org.fcrepo.camel.FcrepoConstants.COMMIT;
021import static org.fcrepo.camel.FcrepoConstants.ROLLBACK;
022import static org.fcrepo.camel.FcrepoConstants.TRANSACTION;
023import static org.fcrepo.client.FcrepoClient.client;
024import static org.slf4j.LoggerFactory.getLogger;
025
026import java.io.InputStream;
027import java.net.URI;
028
029import org.fcrepo.client.FcrepoClient;
030import org.fcrepo.client.FcrepoOperationFailedException;
031import org.fcrepo.client.FcrepoResponse;
032import org.slf4j.Logger;
033import org.springframework.transaction.CannotCreateTransactionException;
034import org.springframework.transaction.TransactionDefinition;
035import org.springframework.transaction.TransactionSystemException;
036import org.springframework.transaction.support.AbstractPlatformTransactionManager;
037import org.springframework.transaction.support.DefaultTransactionStatus;
038
039/**
040 * A Transaction Manager for interacting with fedora-based transactions
041 *
042 * @author Aaron Coburn
043 * @since Feb 16, 2015
044 */
045public class FcrepoTransactionManager extends AbstractPlatformTransactionManager {
046
047    private FcrepoClient fcrepoClient;
048
049    private String baseUrl;
050
051    private String authUsername;
052
053    private String authPassword;
054
055    private String authHost;
056
057    private static final Logger LOGGER = getLogger(FcrepoTransactionManager.class);
058
059    /**
060     * Create a FcrepoTransactionManager
061     */
062    public FcrepoTransactionManager() {
063        super();
064        setNestedTransactionAllowed(false);
065    }
066
067    /**
068     * Set the baseUrl for the transaction manager.
069     *
070     * @param baseUrl the fcrepo base url
071     */
072    public void setBaseUrl(final String baseUrl) {
073        this.baseUrl = baseUrl;
074    }
075
076    /**
077     * Get the base url for the transaction manager.
078     *
079     * @return the fcrepo base url
080     */
081    public String getBaseUrl() {
082        return baseUrl;
083    }
084
085    /**
086     * Set the authUsername for the transaction manager.
087     *
088     * @param authUsername the username for authentication
089     */
090    public void setAuthUsername(final String authUsername) {
091        this.authUsername = authUsername;
092    }
093
094    /**
095     * Get the authUsername for the transaction manager.
096     *
097     * @return the username for authentication
098     */
099    public String getAuthUsername() {
100        return authUsername;
101    }
102
103    /**
104     * Set the authPassword for the transaction manager.
105     *
106     * @param authPassword the password used for authentication
107     */
108    public void setAuthPassword(final String authPassword) {
109        this.authPassword = authPassword;
110    }
111
112    /**
113     * Get the authPassword for the transaction manager.
114     *
115     * @return the password used for authentication
116     */
117    public String getAuthPassword() {
118        return authPassword;
119    }
120
121    /**
122     * Set the authHost for the transaction manager.
123     *
124     * @param authHost the host realm used for authentication
125     */
126    public void setAuthHost(final String authHost) {
127        this.authHost = authHost;
128    }
129
130    /**
131     * Get the authHost for the transaction manager.
132     *
133     * @return the host realm used for authentication
134     */
135    public String getAuthHost() {
136        return authHost;
137    }
138
139    @Override
140    protected void doBegin(final Object transaction, final TransactionDefinition definition) {
141        final FcrepoResponse response;
142        final InputStream is = null;
143        final String contentType = null;
144        final FcrepoTransactionObject tx = (FcrepoTransactionObject)transaction;
145
146        if (tx.getSessionId() == null) {
147            try {
148                response = getClient().post(URI.create(baseUrl + TRANSACTION))
149                    .body(is, contentType).perform();
150            } catch (final FcrepoOperationFailedException ex) {
151                LOGGER.debug("HTTP Operation failed: ", ex);
152                throw new CannotCreateTransactionException("Could not create fcrepo transaction");
153            }
154
155            if (response != null && response.getLocation() != null) {
156                tx.setSessionId(response.getLocation().toString().substring(baseUrl.length() + 1));
157            } else {
158                throw new CannotCreateTransactionException("Invalid response while creating transaction");
159            }
160        }
161    }
162
163    @Override
164    protected void doCommit(final DefaultTransactionStatus status) {
165        final FcrepoTransactionObject tx = (FcrepoTransactionObject)status.getTransaction();
166        final InputStream is = null;
167        final String contentType = null;
168
169        try {
170            getClient().post(URI.create(baseUrl + "/" + tx.getSessionId() + COMMIT))
171                .body(is, contentType).perform();
172        } catch (final FcrepoOperationFailedException ex) {
173            LOGGER.debug("Transaction commit failed: ", ex);
174            throw new TransactionSystemException("Could not commit fcrepo transaction");
175        } finally {
176            tx.setSessionId(null);
177        }
178    }
179
180    @Override
181    protected void doRollback(final DefaultTransactionStatus status) {
182        final FcrepoTransactionObject tx = (FcrepoTransactionObject)status.getTransaction();
183
184        try {
185            getClient().post(URI.create(baseUrl + "/" + tx.getSessionId() + ROLLBACK)).perform();
186        } catch (final FcrepoOperationFailedException ex) {
187            LOGGER.debug("Transaction rollback failed: ", ex);
188            throw new TransactionSystemException("Could not rollback fcrepo transaction");
189        } finally {
190            tx.setSessionId(null);
191        }
192    }
193
194    @Override
195    protected Object doGetTransaction() {
196        return new FcrepoTransactionObject();
197    }
198
199    private FcrepoClient getClient() {
200        if (fcrepoClient == null) {
201            return client().credentials(authUsername, authPassword).authScope(authHost)
202                .throwExceptionOnFailure().build();
203        }
204        return fcrepoClient;
205    }
206}