001    package org.apache.ojb.broker.util.sequence;
002    
003    /* Copyright 2002-2005 The Apache Software Foundation
004     *
005     * Licensed under the Apache License, Version 2.0 (the "License");
006     * you may not use this file except in compliance with the License.
007     * 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    
018    import java.sql.ResultSet;
019    import java.sql.Statement;
020    import java.sql.PreparedStatement;
021    
022    import org.apache.commons.lang.SystemUtils;
023    import org.apache.ojb.broker.PersistenceBroker;
024    import org.apache.ojb.broker.accesslayer.StatementManagerIF;
025    import org.apache.ojb.broker.metadata.ClassDescriptor;
026    import org.apache.ojb.broker.metadata.FieldDescriptor;
027    import org.apache.ojb.broker.query.Query;
028    import org.apache.ojb.broker.util.logging.Logger;
029    import org.apache.ojb.broker.util.logging.LoggerFactory;
030    
031    /**
032     * This SequenceManager implementation uses database
033     * sequence key generation (e.g supported by
034     * Oracle, SAP DB, PostgreSQL, ...).
035     * This class is responsible for creating new unique ID's.
036     * <br/>
037     * It is possible to define a <code>sequence-name</code>
038     * field-descriptor attribute in the repository file. If
039     * such an attribute was not found, the implementation build
040     * an extent aware sequence name by its own.
041     * <br/>
042     * Keep in mind when define a sequence name, that you are responsible
043     * to be aware of extents, that is: if you ask for an uid for an
044     * interface with several
045     * implementor classes, or a baseclass with several subclasses the returned
046     * uid have to be unique accross all tables representing objects of the
047     * extent in question. Thus you have to use the same <code>sequence-name</code>
048     * for all extents.
049     *
050     * <p>
051     * Implementation configuration properties:
052     * </p>
053     *
054     * <table cellspacing="2" cellpadding="2" border="3" frame="box">
055     * <tr>
056     *     <td><strong>Property Key</strong></td>
057     *     <td><strong>Property Values</strong></td>
058     * </tr>
059     * <tr>
060     *     <td>autoNaming</td>
061     *     <td>
062     *          Default was 'true'. If set 'true' OJB try to build a
063     *          sequence name automatic if none found in field-descriptor
064     *          and set this generated name as <code>sequence-name</code>
065     *          in field-descriptor.
066     *          <br/>
067     *          If set 'false' OJB throws an exception
068     *          if none sequence name was found in field-descriptor, ditto
069     *          OJB does NOT try to create a database sequence entry when
070     *          for given sequence name no database sequence could be found.
071     *    </td>
072     * </tr>
073     * </table>
074     *
075     *
076     * <br/>
077     * <p>
078     * <b>Limitations:</b>
079     * <ul>
080     *      <li>none</li>
081     * </ul>
082     * </p>
083     * <br/>
084     * <br/>
085     *
086     * @author Edson Carlos Ericksson Richter
087     * @author Rajeev Kaul
088     * @author Thomas Mahler
089     * @author Armin Waibel
090     * @version $Id: SequenceManagerNextValImpl.java,v 1.1 2007-08-24 22:17:29 ewestfal Exp $
091     */
092    public class SequenceManagerNextValImpl extends AbstractSequenceManager
093    {
094        private Logger log = LoggerFactory.getLogger(SequenceManagerNextValImpl.class);
095    
096        /**
097         *
098         */
099        public SequenceManagerNextValImpl(PersistenceBroker broker)
100        {
101            super(broker);
102        }
103    
104        /**
105         * returns a unique int value for class clazz and field fieldName.
106         * the returned number is unique accross all tables in the extent of clazz.
107         */
108        protected int getUniqueId(FieldDescriptor field) throws SequenceManagerException
109        {
110            return (int) getUniqueLong(field);
111        }
112    
113    
114        /**
115         * returns a unique long value for class clazz and field fieldName.
116         * the returned number is unique accross all tables in the extent of clazz.
117         */
118        protected long getUniqueLong(FieldDescriptor field) throws SequenceManagerException
119        {
120            long result;
121            // lookup sequence name
122            String sequenceName = calculateSequenceName(field);
123            try
124            {
125                result = buildNextSequence(field.getClassDescriptor(), sequenceName);
126            }
127            catch (Throwable e)
128            {
129                // maybe the sequence was not created
130                try
131                {
132                    log.info("Create DB sequence key '"+sequenceName+"'");
133                    createSequence(field.getClassDescriptor(), sequenceName);
134                }
135                catch (Exception e1)
136                {
137                    throw new SequenceManagerException(
138                            SystemUtils.LINE_SEPARATOR +
139                            "Could not grab next id, failed with " + SystemUtils.LINE_SEPARATOR +
140                            e.getMessage() + SystemUtils.LINE_SEPARATOR +
141                            "Creation of new sequence failed with " +
142                            SystemUtils.LINE_SEPARATOR + e1.getMessage() + SystemUtils.LINE_SEPARATOR
143                            , e1);
144                }
145                try
146                {
147                    result = buildNextSequence(field.getClassDescriptor(), sequenceName);
148                }
149                catch (Throwable e1)
150                {
151                    throw new SequenceManagerException("Could not grab next id, sequence seems to exist", e);
152                }
153            }
154            return result;
155        }
156    
157        protected long buildNextSequence(ClassDescriptor cld, String sequenceName) throws Exception
158        {
159            ResultSet rs = null;
160            PreparedStatement stmt = null;
161            long result = -1;
162            StatementManagerIF stmtMan = getBrokerForClass().serviceStatementManager();
163            try
164            {
165                stmt = stmtMan.getPreparedStatement(cld, getPlatform().nextSequenceQuery(sequenceName) ,Query.NOT_SCROLLABLE, 1, false);
166                rs = stmt.executeQuery();
167                rs.next();
168                result = rs.getLong(1);
169            }
170            finally
171            {
172                stmtMan.closeResources(stmt, rs);
173            }
174            return result;
175        }
176    
177        protected void createSequence(ClassDescriptor cld, String sequenceName) throws Exception
178        {
179            Statement stmt = null;
180            StatementManagerIF stmtMan = getBrokerForClass().serviceStatementManager();
181    // arminw: never try to remove existing sequences, because this may lead in unexpected behaviour
182    // if the reason for the create call isn't a missing sequence (e.g. network problems)  
183    //        try
184    //        {
185    //            stmt = stmtMan.getGenericStatement(cld, Query.NOT_SCROLLABLE);
186    //            stmt.execute(getPlatform().dropSequenceQuery(sequenceName));
187    //        }
188    //        catch (Exception ignore)
189    //        {
190    //            // ignore it
191    //        }
192    //        finally
193    //        {
194    //            try
195    //            {
196    //                stmtMan.closeResources(stmt, null);
197    //            }
198    //            catch (Exception ignore)
199    //            {
200    //                // ignore it
201    //            }
202    //        }
203    
204            try
205            {
206                stmt = stmtMan.getGenericStatement(cld, Query.NOT_SCROLLABLE);
207                stmt.execute(getPlatform().createSequenceQuery(sequenceName, getConfigurationProperties()));
208            }
209            finally
210            {
211                try
212                {
213                    stmtMan.closeResources(stmt, null);
214                }
215                catch (Exception e)
216                {
217                    e.printStackTrace();
218                }
219            }
220        }
221    }