001: /***************************************************************
002: * This file is part of the [fleXive](R) project.
003: *
004: * Copyright (c) 1999-2008
005: * UCS - unique computing solutions gmbh (http://www.ucs.at)
006: * All rights reserved
007: *
008: * The [fleXive](R) project is free software; you can redistribute
009: * it and/or modify it under the terms of the GNU General Public
010: * License as published by the Free Software Foundation;
011: * either version 2 of the License, or (at your option) any
012: * later version.
013: *
014: * The GNU General Public License can be found at
015: * http://www.gnu.org/copyleft/gpl.html.
016: * A copy is found in the textfile GPL.txt and important notices to the
017: * license from the author are found in LICENSE.txt distributed with
018: * these libraries.
019: *
020: * This library is distributed in the hope that it will be useful,
021: * but WITHOUT ANY WARRANTY; without even the implied warranty of
022: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
023: * GNU General Public License for more details.
024: *
025: * For further information about UCS - unique computing solutions gmbh,
026: * please see the company website: http://www.ucs.at
027: *
028: * For further information about [fleXive](R), please see the
029: * project website: http://www.flexive.org
030: *
031: *
032: * This copyright notice MUST APPEAR in all copies of the file!
033: ***************************************************************/package com.flexive.ejb.beans;
034:
035: import com.flexive.core.Database;
036: import static com.flexive.core.DatabaseConst.TBL_SEQUENCE;
037: import com.flexive.shared.CustomSequencer;
038: import com.flexive.shared.exceptions.FxApplicationException;
039: import com.flexive.shared.exceptions.FxCreateException;
040: import com.flexive.shared.exceptions.FxDbException;
041: import com.flexive.shared.interfaces.SequencerEngine;
042: import com.flexive.shared.interfaces.SequencerEngineLocal;
043: import org.apache.commons.lang.StringUtils;
044: import org.apache.commons.logging.Log;
045: import org.apache.commons.logging.LogFactory;
046:
047: import javax.annotation.Resource;
048: import javax.ejb.*;
049: import java.sql.Connection;
050: import java.sql.PreparedStatement;
051: import java.sql.ResultSet;
052: import java.sql.SQLException;
053: import java.util.ArrayList;
054: import java.util.List;
055:
056: /**
057: * Sequencer beans.
058: * Generates Id's for data-tables or user defined sequencers.
059: * This beans should be used instead of database specific autoincrements or sequences.
060: *
061: * @author Markus Plesser (markus.plesser@flexive.com), UCS - unique computing solutions gmbh (http://www.ucs.at)
062: * @author Gregor Schober (gregor.schober@flexive.com), UCS - unique computing solutions gmbh (http://www.ucs.at)
063: */
064: @Stateless(name="SequencerEngine")
065: @TransactionManagement(TransactionManagementType.CONTAINER)
066: public class SequencerEngineBean implements SequencerEngine,
067: SequencerEngineLocal {
068: private static transient Log LOG = LogFactory
069: .getLog(SequencerEngineBean.class);
070: @Resource
071: javax.ejb.SessionContext ctx;
072:
073: private final static String SQL_CREATE = "INSERT INTO "
074: + TBL_SEQUENCE + "(ID,NAME,ROLLOVER)VALUES(?,?,?)";
075: private final static String SQL_DELETE = "DELETE FROM "
076: + TBL_SEQUENCE + " WHERE NAME=?";
077: private final static String SQL_EXIST = "SELECT COUNT(*) FROM "
078: + TBL_SEQUENCE + " WHERE NAME=?";
079: private final static String SQL_GET_USER = "SELECT NAME,ROLLOVER,ID FROM "
080: + TBL_SEQUENCE
081: + " WHERE NAME NOT LIKE 'SYS_%' ORDER BY NAME ASC";
082:
083: private final static String SQL_NEXT = "UPDATE " + TBL_SEQUENCE
084: + " SET ID=LAST_INSERT_ID(ID+1) WHERE NAME=?";
085: private final static String SQL_RESET = "UPDATE " + TBL_SEQUENCE
086: + " SET ID=0 WHERE NAME=?";
087: private final static String SQL_GETID = "SELECT LAST_INSERT_ID()";
088: private final static String SQL_GET_ROLLOVER = "SELECT ROLLOVER FROM "
089: + TBL_SEQUENCE + " WHERE NAME=?";
090:
091: /**
092: * {@inheritDoc}
093: */
094: @TransactionAttribute(TransactionAttributeType.REQUIRED)
095: public long getId(final System type) throws FxApplicationException {
096: return fetchId(type.getSequencerName(), type.isAllowRollover());
097: }
098:
099: /**
100: * {@inheritDoc}
101: */
102: @TransactionAttribute(TransactionAttributeType.REQUIRED)
103: public void createSequencer(String name, boolean allowRollover,
104: long startNumber) throws FxApplicationException {
105: if (StringUtils.isEmpty(name)
106: || name.toUpperCase().trim().startsWith("SYS_"))
107: throw new FxCreateException(LOG,
108: "ex.sequencer.create.invalid.name", name);
109: name = name.toUpperCase().trim();
110: if (sequencerExists(name))
111: throw new FxCreateException(LOG,
112: "ex.sequencer.create.invalid.name", name);
113:
114: Connection con = null;
115: PreparedStatement ps = null;
116: try {
117: con = Database.getDbConnection();
118:
119: ps = con.prepareStatement(SQL_CREATE);
120: ps.setLong(1, startNumber);
121: ps.setString(2, name);
122: ps.setBoolean(3, allowRollover);
123: ps.executeUpdate();
124: if (ps.getUpdateCount() == 0)
125: throw new FxCreateException(LOG,
126: "ex.sequencer.create.failed", name);
127: } catch (SQLException exc) {
128: ctx.setRollbackOnly();
129: throw new FxCreateException(LOG, exc,
130: "ex.sequencer.create.failed", name);
131: } finally {
132: Database.closeObjects(SequencerEngineBean.class, con, ps);
133: }
134: }
135:
136: /**
137: * {@inheritDoc}
138: */
139: @TransactionAttribute(TransactionAttributeType.REQUIRED)
140: public void removeSequencer(String name)
141: throws FxApplicationException {
142: if (!sequencerExists(name))
143: throw new FxCreateException(LOG,
144: "ex.sequencer.remove.notFound", name);
145: Connection con = null;
146: PreparedStatement ps = null;
147: try {
148: con = Database.getDbConnection();
149: ps = con.prepareStatement(SQL_DELETE);
150: ps.setString(1, name);
151: ps.executeUpdate();
152: if (ps.getUpdateCount() == 0)
153: throw new FxCreateException(LOG,
154: "ex.sequencer.remove.failed", name);
155: } catch (SQLException exc) {
156: ctx.setRollbackOnly();
157: throw new FxCreateException(LOG, exc,
158: "ex.sequencer.remove.failed", name);
159: } finally {
160: Database.closeObjects(SequencerEngineBean.class, con, ps);
161: }
162: }
163:
164: /**
165: * {@inheritDoc}
166: */
167: @TransactionAttribute(TransactionAttributeType.REQUIRED)
168: public long getId(String name) throws FxApplicationException {
169: if (StringUtils.isEmpty(name)
170: || name.toUpperCase().trim().startsWith("SYS_"))
171: throw new FxCreateException(LOG,
172: "ex.sequencer.fetch.invalid", name);
173: return fetchId(name, false);
174: }
175:
176: /**
177: * {@inheritDoc}
178: */
179: @TransactionAttribute(TransactionAttributeType.REQUIRED)
180: public boolean sequencerExists(String name)
181: throws FxApplicationException {
182: if (StringUtils.isEmpty(name)
183: || name.toUpperCase().trim().startsWith("SYS_"))
184: return false;
185: Connection con = null;
186: PreparedStatement ps = null;
187: try {
188: con = Database.getDbConnection();
189: ps = con.prepareStatement(SQL_EXIST);
190: ps.setString(1, name.trim().toUpperCase());
191: ResultSet rs = ps.executeQuery();
192: return !(rs == null || !rs.next()) && rs.getInt(1) != 0;
193: } catch (SQLException exc) {
194: ctx.setRollbackOnly();
195: throw new FxDbException(LOG, exc, "ex.db.sqlError", exc
196: .getMessage());
197: } finally {
198: Database.closeObjects(SequencerEngineBean.class, con, ps);
199: }
200: }
201:
202: /**
203: * {@inheritDoc}
204: */
205: @TransactionAttribute(TransactionAttributeType.REQUIRED)
206: public List<CustomSequencer> getCustomSequencers()
207: throws FxApplicationException {
208: List<CustomSequencer> res = new ArrayList<CustomSequencer>(20);
209: Connection con = null;
210: PreparedStatement ps = null;
211: try {
212: con = Database.getDbConnection();
213: ps = con.prepareStatement(SQL_GET_USER);
214: ResultSet rs = ps.executeQuery();
215: while (rs != null && rs.next())
216: res.add(new CustomSequencer(rs.getString(1), rs
217: .getBoolean(2), rs.getLong(3)));
218: } catch (SQLException exc) {
219: ctx.setRollbackOnly();
220: throw new FxDbException(LOG, exc, "ex.db.sqlError", exc
221: .getMessage());
222: } finally {
223: Database.closeObjects(SequencerEngineBean.class, con, ps);
224: }
225: return res;
226: }
227:
228: /**
229: * Generates a new unique id for the given type.
230: * Issues a ctx.setRollbackOnly if the DB failes to deliver a new ID
231: *
232: * @param name the name
233: * @param allowRollover if MAX_ID is reached throw exception or reset to zero?
234: * @return the new id
235: * @throws com.flexive.shared.exceptions.FxCreateException
236: * if the function fails
237: */
238: private long fetchId(final String name, boolean allowRollover)
239: throws FxCreateException {
240: Connection con = null;
241: PreparedStatement ps = null;
242: try {
243: // Obtain a database connection
244: con = Database.getDbConnection();
245:
246: // Prepare the new id
247: ps = con.prepareStatement(SQL_NEXT);
248: ps.setString(1, name);
249: ps.executeUpdate();
250: if (ps.getUpdateCount() == 0)
251: throw new FxCreateException(LOG,
252: "ex.sequencer.typeUnknown", name);
253: ps.close();
254:
255: // Get the new id
256: ps = con.prepareStatement(SQL_GETID);
257: ResultSet rs = ps.executeQuery();
258: long newId;
259: if (rs == null || !rs.next())
260: throw new FxCreateException(LOG,
261: "ex.sequencer.fetch.failed", name);
262: newId = rs.getLong(1);
263: if (rs.wasNull())
264: throw new FxCreateException(LOG,
265: "ex.sequencer.fetch.failed", name);
266: if (newId >= MAX_ID) {
267: if (!name.startsWith("SYS_")) {
268: //get allowRollover setting
269: ps.close();
270: ps = con.prepareStatement(SQL_GET_ROLLOVER);
271: ps.setString(1, name);
272: ResultSet rso = ps.executeQuery();
273: if (rso == null || !rso.next())
274: throw new FxCreateException(LOG,
275: "ex.sequencer.fetch.failed", name);
276: allowRollover = rso.getBoolean(1);
277: }
278: if (!allowRollover)
279: throw new FxCreateException(LOG,
280: "ex.sequencer.exhausted", name);
281: ps.close();
282: ps = con.prepareStatement(SQL_RESET);
283: ps.setString(1, name);
284: ps.executeUpdate();
285: if (ps.getUpdateCount() == 0)
286: throw new FxCreateException(LOG,
287: "ex.sequencer.typeUnknown", name);
288: newId = 0;
289: }
290: // Return new id
291: return newId;
292: } catch (SQLException exc) {
293: if (ctx != null) {
294: ctx.setRollbackOnly();
295: }
296: throw new FxCreateException(LOG, exc,
297: "ex.sequencer.fetch.failedMsg", name, exc
298: .getMessage());
299: } finally {
300: Database.closeObjects(SequencerEngineBean.class, con, ps);
301: }
302: }
303:
304: }
|