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 the given session
062     * @param userName the user name
063     */
064
065    public TransactionImpl(final Session session, final String userName) {
066        super();
067        this.session = session;
068        this.created = new Date();
069        this.id = randomUUID().toString();
070        this.expires = Calendar.getInstance();
071        this.updateExpiryDate();
072        this.userName = userName;
073    }
074
075    /* (non-Javadoc)
076     * @see org.fcrepo.kernel.Transaction#getSession()
077     */
078    @Override
079    public Session getSession() {
080        updateExpiryDate();
081        return TxAwareSession.newInstance(session, id);
082    }
083
084    /* (non-Javadoc)
085     * @see org.fcrepo.kernel.Transaction#getCreated()
086     */
087    @Override
088    public Date getCreated() {
089        return new Date(created.getTime());
090    }
091
092    /* (non-Javadoc)
093     * @see org.fcrepo.kernel.Transaction#getId()
094     */
095    @Override
096    public String getId() {
097        return id;
098    }
099
100    /* (non-Javadoc)
101     * @see org.fcrepo.kernel.Transaction#getState()
102     */
103    @Override
104    public State getState() throws RepositoryException {
105        if (this.session != null && this.session.hasPendingChanges()) {
106            return DIRTY;
107        }
108        return state;
109    }
110
111    /* (non-Javadoc)
112     * @see org.fcrepo.kernel.Transaction#getExpires()
113     */
114    @Override
115    public Date getExpires() {
116        return expires.getTime();
117    }
118
119    /* (non-Javadoc)
120     * @see org.fcrepo.kernel.Transaction#commit(org.fcrepo.kernel.services.VersionService)
121     */
122    @Override
123    public void commit() {
124
125        try {
126            this.session.save();
127            this.state = COMMITED;
128            this.expire();
129        } catch (final RepositoryException e) {
130            throw new RepositoryRuntimeException(e);
131        }
132    }
133
134    /* (non-Javadoc)
135     * @see org.fcrepo.kernel.Transaction#expire()
136     */
137    @Override
138    public void expire() {
139        this.session.logout();
140        this.expires.setTimeInMillis(currentTimeMillis());
141    }
142
143    /* (non-Javadoc)
144     * @see org.fcrepo.kernel.Transaction#isAssociatedWithUser()
145     */
146    @Override
147    public boolean isAssociatedWithUser(final String userName) {
148        boolean associatedWith = false;
149        if (this.userName == null) {
150            if (userName == null) {
151                associatedWith = true;
152            }
153        } else {
154            associatedWith = this.userName.equals(userName);
155        }
156        return associatedWith;
157    }
158
159    /* (non-Javadoc)
160     * @see org.fcrepo.kernel.Transaction#rollback()
161     */
162    @Override
163    public void rollback() {
164        this.state = ROLLED_BACK;
165        try {
166            this.session.refresh(false);
167        } catch (final RepositoryException e) {
168            throw new RepositoryRuntimeException(e);
169        }
170        this.expire();
171    }
172
173    /* (non-Javadoc)
174     * @see org.fcrepo.kernel.Transaction#updateExpiryDate()
175     */
176    @Override
177    public void updateExpiryDate() {
178        long duration;
179        if (System.getProperty(TIMEOUT_SYSTEM_PROPERTY) != null) {
180            duration =
181                    Long.parseLong(System.getProperty(TIMEOUT_SYSTEM_PROPERTY));
182        } else {
183            duration = DEFAULT_TIMEOUT;
184        }
185        this.expires.setTimeInMillis(currentTimeMillis() + duration);
186    }
187}