001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    package org.apache.servicemix.jdbc.adapter;
018    
019    import java.io.IOException;
020    import java.sql.Connection;
021    import java.sql.PreparedStatement;
022    import java.sql.ResultSet;
023    import java.sql.SQLException;
024    import java.sql.Statement;
025    import java.util.ArrayList;
026    import java.util.List;
027    
028    import org.apache.commons.logging.Log;
029    import org.apache.commons.logging.LogFactory;
030    import org.apache.servicemix.jdbc.JDBCAdapter;
031    import org.apache.servicemix.jdbc.JDBCAdapterFactory;
032    import org.apache.servicemix.jdbc.Statements;
033    
034    /**
035     * Implements all the default JDBC operations that are used by the
036     * JDBCPersistenceAdapter. <p/> sub-classing is encouraged to override the
037     * default implementation of methods to account for differences in JDBC Driver
038     * implementations. <p/> The JDBCAdapter inserts and extracts BLOB data using
039     * the getBytes()/setBytes() operations. <p/> The databases/JDBC drivers that
040     * use this adapter are:
041     * <ul>
042     * <li></li>
043     * </ul>
044     * 
045     * @org.apache.xbean.XBean element="defaultJDBCAdapter"
046     * 
047     * @version $Revision: 1.10 $
048     */
049    public class DefaultJDBCAdapter implements JDBCAdapter {
050    
051        private static final Log LOG = LogFactory.getLog(DefaultJDBCAdapter.class);
052    
053        protected Statements statements;
054    
055        protected void setBinaryData(PreparedStatement s, int index, byte data[]) throws SQLException {
056            s.setBytes(index, data);
057        }
058    
059        protected byte[] getBinaryData(ResultSet rs, int index) throws SQLException {
060            return rs.getBytes(index);
061        }
062    
063        public void doCreateTables(Connection connection) throws SQLException, IOException {
064            Statement s = null;
065            try {
066                // Check to see if the table already exists. If it does, then don't
067                // log warnings during startup.
068                // Need to run the scripts anyways since they may contain ALTER
069                // statements that upgrade a previous version of the table
070                boolean alreadyExists = false;
071                ResultSet rs = null;
072                try {
073                    rs = connection.getMetaData().getTables(null, null, statements.getFullStoreTableName(),
074                            new String[] {"TABLE" });
075                    alreadyExists = rs.next();
076                } catch (Throwable ignore) {
077                    // Do nothing
078                } finally {
079                    close(rs);
080                }
081    
082                // If the dataSource is a managed DataSource, executing a statement
083                // that throws
084                // an exception will make the connection unusable.
085                // So if the table already exists, do not try to re-create them
086                if (alreadyExists) {
087                    return;
088                }
089    
090                s = connection.createStatement();
091                String[] createStatments = statements.getCreateSchemaStatements();
092                for (int i = 0; i < createStatments.length; i++) {
093                    // This will fail usually since the tables will be
094                    // created already.
095                    try {
096                        LOG.debug("Executing SQL: " + createStatments[i]);
097                        s.execute(createStatments[i]);
098                    } catch (SQLException e) {
099                        if (alreadyExists) {
100                            LOG.debug("Could not create JDBC tables; The message table already existed." + " Failure was: "
101                                    + createStatments[i] + " Message: " + e.getMessage() + " SQLState: " + e.getSQLState()
102                                    + " Vendor code: " + e.getErrorCode());
103                        } else {
104                            LOG.warn("Could not create JDBC tables; they could already exist." + " Failure was: "
105                                    + createStatments[i] + " Message: " + e.getMessage() + " SQLState: " + e.getSQLState()
106                                    + " Vendor code: " + e.getErrorCode());
107                            JDBCAdapterFactory.log("Failure details: ", e);
108                        }
109                    }
110                }
111            } finally {
112                close(s);
113            }
114        }
115    
116        public void doDropTables(Connection connection) throws SQLException, IOException {
117            Statement s = null;
118            try {
119                s = connection.createStatement();
120                String[] dropStatments = statements.getDropSchemaStatements();
121                for (int i = 0; i < dropStatments.length; i++) {
122                    // This will fail usually since the tables will be
123                    // created already.
124                    try {
125                        s.execute(dropStatments[i]);
126                    } catch (SQLException e) {
127                        LOG.warn("Could not drop JDBC tables; they may not exist." + " Failure was: " + dropStatments[i]
128                                + " Message: " + e.getMessage() + " SQLState: " + e.getSQLState() + " Vendor code: "
129                                + e.getErrorCode());
130                        JDBCAdapterFactory.log("Failure details: ", e);
131                    }
132                }
133            } finally {
134                close(s);
135            }
136        }
137    
138        public void doStoreData(Connection connection, String id, byte[] data) throws SQLException, IOException {
139            PreparedStatement s = null;
140            try {
141                if (s == null) {
142                    s = connection.prepareStatement(statements.getStoreDataStatement());
143                }
144                s.setString(1, id);
145                setBinaryData(s, 2, data);
146                if (s.executeUpdate() != 1) {
147                    throw new SQLException("Failed to insert data");
148                }
149            } finally {
150                close(s);
151            }
152        }
153    
154        public byte[] doLoadData(Connection connection, String id) throws SQLException, IOException {
155            PreparedStatement s = null;
156            ResultSet rs = null;
157            try {
158                s = connection.prepareStatement(statements.getFindDataStatement());
159                s.setString(1, id);
160                rs = s.executeQuery();
161                if (!rs.next()) {
162                    return null;
163                }
164                return getBinaryData(rs, 1);
165            } finally {
166                close(rs);
167                close(s);
168            }
169        }
170    
171        public void doUpdateData(Connection connection, String id, byte[] data) throws SQLException, IOException {
172            PreparedStatement s = null;
173            try {
174                if (s == null) {
175                    s = connection.prepareStatement(statements.getUpdateDataStatement());
176                }
177                s.setString(2, id);
178                setBinaryData(s, 1, data);
179                if (s.executeUpdate() != 1) {
180                    throw new SQLException("Failed to update data");
181                }
182            } finally {
183                close(s);
184            }
185        }
186    
187        public void doRemoveData(Connection connection, String id) throws SQLException, IOException {
188            PreparedStatement s = null;
189            try {
190                s = connection.prepareStatement(statements.getRemoveDataStatement());
191                s.setString(1, id);
192                if (s.executeUpdate() != 1) {
193                    throw new SQLException("Failed to remove data");
194                }
195            } finally {
196                close(s);
197            }
198        }
199    
200        private static void close(Statement s) {
201            try {
202                if (s != null) {
203                    s.close();
204                }
205            } catch (Throwable e) {
206                // Do nothing
207            }
208        }
209    
210        private static void close(ResultSet rs) {
211            try {
212                if (rs != null) {
213                    rs.close();
214                }
215            } catch (Throwable e) {
216                // Do nothing
217            }
218        }
219    
220        public Statements getStatements() {
221            return statements;
222        }
223    
224        public void setStatements(Statements statements) {
225            this.statements = statements;
226        }
227    
228        public byte[][] doLoadData(Connection connection, String[] ids) throws SQLException, IOException {
229            PreparedStatement s = null;
230            byte[][] datas = new byte[ids.length][];
231            try {
232                s = connection.prepareStatement(statements.getFindDataStatement());
233                for (int i = 0; i < ids.length; i++) {
234                    s.setString(1, ids[i]);
235                    ResultSet rs = s.executeQuery();
236                    if (rs.next()) {
237                        datas[i] = getBinaryData(rs, 1);
238                    }
239                    close(rs);
240                }
241                return datas;
242            } finally {
243                close(s);
244            }
245        }
246    
247        public void doRemoveData(Connection connection, String[] ids) throws SQLException, IOException {
248            PreparedStatement s = null;
249            try {
250                s = connection.prepareStatement(statements.getRemoveDataStatement());
251                for (int i = 0; i < ids.length; i++) {
252                    s.setString(1, ids[i]);
253                    s.executeUpdate();
254                }
255            } finally {
256                close(s);
257            }
258        }
259    
260        public int doGetCount(Connection connection) throws SQLException, IOException {
261            PreparedStatement s = null;
262            ResultSet rs = null;
263            try {
264                s = connection.prepareStatement(statements.getCountStatement());
265                rs = s.executeQuery();
266                rs.next();
267                return rs.getInt(1);
268            } finally {
269                close(rs);
270                close(s);
271            }
272        }
273    
274        public String[] doGetIds(Connection connection) throws SQLException, IOException {
275            PreparedStatement s = null;
276            ResultSet rs = null;
277            try {
278                List<String> ids = new ArrayList<String>();
279                s = connection.prepareStatement(statements.getFindAllIdsStatement());
280                rs = s.executeQuery();
281                while (rs.next()) {
282                    ids.add(rs.getString(1));
283                }
284                return ids.toArray(new String[ids.size()]);
285            } finally {
286                close(rs);
287                close(s);
288            }
289        }
290    
291        public String[] doGetIds(Connection connection, int fromIndex, int toIndex) throws SQLException, IOException {
292            Statement s = null;
293            ResultSet rs = null;
294            try {
295                s = connection.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
296                s.setFetchSize(toIndex - fromIndex);
297                rs = s.executeQuery(statements.getFindAllIdsStatement());
298                rs.absolute(fromIndex + 1);
299                String[] ids = new String[toIndex - fromIndex];
300                for (int row = 0; row < toIndex - fromIndex; row++) {
301                    ids[row] = rs.getString(1);
302                    if (!rs.next()) {
303                        break;
304                    }
305                }
306                return ids;
307            } finally {
308                close(rs);
309                close(s);
310            }
311        }
312    
313    }