Fix error handling in ResultSet#getRecords
[yaz4j-moved-to-github.git] / src / main / java / org / yaz4j / Connection.java
index 2c5fcf2..4accb99 100644 (file)
 package org.yaz4j;
 
+import org.yaz4j.exception.ZoomException;
 import org.yaz4j.jni.SWIGTYPE_p_ZOOM_connection_p;
 import org.yaz4j.jni.SWIGTYPE_p_ZOOM_query_p;
 import org.yaz4j.jni.SWIGTYPE_p_ZOOM_resultset_p;
 import org.yaz4j.jni.SWIGTYPE_p_ZOOM_scanset_p;
-import org.yaz4j.jni.SWIGTYPE_p_p_char;
 import org.yaz4j.jni.yaz4jlib;
-import org.yaz4j.jni.yaz4jlibConstants;
-
-import sun.reflect.generics.reflectiveObjects.NotImplementedException;
-
-public class Connection
-{
-       private String host ;
-       private int port ;
-       private ConnectionOptionsCollection options = null ;
-       protected SWIGTYPE_p_ZOOM_connection_p zoomConnection = null ;
-       private boolean connected = false;
-       private boolean disposed = false;
-       
-       static
-       {
-               // on Linux   'yaz4j' maps to 'libyaz4j.so' (i.e. 'lib' prefix & '.so'  extension)
-               // on Windows 'yaz4j' maps to 'yaz4j.dll'   (i.e.                '.dll' extension)              
-               String libName = "yaz4j" ;
-               try
-               {
-                       // System.err.println( "Loading library '"+ System.mapLibraryName( libName ) + "'" );
-                       System.loadLibrary( libName );
-               }
-               catch( Throwable e )
-               {
-                       System.err.println( "Fatal Error: Failed to load library '" + System.mapLibraryName( libName ) + "'");
-                       e.printStackTrace();
-               }               
-       }
-       
-       public Connection(String host, int port)
-       {
-               this.host = host ;
-               this.port = port ;
-
-               options = new ConnectionOptionsCollection();
-               zoomConnection = yaz4jlib.ZOOM_connection_create(options.zoomOptions);
-
-               SWIGTYPE_p_p_char cp = null;
-               SWIGTYPE_p_p_char addinfo = null ;              
-               int errorCode = yaz4jlib.ZOOM_connection_error(zoomConnection, cp, addinfo);
-               CheckErrorCodeAndThrow(errorCode);
-       }
-       
-       public void finalize()
-       {
-               Dispose();
-       }
-       
-       private void CheckErrorCodeAndThrow(int errorCode)
-       {
-               String message;
-               
-               if( errorCode == yaz4jlibConstants.ZOOM_ERROR_NONE )
-               {
-                       return ;
-               }
-               else if( errorCode == yaz4jlib.ZOOM_ERROR_CONNECT ) 
-               {
-                       message = String.format("Connection could not be made to %s:%d", host, port);
-                       throw new ConnectionUnavailableException(message);
-               }
-               else if( errorCode == yaz4jlib.ZOOM_ERROR_INVALID_QUERY )
-               {
-                       message = String.format("The query requested is not valid or not supported");
-                       throw new InvalidQueryException(message);
-               }
-               else if( errorCode == yaz4jlib.ZOOM_ERROR_INIT )
-               {
-                       message = String.format("Server %s:%d rejected our init request", host, port);
-                       throw new InitRejectedException(message);                       
-               }
-               else if( errorCode == yaz4jlib.ZOOM_ERROR_TIMEOUT )
-               {
-                       message = String.format("Server %s:%d timed out handling our request", host, port);
-                       throw new ConnectionTimeoutException(message);                  
-               }
-        else if(    ( errorCode == yaz4jlib.ZOOM_ERROR_MEMORY ) 
-                        || ( errorCode == yaz4jlib.ZOOM_ERROR_ENCODE )
-                                || ( errorCode == yaz4jlib.ZOOM_ERROR_DECODE )
-                        || ( errorCode == yaz4jlib.ZOOM_ERROR_CONNECTION_LOST )
-                        || ( errorCode == yaz4jlib.ZOOM_ERROR_INTERNAL )
-                        || ( errorCode == yaz4jlib.ZOOM_ERROR_UNSUPPORTED_PROTOCOL )
-                        || ( errorCode == yaz4jlib.ZOOM_ERROR_UNSUPPORTED_QUERY ) )                     
-        {
-                       message = yaz4jlib.ZOOM_connection_errmsg(zoomConnection);
-                       throw new ZoomImplementationException("A fatal error occurred in Yaz: " + errorCode + " - " + message);
-        }
-        else
-        {
-               String errMsgBib1 = "Bib1Exception: Error Code = " + errorCode + " (" + Bib1Diagnostic.GetError(errorCode) + ")" ; 
-                       throw new Bib1Exception( errMsgBib1 );
-        }
-       }
-
-       private enum QueryType { CQLQuery, PrefixQuery };
-       
-       public ResultSet Search(PrefixQuery query)
-       {
-               return Search( query.getQueryString(), QueryType.PrefixQuery);
-       }
-       
-       public ResultSet Search(CQLQuery query)
-       {
-               return Search( query.getQueryString(), QueryType.CQLQuery);
-       }
-       
-       private ResultSet Search(String query, QueryType queryType)
-       {       
-               EnsureConnected();
-
-               SWIGTYPE_p_ZOOM_query_p yazQuery = yaz4jlib.ZOOM_query_create();
-        ResultSet resultSet = null;
-
-        try
-        {
-               if( queryType == QueryType.CQLQuery )
-                       yaz4jlib.ZOOM_query_cql(yazQuery, query);
-               else if( queryType == QueryType.PrefixQuery )
-                       yaz4jlib.ZOOM_query_prefix(yazQuery, query);
-               else
-                       throw new NotImplementedException();
-               
-               SWIGTYPE_p_ZOOM_resultset_p yazResultSet = yaz4jlib.ZOOM_connection_search(zoomConnection, yazQuery);
-            
-            int errorCode = yaz4jlib.ZOOM_connection_errcode( zoomConnection );
-            if (errorCode != yaz4jlib.ZOOM_ERROR_NONE)
-            {
-               yaz4jlib.ZOOM_resultset_destroy(yazResultSet);
-            }
-            CheckErrorCodeAndThrow(errorCode);
-
-            resultSet = new ResultSet(yazResultSet, zoomConnection);
-        }
-        finally
-        {
-               yaz4jlib.ZOOM_query_destroy(yazQuery); // deallocate yazQuery also when exceptions
-            yazQuery = null;
-        }
-        return resultSet;
-       }
-       
-       public ScanSet Scan(String query)
-       {
-               EnsureConnected();
-               SWIGTYPE_p_ZOOM_scanset_p yazScanSet = yaz4jlib.ZOOM_connection_scan(zoomConnection, query);
-
-               int errorCode = yaz4jlib.ZOOM_connection_errcode(zoomConnection);
-               if( errorCode != yaz4jlib.ZOOM_ERROR_NONE )
-               {
-                       yaz4jlib.ZOOM_scanset_destroy(yazScanSet);
-               }
-               CheckErrorCodeAndThrow(errorCode);
-
-               ScanSet scanSet = new ScanSet(yazScanSet, this);
-               return scanSet;
-       }
-       
-       public ConnectionOptionsCollection getOptions()
-       {
-               return options;
-       }
-
-       protected void EnsureConnected()
-       {
-               if (! connected )
-                       Connect();
-       }
-       
-       public void Connect()
-       {
-               yaz4jlib.ZOOM_connection_connect( zoomConnection, host, port);
-               int errorCode = yaz4jlib.ZOOM_connection_errcode(zoomConnection);
-               CheckErrorCodeAndThrow(errorCode);
-               connected = true;
-       }
-       
-       public void Dispose()
-       {
-               if (! disposed )
-               {
-                       yaz4jlib.ZOOM_connection_destroy(zoomConnection);
-                       zoomConnection = null;
-                       disposed = true;
-               }
-       }
-
-       public String getSyntax()
-       {
-               return options.get("preferredRecordSyntax");
-       }
-       
-       public void setSyntax( String value)
-       {
-               options.set("preferredRecordSyntax", value ) ;
-       }
-
-       public String getDatabaseName()
-       {
-               return options.get("databaseName");
-       }
-       
-       public void setDatabaseName( String value )
-       {
-               options.set("databaseName", value);
-       }
-
-       public String getUsername()
-       {
-               return options.get("user");
-       }
-       
-       public void setUsername( String value )
-       {
-               options.set("user", value);
-       }
-
-       public String getPassword()
-       {
-               return options.get("password");
-       }
-       
-       public void setPassword( String value )
-       {
-               options.set("password", value);
-       }
+
+/**
+ * Class representing an on-going communication with an IR server.
+ *
+ * Creating an instance of this class does not automatically connect (e.g open
+ * a socket) to the remote server as the programmer may want to specify options
+ * on the object before establishing the actual connection.
+ *
+ * The work-flow for synchronous (the only addressed) operation when using this
+ * class should be as follows (in pseudocode):
+ *
+ * <blockquote><pre>
+ *
+ * try {
+ *  c = new Connection(...)
+ *  //possibly set some options
+ *  c.connect //establishes connection
+ *  c.search //or other operation
+ *  //possibly retrieve records
+ * catch (ZoomException e) {
+ *  //handle any protocol- or network-level errors
+ * } finally {
+ *  c.close //close the socket
+ * }
+ *
+ * </pre></blockquote>
+ * @see <a href="http://www.indexdata.com/yaz/doc/zoom.html#zoom-connections">YAZ ZOOM Connection</a>
+ * @author jakub
+ */
+public class Connection {
+
+  private String host;
+  private int port;
+  protected SWIGTYPE_p_ZOOM_connection_p zoomConnection;
+  //connection is initially closed
+  protected boolean closed = true;
+  private boolean disposed = false;
+
+  public enum QueryType {
+
+    CQLQuery, PrefixQuery
+  };
+
+  static {
+    // on Linux   'yaz4j' maps to 'libyaz4j.so' (i.e. 'lib' prefix & '.so'  extension)
+    // on Windows 'yaz4j' maps to 'yaz4j.dll'   (i.e.                '.dll' extension)
+    String libName = "yaz4j";
+    System.loadLibrary(libName);
+  }
+
+  /**
+   * Create new connection object without physically opening a connection to the
+   * remote server.
+   * @param host host name of the server
+   * @param port port of the server
+   */
+  public Connection(String host, int port) {
+    this.host = host;
+    this.port = port;
+    zoomConnection = yaz4jlib.ZOOM_connection_create(null);
+  }
+
+  public void finalize() {
+    _dispose();
+  }
+
+  /**
+   * Performs a search operation (submits the query to the server, waits for
+   * response and creates a new result set that allows to retrieve particular
+   * results)
+   * @deprecated Does not allow specifying sort criteria prior to search
+   * use {@link #search(org.yaz4j.Query) search(Query)} instead.
+   * @param query search query
+   * @param queryType type of the query (e.g RPN. CQL)
+   * @return result set containing records (hits)
+   * @throws ZoomException protocol or network-level error
+   */
+  @Deprecated
+  public ResultSet search(String query, QueryType queryType) throws
+    ZoomException {
+    if (closed) {
+      throw new IllegalStateException("Connection is closed.");
+    }
+    SWIGTYPE_p_ZOOM_query_p yazQuery = null;
+    if (queryType == QueryType.CQLQuery) {
+      yazQuery = yaz4jlib.ZOOM_query_create();
+      yaz4jlib.ZOOM_query_cql(yazQuery, query);
+    } else if (queryType == QueryType.PrefixQuery) {
+      yazQuery = yaz4jlib.ZOOM_query_create();
+      yaz4jlib.ZOOM_query_prefix(yazQuery, query);
+    }
+    SWIGTYPE_p_ZOOM_resultset_p yazResultSet = yaz4jlib.ZOOM_connection_search(
+      zoomConnection, yazQuery);
+    yaz4jlib.ZOOM_query_destroy(yazQuery);
+    ZoomException err = ExceptionUtil.getError(zoomConnection, host,
+      port);
+    if (err != null) {
+      yaz4jlib.ZOOM_resultset_destroy(yazResultSet);
+      throw err;
+    }
+    return new ResultSet(yazResultSet, this);
+  }
+  
+    /**
+   * Performs a search operation (submits the query to the server, waits for
+   * response and creates a new result set that allows to retrieve particular
+   * results). Sort criteria may be specified prior to the search, directly
+   * on the query object.
+   * @param query search query of any type supported by YAZ.
+   * @return result set containing records (hits)
+   * @throws ZoomException protocol or network-level error
+   */
+  public ResultSet search(Query query) throws ZoomException {
+    if (closed) {
+      throw new IllegalStateException("Connection is closed.");
+    }
+    SWIGTYPE_p_ZOOM_resultset_p yazResultSet = yaz4jlib.ZOOM_connection_search(
+      zoomConnection, query.query);
+    ZoomException err = ExceptionUtil.getError(zoomConnection, host,
+      port);
+    if (err != null) {
+      yaz4jlib.ZOOM_resultset_destroy(yazResultSet);
+      throw err;
+    }
+    return new ResultSet(yazResultSet, this);
+  }
+
+  /**
+   * Performs a scan operation (obtains a list of candidate search terms against
+   * a particular access point). 
+   * @deprecated Only allows PQF scan queries, use {@link #scan(org.yaz4j.Query) scan(Query)} instead
+   * @param query query for scanning
+   * @return a scan set with the terms
+   * @throws ZoomException a protocol or network-level error
+   */
+  @Deprecated
+  public ScanSet scan(String query) throws ZoomException {
+    if (closed) {
+      throw new IllegalStateException("Connection is closed.");
+    }
+    SWIGTYPE_p_ZOOM_scanset_p yazScanSet = yaz4jlib.ZOOM_connection_scan(
+      zoomConnection, query);
+    ZoomException err = ExceptionUtil.getError(zoomConnection, host, port);
+    if (err != null) {
+      yaz4jlib.ZOOM_scanset_destroy(yazScanSet);
+      throw err;
+    }
+    ScanSet scanSet = new ScanSet(yazScanSet, this);
+    return scanSet;
+  }
+  
+    /**
+   * Performs a scan operation (obtains a list of candidate search terms against
+   * a particular access point). Allows to use both CQL and PQF for Scan.
+   * @see <a href="http://www.indexdata.com/yaz/doc/zoom.scan.html">ZOOM-API Scan</a>
+   * @param query scan query of type supported by YAZ
+   * @return a scan set with the terms
+   * @throws ZoomException a protocol or network-level error
+   */
+  public ScanSet scan(Query query) throws ZoomException {
+    if (closed) {
+      throw new IllegalStateException("Connection is closed.");
+    }
+    SWIGTYPE_p_ZOOM_scanset_p yazScanSet = yaz4jlib.ZOOM_connection_scan1(
+      zoomConnection, query.query);
+    ZoomException err = ExceptionUtil.getError(zoomConnection, host, port);
+    if (err != null) {
+      yaz4jlib.ZOOM_scanset_destroy(yazScanSet);
+      throw err;
+    }
+    ScanSet scanSet = new ScanSet(yazScanSet, this);
+    return scanSet;
+  }
+
+  /**
+   * Establishes a connection to the remote server.
+   * @throws ZoomException any (possibly network-level) errors that may occurr
+   */
+  public void connect() throws ZoomException {
+    yaz4jlib.ZOOM_connection_connect(zoomConnection, host, port);
+    ZoomException err = ExceptionUtil.getError(zoomConnection, host, port);
+    if (err != null) {
+      throw err;
+    }
+    closed = false;
+  }
+
+  /**
+   * Closes the connection.
+   */
+  public void close() {
+    yaz4jlib.ZOOM_connection_close(zoomConnection);
+    closed = true;
+  }
+  
+  /**
+   * Return exception type from current connection
+   *
+   * @return null if no error
+   */
+  ZoomException getZoomException() {
+    ZoomException err = ExceptionUtil.getError(zoomConnection, host, port);
+    return err;
+  }
+
+  /**
+   * Write option with a given name.
+   * @param name option name
+   * @param value option value
+   * @return connection (self) for chainability
+   */
+  public Connection option(String name, String value) {
+    yaz4jlib.ZOOM_connection_option_set(zoomConnection, name, value);
+    return this;
+  }
+
+  /**
+   * Read option with a given name
+   * @param name option name
+   * @return option value
+   */
+  public String option(String name) {
+    return yaz4jlib.ZOOM_connection_option_get(zoomConnection, name);
+  }
+
+  /**
+   * Same as option("preferredRecordSyntax")
+   * @return value of preferred record syntax
+   */
+  public String getSyntax() {
+    return option("preferredRecordSyntax");
+  }
+
+  /**
+   * Same as option("preferredRecordSyntax", value)
+   * @param value value of preferred record syntax
+   */
+  public void setSyntax(String value) {
+    option("preferredRecordSyntax", value);
+  }
+
+  /**
+   * Same as option("databaseName")
+   * @return value of databaseName
+   */
+  public String getDatabaseName() {
+    return option("databaseName");
+  }
+
+  /**
+   * Same as option("databaseName", value)
+   * @param value value of databaseName
+   */
+  public void setDatabaseName(String value) {
+    option("databaseName", value);
+  }
+
+  /**
+   * Same as option("user")
+   * @return value of user
+   */
+  public String getUsername() {
+    return option("user");
+  }
+
+  /**
+   * Same as option("user", value)
+   * @param value value of user
+   */
+  public void setUsername(String value) {
+    option("user", value);
+  }
+
+  /**
+   * Same as option("password")
+   * @return value of password
+   */
+  public String getPassword() {
+    return option("password");
+  }
+
+  /**
+   * Same as option("password", value)
+   * @param value
+   */
+  public void setPassword(String value) {
+    option("password", value);
+  }
+
+  /**
+   * INTERNAL, GC-ONLY
+   */
+  void _dispose() {
+    if (!disposed) {
+      yaz4jlib.ZOOM_connection_destroy(zoomConnection);
+      zoomConnection = null;
+      disposed = true;
+    }
+  }
 }