/** * Copyright (C) 2010-2017 Structr GmbH * * This file is part of Structr <http://structr.org>. * * Structr is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * Structr is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Structr. If not, see <http://www.gnu.org/licenses/>. */ package org.structr.net.data; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.structr.net.data.time.PseudoTemporalEnvironment; import org.structr.net.peer.Peer; import org.structr.net.protocol.AbstractMessage; import org.structr.net.protocol.Ack; import org.structr.net.protocol.BeginTx; import org.structr.net.protocol.Callback; import org.structr.net.protocol.Commit; import org.structr.net.protocol.Get; import org.structr.net.protocol.Set; import org.structr.net.protocol.Value; import org.structr.net.repository.RepositoryObject; public class RemoteTransaction implements Callback, AutoCloseable { private static final Logger logger = LoggerFactory.getLogger(RemoteTransaction.class.getName()); private PseudoTemporalEnvironment pte = null; private AbstractMessage message = null; private String transactionOwner = null; private final Object lock = new Object(); private String transactionId = null; private boolean hasTimeout = false; private long timeoutValue = 5000L; private Peer peer = null; public RemoteTransaction(final Peer peer) { this.pte = peer.getPseudoTemporalEnvironment().transaction(); this.peer = peer; } public Object getProperty(final RepositoryObject sharedObject, final String key) throws TimeoutException { final Get get = new Get(peer.getUuid(), sharedObject, pte.current(), transactionId, key); peer.registerCallback(get.getId(), this); peer.broadcast(get); waitForCallback(get.getId(), timeoutValue); if (message != null && message instanceof Value) { final Value value = (Value)message; return value.getValue(); } return null; } public void setProperty(final RepositoryObject sharedObject, final String key, final Object value) throws TimeoutException { peer.log("Set(", sharedObject.getUuid(), ", ", key, ", ", value, ")"); final Set set = new Set(peer.getUuid(), sharedObject, pte.next(), transactionId, key, value); peer.registerCallback(set.getId(), this); peer.broadcast(set); waitForCallback(set.getId(), timeoutValue); } public void begin(final RepositoryObject sharedObject) throws TimeoutException { this.transactionOwner = sharedObject.getDeviceId(); peer.log("BeginTx()"); final BeginTx beginTx = new BeginTx(peer.getUuid(), transactionOwner, timeoutValue); peer.registerCallback(beginTx.getId(), this); peer.broadcast(beginTx); waitForCallback(beginTx.getId(), timeoutValue); if (message != null && message instanceof Ack) { final Ack ack = (Ack)message; this.transactionId = ack.getData(); } } public void commit() throws TimeoutException { peer.log("Commit(", transactionId, ")"); final Commit commit = new Commit(peer.getUuid(), transactionOwner, transactionId); peer.registerCallback(commit.getId(), this); peer.broadcast(commit); waitForCallback(commit.getId(), timeoutValue); } @Override public void close() throws Exception { } public String getTransactionId() { return transactionId; } // ----- interface Callback ----- @Override public void callback(final AbstractMessage msg) { synchronized (lock) { hasTimeout = false; message = msg; lock.notify(); } } // ----- private methods ----- private void waitForCallback(final String uuid, final long timeout) throws TimeoutException { synchronized (lock) { this.message = null; this.hasTimeout = true; try { lock.wait(timeout); } catch (InterruptedException iex) { logger.warn("", iex); } // remove timed out callback if (this.hasTimeout) { peer.unregisterCallback(uuid); throw new TimeoutException(); } } } }