Package org.adbcj.postgresql.codec

Source Code of org.adbcj.postgresql.codec.AbstractConnection

/*
*   Copyright (c) 2007 Mike Heath.  All rights reserved.
*
*   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.adbcj.postgresql.codec;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicLong;

import org.adbcj.Connection;
import org.adbcj.DbException;
import org.adbcj.DbFuture;
import org.adbcj.DbSessionClosedException;
import org.adbcj.DbSessionFuture;
import org.adbcj.PreparedStatement;
import org.adbcj.Result;
import org.adbcj.ResultEventHandler;
import org.adbcj.postgresql.codec.frontend.AbstractFrontendMessage;
import org.adbcj.postgresql.codec.frontend.BindMessage;
import org.adbcj.postgresql.codec.frontend.DescribeMessage;
import org.adbcj.postgresql.codec.frontend.ExecuteMessage;
import org.adbcj.postgresql.codec.frontend.SimpleFrontendMessage;
import org.adbcj.postgresql.codec.frontend.ParseMessage;
import org.adbcj.support.AbstractDbSession;
import org.adbcj.support.DefaultDbFuture;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public abstract class AbstractConnection extends AbstractDbSession implements Connection {

  private final Logger logger = LoggerFactory.getLogger(AbstractConnection.class);

  private final AbstractConnectionManager connectionManager;
  private final ConnectionState connectionState;
  private Request<Void> closeRequest; // Access synchronized on lock

  private volatile int pid;
  private volatile int key;

  // Constant Messages
  private static final ExecuteMessage DEFAULT_EXECUTE = new ExecuteMessage();
  private static final BindMessage DEFAULT_BIND = new BindMessage();
  private static final DescribeMessage DEFAULT_DESCRIBE = DescribeMessage.createDescribePortalMessage(null);

  public AbstractConnection(AbstractConnectionManager connectionManager) {
    super(connectionManager.isPipeliningEnabled());
    this.connectionManager = connectionManager;
    this.connectionState = new ConnectionState(connectionManager.getDatabase());
  }

  public AbstractConnectionManager getConnectionManager() {
    return connectionManager;
  }

  public DbFuture<Void> ping() {
    // TODO Implement Postgresql ping
    throw new Error("ping() is not yet implemented");
  }

  @Override
  protected void checkClosed() {
    if (isClosed()) {
      throw new DbSessionClosedException(this, "This connection has been closed");
    }
  }

  public DbSessionFuture<Void> close(boolean immediate) throws DbException {
    // TODO AbstractConnection.finalizeClose(boolean) is almost identical to MySQL finalizeClose method, generify this

    // If the connection is already closed, return existing finalizeClose future
    synchronized (lock) {
      if (isClosed()) {
        if (closeRequest == null) {
          closeRequest = new Request<Void>() {
            @Override
            public void execute() throws Exception {
              // Do nothing since finalizeClose has already occurred
            }
            @Override
            public String toString() {
              return "Connection closed";
            }
          };
          closeRequest.setResult(null);
        }
      } else {
        if (immediate) {
          logger.debug("Executing immediate finalizeClose");
          // If the finalizeClose is immediate, cancel pending requests and send request to server
          cancelPendingRequests(true);
          write(SimpleFrontendMessage.TERMINATE);
          closeRequest = new Request<Void>() {
            @Override
            protected boolean cancelRequest(boolean mayInterruptIfRunning) {
              // Immediate finalizeClose can not be cancelled
              return false;
            }
            @Override
            public void execute() throws Exception {
              // Do nothing, finalizeClose message has already been sent
            }
            @Override
            public String toString() {
              return "Immediate finalizeClose";
            }
          };
        } else {
          // If the finalizeClose is NOT immediate, schedule the finalizeClose
          closeRequest = new Request<Void>() {
            @Override
            public boolean cancelRequest(boolean mayInterruptIfRunning) {
              logger.debug("Cancelling finalizeClose");
              unclose();
              return true;
            }
            @Override
            public void execute() {
              logger.debug("Sending TERMINATE to server (Request queue size: {})", requestQueue.size());
              write(SimpleFrontendMessage.TERMINATE);
            }
            @Override
            public boolean isPipelinable() {
              return false;
            }
            @Override
            public String toString() {
              return "Deferred finalizeClose";
            }
          };
          enqueueRequest(closeRequest);
        }
      }
      return closeRequest;
    }
  }

  private void unclose() {
    synchronized (lock) {
      logger.debug("Unclosing");
      this.closeRequest = null;
    }
  }

  public boolean isClosed() throws DbException {
    synchronized (lock) {
      return closeRequest != null || isConnectionClosing();
    }
  }

  public void finalizeClose() throws DbException {
    // TODO Make a DbSessionClosedException and use here
    errorPendingRequests(new DbException("Connection closed"));
    synchronized (lock) {
      if (closeRequest != null) {
        closeRequest.setResult(null);
      }
    }
  }

  public <T> DbSessionFuture<T> executeQuery(final String sql, ResultEventHandler<T> eventHandler, T accumulator) {
    checkClosed();
    Request<T> request = new Request<T>(eventHandler, accumulator) {
      @Override
      public void execute() throws Exception {
        logger.debug("Issuing query: {}", sql);

        ParseMessage parse = new ParseMessage(sql);
        write(new AbstractFrontendMessage[] {
          parse,
          DEFAULT_BIND,
          DEFAULT_DESCRIBE,
          DEFAULT_EXECUTE,
          SimpleFrontendMessage.SYNC,
        });
      }
      @Override
      public String toString() {
        return "SELECT request: " + sql;
      }
    };
    return enqueueTransactionalRequest(request);
  }

  public DbSessionFuture<Result> executeUpdate(final String sql) {
    checkClosed();
    return enqueueTransactionalRequest(new Request<Result>() {
      @Override
      public void execute() throws Exception {
        logger.debug("Issuing update query: {}", sql);

        ParseMessage parse = new ParseMessage(sql);
        write(new AbstractFrontendMessage[] {
          parse,
          DEFAULT_BIND,
          DEFAULT_DESCRIBE,
          DEFAULT_EXECUTE,
          SimpleFrontendMessage.SYNC
        });
      }

      @Override
      public String toString() {
        return "Update request: " + sql;
      }
    });
  }

  public DbSessionFuture<PreparedStatement> prepareStatement(String sql) {
    // TODO Implement prepareStatement
    throw new IllegalStateException();
  }

  public DbSessionFuture<PreparedStatement> prepareStatement(Object key, String sql) {
    // TODO Implement prepareStatement
    throw new IllegalStateException();
  }

  // ******** Transaction methods ***********************************************************************************

  private final AtomicLong statementCounter = new AtomicLong();
  private final Map<String, String> statementCache = Collections.synchronizedMap(new HashMap<String, String>());

  @Override
  protected void sendBegin() {
    executeStatement("BEGIN");
  }

  @Override
  protected void sendCommit() {
    executeStatement("COMMIT");
  }

  @Override
  protected void sendRollback() {
    executeStatement("ROLLBACK");
  }

  private void executeStatement(String statement) {
    String statementId = statementCache.get(statement);
    if (statementId == null) {
      long id = statementCounter.incrementAndGet();
      statementId = "S_" + id;

      ParseMessage parseMessage = new ParseMessage(statement, statementId);
      write(parseMessage);

      statementCache.put(statement, statementId);
    }
    write(new AbstractFrontendMessage[] {
        new BindMessage(statementId),
        DEFAULT_EXECUTE,
        SimpleFrontendMessage.SYNC
    });
  }

  // ================================================================================================================
  //
  // Non-API methods
  //
  // ================================================================================================================

  @Override
  protected <E> void enqueueRequest(Request<E> request) {
    super.enqueueRequest(request);
  }

  @Override
  public <E> Request<E> getActiveRequest() {
    return super.getActiveRequest();
  }

  public int getPid() {
    return pid;
  }

  public void setPid(int pid) {
    this.pid = pid;
  }

  public int getKey() {
    return key;
  }

  public void setKey(int key) {
    this.key = key;
  }

  public ConnectionState getConnectionState() {
    return connectionState;
  }

  // ================================================================================================================
  //
  // Abstract methods to be implemented by transport specific implementations
  //
  // ================================================================================================================

  protected abstract void write(AbstractFrontendMessage message);

  protected abstract void write(AbstractFrontendMessage[] messages);

  protected abstract boolean isConnectionClosing();

  public abstract DefaultDbFuture<Connection> getConnectFuture();

}
TOP

Related Classes of org.adbcj.postgresql.codec.AbstractConnection

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.