/* * Copyright 2013 cruxframework.org. * * 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 org.cruxframework.crux.core.client.db; import java.util.logging.Level; import org.cruxframework.crux.core.client.collection.Array; import org.cruxframework.crux.core.client.collection.CollectionFactory; import org.cruxframework.crux.core.client.db.websql.SQLDatabase; import org.cruxframework.crux.core.client.db.websql.SQLError; import org.cruxframework.crux.core.client.db.websql.SQLTransaction; import com.google.gwt.logging.client.LogConfiguration; /** * A transaction on Crux Database, To create transactions, use one of {@link Database}'s getTransaction() method. * @author Thiago da Rosa de Bustamante * */ public class WSQLTransaction extends Transaction implements SQLDatabase.SQLTransactionCallback, SQLDatabase.SQLTransactionErrorCallback, SQLDatabase.SQLCallback { private SQLTransaction transaction; protected final WSQLAbstractDatabase db; private RequestProcessor requestProcessor; private boolean aborted; private boolean active; protected WSQLTransaction(final WSQLAbstractDatabase db, String[] storeNames, Mode mode) { super(db, storeNames, mode); this.db = db; this.active = true; this.aborted = false; this.transaction = null; this.requestProcessor = new RequestProcessor(this); if (db == null || !db.isOpen()) { throw new DatabaseException(db.messages.databaseNotOpenedError()); } if (mode != null && Mode.readOnly.equals(getMode())) { db.database.readTransaction(this, this, this); } else { db.database.transaction(this, this, this); } } public void onTransaction(SQLTransaction tx) { transaction = tx; requestProcessor.executeRequests(); } public void onError(SQLError error) { onError(error.getName() + " - " + error.getMessage()); } public void onError(String errorMessage) { String message = db.messages.databaseTransactionError(db.getName(), errorMessage); active = false; requestProcessor.stop(); if (LogConfiguration.loggingIsEnabled()) { logger.log(Level.SEVERE, message); } if (transactionCallback != null) { transactionCallback.onError(message); } } public void onSuccess() { if (LogConfiguration.loggingIsEnabled()) { logger.log(Level.INFO, db.messages.databaseTransactionCompleted(db.getName())); } active = false; requestProcessor.stop(); if (transactionCallback != null) { try { transactionCallback.onComplete(); } catch (Exception e) { String message = db.messages.databaseTransactionError(db.getName(), e.getMessage()); reportError(transactionCallback, message, e); } } } /** * Retrieve an ObjectStore manipulated by the current transaction. * @param <K> * @param <V> * @param storeName * @return */ @Override public <K, V> ObjectStore<K, V> getObjectStore(String storeName) { if (!containsObjectStore(storeName)) { onError(db.messages.databaseTransactionStoreNotFound(storeName)); return null; } WSQLAbstractObjectStore<K, V> objectStore = db.getObjectStore(storeName, this); return objectStore; } /** * Abort current transaction and rollback operations. */ @Override public void abort() { abort(true); } /** * Abort current transaction and rollback operations. * @param fireEvent */ public void abort(boolean fireEvent) { if (!aborted && active) { if (transaction != null) { transaction.executeSQL("invalid sql statement", null, null, new SQLTransaction.SQLStatementErrorCallback() { @Override public boolean onError(SQLTransaction tx, SQLError error) { return true;// tell web sql to rollback transaction } }); transaction = null; } aborted = true; active = false; requestProcessor.stop(); if (fireEvent) { if (LogConfiguration.loggingIsEnabled()) { logger.log(Level.INFO, db.messages.databaseTransactionAborted(db.getName())); } if (transactionCallback != null) { try { transactionCallback.onAbort(); } catch (Exception e) { String message = db.messages.databaseTransactionError(db.getName(), e.getMessage()); reportError(transactionCallback, message, e); } } } } } /** * Retrieve a specialized object store for files handling. * @return */ @Override public FileStore getFileStore() { if (!containsObjectStore(FileStore.OBJECT_STORE_NAME)) { onError(db.messages.databaseTransactionStoreNotFound(FileStore.OBJECT_STORE_NAME)); return null; } // return new WSQLFileStore(db, this); return null;//TODO terminar o fileStore } public void addRequest(RequestOperation operation, Mode[] supportedMode) { if (!active || aborted) { onError(db.messages.databaseTransactionInactive(db.getName())); return; } boolean supported = checkSupportedModes(supportedMode); if (!supported) { onError(db.messages.databaseTransactionNotSupportedOperation(db.getName())); return; } requestProcessor.addRequest(operation).executeRequests(); } public static abstract class RequestOperation { public abstract void doOperation(SQLTransaction tx); } private boolean checkSupportedModes(Mode[] supportedMode) { boolean supported = false; for (Mode mode : supportedMode) { if (mode.equals(getMode())) { supported = true; break; } } return supported; } static class RequestProcessor { private int currentOperationIdx = 0; private boolean processing = false; private Array<RequestOperation> requests; private WSQLTransaction wsqlTransaction; RequestProcessor(WSQLTransaction transaction) { requests = CollectionFactory.createArray(); wsqlTransaction = transaction; } public void stop() { processing = false; requests.clear(); wsqlTransaction = null; } public RequestProcessor addRequest(RequestOperation operation) { requests.add(operation); return this; } public void executeRequests() { if (!processing) { if (wsqlTransaction.transaction != null) { process(); } // else just wait for transaction creation, that will start request processor again. } } private void process() { if (processing) { return; } processing = true; while (currentOperationIdx < requests.size()) { RequestOperation operation = requests.get(currentOperationIdx++); operation.doOperation(wsqlTransaction.transaction); } processing = false; } } static interface Callback { void onSuccess(); } }