1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19 package org.apache.openjpa.jdbc.kernel;
20
21 import java.sql.Connection;
22 import java.sql.SQLException;
23 import javax.sql.DataSource;
24 import javax.transaction.Transaction;
25 import javax.transaction.TransactionManager;
26
27 import org.apache.openjpa.jdbc.conf.JDBCConfiguration;
28 import org.apache.openjpa.jdbc.meta.ClassMapping;
29 import org.apache.openjpa.jdbc.schema.SchemaGroup;
30 import org.apache.openjpa.jdbc.sql.SQLExceptions;
31 import org.apache.openjpa.kernel.StoreContext;
32 import org.apache.openjpa.meta.ClassMetaData;
33 import org.apache.openjpa.util.OpenJPAException;
34 import org.apache.openjpa.util.StoreException;
35
36 /**
37 * Abstract sequence implementation. Handles obtaining the proper
38 * connection to used based on whether the sequence is transactional and
39 * whether a second datasource is configured.
40 *
41 * @author Abe White
42 */
43 public abstract class AbstractJDBCSeq
44 implements JDBCSeq {
45
46 protected int type = TYPE_DEFAULT;
47 protected Object current = null;
48 private static ThreadLocal _outerTransaction = new ThreadLocal();
49
50 /**
51 * Records the sequence type.
52 */
53 public void setType(int type) {
54 this.type = type;
55 }
56
57 public Object next(StoreContext ctx, ClassMetaData meta) {
58 JDBCStore store = getStore(ctx);
59 try {
60 current = nextInternal(store, (ClassMapping) meta);
61 return current;
62 } catch (OpenJPAException ke) {
63 throw ke;
64 } catch (SQLException se) {
65 throw SQLExceptions.getStore(se, store.getDBDictionary());
66 } catch (Exception e) {
67 throw new StoreException(e);
68 }
69 }
70
71 public Object current(StoreContext ctx, ClassMetaData meta) {
72 JDBCStore store = getStore(ctx);
73 try {
74 return currentInternal(store, (ClassMapping) meta);
75 } catch (OpenJPAException ke) {
76 throw ke;
77 } catch (SQLException se) {
78 throw SQLExceptions.getStore(se, store.getDBDictionary());
79 } catch (Exception e) {
80 throw new StoreException(e);
81 }
82 }
83
84 public void allocate(int additional, StoreContext ctx, ClassMetaData meta) {
85 JDBCStore store = getStore(ctx);
86 try {
87 allocateInternal(additional, store, (ClassMapping) meta);
88 } catch (OpenJPAException ke) {
89 throw ke;
90 } catch (SQLException se) {
91 throw SQLExceptions.getStore(se, store.getDBDictionary());
92 } catch (Exception e) {
93 throw new StoreException(e);
94 }
95 }
96
97 /**
98 * No-op.
99 */
100 public void addSchema(ClassMapping mapping, SchemaGroup group) {
101 }
102
103 /**
104 * No-op.
105 */
106 public void close() {
107 }
108
109 /**
110 * Return the next sequence object.
111 */
112 protected abstract Object nextInternal(JDBCStore store,
113 ClassMapping mapping)
114 throws Exception;
115
116 /**
117 * Return the {@link JDBCConfiguration} for this sequence.
118 */
119 public abstract JDBCConfiguration getConfiguration();
120
121 /**
122 * Return the current sequence object. By default returns the last
123 * sequence value used, or null if no sequence values have been requested
124 * yet. Default implementation is not threadsafe.
125 */
126 protected Object currentInternal(JDBCStore store, ClassMapping mapping)
127 throws Exception {
128 return current;
129 }
130
131 /**
132 * Allocate additional sequence values. Does nothing by default.
133 */
134 protected void allocateInternal(int additional, JDBCStore store,
135 ClassMapping mapping)
136 throws Exception {
137 }
138
139 /**
140 * Extract the store from the given context.
141 */
142 private JDBCStore getStore(StoreContext ctx) {
143 return (JDBCStore) ctx.getStoreManager().getInnermostDelegate();
144 }
145
146 /**
147 * Return the connection to use based on the type of sequence. This
148 * connection will automatically be closed; do not close it.
149 */
150 protected Connection getConnection(JDBCStore store)
151 throws SQLException {
152 if (type == TYPE_TRANSACTIONAL || type == TYPE_CONTIGUOUS)
153 return store.getConnection();
154 else if (suspendInJTA()) {
155 try {
156 TransactionManager tm = getConfiguration()
157 .getManagedRuntimeInstance().getTransactionManager();
158 _outerTransaction.set(tm.suspend());
159 tm.begin();
160 return store.getConnection();
161 } catch (Exception e) {
162 throw new StoreException(e);
163 }
164 } else {
165 JDBCConfiguration conf = store.getConfiguration();
166 DataSource ds = conf.getDataSource2(store.getContext());
167 Connection conn = ds.getConnection();
168 if (conn.getAutoCommit())
169 conn.setAutoCommit(false);
170 return conn;
171 }
172 }
173
174 /**
175 * Close the current connection.
176 */
177 protected void closeConnection(Connection conn) {
178 if (conn == null)
179 return;
180
181 if (type == TYPE_TRANSACTIONAL || type == TYPE_CONTIGUOUS) {
182 // do nothing; this seq is part of the business transaction
183 return;
184 } else if (suspendInJTA()) {
185 try {
186 TransactionManager tm = getConfiguration()
187 .getManagedRuntimeInstance().getTransactionManager();
188 tm.commit();
189 try { conn.close(); } catch (SQLException se) {}
190
191 Transaction outerTxn = (Transaction)_outerTransaction.get();
192 if (outerTxn != null)
193 tm.resume(outerTxn);
194
195 } catch (Exception e) {
196 throw new StoreException(e);
197 } finally {
198 _outerTransaction.set(null);
199 }
200 } else {
201 try {
202 conn.commit();
203 } catch (SQLException se) {
204 throw SQLExceptions.getStore(se);
205 } finally {
206 try { conn.close(); } catch (SQLException se) {}
207 }
208 }
209 }
210
211 /**
212 * Detect whether or not OpenJPA should suspend the transaction in
213 * a managed environment.
214 */
215 protected boolean suspendInJTA() {
216 return getConfiguration().isConnectionFactoryModeManaged() &&
217 getConfiguration().getConnectionFactory2() == null;
218 }
219 }