/* * Copyright 2007-2008 Konrad-Zuse-Zentrum für Informationstechnik Berlin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package de.zib.scalaris; import java.io.IOException; import java.util.ArrayList; import com.ericsson.otp.erlang.OtpAuthException; import com.ericsson.otp.erlang.OtpConnection; import com.ericsson.otp.erlang.OtpErlangAtom; import com.ericsson.otp.erlang.OtpErlangExit; import com.ericsson.otp.erlang.OtpErlangInt; import com.ericsson.otp.erlang.OtpErlangList; import com.ericsson.otp.erlang.OtpErlangLong; import com.ericsson.otp.erlang.OtpErlangObject; import com.ericsson.otp.erlang.OtpErlangString; import com.ericsson.otp.erlang.OtpErlangTuple; /** * Provides methods to read and write key/value pairs to a scalaris ring. * * <p> * Each operation is a single transaction. If you are looking for more * transactions, use the {@link Transaction} class instead. * </p> * * <p> * Instances of this class can be generated using a given connection to a * scalaris node using {@link #Scalaris(OtpConnection)} or without a * connection ({@link #Scalaris()}) in which case a new connection is * created using {@link ConnectionFactory#createConnection()}. * </p> * * <p> * There are two paradigms for reading and writing values: * <ul> * <li> using Java {@link String}s: {@link #read(String)}, {@link #write(String, String)} * <p>This is the safe way of accessing scalaris where type conversions * are handled by the API and the user doesn't have to worry about anything else.</p> * <p>Be aware though that this is not the most efficient way of handling strings!</p> * <li> using custom {@link OtpErlangObject}s: {@link #readObject(OtpErlangString)}, * {@link #writeObject(OtpErlangString, OtpErlangObject)} * <p>Here the user can specify custom behaviour and increase performance. * Handling the stored types correctly is at the user's hand.</p> * <p>An example using erlang objects to improve performance for inserting strings is * provided by {@link de.zib.scalaris.examples.CustomOtpFastStringObject} and can be * tested by {@link de.zib.scalaris.examples.FastStringBenchmark}.</p> * </ul> * </p> * * <h3>Reading values</h3> * <pre> * <code style="white-space:pre;"> * String key; * OtpErlangString otpKey; * * Scalaris sc = new Scalaris(); * String value = sc.read(key); // {@link #read(String)} * OtpErlangObject optValue = sc.readObject(otpKey); // {@link #readObject(OtpErlangString)} * </code> * </pre> * * <p>For the full example, see {@link de.zib.scalaris.examples.ScalarisReadExample}</p> * * <h3>Writing values</h3> * <pre> * <code style="white-space:pre;"> * String key; * String value; * OtpErlangString otpKey; * OtpErlangString otpValue; * * Scalaris sc = new Scalaris(); * sc.write(key, value); // {@link #write(String, String)} * sc.writeObject(otpKey, otpValue); // {@link #writeObject(OtpErlangString, OtpErlangObject)} * </code> * </pre> * * <p>For the full example, see {@link de.zib.scalaris.examples.ScalarisWriteExample}</p> * * <h3>Deleting values</h3> * <pre> * <code style="white-space:pre;"> * String key; * int timeout; * DeleteResult result; * * Scalaris sc = new Scalaris(); * sc.delete(key); // {@link #delete(String)} * sc.delete(key, timeout); // {@link #delete(String, int)} * result = sc.getLastDeleteResult(); // {@link #getLastDeleteResult()} * </code> * </pre> * * <h3>Publishing topics</h3> * <pre> * <code style="white-space:pre;"> * String topic; * String content; * OtpErlangString otpTopic; * OtpErlangString otpContent; * * Scalaris sc = new Scalaris(); * sc.publish(topic, content); // {@link #publish(String, String)} * sc.publish(otpTopic, otpContent); // {@link #publish(OtpErlangString, OtpErlangString)} * </code> * </pre> * * <p>For the full example, see {@link de.zib.scalaris.examples.ScalarisPublishExample}</p> * * <h3>Subscribing to topics</h3> * <pre> * <code style="white-space:pre;"> * String topic; * String URL; * OtpErlangString otpTopic; * OtpErlangString otpURL; * * Scalaris sc = new Scalaris(); * sc.subscribe(topic, URL); // {@link #subscribe(String, String)} * sc.subscribe(otpTopic, otpURL); // {@link #subscribe(OtpErlangString, OtpErlangString)} * </code> * </pre> * * <p>For the full example, see {@link de.zib.scalaris.examples.ScalarisSubscribeExample}</p> * * <h3>Unsubscribing from topics</h3> * * Unsubscribing from topics works like subscribing to topics with the exception * of a {@link NotFoundException} being thrown if either the topic does not * exist or the URL is not subscribed to the topic. * * <pre> * <code style="white-space:pre;"> * String topic; * String URL; * OtpErlangString otpTopic; * OtpErlangString otpURL; * * Scalaris sc = new Scalaris(); * sc.unsubscribe(topic, URL); // {@link #unsubscribe(String, String)} * sc.unsubscribe(otpTopic, otpURL); // {@link #unsubscribe(OtpErlangString, OtpErlangString)} * </code> * </pre> * * <h3>Getting a list of subscribers to a topic</h3> * <pre> * <code style="white-space:pre;"> * String topic; * OtpErlangString otpTopic; * * Vector<String> subscribers; * OtpErlangList otpSubscribers; * * // non-static: * Scalaris sc = new Scalaris(); * subscribers = sc.getSubscribers(topic); // {@link #getSubscribers(String)} * otpSubscribers = sc.singleGetSubscribers(otpTopic); // {@link #getSubscribers(OtpErlangString)} * </code> * </pre> * * <p>For the full example, see {@link de.zib.scalaris.examples.ScalarisGetSubscribersExample}</p> * * @author Nico Kruber, kruber@zib.de * @version 2.2 * @since 2.0 */ public class Scalaris { /** * the connection to a chorsharp node */ private OtpConnection connection; /** * Stores the result list returned by erlang during a delete operation. * * @see #delete(String) */ private OtpErlangList lastDeleteResult = null; /** * Constructor, uses the default connection returned by * {@link ConnectionFactory#createConnection()}. * * @throws ConnectionException * if the connection fails */ public Scalaris() throws ConnectionException { connection = ConnectionFactory.getInstance().createConnection(); } /** * Constructor, uses the given connection to an erlang node. * * @param conn * connection to use for the transaction * * @throws ConnectionException * if the connection fails */ public Scalaris(OtpConnection conn) throws ConnectionException { connection = conn; } // ///////////////////////////// // read methods // ///////////////////////////// /** * Gets the value stored under the given <tt>key</tt>. * * @param key * the key to look up * * @return the value stored under the given <tt>key</tt> * * @throws ConnectionException * if the connection is not active or a communication error * occurs or an exit signal was received or the remote node * sends a message containing an invalid cookie * @throws TimeoutException * if a timeout occurred while trying to fetch the value * @throws NotFoundException * if the requested key does not exist * @throws UnknownException * if any other error occurs */ public OtpErlangObject readObject(OtpErlangString key) throws ConnectionException, TimeoutException, UnknownException, NotFoundException { OtpErlangObject received_raw = null; try { connection.sendRPC("transstore.transaction_api", "quorum_read", new OtpErlangList(key)); received_raw = connection.receiveRPC(); OtpErlangTuple received = (OtpErlangTuple) received_raw; /* * possible return values: * - {Value, Version} * - {fail, fail} * - {fail, not_found} * - {fail, timeout} */ if (received.elementAt(0).equals(new OtpErlangAtom("fail"))) { OtpErlangObject reason = received.elementAt(1); if (reason.equals(new OtpErlangAtom("timeout"))) { throw new TimeoutException(received_raw); } else if (reason.equals(new OtpErlangAtom("not_found"))) { throw new NotFoundException(received_raw); } else { throw new UnknownException(received_raw); } } else { // return the value only, not the version: OtpErlangObject value = received.elementAt(0); return value; } } catch (OtpErlangExit e) { // e.printStackTrace(); throw new ConnectionException(e); } catch (OtpAuthException e) { // e.printStackTrace(); throw new ConnectionException(e); } catch (IOException e) { // e.printStackTrace(); throw new ConnectionException(e); } catch (ClassCastException e) { // e.printStackTrace(); // received_raw is not null since the first class cast is after the RPC! throw new UnknownException(e, received_raw); } } /** * Gets the value stored under the given <tt>key</tt>. * * @param key * the key to look up * * @return the (string) value stored under the given <tt>key</tt> * * @throws ConnectionException * if the connection is not active or a communication error * occurs or an exit signal was received or the remote node * sends a message containing an invalid cookie * @throws TimeoutException * if a timeout occurred while trying to fetch the value * @throws NotFoundException * if the requested key does not exist * @throws UnknownException * if any other error occurs * * @see #readObject(OtpErlangString) */ public String read(String key) throws ConnectionException, TimeoutException, UnknownException, NotFoundException { try { CustomOtpStringObject result = new CustomOtpStringObject(); readCustom(key, result); return result.getValue(); // return ((OtpErlangString) readObject(new OtpErlangString(key))) // .stringValue(); } catch (ClassCastException e) { // e.printStackTrace(); throw new UnknownException(e); } } /** * Gets the value stored under the given <tt>key</tt>. * * @param key * the key to look up * @param value * container that stores the value returned by scalaris * * @throws ConnectionException * if the connection is not active or a communication error * occurs or an exit signal was received or the remote node * sends a message containing an invalid cookie * @throws TimeoutException * if a timeout occurred while trying to fetch the value * @throws NotFoundException * if the requested key does not exist * @throws UnknownException * if any other error occurs * * @see #readObject(OtpErlangString) * @since 2.1 */ public void readCustom(String key, CustomOtpObject<?> value) throws ConnectionException, TimeoutException, UnknownException, NotFoundException { try { value.setOtpValue(readObject(new OtpErlangString(key))); } catch (ClassCastException e) { // e.printStackTrace(); throw new UnknownException(e); } } // ///////////////////////////// // write methods // ///////////////////////////// /** * Stores the given <tt>key</tt>/<tt>value</tt> pair. * * @param key * the key to store the value for * @param value * the value to store * * @throws ConnectionException * if the connection is not active or a communication error * occurs or an exit signal was received or the remote node * sends a message containing an invalid cookie * @throws TimeoutException * if a timeout occurred while trying to write the value * @throws UnknownException * if any other error occurs */ public void writeObject(OtpErlangString key, OtpErlangObject value) throws ConnectionException, TimeoutException, UnknownException { OtpErlangObject received_raw = null; try { connection.sendRPC("transstore.transaction_api", "single_write", new OtpErlangList(new OtpErlangObject[] { key, value })); received_raw = connection.receiveRPC(); OtpErlangObject received = received_raw; /* * possible return values: * - commit * - userabort * - {fail, not_found} * - {fail, timeout} * - {fail, fail} * - {fail, abort} */ if (received.equals(new OtpErlangAtom("commit"))) { return; } else if (received.equals(new OtpErlangAtom("userabort"))) { // throw new UnknownException("userabort"); throw new UnknownException(received_raw); } else { // {fail, Reason} OtpErlangTuple returnValue = (OtpErlangTuple) received; if (returnValue.elementAt(1).equals(new OtpErlangAtom("timeout"))) { throw new TimeoutException(received_raw); } else { throw new UnknownException(received_raw); } } } catch (OtpErlangExit e) { // e.printStackTrace(); throw new ConnectionException(e); } catch (OtpAuthException e) { // e.printStackTrace(); throw new ConnectionException(e); } catch (IOException e) { // e.printStackTrace(); throw new ConnectionException(e); } catch (ClassCastException e) { // e.printStackTrace(); // received_raw is not null since the first class cast is after the RPC! throw new UnknownException(e, received_raw); } } /** * Stores the given <tt>key</tt>/<tt>value</tt> pair. * * @param key * the key to store the value for * @param value * the value to store * * @throws ConnectionException * if the connection is not active or a communication error * occurs or an exit signal was received or the remote node * sends a message containing an invalid cookie * @throws TimeoutException * if a timeout occurred while trying to write the value * @throws UnknownException * if any other error occurs * * @see #writeObject(OtpErlangString, OtpErlangObject) */ public void write(String key, String value) throws ConnectionException, TimeoutException, UnknownException { writeCustom(key, new CustomOtpStringObject(value)); // writeObject(new OtpErlangString(key), new OtpErlangString(value)); } /** * Stores the given <tt>key</tt>/<tt>value</tt> pair. * * @param key * the key to store the value for * @param value * the value to store * * @throws ConnectionException * if the connection is not active or a communication error * occurs or an exit signal was received or the remote node * sends a message containing an invalid cookie * @throws TimeoutException * if a timeout occurred while trying to write the value * @throws UnknownException * if any other error occurs * * @see #writeObject(OtpErlangString, OtpErlangObject) * @since 2.1 */ public void writeCustom(String key, CustomOtpObject<?> value) throws ConnectionException, TimeoutException, UnknownException { writeObject(new OtpErlangString(key), value.getOtpValue()); } // ///////////////////////////// // publish methods // ///////////////////////////// /** * Publishes an event under a given <tt>topic</tt>. * * @param topic * the topic to publish the content under * @param content * the content to publish * * @throws ConnectionException * if the connection is not active or a communication error * occurs or an exit signal was received or the remote node * sends a message containing an invalid cookie */ public void publish(OtpErlangString topic, OtpErlangString content) throws ConnectionException { try { connection .sendRPC("pubsub.pubsub_api", "publish", new OtpErlangList( new OtpErlangObject[] { topic, content })); /** * The specification of <tt>pubsub.pubsub_api:publish/2</tt> states * that the only returned value is <tt>ok</tt>, so no further evaluation is * necessary. */ connection.receiveRPC(); // OtpErlangObject received = connection.receiveRPC(); } catch (OtpErlangExit e) { // e.printStackTrace(); throw new ConnectionException(e); } catch (OtpAuthException e) { // e.printStackTrace(); throw new ConnectionException(e); } catch (IOException e) { // e.printStackTrace(); throw new ConnectionException(e); } } /** * Publishes an event under a given <tt>topic</tt>. * * @param topic * the topic to publish the content under * @param content * the content to publish * * @throws ConnectionException * if the connection is not active or a communication error * occurs or an exit signal was received or the remote node * sends a message containing an invalid cookie */ public void publish(String topic, String content) throws ConnectionException { publish(new OtpErlangString(topic), new OtpErlangString(content)); } // ///////////////////////////// // subscribe methods // ///////////////////////////// /** * Subscribes a url to a <tt>topic</tt>. * * @param topic * the topic to subscribe the url to * @param url * the url of the subscriber (this is where the events are send * to) * * @throws ConnectionException * if the connection is not active or a communication error * occurs or an exit signal was received or the remote node * sends a message containing an invalid cookie * @throws TimeoutException * if a timeout occurred while trying to write the value * @throws UnknownException * if any other error occurs */ public void subscribe(OtpErlangString topic, OtpErlangString url) throws ConnectionException, TimeoutException, UnknownException { OtpErlangObject received_raw = null; try { connection.sendRPC("pubsub.pubsub_api", "subscribe", new OtpErlangList(new OtpErlangObject[] { topic, url })); received_raw = connection.receiveRPC(); OtpErlangObject received = received_raw; /* * possible return values: - ok - {fail, not_found} - {fail, * timeout} - {fail, fail} - {fail, abort} */ if (received.equals(new OtpErlangAtom("ok"))) { return; } else { // {fail, Reason} OtpErlangTuple returnValue = (OtpErlangTuple) received; if (returnValue.elementAt(1).equals(new OtpErlangAtom("timeout"))) { throw new TimeoutException(received_raw); } else { throw new UnknownException(received_raw); } } } catch (OtpErlangExit e) { // e.printStackTrace(); throw new ConnectionException(e); } catch (OtpAuthException e) { // e.printStackTrace(); throw new ConnectionException(e); } catch (IOException e) { // e.printStackTrace(); throw new ConnectionException(e); } catch (ClassCastException e) { // e.printStackTrace(); // received_raw is not null since the first class cast is after the RPC! throw new UnknownException(e, received_raw); } } /** * Subscribes a url to a <tt>topic</tt>. * * @param topic * the topic to subscribe the url to * @param url * the url of the subscriber (this is where the events are send * to) * * @throws ConnectionException * if the connection is not active or a communication error * occurs or an exit signal was received or the remote node * sends a message containing an invalid cookie * @throws TimeoutException * if a timeout occurred while trying to write the value * @throws UnknownException * if any other error occurs */ public void subscribe(String topic, String url) throws ConnectionException, TimeoutException, UnknownException { subscribe(new OtpErlangString(topic), new OtpErlangString(url)); } // ///////////////////////////// // unsubscribe methods // ///////////////////////////// /** * Unsubscribes a url from a <tt>topic</tt>. * * @param topic * the topic to unsubscribe the url from * @param url * the url of the subscriber * * @throws ConnectionException * if the connection is not active or a communication error * occurs or an exit signal was received or the remote node * sends a message containing an invalid cookie * @throws TimeoutException * if a timeout occurred while trying to write the value * @throws NotFoundException * if the topic does not exist or the given subscriber is not * subscribed to the given topic * @throws UnknownException * if any other error occurs */ public void unsubscribe(OtpErlangString topic, OtpErlangString url) throws ConnectionException, TimeoutException, NotFoundException, UnknownException { OtpErlangObject received_raw = null; try { connection.sendRPC("pubsub.pubsub_api", "unsubscribe", new OtpErlangList(new OtpErlangObject[] { topic, url })); received_raw = connection.receiveRPC(); OtpErlangObject received = received_raw; /* * possible return values: - ok - {fail, not_found} - {fail, * timeout} - {fail, fail} - {fail, abort} */ if (received.equals(new OtpErlangAtom("ok"))) { return; } else { // {fail, Reason} OtpErlangTuple returnValue = (OtpErlangTuple) received; if (returnValue.elementAt(1).equals(new OtpErlangAtom("timeout"))) { throw new TimeoutException(received_raw); } else if (returnValue.elementAt(1).equals(new OtpErlangAtom("not_found"))) { throw new NotFoundException(received_raw); } else { throw new UnknownException(received_raw); } } } catch (OtpErlangExit e) { // e.printStackTrace(); throw new ConnectionException(e); } catch (OtpAuthException e) { // e.printStackTrace(); throw new ConnectionException(e); } catch (IOException e) { // e.printStackTrace(); throw new ConnectionException(e); } catch (ClassCastException e) { // e.printStackTrace(); // received_raw is not null since the first class cast is after the RPC! throw new UnknownException(e, received_raw); } } /** * Unsubscribes a url from a <tt>topic</tt>. * * @param topic * the topic to unsubscribe the url from * @param url * the url of the subscriber * * @throws ConnectionException * if the connection is not active or a communication error * occurs or an exit signal was received or the remote node * sends a message containing an invalid cookie * @throws TimeoutException * if a timeout occurred while trying to write the value * @throws NotFoundException * if the topic does not exist or the given subscriber is not * subscribed to the given topic * @throws UnknownException * if any other error occurs */ public void unsubscribe(String topic, String url) throws ConnectionException, TimeoutException, NotFoundException, UnknownException { unsubscribe(new OtpErlangString(topic), new OtpErlangString(url)); } // ///////////////////////////// // utility methods // ///////////////////////////// /** * Converts the given erlang <tt>list</tt> of erlang strings to a Java {@link ArrayList}. */ private static ArrayList<String> erlStrListToStrArrayList(OtpErlangList list) { ArrayList<String> result = new ArrayList<String>(list.arity()); for (int i = 0; i < list.arity(); ++i) { OtpErlangString elem = (OtpErlangString) list.elementAt(i); result.add(elem.stringValue()); } return result; } // ///////////////////////////// // get subscribers methods // ///////////////////////////// /** * Gets a list of subscribers to a <tt>topic</tt>. * * @param topic * the topic to get the subscribers for * * @return the subscriber URLs * * @throws ConnectionException * if the connection is not active or a communication error * occurs or an exit signal was received or the remote node * sends a message containing an invalid cookie * @throws UnknownException * is thrown if the return type of the erlang method does not * match the expected one */ public OtpErlangList getSubscribers( OtpErlangString topic) throws ConnectionException, UnknownException { OtpErlangObject received_raw = null; try { connection.sendRPC("pubsub.pubsub_api", "get_subscribers", new OtpErlangList(topic)); // return value: [string,...] received_raw = connection.receiveRPC(); OtpErlangList received = (OtpErlangList) received_raw; return received; } catch (OtpErlangExit e) { // e.printStackTrace(); throw new ConnectionException(e); } catch (OtpAuthException e) { // e.printStackTrace(); throw new ConnectionException(e); } catch (IOException e) { // e.printStackTrace(); throw new ConnectionException(e); } catch (ClassCastException e) { // e.printStackTrace(); // received_raw is not null since the first class cast is after the RPC! throw new UnknownException(e, received_raw); } } /** * Gets a list of subscribers to a <tt>topic</tt>. * * @param topic * the topic to get the subscribers for * * @return the subscriber URLs * * @throws ConnectionException * if the connection is not active or a communication error * occurs or an exit signal was received or the remote node * sends a message containing an invalid cookie * @throws UnknownException * is thrown if the return type of the erlang method does not * match the expected one */ public ArrayList<String> getSubscribers( String topic) throws ConnectionException, UnknownException { return erlStrListToStrArrayList(getSubscribers(new OtpErlangString(topic))); } // ///////////////////////////// // delete methods // ///////////////////////////// /** * Tries to delete all replicas of the given <tt>key</tt> in 2000ms. * * @param key * the key to delete * * @return the number of successfully deleted replicas * * @throws ConnectionException * if the connection is not active or a communication error * occurs or an exit signal was received or the remote node * sends a message containing an invalid cookie * @throws TimeoutException * if a timeout occurred while trying to delete the value * @throws NodeNotFoundException * if no scalaris node was found * @throws UnknownException * if any other error occurs * * @since 2.2 * * @see #delete(String, int) */ public long delete(String key) throws ConnectionException, TimeoutException, UnknownException, NodeNotFoundException { return delete(key, 2000); } /** * Tries to delete all replicas of the given <tt>key</tt>. * * WARNING: This function can lead to inconsistent data (e.g. deleted items * can re-appear). Also when re-creating an item the version before the * delete can re-appear. * * @param key * the key to delete * @param timeout * the time (in milliseconds) to wait for results * * @return the number of successfully deleted replicas * * @throws ConnectionException * if the connection is not active or a communication error * occurs or an exit signal was received or the remote node * sends a message containing an invalid cookie * @throws TimeoutException * if a timeout occurred while trying to delete the value * @throws NodeNotFoundException * if no scalaris node was found * @throws UnknownException * if any other error occurs * * @since 2.2 * * @see #delete(String) */ public long delete(String key, int timeout) throws ConnectionException, TimeoutException, UnknownException, NodeNotFoundException { OtpErlangObject received_raw = null; try { connection.sendRPC("transstore.transaction_api", "delete", new OtpErlangList( new OtpErlangObject[] { new OtpErlangString(key), new OtpErlangInt(timeout) })); received_raw = connection.receiveRPC(); OtpErlangTuple received = (OtpErlangTuple) received_raw; /* * possible return values: * - {ok, pos_integer(), list()} * - {fail, timeout} * - {fail, timeout, pos_integer(), list()} * - {fail, node_not_found} */ if (received.elementAt(0).equals(new OtpErlangAtom("fail"))) { OtpErlangObject reason = received.elementAt(1); if (reason.equals(new OtpErlangAtom("timeout"))) { if (received.arity() > 2) { lastDeleteResult = (OtpErlangList) received.elementAt(3); } else { lastDeleteResult = null; } throw new TimeoutException(received_raw); } else if (reason.equals(new OtpErlangAtom("node_not_found"))) { lastDeleteResult = null; throw new NodeNotFoundException(received_raw); } else { lastDeleteResult = null; throw new UnknownException(received_raw); } } else { lastDeleteResult = (OtpErlangList) received.elementAt(2); long succeeded = ((OtpErlangLong) received.elementAt(1)).longValue(); return succeeded; } } catch (OtpErlangExit e) { // e.printStackTrace(); throw new ConnectionException(e); } catch (OtpAuthException e) { // e.printStackTrace(); throw new ConnectionException(e); } catch (IOException e) { // e.printStackTrace(); throw new ConnectionException(e); } catch (ClassCastException e) { // e.printStackTrace(); // received_raw is not null since the first class cast is after the RPC! throw new UnknownException(e, received_raw); } } /** * Returns the result of the last call to {@link #delete(String)}. * * NOTE: This function traverses the result list returned by erlang and * therefore takes some time to process. It is advised to store the returned * result object once generated. * * @return the delete result * * @throws UnknownException * is thrown if an unknown reason was encountered * * @see #delete(String) */ public DeleteResult getLastDeleteResult() throws UnknownException { try { return new DeleteResult(lastDeleteResult); } catch (UnknownException e) { throw new UnknownException(e, lastDeleteResult); } } /** * Closes the transaction's connection to a scalaris node. * * Note: Subsequent calls to the other methods will throw * {@link ConnectionException}s! */ public void closeConnection() { connection.close(); } }