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.http.commons.session; 019 020import static org.fcrepo.http.commons.session.TransactionConstants.ATOMIC_ID_HEADER; 021import static org.fcrepo.http.commons.session.TransactionConstants.TX_PREFIX; 022import static org.slf4j.LoggerFactory.getLogger; 023 024import java.net.URI; 025import java.util.regex.Matcher; 026import java.util.regex.Pattern; 027 028import javax.servlet.http.HttpServletRequest; 029 030import org.apache.commons.lang3.StringUtils; 031import org.fcrepo.kernel.api.Transaction; 032import org.fcrepo.kernel.api.TransactionManager; 033import org.fcrepo.kernel.api.exception.TransactionRuntimeException; 034import org.glassfish.hk2.api.Factory; 035import org.glassfish.jersey.uri.internal.JerseyUriBuilder; 036import org.slf4j.Logger; 037 038/** 039 * Provide a fedora tranasction within the current request context 040 * 041 * @author awoods 042 */ 043public class TransactionProvider implements Factory<Transaction> { 044 045 private static final Logger LOGGER = getLogger(TransactionProvider.class); 046 047 static final String JMS_BASEURL_PROP = "fcrepo.jms.baseUrl"; 048 049 private final TransactionManager txManager; 050 051 private final HttpServletRequest request; 052 053 private final Pattern txIdPattern; 054 055 private final URI baseUri; 056 057 /** 058 * Create a new transaction provider for a request 059 * @param txManager the transaction manager 060 * @param request the request 061 * @param baseUri base uri for the application 062 */ 063 public TransactionProvider(final TransactionManager txManager, final HttpServletRequest request, 064 final URI baseUri) { 065 this.txManager = txManager; 066 this.request = request; 067 this.txIdPattern = Pattern.compile("(^|" + baseUri + TX_PREFIX + ")([0-9a-f\\-]+)$"); 068 this.baseUri = baseUri; 069 } 070 071 @Override 072 public Transaction provide() { 073 final Transaction transaction = getTransactionForRequest(); 074 if (!transaction.isShortLived()) { 075 transaction.refresh(); 076 } 077 LOGGER.trace("Providing new transaction {}", transaction); 078 return transaction; 079 } 080 081 @Override 082 public void dispose(final Transaction transaction) { 083 if (transaction.isShortLived()) { 084 LOGGER.trace("Disposing transaction {}", transaction); 085 transaction.expire(); 086 } 087 } 088 089 /** 090 * Returns the transaction for the Request. If the request has ATOMIC_ID_HEADER header, 091 * the transaction corresponding to that ID is returned, otherwise, a new transaction is 092 * created. 093 * 094 * @return the transaction for the request 095 */ 096 public Transaction getTransactionForRequest() { 097 String txId = null; 098 // Transaction id either comes from header or is the path 099 String txUri = request.getHeader(ATOMIC_ID_HEADER); 100 if (!StringUtils.isEmpty(txUri)) { 101 final Matcher txMatcher = txIdPattern.matcher(txUri); 102 if (txMatcher.matches()) { 103 txId = txMatcher.group(2); 104 } else { 105 throw new TransactionRuntimeException("Invalid transaction id"); 106 } 107 } else { 108 txUri = request.getPathInfo(); 109 if (txUri != null) { 110 final Matcher txMatcher = txIdPattern.matcher(txUri); 111 if (txMatcher.matches()) { 112 txId = txMatcher.group(2); 113 } 114 } 115 } 116 117 if (!StringUtils.isEmpty(txId)) { 118 return txManager.get(txId); 119 } else { 120 final var transaction = txManager.create(); 121 transaction.setUserAgent(request.getHeader("user-agent")); 122 transaction.setBaseUri(resolveBaseUri()); 123 return transaction; 124 } 125 } 126 127 private String resolveBaseUri() { 128 final String baseURL = getBaseUrlProperty(); 129 if (baseURL.length() > 0) { 130 return baseURL; 131 } 132 return baseUri.toString(); 133 } 134 135 /** 136 * Produce a baseURL for JMS events using the system property fcrepo.jms.baseUrl of the form http[s]://host[:port], 137 * if it exists. 138 * 139 * @return String the base Url 140 */ 141 private String getBaseUrlProperty() { 142 final String propBaseURL = System.getProperty(JMS_BASEURL_PROP, ""); 143 if (propBaseURL.length() > 0 && propBaseURL.startsWith("http")) { 144 final URI propBaseUri = URI.create(propBaseURL); 145 if (propBaseUri.getPort() < 0) { 146 return JerseyUriBuilder.fromUri(baseUri).port(-1).uri(propBaseUri).toString(); 147 } 148 return JerseyUriBuilder.fromUri(baseUri).uri(propBaseUri).toString(); 149 } 150 return ""; 151 } 152 153}