JDBC implementation of the MDR storage contributed by J. Sichi integrated BLD200402042045
authormmatula@netbeans.org
Wed, 04 Feb 2004 06:38:10 +0000
changeset 14358715f9f88ff0
parent 1434 4053459d5520
child 1436 acf20fdf455f
JDBC implementation of the MDR storage contributed by J. Sichi integrated
mdr/extras/jdbcstorage/README
mdr/extras/jdbcstorage/src/org/netbeans/mdr/persistence/jdbcimpl/JdbcCollection.java
mdr/extras/jdbcstorage/src/org/netbeans/mdr/persistence/jdbcimpl/JdbcIndex.java
mdr/extras/jdbcstorage/src/org/netbeans/mdr/persistence/jdbcimpl/JdbcMultivaluedIndex.java
mdr/extras/jdbcstorage/src/org/netbeans/mdr/persistence/jdbcimpl/JdbcMultivaluedOrderedIndex.java
mdr/extras/jdbcstorage/src/org/netbeans/mdr/persistence/jdbcimpl/JdbcPrimaryIndex.java
mdr/extras/jdbcstorage/src/org/netbeans/mdr/persistence/jdbcimpl/JdbcSet.java
mdr/extras/jdbcstorage/src/org/netbeans/mdr/persistence/jdbcimpl/JdbcSinglevaluedIndex.java
mdr/extras/jdbcstorage/src/org/netbeans/mdr/persistence/jdbcimpl/JdbcStorage.java
mdr/extras/jdbcstorage/src/org/netbeans/mdr/persistence/jdbcimpl/JdbcStorageException.java
mdr/extras/jdbcstorage/src/org/netbeans/mdr/persistence/jdbcimpl/JdbcStorageFactory.java
mdr/extras/jdbcstorage/src/org/netbeans/mdr/persistence/jdbcimpl/LazyPreparedStatement.java
mdr/extras/jdbcstorage/src/org/netbeans/mdr/persistence/jdbcimpl/package.html
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/mdr/extras/jdbcstorage/README	Wed Feb 04 06:38:10 2004 +0000
     1.3 @@ -0,0 +1,22 @@
     1.4 +JdbcStorage for MDR
     1.5 +
     1.6 +----------------------------------------------------------------------
     1.7 +
     1.8 +/*
     1.9 + *                 Sun Public License Notice
    1.10 + * 
    1.11 + * The contents of this file are subject to the Sun Public License
    1.12 + * Version 1.0 (the "License"). You may not use this file except in
    1.13 + * compliance with the License. A copy of the License is available at
    1.14 + * http://www.sun.com/
    1.15 + * 
    1.16 + * The Original Code is NetBeans. The Initial Developer of the Original
    1.17 + * Code is Sun Microsystems, Inc. Portions Copyright 1997-2001 Sun
    1.18 + * Microsystems, Inc. All Rights Reserved.
    1.19 + */
    1.20 +
    1.21 +----------------------------------------------------------------------
    1.22 +
    1.23 +Please see org/netbeans/mdr/persistence/jdbcimpl/package.html for an
    1.24 +overview.  Send all questions/comments to John Sichi
    1.25 +(jsichi@yahoo.com).
     2.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     2.2 +++ b/mdr/extras/jdbcstorage/src/org/netbeans/mdr/persistence/jdbcimpl/JdbcCollection.java	Wed Feb 04 06:38:10 2004 +0000
     2.3 @@ -0,0 +1,59 @@
     2.4 +/*
     2.5 + *                 Sun Public License Notice
     2.6 + * 
     2.7 + * The contents of this file are subject to the Sun Public License
     2.8 + * Version 1.0 (the "License"). You may not use this file except in
     2.9 + * compliance with the License. A copy of the License is available at
    2.10 + * http://www.sun.com/
    2.11 + * 
    2.12 + * The Original Code is NetBeans. The Initial Developer of the Original
    2.13 + * Code is Sun Microsystems, Inc. Portions Copyright 1997-2001 Sun
    2.14 + * Microsystems, Inc. All Rights Reserved.
    2.15 + */
    2.16 +package org.netbeans.mdr.persistence.jdbcimpl;
    2.17 +
    2.18 +import org.netbeans.mdr.persistence.*;
    2.19 +
    2.20 +import java.util.*;
    2.21 +
    2.22 +/**
    2.23 + * JdbcCollection is a read-only Collection backed by queries.
    2.24 + *
    2.25 + * @author John V. Sichi
    2.26 + * @version $Id$
    2.27 + */
    2.28 +class JdbcCollection extends AbstractCollection
    2.29 +{
    2.30 +    // delegate to JdbcSet for everything
    2.31 +    private JdbcSet set;
    2.32 +
    2.33 +    JdbcCollection(
    2.34 +        JdbcStorage storage,
    2.35 +        Storage.EntryType entryType,
    2.36 +        LazyPreparedStatement sqlIterator,
    2.37 +        LazyPreparedStatement sqlSize,
    2.38 +        LazyPreparedStatement sqlContains)
    2.39 +    {
    2.40 +        set = new JdbcSet(storage,entryType,sqlIterator,sqlSize,sqlContains);
    2.41 +    }
    2.42 +
    2.43 +    // override AbstractSet
    2.44 +    public boolean contains(Object obj)
    2.45 +    {
    2.46 +        return set.contains(obj);
    2.47 +    }
    2.48 +    
    2.49 +    // implement AbstractCollection
    2.50 +    public Iterator iterator()
    2.51 +    {
    2.52 +        return set.iterator();
    2.53 +    }
    2.54 +
    2.55 +    // implement AbstractCollection
    2.56 +    public int size()
    2.57 +    {
    2.58 +        return set.size();
    2.59 +    }
    2.60 +}
    2.61 +
    2.62 +// End JdbcCollection.java
     3.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     3.2 +++ b/mdr/extras/jdbcstorage/src/org/netbeans/mdr/persistence/jdbcimpl/JdbcIndex.java	Wed Feb 04 06:38:10 2004 +0000
     3.3 @@ -0,0 +1,162 @@
     3.4 +/*
     3.5 + *                 Sun Public License Notice
     3.6 + * 
     3.7 + * The contents of this file are subject to the Sun Public License
     3.8 + * Version 1.0 (the "License"). You may not use this file except in
     3.9 + * compliance with the License. A copy of the License is available at
    3.10 + * http://www.sun.com/
    3.11 + * 
    3.12 + * The Original Code is NetBeans. The Initial Developer of the Original
    3.13 + * Code is Sun Microsystems, Inc. Portions Copyright 1997-2001 Sun
    3.14 + * Microsystems, Inc. All Rights Reserved.
    3.15 + */
    3.16 +package org.netbeans.mdr.persistence.jdbcimpl;
    3.17 +
    3.18 +import org.netbeans.mdr.persistence.*;
    3.19 +import org.netbeans.mdr.util.*;
    3.20 +
    3.21 +import java.util.*;
    3.22 +import java.io.*;
    3.23 +
    3.24 +/**
    3.25 + * JdbcIndex implements the MDR Index interface using JDBC.
    3.26 + *
    3.27 + * @author John V. Sichi
    3.28 + * @version $Id$
    3.29 + */
    3.30 +abstract class JdbcIndex implements Index
    3.31 +{
    3.32 +    protected JdbcStorage storage;
    3.33 +    protected String tableName;
    3.34 +    protected String name;
    3.35 +    protected String keyColName;
    3.36 +    protected String valColName;
    3.37 +    protected Storage.EntryType keyType;
    3.38 +    protected Storage.EntryType valueType;
    3.39 +    protected boolean needSurrogate;
    3.40 +
    3.41 +    protected LazyPreparedStatement sqlKeySetIterator;
    3.42 +    protected LazyPreparedStatement sqlKeySetSize;
    3.43 +    protected LazyPreparedStatement sqlKeySetContains;
    3.44 +    protected LazyPreparedStatement sqlInsert;
    3.45 +    protected LazyPreparedStatement sqlDelete;
    3.46 +    protected LazyPreparedStatement sqlFind;
    3.47 +
    3.48 +    void init(
    3.49 +        JdbcStorage storage,
    3.50 +        String tableName,
    3.51 +        String name,
    3.52 +        String keyColName,
    3.53 +        String valColName,
    3.54 +        Storage.EntryType keyType,
    3.55 +        Storage.EntryType valueType,
    3.56 +        boolean needSurrogate)
    3.57 +    {
    3.58 +        this.storage = storage;
    3.59 +        this.tableName = tableName;
    3.60 +        this.name = name;
    3.61 +        this.keyColName = keyColName;
    3.62 +        this.valColName = valColName;
    3.63 +        this.keyType = keyType;
    3.64 +        this.valueType = valueType;
    3.65 +        this.needSurrogate = needSurrogate;
    3.66 +        defineSql();
    3.67 +    }
    3.68 +
    3.69 +    protected void defineSql()
    3.70 +    {
    3.71 +        if (isKeyUnique()) {
    3.72 +            sqlKeySetIterator = new LazyPreparedStatement(
    3.73 +                "select " + keyColName + " from " + tableName);
    3.74 +            sqlKeySetSize = new LazyPreparedStatement(
    3.75 +                "select count(*) from " + tableName);
    3.76 +        } else {
    3.77 +            sqlKeySetIterator = new LazyPreparedStatement(
    3.78 +                "select distinct " + keyColName
    3.79 +                + " from " + tableName);
    3.80 +            sqlKeySetSize = new LazyPreparedStatement(
    3.81 +                "select count(distinct " + keyColName
    3.82 +                + ") from " + tableName);
    3.83 +        }
    3.84 +        
    3.85 +        sqlKeySetContains = new LazyPreparedStatement(
    3.86 +            "select count(*) from " + tableName + " where "
    3.87 +            + keyColName + " = ?");
    3.88 +        
    3.89 +        sqlInsert = new LazyPreparedStatement(
    3.90 +            "insert into " + tableName
    3.91 +            + " values(?,?"
    3.92 +            + (needSurrogate ? ",?" : "")
    3.93 +            + ")");
    3.94 +        
    3.95 +        sqlDelete = new LazyPreparedStatement(
    3.96 +            "delete from " + tableName
    3.97 +            + " where " + keyColName + " = ?");
    3.98 +        
    3.99 +        sqlFind = new LazyPreparedStatement(
   3.100 +            "select " + valColName + " from " + tableName
   3.101 +            + " where " + keyColName + " = ?");
   3.102 +    }
   3.103 +
   3.104 +    protected boolean isKeyUnique()
   3.105 +    {
   3.106 +        return false;
   3.107 +    }
   3.108 +
   3.109 +    // implement Index
   3.110 +    public String getName() throws StorageException
   3.111 +    {
   3.112 +        return name;
   3.113 +    }
   3.114 +
   3.115 +    // implement Index
   3.116 +    public Storage.EntryType getValueType() throws StorageException
   3.117 +    {
   3.118 +        return valueType;
   3.119 +    }
   3.120 +
   3.121 +    // implement Index
   3.122 +    public Storage.EntryType getKeyType() throws StorageException
   3.123 +    {
   3.124 +        return keyType;
   3.125 +    }
   3.126 +
   3.127 +    // implement Index
   3.128 +    public Set keySet() throws StorageException
   3.129 +    {
   3.130 +        return new JdbcSet(
   3.131 +            storage,
   3.132 +            getKeyType(),
   3.133 +            sqlKeySetIterator,sqlKeySetSize,sqlKeySetContains);
   3.134 +    }
   3.135 +
   3.136 +    // implement Index
   3.137 +    public void add(Object key, Object value) throws StorageException
   3.138 +    {
   3.139 +        addImpl(key,value);
   3.140 +    }
   3.141 +
   3.142 +    protected void addImpl(Object key, Object value) throws StorageException
   3.143 +    {
   3.144 +        Object [] args;
   3.145 +        if (needSurrogate) {
   3.146 +            args = new Object[]{key,value,new Long(storage.getSerialNumber())};
   3.147 +        } else {
   3.148 +            args = new Object[]{key,value};
   3.149 +        }
   3.150 +        storage.executeUpdate(sqlInsert,args);
   3.151 +    }
   3.152 +    
   3.153 +    // implement Index
   3.154 +    public boolean remove(Object key) throws StorageException
   3.155 +    {
   3.156 +        return removeImpl(key);
   3.157 +    }
   3.158 +
   3.159 +    protected boolean removeImpl(Object key) throws StorageException
   3.160 +    {
   3.161 +        return storage.executeUpdate(sqlDelete,new Object[]{key}) > 0;
   3.162 +    }
   3.163 +}
   3.164 +
   3.165 +// End JdbcIndex.java
     4.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     4.2 +++ b/mdr/extras/jdbcstorage/src/org/netbeans/mdr/persistence/jdbcimpl/JdbcMultivaluedIndex.java	Wed Feb 04 06:38:10 2004 +0000
     4.3 @@ -0,0 +1,214 @@
     4.4 +/*
     4.5 + *                 Sun Public License Notice
     4.6 + * 
     4.7 + * The contents of this file are subject to the Sun Public License
     4.8 + * Version 1.0 (the "License"). You may not use this file except in
     4.9 + * compliance with the License. A copy of the License is available at
    4.10 + * http://www.sun.com/
    4.11 + * 
    4.12 + * The Original Code is NetBeans. The Initial Developer of the Original
    4.13 + * Code is Sun Microsystems, Inc. Portions Copyright 1997-2001 Sun
    4.14 + * Microsystems, Inc. All Rights Reserved.
    4.15 + */
    4.16 +package org.netbeans.mdr.persistence.jdbcimpl;
    4.17 +
    4.18 +import org.netbeans.mdr.persistence.*;
    4.19 +import org.netbeans.mdr.util.*;
    4.20 +
    4.21 +import java.util.*;
    4.22 +import java.io.*;
    4.23 +
    4.24 +/**
    4.25 + * JdbcMultivaluedIndex implements the MDR MultivaluedIndex interface using
    4.26 + * JDBC.
    4.27 + *
    4.28 + * @author John V. Sichi
    4.29 + * @version $Id$
    4.30 + */
    4.31 +class JdbcMultivaluedIndex
    4.32 +    extends JdbcIndex implements MultivaluedIndex
    4.33 +{
    4.34 +    protected LazyPreparedStatement sqlDeleteWithValue;
    4.35 +    protected LazyPreparedStatement sqlFindCount;
    4.36 +    protected LazyPreparedStatement sqlFindWithValue;
    4.37 +    
    4.38 +    protected void defineSql()
    4.39 +    {
    4.40 +        super.defineSql();
    4.41 +        
    4.42 +        sqlDeleteWithValue = new LazyPreparedStatement(
    4.43 +            "delete from " + tableName
    4.44 +            + " where " + keyColName + " = ?"
    4.45 +            + " and " + valColName + " = ?");
    4.46 +        
    4.47 +        sqlFindCount = new LazyPreparedStatement(
    4.48 +            "select count(*) from " + tableName
    4.49 +            + " where " + keyColName + " = ?");
    4.50 +        
    4.51 +        sqlFindWithValue = new LazyPreparedStatement(
    4.52 +            "select count(*) from " + tableName
    4.53 +            + " where " + keyColName + " = ?"
    4.54 +            + " and " + valColName + " = ?");
    4.55 +    }
    4.56 +    
    4.57 +    // implement MultivaluedIndex
    4.58 +    public Collection getItems(Object key) throws StorageException
    4.59 +    {
    4.60 +        return new ItemCollection(key,null);
    4.61 +    }
    4.62 +    
    4.63 +    // implement MultivaluedIndex
    4.64 +    public Collection getObjects(
    4.65 +        Object key, SinglevaluedIndex repos) throws StorageException
    4.66 +    {
    4.67 +        if (keyType == Storage.EntryType.MOFID) {
    4.68 +            return new ItemCollection(key,repos);
    4.69 +        } else {
    4.70 +            return getItems(key);
    4.71 +        }
    4.72 +    }
    4.73 +    
    4.74 +    // implement MultivaluedIndex
    4.75 +    public boolean isUnique() throws StorageException
    4.76 +    {
    4.77 +        return !needSurrogate;
    4.78 +    }
    4.79 +
    4.80 +    // override JdbcIndex
    4.81 +    public void add(Object key, Object value) throws StorageException
    4.82 +    {
    4.83 +        if (!needSurrogate) {
    4.84 +            // REVIEW: Values are supposed to be unique.  Apparently this means
    4.85 +            // duplicates are discarded rather than causing errors, since
    4.86 +            // during MOF boot duplicate keys are generated.  It would be most
    4.87 +            // efficient to get the INSERT statement to detect the problem and
    4.88 +            // suppress the resulting exception; however, that requires
    4.89 +            // detailed backend knowledge.  Instead, we prevent the error by
    4.90 +            // checking first.  What I don't understand is why this isn't an
    4.91 +            // issue with BtreeStorage, which also enforces constraints.
    4.92 +            int n = storage.getSingletonInt(
    4.93 +                sqlFindWithValue,
    4.94 +                new Object[]{key,value});
    4.95 +            if (n == 1) {
    4.96 +                return;
    4.97 +            }
    4.98 +            // TODO:  assert n == 0
    4.99 +        }
   4.100 +        super.add(key,value);
   4.101 +    }
   4.102 +
   4.103 +    // implement MultivaluedIndex
   4.104 +    public boolean remove(Object key, Object value) throws StorageException
   4.105 +    {
   4.106 +        // REVIEW:  spec says we're only supposed to delete the "first"
   4.107 +        // one; does this matter?
   4.108 +        return storage.executeUpdate(
   4.109 +            sqlDeleteWithValue,new Object[]{key,value}) > 0;
   4.110 +    }
   4.111 +    
   4.112 +    // implement MultivaluedIndex
   4.113 +    public Collection queryByKeyPrefix(
   4.114 +        Object prefix, SinglevaluedIndex repos) throws StorageException
   4.115 +    {
   4.116 +        // TODO:
   4.117 +        throw new RuntimeException("oops, not yet implemented");
   4.118 +    }
   4.119 +
   4.120 +    private class ItemCollection extends AbstractCollection
   4.121 +    {
   4.122 +        private Object key;
   4.123 +        private SinglevaluedIndex repos;
   4.124 +
   4.125 +        ItemCollection(Object key,SinglevaluedIndex repos)
   4.126 +        {
   4.127 +            this.key = key;
   4.128 +            this.repos = repos;
   4.129 +        }
   4.130 +        
   4.131 +        // implement AbstractCollection
   4.132 +        public Iterator iterator()
   4.133 +        {
   4.134 +            try {
   4.135 +                return new ItemCollectionIter(
   4.136 +                    storage.getResultSetIterator(
   4.137 +                        sqlFind,
   4.138 +                        new Object[]{key},
   4.139 +                        getValueType()),
   4.140 +                    repos,
   4.141 +                    key);
   4.142 +            } catch (StorageException ex) {
   4.143 +                throw new RuntimeStorageException(ex);
   4.144 +            }
   4.145 +        }
   4.146 +
   4.147 +        public int size()
   4.148 +        {
   4.149 +            try {
   4.150 +                return storage.getSingletonInt(sqlFindCount,new Object[]{key});
   4.151 +            } catch (StorageException ex) {
   4.152 +                throw new RuntimeStorageException(ex);
   4.153 +            }
   4.154 +        }
   4.155 +
   4.156 +        // implement AbstractCollection
   4.157 +        public boolean add(Object value)
   4.158 +        {
   4.159 +            try {
   4.160 +                JdbcMultivaluedIndex.this.add(key,value);
   4.161 +                return true;
   4.162 +            } catch (StorageException ex) {
   4.163 +                throw new RuntimeStorageException(ex);
   4.164 +            }
   4.165 +        }
   4.166 +    }
   4.167 +
   4.168 +    private class ItemCollectionIter implements Iterator
   4.169 +    {
   4.170 +        private Iterator iter;
   4.171 +        private SinglevaluedIndex repos;
   4.172 +        private Object key;
   4.173 +        private Object value;
   4.174 +
   4.175 +        ItemCollectionIter(Iterator iter,SinglevaluedIndex repos,Object key)
   4.176 +        {
   4.177 +            this.iter = iter;
   4.178 +            this.repos = repos;
   4.179 +            this.key = key;
   4.180 +        }
   4.181 +
   4.182 +        // implement Iterator
   4.183 +        public boolean hasNext()
   4.184 +        {
   4.185 +            return iter.hasNext();
   4.186 +        }
   4.187 +
   4.188 +        // implement Iterator
   4.189 +        public Object next()
   4.190 +        {
   4.191 +            value = iter.next();
   4.192 +            if (repos != null) {
   4.193 +                try {
   4.194 +                    value = repos.get(value);
   4.195 +                } catch (StorageException ex) {
   4.196 +                    throw new RuntimeStorageException(ex);
   4.197 +                }
   4.198 +            }
   4.199 +            return value;
   4.200 +        }
   4.201 +
   4.202 +        // implement Iterator
   4.203 +        public void remove()
   4.204 +        {
   4.205 +            // REVIEW:  if values aren't unique, this will remove
   4.206 +            // all matching values; does this matter?  if so, need
   4.207 +            // to remember the surrogate and use it to narrow delete
   4.208 +            try {
   4.209 +                JdbcMultivaluedIndex.this.remove(key,value);
   4.210 +            } catch (StorageException ex) {
   4.211 +                throw new RuntimeStorageException(ex);
   4.212 +            }
   4.213 +        }
   4.214 +    }
   4.215 +}
   4.216 +
   4.217 +// End JdbcMultivaluedIndex.java
     5.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     5.2 +++ b/mdr/extras/jdbcstorage/src/org/netbeans/mdr/persistence/jdbcimpl/JdbcMultivaluedOrderedIndex.java	Wed Feb 04 06:38:10 2004 +0000
     5.3 @@ -0,0 +1,312 @@
     5.4 +/*
     5.5 + *                 Sun Public License Notice
     5.6 + * 
     5.7 + * The contents of this file are subject to the Sun Public License
     5.8 + * Version 1.0 (the "License"). You may not use this file except in
     5.9 + * compliance with the License. A copy of the License is available at
    5.10 + * http://www.sun.com/
    5.11 + * 
    5.12 + * The Original Code is NetBeans. The Initial Developer of the Original
    5.13 + * Code is Sun Microsystems, Inc. Portions Copyright 1997-2001 Sun
    5.14 + * Microsystems, Inc. All Rights Reserved.
    5.15 + */
    5.16 +package org.netbeans.mdr.persistence.jdbcimpl;
    5.17 +
    5.18 +import org.netbeans.mdr.persistence.*;
    5.19 +import org.netbeans.mdr.util.*;
    5.20 +
    5.21 +import java.sql.*;
    5.22 +import java.util.*;
    5.23 +import java.io.*;
    5.24 +
    5.25 +/**
    5.26 + * JdbcMultivaluedOrderedIndex implements the MDR MultivaluedOrderedIndex
    5.27 + * using JDBC.
    5.28 + *
    5.29 + * @author John V. Sichi
    5.30 + * @version $Id$
    5.31 + */
    5.32 +class JdbcMultivaluedOrderedIndex
    5.33 +    extends JdbcMultivaluedIndex implements MultivaluedOrderedIndex
    5.34 +{
    5.35 +    protected LazyPreparedStatement sqlInsertWithOrdinal;
    5.36 +    protected LazyPreparedStatement sqlUpdateWithOrdinal;
    5.37 +    protected LazyPreparedStatement sqlDeleteWithOrdinal;
    5.38 +    protected LazyPreparedStatement sqlShiftOrdinals;
    5.39 +    protected LazyPreparedStatement sqlFindOrdered;
    5.40 +    
    5.41 +    protected void defineSql()
    5.42 +    {
    5.43 +        super.defineSql();
    5.44 +
    5.45 +        // TODO:  assert needSurrogate
    5.46 +
    5.47 +        sqlInsertWithOrdinal = new LazyPreparedStatement(
    5.48 +            "insert into " + tableName
    5.49 +            + " values(?,?,?,?)");
    5.50 +        
    5.51 +        sqlUpdateWithOrdinal = new LazyPreparedStatement(
    5.52 +            "update " + tableName + " set " + valColName
    5.53 +            + " = ? where " + keyColName + " = ? and "
    5.54 +            + storage.ORDINAL_COL_NAME + " = ?");
    5.55 +        
    5.56 +        sqlDeleteWithOrdinal = new LazyPreparedStatement(
    5.57 +            "delete from " + tableName
    5.58 +            + " where " + keyColName + " = ?"
    5.59 +            + " and " + storage.ORDINAL_COL_NAME + " = ?");
    5.60 +        
    5.61 +        sqlShiftOrdinals = new LazyPreparedStatement(
    5.62 +            "update " + tableName + " set "
    5.63 +            + storage.ORDINAL_COL_NAME + " = " + storage.ORDINAL_COL_NAME
    5.64 +            + " + ? where " + keyColName + " = ? and "
    5.65 +            + storage.ORDINAL_COL_NAME + " >= ?");
    5.66 +
    5.67 +        // NOTE:  keyColName is redundant in ORDER BY, but maybe it improves
    5.68 +        // the chances that even a stupid optimizer will realize it can use the
    5.69 +        // primary key index for ordering
    5.70 +        sqlFindOrdered = new LazyPreparedStatement(
    5.71 +            "select " + valColName + " from " + tableName
    5.72 +            + " where " + keyColName + " = ? order by "
    5.73 +            + keyColName + ", " + storage.ORDINAL_COL_NAME);
    5.74 +    }
    5.75 +    
    5.76 +    // implement MultivaluedOrderedIndex
    5.77 +    public List getItemsOrdered(Object key) throws StorageException
    5.78 +    {
    5.79 +        return new ItemList(key,null);
    5.80 +    }
    5.81 +    
    5.82 +    // implement MultivaluedOrderedIndex
    5.83 +    public Collection getObjectsOrdered(
    5.84 +        Object key, SinglevaluedIndex repos) throws StorageException
    5.85 +    {
    5.86 +        if (keyType == Storage.EntryType.MOFID) {
    5.87 +            return new ItemList(key,repos);
    5.88 +        } else {
    5.89 +            return getItemsOrdered(key);
    5.90 +        }
    5.91 +    }
    5.92 +    
    5.93 +    // implement MultivaluedOrderedIndex
    5.94 +    public void add(Object key, int index, Object value)
    5.95 +        throws StorageException
    5.96 +    {
    5.97 +        addImpl(key,index,value,true);
    5.98 +    }
    5.99 +    
   5.100 +    // override JdbcIndex
   5.101 +    public void add(Object key, Object value) throws StorageException
   5.102 +    {
   5.103 +        // Have to query to get the current size as the new index.  But as an
   5.104 +        // optimization, there's no need to shift the ordinals of the existing
   5.105 +        // entries.
   5.106 +        int index = getItemsOrdered(key).size();
   5.107 +        addImpl(key,index,value,false);
   5.108 +    }
   5.109 +
   5.110 +    private void addImpl(
   5.111 +        Object key, int index, Object value,boolean shiftOrdinals)
   5.112 +        throws StorageException
   5.113 +    {
   5.114 +        if (shiftOrdinals) {
   5.115 +            storage.executeUpdate(
   5.116 +                sqlShiftOrdinals,
   5.117 +                new Object[]{new Integer(1),key,new Integer(index)});
   5.118 +        }
   5.119 +        
   5.120 +        Object [] args = new Object[]{
   5.121 +            key,value,new Integer(index),
   5.122 +            new Long(storage.getSerialNumber())};
   5.123 +        storage.executeUpdate(sqlInsertWithOrdinal,args);
   5.124 +    }
   5.125 +    
   5.126 +    // implement MultivaluedOrderedIndex
   5.127 +    public boolean remove(Object key, int index)
   5.128 +        throws StorageException
   5.129 +    {
   5.130 +        return removeImpl(key,index,true);
   5.131 +    }
   5.132 +    
   5.133 +    private boolean removeImpl(Object key, int index,boolean shiftOrdinals)
   5.134 +        throws StorageException
   5.135 +    {
   5.136 +        storage.executeUpdate(
   5.137 +            sqlDeleteWithOrdinal,
   5.138 +            new Object[]{key,new Integer(index)});
   5.139 +
   5.140 +        if (shiftOrdinals) {
   5.141 +            storage.executeUpdate(
   5.142 +                sqlShiftOrdinals,
   5.143 +                new Object[]{new Integer(-1),key,new Integer(index)});
   5.144 +        }
   5.145 +        
   5.146 +        return true;
   5.147 +    }
   5.148 +    
   5.149 +    // implement MultivaluedOrderedIndex
   5.150 +    public void replace(Object key, int index, Object element)
   5.151 +        throws StorageException
   5.152 +    {
   5.153 +        storage.executeUpdate(
   5.154 +            sqlUpdateWithOrdinal,
   5.155 +            new Object[]{element,key,new Integer(index)});
   5.156 +    }
   5.157 +
   5.158 +    private class ItemList extends AbstractSequentialList
   5.159 +    {
   5.160 +        private Object key;
   5.161 +        private SinglevaluedIndex repos;
   5.162 +
   5.163 +        ItemList(Object key,SinglevaluedIndex repos)
   5.164 +        {
   5.165 +            this.key = key;
   5.166 +            this.repos = repos;
   5.167 +        }
   5.168 +
   5.169 +        // implement AbstractSequentialList
   5.170 +        public ListIterator listIterator(int index)
   5.171 +        {
   5.172 +            try {
   5.173 +                ListIterator iter = new ItemListIter(
   5.174 +                    storage.getResultSetIterator(
   5.175 +                        sqlFindOrdered,
   5.176 +                        new Object[]{key},
   5.177 +                        getValueType()),
   5.178 +                    repos,
   5.179 +                    key);
   5.180 +                while (iter.nextIndex() != index) {
   5.181 +                    iter.next();
   5.182 +                }
   5.183 +                return iter;
   5.184 +            } catch (StorageException ex) {
   5.185 +                throw new RuntimeStorageException(ex);
   5.186 +            }
   5.187 +        }
   5.188 +
   5.189 +        // implement AbstractSequentialList
   5.190 +        public int size()
   5.191 +        {
   5.192 +            try {
   5.193 +                return storage.getSingletonInt(sqlFindCount,new Object[]{key});
   5.194 +            } catch (StorageException ex) {
   5.195 +                throw new RuntimeStorageException(ex);
   5.196 +            }
   5.197 +        }
   5.198 +    }
   5.199 +
   5.200 +    private class ItemListIter implements ListIterator
   5.201 +    {
   5.202 +        private ListIterator iter;
   5.203 +        private SinglevaluedIndex repos;
   5.204 +        private Object key;
   5.205 +        private int lastPos;
   5.206 +
   5.207 +        ItemListIter(
   5.208 +            ListIterator iter,SinglevaluedIndex repos,Object key)
   5.209 +        {
   5.210 +            this.iter = iter;
   5.211 +            this.repos = repos;
   5.212 +            this.key = key;
   5.213 +
   5.214 +            lastPos = -1;
   5.215 +        }
   5.216 +
   5.217 +        // implement ListIterator
   5.218 +        public void add(Object obj)
   5.219 +        {
   5.220 +            try {
   5.221 +                int index = nextIndex();
   5.222 +                // if at end, no need to shift ordinals
   5.223 +                addImpl(key,index,obj,iter.hasNext());
   5.224 +            } catch (StorageException ex) {
   5.225 +                throw new RuntimeStorageException(ex);
   5.226 +            }
   5.227 +            iter.add(obj);
   5.228 +            lastPos = -1;
   5.229 +        }
   5.230 +
   5.231 +        // implement ListIterator
   5.232 +        public boolean hasNext()
   5.233 +        {
   5.234 +            return iter.hasNext();
   5.235 +        }
   5.236 +
   5.237 +        // implement ListIterator
   5.238 +        public boolean hasPrevious()
   5.239 +        {
   5.240 +            return iter.hasPrevious();
   5.241 +        }
   5.242 +
   5.243 +        private Object getValue(Object obj)
   5.244 +        {
   5.245 +            if (repos != null) {
   5.246 +                try {
   5.247 +                    return repos.get(obj);
   5.248 +                } catch (StorageException ex) {
   5.249 +                    throw new RuntimeStorageException(ex);
   5.250 +                }
   5.251 +            } else {
   5.252 +                return obj;
   5.253 +            }
   5.254 +        }
   5.255 +
   5.256 +        // implement ListIterator
   5.257 +        public Object next()
   5.258 +        {
   5.259 +            lastPos = iter.nextIndex();
   5.260 +            return getValue(iter.next());
   5.261 +        }
   5.262 +
   5.263 +        // implement ListIterator
   5.264 +        public int nextIndex() 
   5.265 +        {
   5.266 +            return iter.nextIndex();
   5.267 +        }
   5.268 +
   5.269 +        // implement ListIterator
   5.270 +        public Object previous()
   5.271 +        {
   5.272 +            lastPos = iter.previousIndex();
   5.273 +            return getValue(iter.previous());
   5.274 +        }
   5.275 +
   5.276 +        // implement ListIterator
   5.277 +        public int previousIndex()
   5.278 +        {
   5.279 +            return iter.previousIndex();
   5.280 +        }
   5.281 +
   5.282 +        // implement ListIterator
   5.283 +        public void remove()
   5.284 +        {
   5.285 +            if (lastPos == -1) {
   5.286 +                throw new IllegalStateException();
   5.287 +            }
   5.288 +            // remove from iter first, in case we just did a previous() from end
   5.289 +            iter.remove();
   5.290 +            try {
   5.291 +                // if at end, no need to shift ordinals
   5.292 +                removeImpl(
   5.293 +                    key,lastPos,iter.hasNext());
   5.294 +            } catch (StorageException ex) {
   5.295 +                throw new RuntimeStorageException(ex);
   5.296 +            }
   5.297 +            lastPos = -1;
   5.298 +        }
   5.299 +
   5.300 +        // implement ListIterator
   5.301 +        public void set(Object obj)
   5.302 +        {
   5.303 +            iter.set(obj);
   5.304 +            try {
   5.305 +                JdbcMultivaluedOrderedIndex.this.replace(
   5.306 +                    key,lastPos,obj);
   5.307 +            } catch (StorageException ex) {
   5.308 +                throw new RuntimeStorageException(ex);
   5.309 +            }
   5.310 +            lastPos = -1;
   5.311 +        }
   5.312 +    }
   5.313 +}
   5.314 +
   5.315 +// End JdbcMultivaluedOrderedIndex.java
     6.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     6.2 +++ b/mdr/extras/jdbcstorage/src/org/netbeans/mdr/persistence/jdbcimpl/JdbcPrimaryIndex.java	Wed Feb 04 06:38:10 2004 +0000
     6.3 @@ -0,0 +1,238 @@
     6.4 +/*
     6.5 + *                 Sun Public License Notice
     6.6 + * 
     6.7 + * The contents of this file are subject to the Sun Public License
     6.8 + * Version 1.0 (the "License"). You may not use this file except in
     6.9 + * compliance with the License. A copy of the License is available at
    6.10 + * http://www.sun.com/
    6.11 + * 
    6.12 + * The Original Code is NetBeans. The Initial Developer of the Original
    6.13 + * Code is Sun Microsystems, Inc. Portions Copyright 1997-2001 Sun
    6.14 + * Microsystems, Inc. All Rights Reserved.
    6.15 + */
    6.16 +package org.netbeans.mdr.persistence.jdbcimpl;
    6.17 +
    6.18 +import org.netbeans.mdr.persistence.btreeimpl.btreestorage.*;
    6.19 +import org.netbeans.mdr.persistence.*;
    6.20 +
    6.21 +import java.io.*;
    6.22 +import java.text.*;
    6.23 +import java.util.*;
    6.24 +
    6.25 +/**
    6.26 + * JdbcPrimaryIndex implements primary index storage using JDBC.
    6.27 + *
    6.28 + * @author John V. Sichi
    6.29 + * @version $Id$
    6.30 + */
    6.31 +class JdbcPrimaryIndex
    6.32 +    extends JdbcSinglevaluedIndex implements MDRCache.OverflowHandler
    6.33 +{
    6.34 +    private final MDRCache cache;
    6.35 +
    6.36 +    // NOTE:  most of this class was ripped from BtreeDatabase
    6.37 +    static final int MDR_CACHE_SIZE = 2048;
    6.38 +    static final int MDR_CACHE_THRESHHOLD = 1000;
    6.39 +    
    6.40 +    JdbcPrimaryIndex()
    6.41 +    {
    6.42 +        cache = new MDRCache(MDR_CACHE_SIZE, this, MDR_CACHE_THRESHHOLD);
    6.43 +    }
    6.44 +
    6.45 +    // implement MDRCache.OverflowHandler
    6.46 +	public void cacheThreshholdReached(MDRCache cache, int size) 
    6.47 +        throws StorageException
    6.48 +    {
    6.49 +        flushChanges();
    6.50 +    }
    6.51 +
    6.52 +    void flushChanges()
    6.53 +        throws StorageException
    6.54 +    {
    6.55 +        // TODO:  optimize with batched JDBC calls where supported
    6.56 +        Iterator iter = cache.iterateDeleted();
    6.57 +        while (iter.hasNext()) {
    6.58 +            super.removeImpl(iter.next());
    6.59 +        }
    6.60 +        iter = cache.getDirty().iterator();
    6.61 +        while (iter.hasNext()) {
    6.62 +            Object key = iter.next();
    6.63 +            super.replaceImpl(key,cache.get(key));
    6.64 +        }
    6.65 +        iter = cache.iterateNew();
    6.66 +        while (iter.hasNext()) {
    6.67 +            Object key = iter.next();
    6.68 +            super.addImpl(key,cache.get(key));
    6.69 +        }
    6.70 +        cache.clearLists();
    6.71 +    }
    6.72 +
    6.73 +    private MOFID makeMOFID(Object key) throws StorageException
    6.74 +    {
    6.75 +        if (key instanceof MOFID) {
    6.76 +            return (MOFID) key;
    6.77 +        } else {
    6.78 +            throw new IllegalArgumentException(
    6.79 +                "Argument must be of org.netbeans.mdr.persistence.MOFID type");
    6.80 +        }
    6.81 +    }
    6.82 +
    6.83 +    void objectStateChanged(Object key) throws StorageException
    6.84 +    {
    6.85 +        cache.setDirty(makeMOFID(key));
    6.86 +    }
    6.87 +
    6.88 +    private boolean exists(MOFID key) throws StorageException
    6.89 +    {
    6.90 +        if (cache.get(key) != null) {   
    6.91 +            return true;
    6.92 +        } else if (cache.isDeleted(key)) {
    6.93 +            return false;
    6.94 +        } else {
    6.95 +            return super.getIfExists(key) != null;
    6.96 +        }
    6.97 +    }
    6.98 +    
    6.99 +    private void noSuchRecord(Object key) throws StorageException
   6.100 +    {
   6.101 +        throw new StorageBadRequestException(
   6.102 +            MessageFormat.format("No record exists with key {0}",
   6.103 +                new Object[] {key} ) );
   6.104 +    }
   6.105 +    
   6.106 +    private void addToCache(MOFID key, Object value) throws StorageException
   6.107 +    {
   6.108 +        cache.put(key, value);
   6.109 +        cache.setNew(key);
   6.110 +    }
   6.111 +    
   6.112 +    private void replaceInCache(MOFID key, Object value)
   6.113 +        throws StorageException
   6.114 +    {
   6.115 +        boolean isNew = cache.isNew(key);
   6.116 +        
   6.117 +        // TODO:  should be cache.replace(key, value);
   6.118 +        AccessHack.cacheReplace(cache,key,value);
   6.119 +        
   6.120 +        if (isNew) {
   6.121 +            cache.setNew(key);
   6.122 +        } else {
   6.123 +            cache.setDirty(key);
   6.124 +        }
   6.125 +    }
   6.126 +    
   6.127 +    // override JdbcSinglevaluedIndex
   6.128 +    public synchronized boolean put(
   6.129 +        Object key,Object value) throws StorageException
   6.130 +    {
   6.131 +        MOFID mKey = makeMOFID(key);
   6.132 +        if (!exists(mKey)) {
   6.133 +            addToCache(mKey,value);
   6.134 +            return false;
   6.135 +        } else {
   6.136 +            replaceInCache(mKey,value);
   6.137 +            return true;
   6.138 +        }
   6.139 +    }
   6.140 +    
   6.141 +    // override JdbcSinglevaluedIndex
   6.142 +    public synchronized void replace(Object key, Object value)
   6.143 +        throws StorageException, StorageBadRequestException
   6.144 +    {
   6.145 +        MOFID mKey = makeMOFID(key);
   6.146 +        
   6.147 +        if (!exists(mKey)) {
   6.148 +            noSuchRecord(mKey);
   6.149 +        }
   6.150 +
   6.151 +        replaceInCache(mKey, value);
   6.152 +    }
   6.153 +    
   6.154 +    // override JdbcSinglevaluedIndex
   6.155 +    public Object get(Object key)
   6.156 +        throws StorageException, StorageBadRequestException
   6.157 +    {
   6.158 +        Object retval = getIfExists(key);
   6.159 +        if (retval == null) {
   6.160 +            noSuchRecord(key);
   6.161 +        }
   6.162 +
   6.163 +        return retval;
   6.164 +    }
   6.165 +
   6.166 +    // override JdbcSinglevaluedIndex
   6.167 +    public Object getObject(Object key, SinglevaluedIndex repos)
   6.168 +        throws StorageException
   6.169 +    {
   6.170 +        return get(key);
   6.171 +    }
   6.172 +
   6.173 +    // override JdbcSinglevaluedIndex
   6.174 +    public synchronized Object getIfExists(Object key) throws StorageException
   6.175 +    {
   6.176 +        MOFID mKey = makeMOFID(key);
   6.177 +        Object retval;
   6.178 +        retval = cache.get(mKey);
   6.179 +        if (retval == null) {
   6.180 +            if (cache.isDeleted(mKey)) {
   6.181 +                return null;
   6.182 +            }
   6.183 +            retval = super.getIfExists(mKey);
   6.184 +            if (retval != null) {
   6.185 +                cache.put(mKey, retval);
   6.186 +            }
   6.187 +        }
   6.188 +
   6.189 +        return retval;
   6.190 +    }
   6.191 +    
   6.192 +    // override JdbcSinglevaluedIndex
   6.193 +    public Object getObjectIfExists(Object key, SinglevaluedIndex repos)
   6.194 +        throws StorageException
   6.195 +    {
   6.196 +        return getIfExists(key);
   6.197 +    }
   6.198 +
   6.199 +    // NOTE:  it's a pain to implement keySet() and values() correctly,
   6.200 +    // and as far as I can tell they're never used for primary indexes
   6.201 +    
   6.202 +    // override JdbcSinglevaluedIndex
   6.203 +    public Collection values()
   6.204 +        throws StorageException
   6.205 +    {
   6.206 +        throw new RuntimeException("oops, not yet implemented");
   6.207 +    }
   6.208 +    
   6.209 +    // override JdbcIndex
   6.210 +    public Set keySet() throws StorageException
   6.211 +    {
   6.212 +        throw new RuntimeException("oops, not yet implemented");
   6.213 +    }
   6.214 +
   6.215 +    // override JdbcIndex
   6.216 +    public synchronized void add(
   6.217 +        Object key, Object value) throws StorageException
   6.218 +    {
   6.219 +        MOFID mKey = makeMOFID(key);
   6.220 +        if (exists(mKey)) {
   6.221 +            throw new StorageBadRequestException(
   6.222 +                MessageFormat.format("Record with key {0} already exists",
   6.223 +                    new Object[] {mKey} ) );
   6.224 +        }
   6.225 +        addToCache(mKey, value);
   6.226 +    }
   6.227 +    
   6.228 +    // override JdbcIndex
   6.229 +    public synchronized boolean remove(Object key) throws StorageException
   6.230 +    {
   6.231 +        MOFID mKey = makeMOFID(key);
   6.232 +        if (!exists(mKey)) {
   6.233 +            return false;
   6.234 +        } else {
   6.235 +            cache.remove(mKey);
   6.236 +            return true;
   6.237 +        }
   6.238 +    }
   6.239 +}
   6.240 +
   6.241 +// End JdbcPrimaryIndex.java
     7.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     7.2 +++ b/mdr/extras/jdbcstorage/src/org/netbeans/mdr/persistence/jdbcimpl/JdbcSet.java	Wed Feb 04 06:38:10 2004 +0000
     7.3 @@ -0,0 +1,86 @@
     7.4 +/*
     7.5 + *                 Sun Public License Notice
     7.6 + * 
     7.7 + * The contents of this file are subject to the Sun Public License
     7.8 + * Version 1.0 (the "License"). You may not use this file except in
     7.9 + * compliance with the License. A copy of the License is available at
    7.10 + * http://www.sun.com/
    7.11 + * 
    7.12 + * The Original Code is NetBeans. The Initial Developer of the Original
    7.13 + * Code is Sun Microsystems, Inc. Portions Copyright 1997-2001 Sun
    7.14 + * Microsystems, Inc. All Rights Reserved.
    7.15 + */
    7.16 +package org.netbeans.mdr.persistence.jdbcimpl;
    7.17 +
    7.18 +import org.netbeans.mdr.persistence.*;
    7.19 +
    7.20 +import java.util.*;
    7.21 +
    7.22 +/**
    7.23 + * JdbcSet is a read-only Set backed by queries.
    7.24 + *
    7.25 + * @author John V. Sichi
    7.26 + * @version $Id$
    7.27 + */
    7.28 +class JdbcSet extends AbstractSet
    7.29 +{
    7.30 +    private final JdbcStorage storage;
    7.31 +    private final Storage.EntryType entryType;
    7.32 +    private final LazyPreparedStatement sqlIterator;
    7.33 +    private final LazyPreparedStatement sqlSize;
    7.34 +    private final LazyPreparedStatement sqlContains;
    7.35 +
    7.36 +    JdbcSet(
    7.37 +        JdbcStorage storage,
    7.38 +        Storage.EntryType entryType,
    7.39 +        LazyPreparedStatement sqlIterator,
    7.40 +        LazyPreparedStatement sqlSize,
    7.41 +        LazyPreparedStatement sqlContains)
    7.42 +    {
    7.43 +        this.storage = storage;
    7.44 +        this.entryType = entryType;
    7.45 +        this.sqlIterator = sqlIterator;
    7.46 +        this.sqlSize = sqlSize;
    7.47 +        this.sqlContains = sqlContains;
    7.48 +    }
    7.49 +
    7.50 +    // implement AbstractSet
    7.51 +    public Iterator iterator()
    7.52 +    {
    7.53 +        try {
    7.54 +            return storage.getResultSetIterator(sqlIterator,null,entryType);
    7.55 +        } catch (StorageException ex) {
    7.56 +            throw new RuntimeStorageException(ex);
    7.57 +        }
    7.58 +    }
    7.59 +
    7.60 +    // override AbstractSet
    7.61 +    public boolean contains(Object obj)
    7.62 +    {
    7.63 +        try {
    7.64 +            int n = storage.getSingletonInt(
    7.65 +                sqlContains,new Object[]{obj});
    7.66 +            return n > 0;
    7.67 +        } catch (StorageException ex) {
    7.68 +            throw new RuntimeStorageException(ex);
    7.69 +        }
    7.70 +    }
    7.71 +
    7.72 +    // implement AbstractSet
    7.73 +    public int size()
    7.74 +    {
    7.75 +        try {
    7.76 +            if (sqlSize != null) {
    7.77 +                return storage.getSingletonInt(sqlSize,null);
    7.78 +            } else {
    7.79 +                // this could be used to compensate for a DBMS without
    7.80 +                // COUNT(DISTINCT)
    7.81 +                return storage.getResultSetCount(sqlIterator,null);
    7.82 +            }
    7.83 +        } catch (StorageException ex) {
    7.84 +            throw new RuntimeStorageException(ex);
    7.85 +        }
    7.86 +    }
    7.87 +}
    7.88 +
    7.89 +// End JdbcSet.java
     8.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     8.2 +++ b/mdr/extras/jdbcstorage/src/org/netbeans/mdr/persistence/jdbcimpl/JdbcSinglevaluedIndex.java	Wed Feb 04 06:38:10 2004 +0000
     8.3 @@ -0,0 +1,170 @@
     8.4 +/*
     8.5 + *                 Sun Public License Notice
     8.6 + * 
     8.7 + * The contents of this file are subject to the Sun Public License
     8.8 + * Version 1.0 (the "License"). You may not use this file except in
     8.9 + * compliance with the License. A copy of the License is available at
    8.10 + * http://www.sun.com/
    8.11 + * 
    8.12 + * The Original Code is NetBeans. The Initial Developer of the Original
    8.13 + * Code is Sun Microsystems, Inc. Portions Copyright 1997-2001 Sun
    8.14 + * Microsystems, Inc. All Rights Reserved.
    8.15 + */
    8.16 +package org.netbeans.mdr.persistence.jdbcimpl;
    8.17 +
    8.18 +import org.netbeans.mdr.persistence.*;
    8.19 +import org.netbeans.mdr.util.*;
    8.20 +
    8.21 +import java.util.*;
    8.22 +import java.io.*;
    8.23 +
    8.24 +/**
    8.25 + * JdbcSinglevaluedIndex implements the MDR SinglevaluedIndex interface 
    8.26 + * using JDBC.
    8.27 + *
    8.28 + * @author John V. Sichi
    8.29 + * @version $Id$
    8.30 + */
    8.31 +class JdbcSinglevaluedIndex
    8.32 +    extends JdbcIndex implements SinglevaluedIndex
    8.33 +{
    8.34 +    protected LazyPreparedStatement sqlValuesIterator;
    8.35 +    protected LazyPreparedStatement sqlValuesSize;
    8.36 +    protected LazyPreparedStatement sqlValuesContains;
    8.37 +    protected LazyPreparedStatement sqlUpdate;
    8.38 +    
    8.39 +    protected void defineSql()
    8.40 +    {
    8.41 +        super.defineSql();
    8.42 +
    8.43 +        sqlValuesIterator = new LazyPreparedStatement(
    8.44 +            "select " + valColName + " from " + tableName);
    8.45 +        
    8.46 +        sqlValuesSize = new LazyPreparedStatement(
    8.47 +            "select count(*) from " + tableName);
    8.48 +        
    8.49 +        sqlValuesContains = new LazyPreparedStatement(
    8.50 +            "select count(*) from " + tableName + " where "
    8.51 +            + valColName + " = ?");
    8.52 +        
    8.53 +        sqlUpdate = new LazyPreparedStatement(
    8.54 +            "update " + tableName + " set " + valColName
    8.55 +            + " = ? where " + keyColName + " = ?");
    8.56 +    }
    8.57 +    
    8.58 +    protected boolean isKeyUnique()
    8.59 +    {
    8.60 +        return true;
    8.61 +    }
    8.62 +
    8.63 +    // implement SinglevaluedIndex
    8.64 +    public boolean put(Object key,Object value) throws StorageException
    8.65 +    {
    8.66 +        return putImpl(key,value);
    8.67 +    }
    8.68 +    
    8.69 +    protected boolean putImpl(Object key,Object value) throws StorageException
    8.70 +    {
    8.71 +        // REVIEW:  this optimizes for update rather than insert; could
    8.72 +        // use UPSERT on databases that support it
    8.73 +        int rowCount = storage.executeUpdate(
    8.74 +            sqlUpdate,new Object[]{value,key});
    8.75 +        // TODO:  assert rowCount == 0 or 1
    8.76 +        if (rowCount == 0) {
    8.77 +            // key did not exist; insert instead
    8.78 +            addImpl(key,value);
    8.79 +            return false;
    8.80 +        } else {
    8.81 +            // key existed and has already been updated
    8.82 +            return true;
    8.83 +        }
    8.84 +    }
    8.85 +    
    8.86 +    // implement SinglevaluedIndex
    8.87 +    public void replace(Object key, Object value)
    8.88 +        throws StorageException, StorageBadRequestException
    8.89 +    {
    8.90 +        replaceImpl(key,value);
    8.91 +    }
    8.92 +    
    8.93 +    protected void replaceImpl(Object key, Object value)
    8.94 +        throws StorageException, StorageBadRequestException
    8.95 +    {
    8.96 +        if (!putImpl(key,value)) {
    8.97 +            throw new StorageBadRequestException(
    8.98 +                "Cannot replace item that does not exist in the index.");
    8.99 +        }
   8.100 +    }
   8.101 +    
   8.102 +    // implement SinglevaluedIndex
   8.103 +    public Object get(Object key)
   8.104 +        throws StorageException, StorageBadRequestException
   8.105 +    {
   8.106 +        Object obj = getIfExists(key);
   8.107 +        if (obj == null) {
   8.108 +            throw new StorageBadRequestException ("Item not found: " + key);
   8.109 +        }
   8.110 +        return obj;
   8.111 +    }
   8.112 +
   8.113 +    // implement SinglevaluedIndex
   8.114 +    public Object getObject(Object key, SinglevaluedIndex repos)
   8.115 +        throws StorageException
   8.116 +    {
   8.117 +        // NOTE: If JdbcPrimaryIndex did no caching, we could optimize this
   8.118 +        // with an SQL join.  But we can't join against the cache.  Same goes
   8.119 +        // for all of the other calls that take a repos argument.
   8.120 +        if (keyType == Storage.EntryType.MOFID) {
   8.121 +            return repos.get(get(key));
   8.122 +        } else {
   8.123 +            return get(key);
   8.124 +        }
   8.125 +    }
   8.126 +
   8.127 +    // implement SinglevaluedIndex
   8.128 +    public Object getIfExists(Object key) throws StorageException
   8.129 +    {
   8.130 +        Iterator iter = storage.getResultSetIterator(
   8.131 +            sqlFind,new Object[]{key},getValueType());
   8.132 +        if (!iter.hasNext()) {
   8.133 +            return null;
   8.134 +        }
   8.135 +        return iter.next();
   8.136 +    }
   8.137 +    
   8.138 +    // implement SinglevaluedIndex
   8.139 +    public Object getObjectIfExists(Object key, SinglevaluedIndex repos)
   8.140 +        throws StorageException
   8.141 +    {
   8.142 +    	Object val = getIfExists(key);
   8.143 +        if (val == null) {
   8.144 +            return null;
   8.145 +        } else {
   8.146 +            if (keyType == Storage.EntryType.MOFID) {
   8.147 +                return repos.get(val);
   8.148 +            } else {
   8.149 +                return val;
   8.150 +            }
   8.151 +        }
   8.152 +    }
   8.153 +
   8.154 +    // implement SinglevaluedIndex
   8.155 +    public Collection values()
   8.156 +        throws StorageException
   8.157 +    {
   8.158 +        return new JdbcCollection(
   8.159 +            storage,
   8.160 +            getValueType(),
   8.161 +            sqlValuesIterator,sqlValuesSize,sqlValuesContains);
   8.162 +    }
   8.163 +    
   8.164 +    // implement SinglevaluedIndex
   8.165 +    public Collection queryByKeyPrefix(
   8.166 +        Object prefix, SinglevaluedIndex repos) throws StorageException
   8.167 +    {
   8.168 +        // TODO:  optimize with LIKE
   8.169 +        throw new RuntimeException("oops, not yet implemented");
   8.170 +    }
   8.171 +}
   8.172 +
   8.173 +// End JdbcSinglevaluedIndex.java
     9.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     9.2 +++ b/mdr/extras/jdbcstorage/src/org/netbeans/mdr/persistence/jdbcimpl/JdbcStorage.java	Wed Feb 04 06:38:10 2004 +0000
     9.3 @@ -0,0 +1,934 @@
     9.4 +/*
     9.5 + *                 Sun Public License Notice
     9.6 + * 
     9.7 + * The contents of this file are subject to the Sun Public License
     9.8 + * Version 1.0 (the "License"). You may not use this file except in
     9.9 + * compliance with the License. A copy of the License is available at
    9.10 + * http://www.sun.com/
    9.11 + * 
    9.12 + * The Original Code is NetBeans. The Initial Developer of the Original
    9.13 + * Code is Sun Microsystems, Inc. Portions Copyright 1997-2001 Sun
    9.14 + * Microsystems, Inc. All Rights Reserved.
    9.15 + */
    9.16 +package org.netbeans.mdr.persistence.jdbcimpl;
    9.17 +
    9.18 +import org.netbeans.mdr.persistence.*;
    9.19 +import org.netbeans.mdr.util.*;
    9.20 +
    9.21 +import java.sql.*;
    9.22 +import java.util.*;
    9.23 +import java.io.*;
    9.24 +
    9.25 +/**
    9.26 + * JdbcStorage implements the MDR Storage interface using JDBC.
    9.27 + *
    9.28 + * @author John V. Sichi
    9.29 + * @version $Id$
    9.30 + */
    9.31 +class JdbcStorage implements Storage
    9.32 +{
    9.33 +    public static final String MOFID_SEQ_TABLE_NAME = "MOFID_SEQ";
    9.34 +    
    9.35 +    public static final String MOFID_SEQ_COL_NAME = "MOFID_SEQ_NEXT";
    9.36 +    
    9.37 +    public static final String KEY_COL_PREFIX = "IDX_KEY";
    9.38 +    
    9.39 +    public static final String SINGLE_VAL_COL_PREFIX = "IDX_SVAL";
    9.40 +    
    9.41 +    public static final String MULTI_VAL_COL_PREFIX = "IDX_MVAL";
    9.42 +    
    9.43 +    public static final String ORDINAL_COL_NAME = "IDX_ORD";
    9.44 +    
    9.45 +    public static final String SURROGATE_COL_NAME = "IDX_SUR";
    9.46 +
    9.47 +    public static final String PRIMARY_INDEX_NAME = "PRIMARY_INDEX";
    9.48 +    
    9.49 +    private final Connection jdbcConnection;
    9.50 +
    9.51 +    private final DatabaseMetaData dbMetaData;
    9.52 +
    9.53 +    private final Statement jdbcStmt;
    9.54 +    
    9.55 +    private final String idQuote;
    9.56 +
    9.57 +    private final String schemaName;
    9.58 +
    9.59 +    private final String userName;
    9.60 +
    9.61 +    private final String storageId;
    9.62 +
    9.63 +    private final boolean realSchema;
    9.64 +
    9.65 +    private final Map entryTypeToDataTypeMap;
    9.66 +
    9.67 +    private ResultSet jdbcResultSet;
    9.68 +
    9.69 +    private long nextSerialNumber;
    9.70 +    
    9.71 +    private Map nameToIndexMap;
    9.72 +
    9.73 +    private LazyPreparedStatement sqlUpdateSerialNumber;
    9.74 +
    9.75 +    // REVIEW: if this gets too bloated for a big model, may need to turn this
    9.76 +    // into a cache instead of a map
    9.77 +    private Map sqlToPreparedStatementMap;
    9.78 +
    9.79 +    private JdbcPrimaryIndex primaryIndex;
    9.80 +
    9.81 +    JdbcStorage(
    9.82 +        Properties properties,
    9.83 +        String storageId)
    9.84 +        throws StorageException
    9.85 +    {
    9.86 +        this.storageId = storageId;
    9.87 +        
    9.88 +        schemaName = properties.getProperty(
    9.89 +            JdbcStorageFactory.STORAGE_SCHEMA_NAME);
    9.90 +        userName = properties.getProperty(
    9.91 +            JdbcStorageFactory.STORAGE_USER_NAME);
    9.92 +
    9.93 +        entryTypeToDataTypeMap = new HashMap();
    9.94 +        createTypeMap(properties);
    9.95 +        
    9.96 +        String url = properties.getProperty(
    9.97 +            JdbcStorageFactory.STORAGE_URL);
    9.98 +        String password = properties.getProperty(
    9.99 +            JdbcStorageFactory.STORAGE_PASSWORD);
   9.100 +        boolean success = false;
   9.101 +        
   9.102 +        try {
   9.103 +            // REVIEW:  maybe connect/disconnect should correspond to
   9.104 +            // open/close instead of constructor/shutdown?
   9.105 +            jdbcConnection =
   9.106 +                DriverManager.getConnection(url,userName,password);
   9.107 +            jdbcConnection.setAutoCommit(false);
   9.108 +            jdbcStmt = jdbcConnection.createStatement();
   9.109 +            dbMetaData = jdbcConnection.getMetaData();
   9.110 +            realSchema = dbMetaData.supportsSchemasInTableDefinitions();
   9.111 +            idQuote = dbMetaData.getIdentifierQuoteString();
   9.112 +            success = true;
   9.113 +        } catch (SQLException ex) {
   9.114 +            throw new JdbcStorageException(ex);
   9.115 +        } finally {
   9.116 +            if (!success) {
   9.117 +                shutDown();
   9.118 +            }
   9.119 +        }
   9.120 +    }
   9.121 +
   9.122 +    private void createTypeMap(Properties properties)
   9.123 +    {
   9.124 +        entryTypeToDataTypeMap.put(
   9.125 +            EntryType.MOFID,
   9.126 +            properties.getProperty(
   9.127 +                JdbcStorageFactory.STORAGE_DATATYPE_MOFID,
   9.128 +                "BIGINT"));
   9.129 +        
   9.130 +        entryTypeToDataTypeMap.put(
   9.131 +            EntryType.STREAMABLE,
   9.132 +            properties.getProperty(
   9.133 +                JdbcStorageFactory.STORAGE_DATATYPE_STREAMABLE,
   9.134 +                "LONGVARBINARY"));
   9.135 +        
   9.136 +        entryTypeToDataTypeMap.put(
   9.137 +            EntryType.STRING,
   9.138 +            properties.getProperty(
   9.139 +                JdbcStorageFactory.STORAGE_DATATYPE_STRING,
   9.140 +                "VARCHAR(2000)"));
   9.141 +        
   9.142 +        entryTypeToDataTypeMap.put(
   9.143 +            EntryType.INT,            
   9.144 +            properties.getProperty(
   9.145 +                JdbcStorageFactory.STORAGE_DATATYPE_INT,
   9.146 +                "BIGINT"));
   9.147 +    }
   9.148 +
   9.149 +    // implement Storage
   9.150 +    public String getName()
   9.151 +    {
   9.152 +        return schemaName + ".jdbc";
   9.153 +    }
   9.154 +    
   9.155 +    // implement Storage
   9.156 +    public String getStorageId ()
   9.157 +    {
   9.158 +        return storageId;
   9.159 +    }
   9.160 +
   9.161 +    private void writeSerialNumber(long serialNumber)
   9.162 +        throws StorageException
   9.163 +    {
   9.164 +        executeUpdate(
   9.165 +            sqlUpdateSerialNumber,
   9.166 +            new Object[]{new Long(serialNumber)});
   9.167 +    }
   9.168 +    
   9.169 +    // implement Storage
   9.170 +    public synchronized long getSerialNumber()
   9.171 +    {
   9.172 +        return nextSerialNumber++;
   9.173 +    }
   9.174 +    
   9.175 +    // implement Storage
   9.176 +    public MOFID readMOFID (java.io.InputStream inputStream)
   9.177 +        throws StorageException
   9.178 +    {
   9.179 +        // NOTE:  ripped from memoryimpl
   9.180 +        try {
   9.181 +            String storageId = IOUtils.readString(inputStream);
   9.182 +            if (storageId == null) {
   9.183 +                storageId = this.storageId;
   9.184 +            }
   9.185 +            long serial = IOUtils.readLong(inputStream);
   9.186 +            return new MOFID(serial, storageId);
   9.187 +        } catch (java.io.IOException ioException) {
   9.188 +            throw new StorageIOException(ioException);
   9.189 +        }
   9.190 +    }
   9.191 +
   9.192 +    // implement Storage
   9.193 +    public void writeMOFID (java.io.OutputStream outputStream, MOFID mofid)
   9.194 +        throws StorageException
   9.195 +    {
   9.196 +        // NOTE:  ripped from memoryimpl
   9.197 +        try {
   9.198 +            if (storageId.equals(mofid.getStorageID())) {
   9.199 +                IOUtils.writeString(outputStream, null);
   9.200 +            } else {
   9.201 +                IOUtils.writeString(outputStream, mofid.getStorageID());
   9.202 +            }
   9.203 +            IOUtils.writeLong(outputStream, mofid.getSerialNumber());
   9.204 +        } catch (IOException ioException) {
   9.205 +            throw new StorageIOException(ioException);
   9.206 +        }
   9.207 +    }
   9.208 +
   9.209 +    private String getQualifiedTableName(String tableName)
   9.210 +    {
   9.211 +        if (realSchema) {
   9.212 +            return idQuote + schemaName + idQuote + "." + idQuote
   9.213 +                + tableName + idQuote;
   9.214 +        } else {
   9.215 +            return idQuote + schemaName + "_" + tableName + idQuote;
   9.216 +        }
   9.217 +    }
   9.218 +    
   9.219 +    private String getQualifiedSchemaName()
   9.220 +    {
   9.221 +        return idQuote + schemaName + idQuote;
   9.222 +    }
   9.223 +
   9.224 +    private void rollbackConnection()
   9.225 +    {
   9.226 +        if (jdbcResultSet != null) {
   9.227 +            closeResultSet(jdbcResultSet);
   9.228 +        }
   9.229 +        try {
   9.230 +            jdbcConnection.rollback();
   9.231 +        } catch (SQLException ex) {
   9.232 +            // TODO: trace
   9.233 +        }
   9.234 +    }
   9.235 +
   9.236 +    private long readSerialNumber()
   9.237 +    {
   9.238 +        try {
   9.239 +            jdbcResultSet = jdbcStmt.executeQuery(
   9.240 +                "select * from "+getQualifiedTableName(MOFID_SEQ_TABLE_NAME));
   9.241 +            jdbcResultSet.next();
   9.242 +            long x = jdbcResultSet.getLong(1);
   9.243 +            return x;
   9.244 +        } catch (SQLException ex) {
   9.245 +            return -1;
   9.246 +        }
   9.247 +    }
   9.248 +    
   9.249 +    // implement Storage
   9.250 +    public synchronized boolean exists() throws StorageException
   9.251 +    {
   9.252 +        long x = readSerialNumber();
   9.253 +        return x != -1;
   9.254 +    }
   9.255 +    
   9.256 +    // implement Storage
   9.257 +    public synchronized boolean delete() throws StorageException
   9.258 +    {
   9.259 +        rollbackConnection();
   9.260 +        try {
   9.261 +            if (realSchema) {
   9.262 +                jdbcStmt.execute(
   9.263 +                    "drop schema " + getQualifiedSchemaName() + " cascade");
   9.264 +            } else {
   9.265 +                jdbcResultSet = dbMetaData.getTables(
   9.266 +                    null,null,schemaName + "%",null);
   9.267 +                List tables = new ArrayList();
   9.268 +                while (jdbcResultSet.next()) {
   9.269 +                    tables.add(jdbcResultSet.getString("TABLE_NAME"));
   9.270 +                }
   9.271 +                closeResultSet(jdbcResultSet);
   9.272 +                Iterator iter = tables.iterator();
   9.273 +                while (iter.hasNext()) {
   9.274 +                    String tableName = (String) iter.next();
   9.275 +                    jdbcStmt.execute(
   9.276 +                        "drop table " + idQuote + tableName + idQuote);
   9.277 +                }
   9.278 +            }
   9.279 +            jdbcConnection.commit();
   9.280 +            return true;
   9.281 +        } catch (SQLException ex) {
   9.282 +            rollbackConnection();
   9.283 +            return false;
   9.284 +        }
   9.285 +    }
   9.286 +
   9.287 +    private boolean isBlank(String s)
   9.288 +    {
   9.289 +        return (s == null) || (s.length() == 0);
   9.290 +    }
   9.291 +    
   9.292 +    // implement Storage
   9.293 +    public synchronized void create(boolean replace, ObjectResolver resolver)
   9.294 +        throws StorageException
   9.295 +    {
   9.296 +        try {
   9.297 +            if (replace) {
   9.298 +                delete();
   9.299 +            }
   9.300 +            rollbackConnection();
   9.301 +            if (realSchema) {
   9.302 +                String sql = "create schema " + getQualifiedSchemaName();
   9.303 +                if (!isBlank(userName)) {
   9.304 +                    sql = sql + " authorization " + userName;
   9.305 +                }
   9.306 +                jdbcStmt.execute(sql);
   9.307 +            }
   9.308 +            String intType = getDataType(EntryType.INT);
   9.309 +            jdbcStmt.execute(
   9.310 +                "create table " + getQualifiedTableName(MOFID_SEQ_TABLE_NAME)
   9.311 +                + "(" + MOFID_SEQ_COL_NAME + " " + intType
   9.312 +                + " not null primary key)");
   9.313 +            jdbcStmt.executeUpdate(
   9.314 +                "insert into " + getQualifiedTableName(MOFID_SEQ_TABLE_NAME)
   9.315 +                + " values(1)");
   9.316 +            nextSerialNumber = 1;
   9.317 +            openImpl();
   9.318 +            createSinglevaluedIndex(
   9.319 +                PRIMARY_INDEX_NAME,
   9.320 +                EntryType.MOFID,
   9.321 +                EntryType.STREAMABLE);
   9.322 +            loadPrimaryIndex();
   9.323 +            jdbcConnection.commit();
   9.324 +        } catch (SQLException ex) {
   9.325 +            rollbackConnection();
   9.326 +            throw new JdbcStorageException(ex);
   9.327 +        }
   9.328 +    }
   9.329 +    
   9.330 +    // implement Storage
   9.331 +    public synchronized void open(
   9.332 +        boolean createOnNoExist, ObjectResolver resolver)
   9.333 +        throws StorageException
   9.334 +    {
   9.335 +        nextSerialNumber = readSerialNumber();
   9.336 +        if (nextSerialNumber == -1) {
   9.337 +            if (createOnNoExist) {
   9.338 +                create(false,resolver);
   9.339 +                return;
   9.340 +            } else {
   9.341 +                throw new StorageBadRequestException(
   9.342 +                    "Storage " + getName() + " does not exist.");
   9.343 +            }
   9.344 +        }
   9.345 +        try {
   9.346 +            openImpl();
   9.347 +            loadPrimaryIndex();
   9.348 +        } catch (SQLException ex) {
   9.349 +            throw new JdbcStorageException(ex);
   9.350 +        }
   9.351 +    }
   9.352 +
   9.353 +    private void openImpl() throws SQLException
   9.354 +    {
   9.355 +        nameToIndexMap = new HashMap();
   9.356 +        sqlToPreparedStatementMap = new HashMap();
   9.357 +        sqlUpdateSerialNumber = new LazyPreparedStatement(
   9.358 +            "update " + getQualifiedTableName(MOFID_SEQ_TABLE_NAME)
   9.359 +            + " set " + MOFID_SEQ_COL_NAME + " = ?");
   9.360 +    }
   9.361 +
   9.362 +    private void loadPrimaryIndex()
   9.363 +        throws StorageException
   9.364 +    {
   9.365 +        primaryIndex = (JdbcPrimaryIndex) getIndex(PRIMARY_INDEX_NAME);
   9.366 +    }
   9.367 +    
   9.368 +    // implement Storage
   9.369 +    public synchronized void close() throws StorageException
   9.370 +    {
   9.371 +        nameToIndexMap = null;
   9.372 +        sqlUpdateSerialNumber = null;
   9.373 +        if (sqlToPreparedStatementMap != null) {
   9.374 +            Iterator iter = sqlToPreparedStatementMap.values().iterator();
   9.375 +            while (iter.hasNext()) {
   9.376 +                PreparedStatement ps = (PreparedStatement) iter.next();
   9.377 +                closePreparedStatement(ps);
   9.378 +            }
   9.379 +            sqlToPreparedStatementMap = null;
   9.380 +        }
   9.381 +        rollbackConnection();
   9.382 +    }
   9.383 +
   9.384 +    private void closePreparedStatement(PreparedStatement ps)
   9.385 +    {
   9.386 +        if (ps == null) {
   9.387 +            return;
   9.388 +        }
   9.389 +        try {
   9.390 +            ps.close();
   9.391 +        } catch (SQLException ex) {
   9.392 +            // TODO:  trace
   9.393 +        }
   9.394 +    }
   9.395 +
   9.396 +    private void closeStatement(Statement s)
   9.397 +    {
   9.398 +        if (s == null) {
   9.399 +            return;
   9.400 +        }
   9.401 +        try {
   9.402 +            s.close();
   9.403 +        } catch (SQLException ex) {
   9.404 +            // TODO:  trace
   9.405 +        }
   9.406 +    }
   9.407 +
   9.408 +    private void closeResultSet(ResultSet rs)
   9.409 +    {
   9.410 +        if (rs == null) {
   9.411 +            return;
   9.412 +        }
   9.413 +        try {
   9.414 +            rs.close();
   9.415 +        } catch (SQLException ex) {
   9.416 +            // TODO:  trace
   9.417 +        }
   9.418 +    }
   9.419 +
   9.420 +    private void closeConnection(Connection c)
   9.421 +    {
   9.422 +        if (c == null) {
   9.423 +            return;
   9.424 +        }
   9.425 +        try {
   9.426 +            c.close();
   9.427 +        } catch (SQLException ex) {
   9.428 +            // TODO:  trace
   9.429 +        }
   9.430 +    }
   9.431 +
   9.432 +    // implement Storage
   9.433 +    public synchronized SinglevaluedIndex createSinglevaluedIndex(
   9.434 +        String name, EntryType keyType,
   9.435 +        EntryType valueType) throws StorageException
   9.436 +    {
   9.437 +        createIndex(name,keyType,valueType,true,true,false);
   9.438 +        return getSinglevaluedIndex(name);
   9.439 +    }
   9.440 +
   9.441 +    // implement Storage
   9.442 +    public synchronized MultivaluedOrderedIndex createMultivaluedOrderedIndex(
   9.443 +        String name, EntryType keyType, EntryType valueType, boolean unique)
   9.444 +        throws StorageException
   9.445 +    {
   9.446 +        // NOTE:  ignore unique because it appears to lie
   9.447 +        createIndex(name,keyType,valueType,false,false,true);
   9.448 +        return getMultivaluedOrderedIndex(name);
   9.449 +    }
   9.450 +
   9.451 +    // implement Storage
   9.452 +    public synchronized MultivaluedIndex createMultivaluedIndex(
   9.453 +        String name, EntryType keyType, EntryType valueType, boolean unique)
   9.454 +        throws StorageException
   9.455 +    {
   9.456 +        createIndex(name,keyType,valueType,false,unique,false);
   9.457 +        return getMultivaluedIndex(name);
   9.458 +    }
   9.459 +
   9.460 +    private String getDataType(EntryType entryType)
   9.461 +    {
   9.462 +        return (String) entryTypeToDataTypeMap.get(entryType);
   9.463 +    }
   9.464 +
   9.465 +    private EntryType getEntryType(ResultSetMetaData md,int i)
   9.466 +        throws SQLException
   9.467 +    {
   9.468 +        String colName = md.getColumnName(i).toUpperCase();
   9.469 +        int lastUnderscore = colName.lastIndexOf('_');
   9.470 +        String entryTypeName =
   9.471 +            colName.substring(lastUnderscore + 1);
   9.472 +        return EntryType.decodeEntryType(entryTypeName);
   9.473 +    }
   9.474 +
   9.475 +    private String stripMofId(String indexName)
   9.476 +    {
   9.477 +        int i = indexName.indexOf(storageId);
   9.478 +        if (i == -1) {
   9.479 +            return indexName;
   9.480 +        }
   9.481 +        int j = i + storageId.length();
   9.482 +        if (indexName.charAt(j) != ':') {
   9.483 +            return indexName;
   9.484 +        }
   9.485 +        int n = indexName.length();
   9.486 +        for (++j; j < n; ++j) {
   9.487 +            if (indexName.charAt(j) != '0') {
   9.488 +                break;
   9.489 +            }
   9.490 +        }
   9.491 +        return indexName.substring(0,i) + indexName.substring(j);
   9.492 +    }
   9.493 +    
   9.494 +    private String getTableNameForIndex(String indexName)
   9.495 +    {
   9.496 +        // Assume we're getting something of the form
   9.497 +        // <prefix>:<mofid1>:<mofid2> for indexName.
   9.498 +        // Replace this with
   9.499 +        // <prefix>:<serial1>:<serial2>
   9.500 +        indexName = stripMofId(indexName);
   9.501 +        indexName = stripMofId(indexName);
   9.502 +        return getQualifiedTableName(indexName);
   9.503 +    }
   9.504 +    
   9.505 +    private void createIndex(
   9.506 +        String name, EntryType keyType, EntryType valueType,
   9.507 +        boolean singleValued,boolean uniqueValued,boolean ordered)
   9.508 +        throws StorageException
   9.509 +    {
   9.510 +        try {
   9.511 +            StringBuffer sb = new StringBuffer();
   9.512 +            sb.append("create table ");
   9.513 +            sb.append(getTableNameForIndex(name));
   9.514 +            sb.append("(");
   9.515 +
   9.516 +            String keyColName = KEY_COL_PREFIX + "_" + keyType;
   9.517 +            sb.append(keyColName);
   9.518 +            sb.append(" ");
   9.519 +            sb.append(getDataType(keyType));
   9.520 +            sb.append(" not null, ");
   9.521 +
   9.522 +            String valColName;
   9.523 +            if (singleValued) {
   9.524 +                valColName = SINGLE_VAL_COL_PREFIX;
   9.525 +            } else {
   9.526 +                valColName = MULTI_VAL_COL_PREFIX;
   9.527 +            }
   9.528 +            valColName = valColName + "_" + valueType;
   9.529 +            sb.append(valColName);
   9.530 +            sb.append(" ");
   9.531 +            sb.append(getDataType(valueType));
   9.532 +            sb.append(" not null,");
   9.533 +
   9.534 +            String intType = getDataType(EntryType.INT);
   9.535 +            
   9.536 +            if (ordered) {
   9.537 +                sb.append(ORDINAL_COL_NAME);
   9.538 +                sb.append(" ");
   9.539 +                sb.append(intType);
   9.540 +                sb.append(" not null,");
   9.541 +            }
   9.542 +
   9.543 +            if (!uniqueValued) {
   9.544 +                sb.append(SURROGATE_COL_NAME);
   9.545 +                sb.append(" ");
   9.546 +                sb.append(intType);
   9.547 +                sb.append(" not null,");
   9.548 +            }
   9.549 +            
   9.550 +            sb.append(" primary key(");
   9.551 +            sb.append(keyColName);
   9.552 +            if (singleValued) {
   9.553 +                // nothing more needed
   9.554 +            } else if (uniqueValued) {
   9.555 +                sb.append(",");
   9.556 +                sb.append(valColName);
   9.557 +            } else {
   9.558 +                if (ordered) {
   9.559 +                    // NOTE: could use just (KEY,ORDINAL) as primary key.
   9.560 +                    // However, we have to be able to modify ordinals, and even
   9.561 +                    // PostgreSQL doesn't get the deferred constraint
   9.562 +                    // enforcement right in that case).  So, throw in both the
   9.563 +                    // ORDINAL and the SURROGATE so that ORDER BY can use the
   9.564 +                    // index.
   9.565 +                    sb.append(",");
   9.566 +                    sb.append(ORDINAL_COL_NAME);
   9.567 +                }
   9.568 +                sb.append(",");
   9.569 +                sb.append(SURROGATE_COL_NAME);
   9.570 +            }
   9.571 +            sb.append(")");
   9.572 +            
   9.573 +            sb.append(")");
   9.574 +            jdbcStmt.execute(sb.toString());
   9.575 +        } catch (SQLException ex) {
   9.576 +            throw new JdbcStorageException(ex);
   9.577 +        }
   9.578 +    }
   9.579 +
   9.580 +    // implement Storage
   9.581 +    public synchronized SinglevaluedIndex getPrimaryIndex()
   9.582 +        throws StorageException
   9.583 +    {
   9.584 +        return getSinglevaluedIndex(PRIMARY_INDEX_NAME);
   9.585 +    }
   9.586 +
   9.587 +    private Index loadIndex(String name)
   9.588 +        throws StorageException
   9.589 +    {
   9.590 +        PreparedStatement ps = null;
   9.591 +        try {
   9.592 +            ps = jdbcConnection.prepareStatement(
   9.593 +                "select * from "+getTableNameForIndex(name));
   9.594 +            ResultSetMetaData md = null;
   9.595 +            try {
   9.596 +                md = ps.getMetaData();
   9.597 +            } catch (SQLException ex) {
   9.598 +                // Some drivers don't support metadata pre-execution.
   9.599 +                // Fall through to recovery below.
   9.600 +            }
   9.601 +            if (md == null) {
   9.602 +                jdbcResultSet = ps.executeQuery();
   9.603 +                md = jdbcResultSet.getMetaData();
   9.604 +            }
   9.605 +            EntryType keyType = getEntryType(md,1);
   9.606 +            EntryType valueType = getEntryType(md,2);
   9.607 +            boolean singleValued = false;
   9.608 +            boolean ordered = false;
   9.609 +            boolean needSurrogate = false;
   9.610 +            String keyColName = md.getColumnName(1).toUpperCase();
   9.611 +            String valColName = md.getColumnName(2).toUpperCase();
   9.612 +            if (valColName.startsWith(SINGLE_VAL_COL_PREFIX)) {
   9.613 +                singleValued = true;
   9.614 +            }
   9.615 +            for (int i = 3; i <= md.getColumnCount(); ++i) {
   9.616 +                String colName = md.getColumnName(i).toUpperCase();
   9.617 +                if (colName.equals(ORDINAL_COL_NAME)) {
   9.618 +                    ordered = true;
   9.619 +                } else if (colName.equals(SURROGATE_COL_NAME)) {
   9.620 +                    needSurrogate = true;
   9.621 +                } else {
   9.622 +                    // TODO:  assert
   9.623 +                }
   9.624 +            }
   9.625 +            JdbcIndex index;
   9.626 +            if (singleValued) {
   9.627 +                if (name.equals(PRIMARY_INDEX_NAME)) {
   9.628 +                    index = new JdbcPrimaryIndex();
   9.629 +                } else {
   9.630 +                    index = new JdbcSinglevaluedIndex();
   9.631 +                }
   9.632 +            } else {
   9.633 +                if (ordered) {
   9.634 +                    index = new JdbcMultivaluedOrderedIndex();
   9.635 +                } else {
   9.636 +                    index = new JdbcMultivaluedIndex();
   9.637 +                }
   9.638 +            }
   9.639 +            index.init(
   9.640 +                this,
   9.641 +                getTableNameForIndex(name),
   9.642 +                name,keyColName,valColName,keyType,valueType,
   9.643 +                needSurrogate);
   9.644 +            nameToIndexMap.put(name,index);
   9.645 +            return index;
   9.646 +        } catch (SQLException ex) {
   9.647 +            throw new JdbcStorageException(ex);
   9.648 +        } finally {
   9.649 +            closeResultSet(jdbcResultSet);
   9.650 +            closePreparedStatement(ps);
   9.651 +        }
   9.652 +    }
   9.653 +
   9.654 +    // implement Storage
   9.655 +    public synchronized Index getIndex(String name) throws StorageException
   9.656 +    {
   9.657 +        synchronized(nameToIndexMap) {
   9.658 +            Index index = (Index) nameToIndexMap.get(name);
   9.659 +            if (index == null) {
   9.660 +                index = loadIndex(name);
   9.661 +            }
   9.662 +            return index;
   9.663 +        }
   9.664 +    }
   9.665 +    
   9.666 +    // implement Storage
   9.667 +    public synchronized SinglevaluedIndex getSinglevaluedIndex(String name)
   9.668 +        throws StorageException
   9.669 +    {
   9.670 +        return (SinglevaluedIndex) getIndex(name);
   9.671 +    }
   9.672 +    
   9.673 +    // implement Storage
   9.674 +    public synchronized MultivaluedIndex getMultivaluedIndex(String name)
   9.675 +        throws StorageException
   9.676 +    {
   9.677 +        return (MultivaluedIndex) getIndex(name);
   9.678 +    }
   9.679 +    
   9.680 +    // implement Storage
   9.681 +    public synchronized MultivaluedOrderedIndex getMultivaluedOrderedIndex(
   9.682 +        String name) throws StorageException
   9.683 +    {
   9.684 +        return (MultivaluedOrderedIndex) getIndex(name);
   9.685 +    }
   9.686 +    
   9.687 +    // implement Storage
   9.688 +    public synchronized void dropIndex(String name) throws StorageException
   9.689 +    {
   9.690 +        try {
   9.691 +            jdbcStmt.execute("drop table "+getTableNameForIndex(name));
   9.692 +        } catch (SQLException ex) {
   9.693 +            throw new JdbcStorageException(ex);
   9.694 +        }
   9.695 +        nameToIndexMap.remove(name);
   9.696 +    }
   9.697 +
   9.698 +    // implement Storage
   9.699 +    public void objectStateWillChange(Object key) throws StorageException
   9.700 +    {
   9.701 +        // ignore
   9.702 +    }
   9.703 +    
   9.704 +    // implement Storage
   9.705 +    public synchronized void objectStateChanged(Object key)
   9.706 +        throws StorageException
   9.707 +    {
   9.708 +        primaryIndex.objectStateChanged(key);
   9.709 +    }
   9.710 +    
   9.711 +    // implement Storage
   9.712 +    public synchronized void commitChanges() throws StorageException
   9.713 +    {
   9.714 +        try {
   9.715 +            writeSerialNumber(nextSerialNumber);
   9.716 +            primaryIndex.flushChanges();
   9.717 +            jdbcConnection.commit();
   9.718 +        } catch (SQLException ex) {
   9.719 +            throw new JdbcStorageException(ex);
   9.720 +        }
   9.721 +    }
   9.722 +
   9.723 +    // implement Storage
   9.724 +    public synchronized void rollBackChanges () throws StorageException
   9.725 +    {
   9.726 +        try {
   9.727 +            jdbcConnection.rollback();
   9.728 +        } catch (SQLException ex) {
   9.729 +            throw new JdbcStorageException(ex);
   9.730 +        }
   9.731 +        // NOTE:  this is taken from BtreeStorage.  It seems rather brutal,
   9.732 +        // but it means that we don't have to worry about state inconsistencies
   9.733 +        // with nameToIndexMap, primary index cache, etc.
   9.734 +        close();
   9.735 +        open(false,null);
   9.736 +    }
   9.737 +    
   9.738 +    // implement Storage
   9.739 +    public synchronized void shutDown() throws StorageException
   9.740 +    {
   9.741 +        closeStatement(jdbcStmt);
   9.742 +        rollbackConnection();
   9.743 +        closeConnection(jdbcConnection);
   9.744 +    }
   9.745 +
   9.746 +    private PreparedStatement prepareStatement(
   9.747 +        LazyPreparedStatement lps)
   9.748 +        throws StorageException
   9.749 +    {
   9.750 +        if (lps.ps != null) {
   9.751 +            // lps is already prepared
   9.752 +            return lps.ps;
   9.753 +        }
   9.754 +        PreparedStatement ps = (PreparedStatement)
   9.755 +            sqlToPreparedStatementMap.get(lps.sql);
   9.756 +        if (ps != null) {
   9.757 +            lps.ps = ps;
   9.758 +            return ps;
   9.759 +        }
   9.760 +        try {
   9.761 +            ps = jdbcConnection.prepareStatement(lps.sql);
   9.762 +            sqlToPreparedStatementMap.put(lps.sql,ps);
   9.763 +            lps.ps = ps;
   9.764 +            return ps;
   9.765 +        } catch (SQLException ex) {
   9.766 +            throw new JdbcStorageException(ex);
   9.767 +        }
   9.768 +    }
   9.769 +
   9.770 +    private void bindArgs(PreparedStatement ps,Object [] args)
   9.771 +        throws SQLException, StorageException
   9.772 +    {
   9.773 +        if (args == null) {
   9.774 +            return;
   9.775 +        }
   9.776 +        for (int i = 0; i < args.length; ++i) {
   9.777 +            Object arg = args[i];
   9.778 +            int iParam = i + 1;
   9.779 +            if (arg instanceof MOFID) {
   9.780 +                MOFID mofid = (MOFID) arg;
   9.781 +                if (!mofid.getStorageID().equals(storageId)) {
   9.782 +                    throw new IllegalArgumentException("Foreign MOFID");
   9.783 +                }
   9.784 +                ps.setLong(
   9.785 +                    iParam,
   9.786 +                    mofid.getSerialNumber());
   9.787 +            } else if (arg instanceof Streamable) {
   9.788 +                ps.setBytes(
   9.789 +                    iParam,
   9.790 +                    writeByteArray((Streamable) arg));
   9.791 +            } else {
   9.792 +                ps.setObject(
   9.793 +                    iParam,
   9.794 +                    arg);
   9.795 +            }
   9.796 +        }
   9.797 +    }
   9.798 +
   9.799 +    private Object getResultObj(ResultSet jdbcResultSet,EntryType entryType) 
   9.800 +        throws StorageException, SQLException
   9.801 +    {
   9.802 +        if (entryType == EntryType.MOFID) {
   9.803 +            return new MOFID(
   9.804 +                jdbcResultSet.getLong(1),
   9.805 +                storageId);
   9.806 +        } else if (entryType == EntryType.STRING) {
   9.807 +            return jdbcResultSet.getString(1);
   9.808 +        } else if (entryType == EntryType.INT) {
   9.809 +            return new Long(jdbcResultSet.getLong(1));
   9.810 +        } else {
   9.811 +            return readByteArray(jdbcResultSet.getBytes(1));
   9.812 +        }
   9.813 +    }
   9.814 +
   9.815 +    synchronized ListIterator getResultSetIterator(
   9.816 +        LazyPreparedStatement lps,Object [] args,EntryType entryType)
   9.817 +        throws StorageException
   9.818 +    {
   9.819 +        try {
   9.820 +            PreparedStatement ps = prepareStatement(lps);
   9.821 +            bindArgs(ps,args);
   9.822 +            jdbcResultSet = ps.executeQuery();
   9.823 +            // TODO:  assert exactly one column
   9.824 +            try {
   9.825 +                List list = new ArrayList();
   9.826 +                while (jdbcResultSet.next()) {
   9.827 +                    Object obj = getResultObj(jdbcResultSet,entryType);
   9.828 +                    list.add(obj);
   9.829 +                }
   9.830 +                return list.listIterator();
   9.831 +            } finally {
   9.832 +                closeResultSet(jdbcResultSet);
   9.833 +            }
   9.834 +        } catch (SQLException ex) {
   9.835 +            throw new JdbcStorageException(ex);
   9.836 +        }
   9.837 +    }
   9.838 +
   9.839 +    synchronized int getResultSetCount(
   9.840 +        LazyPreparedStatement lps,Object [] args)
   9.841 +        throws StorageException
   9.842 +    {
   9.843 +        try {
   9.844 +            PreparedStatement ps = prepareStatement(lps);
   9.845 +            bindArgs(ps,args);
   9.846 +            jdbcResultSet = ps.executeQuery();
   9.847 +            try {
   9.848 +                int n = 0;
   9.849 +                while (jdbcResultSet.next()) {
   9.850 +                    ++n;
   9.851 +                }
   9.852 +                return n;
   9.853 +            } finally {
   9.854 +                closeResultSet(jdbcResultSet);
   9.855 +            }
   9.856 +        } catch (SQLException ex) {
   9.857 +            throw new JdbcStorageException(ex);
   9.858 +        }
   9.859 +    }
   9.860 +
   9.861 +    synchronized int getSingletonInt(
   9.862 +        LazyPreparedStatement lps,Object [] args)
   9.863 +        throws StorageException
   9.864 +    {
   9.865 +        try {
   9.866 +            PreparedStatement ps = prepareStatement(lps);
   9.867 +            bindArgs(ps,args);
   9.868 +            jdbcResultSet = ps.executeQuery();
   9.869 +            try {
   9.870 +                // TODO:  assert exactly one row, one column
   9.871 +                jdbcResultSet.next();
   9.872 +                return jdbcResultSet.getInt(1);
   9.873 +            } finally {
   9.874 +                closeResultSet(jdbcResultSet);
   9.875 +            }
   9.876 +        } catch (SQLException ex) {
   9.877 +            throw new JdbcStorageException(ex);
   9.878 +        }
   9.879 +    }
   9.880 +
   9.881 +    private byte [] writeByteArray(Streamable data)
   9.882 +        throws StorageException
   9.883 +    {
   9.884 +        try {
   9.885 +            ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
   9.886 +            DataOutputStream dataStream = new DataOutputStream(byteStream);
   9.887 +            dataStream.writeUTF(data.getClass().getName());
   9.888 +            data.write(dataStream);
   9.889 +            dataStream.flush();
   9.890 +            return byteStream.toByteArray();
   9.891 +        } catch (IOException ex) {
   9.892 +            throw new StorageIOException(ex);
   9.893 +        }
   9.894 +    }
   9.895 +
   9.896 +    private Streamable readByteArray(byte [] bytes)
   9.897 +        throws StorageException
   9.898 +    {
   9.899 +        ByteArrayInputStream byteStream = new ByteArrayInputStream(bytes);
   9.900 +        DataInputStream dataStream = new DataInputStream(byteStream);
   9.901 +
   9.902 +        // TODO:  rip classCode stuff from BtreeDatabase
   9.903 +        String className;
   9.904 +        Streamable data;
   9.905 +        
   9.906 +        try {
   9.907 +            className = dataStream.readUTF();
   9.908 +        } catch (IOException ex) {
   9.909 +            throw new StorageIOException(ex);
   9.910 +        }
   9.911 +        try {
   9.912 +            Class cls = Class.forName(className);
   9.913 +            data = (Streamable)cls.newInstance();
   9.914 +        } catch (Exception ex) {
   9.915 +            throw new StoragePersistentDataException(ex.getMessage());
   9.916 +        }
   9.917 +        if (data instanceof StorageClient) {
   9.918 +            ((StorageClient)data).setStorage(this);
   9.919 +        }
   9.920 +        data.read(dataStream);
   9.921 +        return data;
   9.922 +    }
   9.923 +
   9.924 +    synchronized int executeUpdate(LazyPreparedStatement lps,Object [] args)
   9.925 +        throws StorageException
   9.926 +    {
   9.927 +        try {
   9.928 +            PreparedStatement ps = prepareStatement(lps);
   9.929 +            bindArgs(ps,args);
   9.930 +            return ps.executeUpdate();
   9.931 +        } catch (SQLException ex) {
   9.932 +            throw new JdbcStorageException(ex);
   9.933 +        }
   9.934 +    }
   9.935 +}
   9.936 +
   9.937 +// End JdbcStorage.java
    10.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    10.2 +++ b/mdr/extras/jdbcstorage/src/org/netbeans/mdr/persistence/jdbcimpl/JdbcStorageException.java	Wed Feb 04 06:38:10 2004 +0000
    10.3 @@ -0,0 +1,80 @@
    10.4 +/*
    10.5 + *                 Sun Public License Notice
    10.6 + * 
    10.7 + * The contents of this file are subject to the Sun Public License
    10.8 + * Version 1.0 (the "License"). You may not use this file except in
    10.9 + * compliance with the License. A copy of the License is available at
   10.10 + * http://www.sun.com/
   10.11 + * 
   10.12 + * The Original Code is NetBeans. The Initial Developer of the Original
   10.13 + * Code is Sun Microsystems, Inc. Portions Copyright 1997-2001 Sun
   10.14 + * Microsystems, Inc. All Rights Reserved.
   10.15 + */
   10.16 +package org.netbeans.mdr.persistence.jdbcimpl;
   10.17 +
   10.18 +import org.netbeans.mdr.persistence.*;
   10.19 +import java.sql.*;
   10.20 +import java.io.*;
   10.21 +
   10.22 +/**
   10.23 + * This is thrown to wrap an SQLException as a StorageException.
   10.24 + *
   10.25 + * @author John V. Sichi
   10.26 + * @version $Id$
   10.27 + *
   10.28 + * @see StorageIOException
   10.29 + */
   10.30 +public class JdbcStorageException extends StorageException
   10.31 +{
   10.32 +    /**
   10.33 +     * The original SQLException that we are converting to a StorageException.
   10.34 +     */
   10.35 +    private SQLException sqlException;
   10.36 +
   10.37 +    /** this constructs a JdbcStorageException from an SQLException
   10.38 +    * @param err the SQLException
   10.39 +    */
   10.40 +    public JdbcStorageException(SQLException err)
   10.41 +    {
   10.42 +        super(err.getMessage());
   10.43 +        sqlException = err;
   10.44 +    }
   10.45 +
   10.46 +    /** return the original SQLException's message */
   10.47 +    public String getMessage()
   10.48 +    {
   10.49 +        return sqlException.getMessage();
   10.50 +    }
   10.51 +	
   10.52 +    /** return the original SQLException's localized message */
   10.53 +    public String getLocalizedMessage()
   10.54 +    {
   10.55 +        return sqlException.getLocalizedMessage();
   10.56 +    }
   10.57 +
   10.58 +    /** print the original SQLException's stack trace */
   10.59 +    public void printStackTrace()
   10.60 +    {
   10.61 +        sqlException.printStackTrace();
   10.62 +    }
   10.63 +
   10.64 +    /** print the original SQLException's stack trace */
   10.65 +    public void printStackTrace(PrintStream ps)
   10.66 +    {
   10.67 +        sqlException.printStackTrace(ps);
   10.68 +    }
   10.69 +
   10.70 +    /** print the original SQLException's stack trace */
   10.71 +    public void printStackTrace(PrintWriter pw)
   10.72 +    {
   10.73 +        sqlException.printStackTrace(pw);
   10.74 +    }
   10.75 +
   10.76 +    /** get the original SQLException's cause */
   10.77 +    public Throwable getCause()
   10.78 +    {
   10.79 +        return sqlException.getNextException();
   10.80 +    }
   10.81 +}
   10.82 +
   10.83 +// End JdbcStorageException.java
    11.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    11.2 +++ b/mdr/extras/jdbcstorage/src/org/netbeans/mdr/persistence/jdbcimpl/JdbcStorageFactory.java	Wed Feb 04 06:38:10 2004 +0000
    11.3 @@ -0,0 +1,93 @@
    11.4 +/*
    11.5 + *                 Sun Public License Notice
    11.6 + * 
    11.7 + * The contents of this file are subject to the Sun Public License
    11.8 + * Version 1.0 (the "License"). You may not use this file except in
    11.9 + * compliance with the License. A copy of the License is available at
   11.10 + * http://www.sun.com/
   11.11 + * 
   11.12 + * The Original Code is NetBeans. The Initial Developer of the Original
   11.13 + * Code is Sun Microsystems, Inc. Portions Copyright 1997-2001 Sun
   11.14 + * Microsystems, Inc. All Rights Reserved.
   11.15 + */
   11.16 +package org.netbeans.mdr.persistence.jdbcimpl;
   11.17 +
   11.18 +import org.netbeans.mdr.persistence.*;
   11.19 +
   11.20 +import java.sql.*;
   11.21 +import java.util.*;
   11.22 +
   11.23 +/**
   11.24 + * JdbcStorageFactory implements the StorageFactory interface by
   11.25 + * creating instances of JdbcStorage.
   11.26 + *
   11.27 + * @author John V. Sichi
   11.28 + * @version $Id$
   11.29 + */
   11.30 +public class JdbcStorageFactory implements StorageFactory
   11.31 +{
   11.32 +    public static final String STORAGE_URL =
   11.33 +    "org.netbeans.mdr.persistence.jdbcimpl.url";
   11.34 +    
   11.35 +    public static final String STORAGE_SCHEMA_NAME =
   11.36 +    "org.netbeans.mdr.persistence.jdbcimpl.schemaName";
   11.37 +
   11.38 +    public static final String STORAGE_USER_NAME =
   11.39 +    "org.netbeans.mdr.persistence.jdbcimpl.userName";
   11.40 +
   11.41 +    public static final String STORAGE_PASSWORD =
   11.42 +    "org.netbeans.mdr.persistence.jdbcimpl.password";
   11.43 +
   11.44 +    public static final String STORAGE_DRIVER_CLASS_NAME =
   11.45 +    "org.netbeans.mdr.persistence.jdbcimpl.driverClassName";
   11.46 +
   11.47 +    public static final String STORAGE_DATATYPE_MOFID =
   11.48 +    "org.netbeans.mdr.persistence.jdbcimpl.datatype.mofid";
   11.49 +
   11.50 +    public static final String STORAGE_DATATYPE_STREAMABLE =
   11.51 +    "org.netbeans.mdr.persistence.jdbcimpl.datatype.streamable";
   11.52 +
   11.53 +    public static final String STORAGE_DATATYPE_STRING =
   11.54 +    "org.netbeans.mdr.persistence.jdbcimpl.datatype.string";
   11.55 +
   11.56 +    public static final String STORAGE_DATATYPE_INT =
   11.57 +    "org.netbeans.mdr.persistence.jdbcimpl.datatype.int";
   11.58 +
   11.59 +    // ripped from memoryimpl
   11.60 +    private static final String NULL_STORAGE_ID = ".";
   11.61 +    private static final MOFID NULL_MOFID = new MOFID(0, NULL_STORAGE_ID);
   11.62 +    
   11.63 +    // implement StorageFactory
   11.64 +    public Storage createStorage(Map ignoredProperties) throws StorageException
   11.65 +    {
   11.66 +        // TODO:  use parameter once Netbeans knows about our properties?
   11.67 +        Properties properties = System.getProperties();
   11.68 +        
   11.69 +        String url = properties.getProperty(STORAGE_URL);
   11.70 +        String schemaName = properties.getProperty(STORAGE_SCHEMA_NAME);
   11.71 +        String driverClassName =
   11.72 +            properties.getProperty(STORAGE_DRIVER_CLASS_NAME);
   11.73 +        if (url == null || schemaName == null) {
   11.74 +            throw new StorageBadRequestException(
   11.75 +                "JdbcStorageFactory requires parameters "
   11.76 +                + STORAGE_URL + " and " + STORAGE_SCHEMA_NAME);
   11.77 +        }
   11.78 +        if (driverClassName != null) {
   11.79 +            try {
   11.80 +                Class.forName(driverClassName);
   11.81 +            } catch (ClassNotFoundException ex) {
   11.82 +                // let driver manager report "no suitable driver" when
   11.83 +                // the connection is attempted
   11.84 +            }
   11.85 +        }
   11.86 +        return new JdbcStorage(properties,NULL_STORAGE_ID);
   11.87 +    }
   11.88 +    
   11.89 +    // implement StorageFactory
   11.90 +    public MOFID createNullMOFID() throws StorageException
   11.91 +    {
   11.92 +        return NULL_MOFID;
   11.93 +    }
   11.94 +}
   11.95 +
   11.96 +// End JdbcStorageFactory.java
    12.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    12.2 +++ b/mdr/extras/jdbcstorage/src/org/netbeans/mdr/persistence/jdbcimpl/LazyPreparedStatement.java	Wed Feb 04 06:38:10 2004 +0000
    12.3 @@ -0,0 +1,35 @@
    12.4 +/*
    12.5 + *                 Sun Public License Notice
    12.6 + * 
    12.7 + * The contents of this file are subject to the Sun Public License
    12.8 + * Version 1.0 (the "License"). You may not use this file except in
    12.9 + * compliance with the License. A copy of the License is available at
   12.10 + * http://www.sun.com/
   12.11 + * 
   12.12 + * The Original Code is NetBeans. The Initial Developer of the Original
   12.13 + * Code is Sun Microsystems, Inc. Portions Copyright 1997-2001 Sun
   12.14 + * Microsystems, Inc. All Rights Reserved.
   12.15 + */
   12.16 +package org.netbeans.mdr.persistence.jdbcimpl;
   12.17 +
   12.18 +import java.sql.*;
   12.19 +
   12.20 +/**
   12.21 + * LazyPreparedStatement is a wrapper for a JDBC PreparedStatement
   12.22 + * which can be prepared on demand.
   12.23 + *
   12.24 + * @author John V. Sichi
   12.25 + * @version $Id$
   12.26 + */
   12.27 +class LazyPreparedStatement
   12.28 +{
   12.29 +    final String sql;
   12.30 +    PreparedStatement ps;
   12.31 +
   12.32 +    public LazyPreparedStatement(String sql)
   12.33 +    {
   12.34 +        this.sql = sql;
   12.35 +    }
   12.36 +}
   12.37 +
   12.38 +// End LazyPreparedStatement.java
    13.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    13.2 +++ b/mdr/extras/jdbcstorage/src/org/netbeans/mdr/persistence/jdbcimpl/package.html	Wed Feb 04 06:38:10 2004 +0000
    13.3 @@ -0,0 +1,313 @@
    13.4 +<!--
    13.5 +                Sun Public License Notice
    13.6 +
    13.7 +The contents of this file are subject to the Sun Public License
    13.8 +Version 1.0 (the "License"). You may not use this file except in
    13.9 +compliance with the License. A copy of the License is available at
   13.10 +http://www.sun.com/
   13.11 +
   13.12 +The Original Code is NetBeans. The Initial Developer of the Original
   13.13 +Code is Sun Microsystems, Inc. Portions Copyright 1997-2001 Sun
   13.14 +Microsystems, Inc. All Rights Reserved.
   13.15 +-->
   13.16 +
   13.17 +<HTML>
   13.18 +<body>
   13.19 +
   13.20 +Provides an implementation for the {@link org.netbeans.mdr.persistence
   13.21 +abstract MDR persistence interfaces } in terms of {@link java.sql JDBC
   13.22 +}.
   13.23 +
   13.24 +<h2>JDBC Persistence for MDR</h2>
   13.25 +
   13.26 +The JdbcStorage implementation relies on a JDBC driver and associated
   13.27 +SQL DBMS for transactional persistence.  The DBMS can be a
   13.28 +lightweight Java database running in the same process as MDR, a
   13.29 +heavyweight database running out-of-process on the same machine, or
   13.30 +even across the network (although performance may be poor in this
   13.31 +configuration).
   13.32 +
   13.33 +<h3>Parameters</h3>
   13.34 +
   13.35 +In order to use JdbcStorage, some mandatory properties must be
   13.36 +specified:
   13.37 +
   13.38 +<ul>
   13.39 +
   13.40 +<li>org.netbeans.mdr.storagemodel.StorageFactoryClassName = 
   13.41 +org.netbeans.mdr.persistence.jdbcimpl.JdbcStorageFactory
   13.42 +
   13.43 +<li>org.netbeans.mdr.persistence.jdbcimpl.driverClassName = the fully
   13.44 +qualified name of the JDBC driver to use (e.g. org.postgresql.Driver)
   13.45 +
   13.46 +<li>org.netbeans.mdr.persistence.jdbcimpl.url = the JDBC URL to use to
   13.47 +connect to the database (e.g. jdbc:postgresql://localhost/mdrdb)
   13.48 +
   13.49 +<li>org.netbeans.mdr.persistence.jdbcimpl.schemaName = the name of the
   13.50 +schema which will contain the MDR tables; this schema will be created
   13.51 +automatically by JdbcStorage, so it should not be created manually
   13.52 +
   13.53 +</ul>
   13.54 +
   13.55 +There are also some optional connection parameters:
   13.56 +
   13.57 +<ul>
   13.58 +
   13.59 +<li>org.netbeans.mdr.persistence.jdbcimpl.userName = 
   13.60 +user name for connection; this user must have the right to create the
   13.61 +schema and tables in the database
   13.62 +
   13.63 +<li>org.netbeans.mdr.persistence.jdbcimpl.password = 
   13.64 +password corresponding to userName
   13.65 +
   13.66 +The next section defines additional parameters affecting
   13.67 +DBMS-specific usage of SQL.
   13.68 +
   13.69 +</ul>
   13.70 +
   13.71 +<h3>Index to Table Mapping</h3>
   13.72 +
   13.73 +JdbcStorage creates one table per MDR index.  It also creates one
   13.74 +private table (MOFID_SEQ) for implementing the MOFID sequence
   13.75 +generator.  No metadata tables are created; instead, JdbcStorage
   13.76 +relies on DBMS metadata.  This avoids redundancy and the
   13.77 +possibility for inconsistency, and makes it easier to see the
   13.78 +relationship between MDR objects and their SQL implementations.
   13.79 +
   13.80 +Datatypes for index key and value columns can be controlled via the
   13.81 +properties listed below.  The defaults may not be supported by every
   13.82 +DBMS.  For example, PostgreSQL requires usage of type BYTEA in place
   13.83 +of LONGVARBINARY.  Also, users may wish to override the default
   13.84 +precision depending on the application.
   13.85 +
   13.86 +<ul>
   13.87 +
   13.88 +<li>org.netbeans.mdr.persistence.jdbcimpl.datatype.mofid:  
   13.89 +default BIGINT
   13.90 +
   13.91 +<li>org.netbeans.mdr.persistence.jdbcimpl.datatype.streamable:
   13.92 +default LONGVARBINARY
   13.93 +
   13.94 +<li>org.netbeans.mdr.persistence.jdbcimpl.datatype.string:
   13.95 +default VARCHAR(2000)
   13.96 +
   13.97 +<li>org.netbeans.mdr.persistence.jdbcimpl.datatype.int:
   13.98 +default BIGINT
   13.99 +</ul>
  13.100 +
  13.101 +Additional columns may be created as follows (using the datatype
  13.102 +specified by the datatype.int property above):
  13.103 +
  13.104 +<ul>
  13.105 +
  13.106 +<li>
  13.107 +For a MultivaluedOrderedIndex, a 0-based ordinal column is used to
  13.108 +implement ordering.
  13.109 +
  13.110 +<li>
  13.111 +For a MultivaluedIndex with non-unique values, an additional surrogate
  13.112 +key column is used to ensure that each stored row has a unique key.
  13.113 +Surrogate key values are generated from the same sequence as MOFID
  13.114 +serial numbers.
  13.115 +
  13.116 +</ul>
  13.117 +
  13.118 +The primary key is defined as the key column, plus the ordinal and
  13.119 +surrogate key columns (if any).
  13.120 +
  13.121 +<h3>Table Names</h3>
  13.122 +
  13.123 +If the DBMS does not support schemas, JdbcStorage fakes them by
  13.124 +prepending the specified schema name to the name of each table created
  13.125 +(e.g. MDR_MOFID_SEQ).  In this case, a distinguishing value should be
  13.126 +used for the schema name, since JdbcStorage.delete() will delete all
  13.127 +tables whose names have this prefix.
  13.128 +
  13.129 +<p>
  13.130 +
  13.131 +Table names are derived from the index names requested by MDR.
  13.132 +However, the index name is not used directly, because MDR generates
  13.133 +very long index names based on MOFID's, and many DBMS products have a
  13.134 +low limit like 31 characters for identifiers.  To shorten the name,
  13.135 +the StorageId portion of each MOFID is eliminated, and all leading zeros
  13.136 +of the serial number are trimmed.
  13.137 +
  13.138 +<h3>Caching</h3>
  13.139 +
  13.140 +{@link org.netbeans.mdr.persistence.jdbcimpl.JdbcPrimaryIndex} reuses
  13.141 +the streamable object caching strategy from {@link
  13.142 +org.netbeans.mdr.persistence.btreeimpl.btreestorage.MDRCache }.  For
  13.143 +other indexes, no caching is currently performed.
  13.144 +
  13.145 +<h3>Driver and DBMS Requirements</h3>
  13.146 +
  13.147 +JdbcStorage has been tested with hsqldb 1.7.1 and PostgreSQL 7.4.1.
  13.148 +It does not require a sophisticated JDBC driver or DBMS, but may
  13.149 +require some tweaks for other products.  Below is a list of the JDBC
  13.150 +and SQL features it uses:
  13.151 +
  13.152 +<ul>
  13.153 +
  13.154 +<li>
  13.155 +CREATE TABLE with NOT NULL and PRIMARY KEY constraints.
  13.156 +
  13.157 +<li>
  13.158 +Quoted identifiers for schema and table names, containing strange characters
  13.159 +like "." and ":" allowed.
  13.160 +
  13.161 +<li>
  13.162 +SELECT COUNT, DISTINCT and COUNT(DISTINCT).  No joins, subqueries or
  13.163 +GROUP BY clauses are used.  WHERE clauses are very simple (e.g.  WHERE
  13.164 +COLUMN = value).  ORDER BY is used for accessing
  13.165 +MultivaluedOrderedIndexes.
  13.166 +
  13.167 +<li>
  13.168 +INSERT/UPDATE/DELETE.
  13.169 +
  13.170 +<li>
  13.171 +Prepared statements with dynamic parameters.
  13.172 +
  13.173 +<li>
  13.174 +A single JDBC connection is shared by all calls to the repository, so
  13.175 +the JDBC driver must allow calls from any thread.  However, access to
  13.176 +the connection is synchronized internally, so the JDBC driver does not
  13.177 +need to support concurrent threads of any kind.
  13.178 +
  13.179 +<li>
  13.180 +The JDBC driver must support transactions.  Since MDR only allows one
  13.181 +write transaction at a time, isolation level is irrelevant.  Some
  13.182 +DBMS's don't support mixing DDL and DML within a transaction, and
  13.183 +autocommit on DDL instead.  In that case, createIndex() will lead to
  13.184 +an implicit commit, which could result in inconsistencies after a
  13.185 +subsequent rollback.
  13.186 +
  13.187 +<li>
  13.188 +The Storage.delete() method implementation depends on whether the DBMS
  13.189 +supports schemas.  If schemas are not supported, the JDBC driver must
  13.190 +support enumerating tables with a name prefix so they can be dropped
  13.191 +individually.  If schemas are supported, the DBMS must also support
  13.192 +DROP SCHEMA CASCADE.
  13.193 +
  13.194 +<li>
  13.195 +ResultSetMetaData must support getColumnCount() and getColumnName().
  13.196 +
  13.197 +<li>
  13.198 +ResultSet must support getInt(), getString(), getLong(), and
  13.199 +getBytes().
  13.200 +
  13.201 +<li>
  13.202 +PreparedStatement must support setLong(), setBytes(), and setObject().
  13.203 +
  13.204 +</ul>
  13.205 +
  13.206 +<h3>Limitations and Unresolved Issues</h3>
  13.207 +
  13.208 +Since this is a first cut, there is lots of room for improvement:
  13.209 +
  13.210 +<ul>
  13.211 +
  13.212 +<li>
  13.213 +The StorageId prefix "." is used for all MOFID's, and all MOFID's
  13.214 +stored via JdbcStorage must have this prefix.  MOFID's are stored as
  13.215 +integer serial numbers with no prefix.  This means a JdbcStorage-based
  13.216 +repository cannot be used for federation of any kind.  For some
  13.217 +applications, this may be acceptable, and using integers for keys is
  13.218 +generally good for SQL performance.  Probably we should allow the user
  13.219 +a choice on a per-storage basis: either store MOFID's as integers and
  13.220 +disallow federation, or store MOFID's as strings or byte arrays and
  13.221 +allow federation (factoring out some commonality from btreeimpl).
  13.222 +
  13.223 +<li>
  13.224 +Caching for non-primary indexes is probably required for acceptable
  13.225 +performance in out-of-process configurations.  A very bad case is when
  13.226 +values are inserted sequentially into a MultivaluedOrderedIndex
  13.227 +without using an iterator; each one requires an extra query in order
  13.228 +to generate an ordinal.  A subquery in the VALUES clause would save a
  13.229 +round-trip, but caching would be better.
  13.230 +
  13.231 +<li>
  13.232 +Using JDBC batched execution support for
  13.233 +flushing the cache could significantly improve performance for bulk
  13.234 +write operations such as import.
  13.235 +
  13.236 +<li>
  13.237 +Each SQL implementation typically has its own subtle non-standard
  13.238 +behavior and performance, so a lot of testing and tweaking will be
  13.239 +required in order to make JdbcStorage plug-and-play.
  13.240 +
  13.241 +<li>
  13.242 +Other than datatypes, there is no way to configure the mapping from
  13.243 +indexes to tables.  For example, some users might wish to store all
  13.244 +associations in a single table, rather than creating one table per
  13.245 +association.  And others might wish to store streamable objects in
  13.246 +normalized format for direct SQL querying.  Some kind of configurable
  13.247 +object mapping rules would be nice.
  13.248 +
  13.249 +<li>
  13.250 +The synchronization imposed for all JDBC access could become a
  13.251 +bottleneck in read-mostly multi-threaded apps.  Loosening this is
  13.252 +tricky because different JDBC drivers have different multi-threading
  13.253 +rules (some require multiple connections).  Also, it would be nice to
  13.254 +take advantage of advanced DBMS concurrency control and allow multiple
  13.255 +concurrent read/write transactions.  However, I'm not sure if the
  13.256 +higher levels of MDR can handle this.
  13.257 +
  13.258 +</ul>
  13.259 +
  13.260 +In addition, there are various unresolved issues which came up during
  13.261 +development:
  13.262 +
  13.263 +<ul>
  13.264 +
  13.265 +<li>
  13.266 +JdbcStorageFactory currently reads System.getProperties() directly,
  13.267 +rather than using the properties passed to createStorage().  The
  13.268 +reason is that MDR doesn't know about the JDBC-specific properties.
  13.269 +Once it does, JdbcStorageFactory should stop using System.getProperties().
  13.270 +
  13.271 +<li>
  13.272 +I wasn't clear on the semantics of unique-valued MultivaluedIndexes,
  13.273 +and memoryimpl and btreeimpl didn't provide a consistent answer.  The
  13.274 +MOF boot sequence resulted in some attempts to insert duplicates.  To
  13.275 +prevent this from causing a primary key violation, I put in an extra
  13.276 +existence check on each insert, and skipped the insert if the value
  13.277 +already existed.  Also, I didn't strictly follow the spec for
  13.278 +non-unique values:  remove(key,value) removes all, not just the
  13.279 +"first" one (since "first" has no meaning for an unordered index), and
  13.280 +the same goes for the iter.remove() equivalent. 
  13.281 +
  13.282 +<li>
  13.283 +I did not implement keySet() and values() for JdbcPrimaryIndex because
  13.284 +in my testing I never saw them get called.  Nor did I implement
  13.285 +queryByKeyPrefix() for any of the indexes, or fail-fast iterators anywhere.
  13.286 +
  13.287 +<li>
  13.288 +Due to (I'm guessing) an oversight, MDRCache.replace() is not public as
  13.289 +it should be.  To work around this, I injected 
  13.290 +{@link org.netbeans.mdr.persistence.btreeimpl.btreestorage.AccessHack}
  13.291 +to sneak in the required access.
  13.292 +
  13.293 +<li>
  13.294 +I did not implement the classCode optimization from BtreeDatabase.
  13.295 +That should probably be factored out into a reusable class.  For now,
  13.296 +I just serialized the class name and used Class.forName for
  13.297 +resurrection.
  13.298 +
  13.299 +<li>
  13.300 +Currently, each call to JdbcStorageFactory.createStorage() results in
  13.301 +a JDBC connection being established and held open until a
  13.302 +corresponding call to JdbcStorage.shutDown().  I'm not sure if the
  13.303 +lifetime should have been between open()/create() and close() on
  13.304 +JdbcStorage instead.
  13.305 +
  13.306 +<li>
  13.307 +JdbcStorage maintains on-demand maps of descriptors and JDBC
  13.308 +PreparedStatements for all indexes which have been accessed since the
  13.309 +storage was opened.  These maps could lead to memory constipation for
  13.310 +very large repository models.  Perhaps they need to be turned into
  13.311 +caches instead.
  13.312 +
  13.313 +</ul>
  13.314 +
  13.315 +</body>
  13.316 +</HTML>