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.kernel.impl;
017
018import static java.lang.System.currentTimeMillis;
019import static java.util.UUID.randomUUID;
020import static org.fcrepo.kernel.Transaction.State.COMMITED;
021import static org.fcrepo.kernel.Transaction.State.DIRTY;
022import static org.fcrepo.kernel.Transaction.State.NEW;
023import static org.fcrepo.kernel.Transaction.State.ROLLED_BACK;
024
025import java.util.Calendar;
026import java.util.Date;
027
028import javax.jcr.RepositoryException;
029import javax.jcr.Session;
030
031import org.fcrepo.kernel.Transaction;
032import org.fcrepo.kernel.exception.RepositoryRuntimeException;
033
034/**
035 * A Fedora Transaction wraps a JCR session with some expiration logic.
036 * Whenever the transaction's session is requested, the expiration is extended
037 *
038 * @author bbpennel
039 */
040public class TransactionImpl implements Transaction {
041
042    // the default timeout is 3 minutes
043    public static final long DEFAULT_TIMEOUT = 3L * 60L * 1000L;
044
045    public static final String TIMEOUT_SYSTEM_PROPERTY = "fcrepo.transactions.timeout";
046
047    private final Session session;
048
049    private final String id;
050
051    private final String userName;
052
053    private final Date created;
054
055    private final Calendar expires;
056
057    private State state = NEW;
058
059    /**
060     * Create a transaction for the given Session
061     * @param session
062     */
063
064    public TransactionImpl(final Session session, final String userName) {
065        super();
066        this.session = session;
067        this.created = new Date();
068        this.id = randomUUID().toString();
069        this.expires = Calendar.getInstance();
070        this.updateExpiryDate();
071        this.userName = userName;
072    }
073
074    /* (non-Javadoc)
075     * @see org.fcrepo.kernel.Transaction#getSession()
076     */
077    @Override
078    public Session getSession() {
079        updateExpiryDate();
080        return TxAwareSession.newInstance(session, id);
081    }
082
083    /* (non-Javadoc)
084     * @see org.fcrepo.kernel.Transaction#getCreated()
085     */
086    @Override
087    public Date getCreated() {
088        return new Date(created.getTime());
089    }
090
091    /* (non-Javadoc)
092     * @see org.fcrepo.kernel.Transaction#getId()
093     */
094    @Override
095    public String getId() {
096        return id;
097    }
098
099    /* (non-Javadoc)
100     * @see org.fcrepo.kernel.Transaction#getState()
101     */
102    @Override
103    public State getState() throws RepositoryException {
104        if (this.session != null && this.session.hasPendingChanges()) {
105            return DIRTY;
106        }
107        return state;
108    }
109
110    /* (non-Javadoc)
111     * @see org.fcrepo.kernel.Transaction#getExpires()
112     */
113    @Override
114    public Date getExpires() {
115        return expires.getTime();
116    }
117
118    /* (non-Javadoc)
119     * @see org.fcrepo.kernel.Transaction#commit(org.fcrepo.kernel.services.VersionService)
120     */
121    @Override
122    public void commit() {
123
124        try {
125            this.session.save();
126            this.state = COMMITED;
127            this.expire();
128        } catch (final RepositoryException e) {
129            throw new RepositoryRuntimeException(e);
130        }
131    }
132
133    /* (non-Javadoc)
134     * @see org.fcrepo.kernel.Transaction#expire()
135     */
136    @Override
137    public void expire() {
138        this.session.logout();
139        this.expires.setTimeInMillis(currentTimeMillis());
140    }
141
142    /* (non-Javadoc)
143     * @see org.fcrepo.kernel.Transaction#isAssociatedWithUser()
144     */
145    @Override
146    public boolean isAssociatedWithUser(final String userName) {
147        boolean associatedWith = false;
148        if (this.userName == null) {
149            if (userName == null) {
150                associatedWith = true;
151            }
152        } else {
153            associatedWith = this.userName.equals(userName);
154        }
155        return associatedWith;
156    }
157
158    /* (non-Javadoc)
159     * @see org.fcrepo.kernel.Transaction#rollback()
160     */
161    @Override
162    public void rollback() {
163        this.state = ROLLED_BACK;
164        try {
165            this.session.refresh(false);
166        } catch (final RepositoryException e) {
167            throw new RepositoryRuntimeException(e);
168        }
169        this.expire();
170    }
171
172    /* (non-Javadoc)
173     * @see org.fcrepo.kernel.Transaction#updateExpiryDate()
174     */
175    @Override
176    public void updateExpiryDate() {
177        long duration;
178        if (System.getProperty(TIMEOUT_SYSTEM_PROPERTY) != null) {
179            duration =
180                    Long.parseLong(System.getProperty(TIMEOUT_SYSTEM_PROPERTY));
181        } else {
182            duration = DEFAULT_TIMEOUT;
183        }
184        this.expires.setTimeInMillis(currentTimeMillis() + duration);
185    }
186}