EMMA Coverage Report (generated Fri Jan 30 13:20:29 EST 2009)
[all classes][org.springframework.batch.core.repository.dao]

COVERAGE SUMMARY FOR SOURCE FILE [JdbcExecutionContextDao.java]

nameclass, %method, %block, %line, %
JdbcExecutionContextDao.java100% (5/5)100% (20/20)88%  (601/685)90%  (121/135)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class JdbcExecutionContextDao$ExecutionContextRowCallbackHandler100% (1/1)100% (2/2)71%  (55/77)88%  (14/16)
processRow (ResultSet): void 100% (1/1)69%  (49/71)85%  (11/13)
JdbcExecutionContextDao$ExecutionContextRowCallbackHandler (ExecutionContext)... 100% (1/1)100% (6/6)100% (3/3)
     
class JdbcExecutionContextDao$1100% (1/1)100% (2/2)82%  (119/145)81%  (22/27)
setValues (PreparedStatement, LobCreator): void 100% (1/1)79%  (100/126)81%  (21/26)
JdbcExecutionContextDao$1 (JdbcExecutionContextDao, LobHandler, Long, String,... 100% (1/1)100% (19/19)100% (1/1)
     
class JdbcExecutionContextDao$2100% (1/1)100% (2/2)83%  (127/153)82%  (23/28)
setValues (PreparedStatement, LobCreator): void 100% (1/1)80%  (105/131)81%  (22/27)
JdbcExecutionContextDao$2 (JdbcExecutionContextDao, LobHandler, Long, String,... 100% (1/1)100% (22/22)100% (1/1)
     
class JdbcExecutionContextDao100% (1/1)100% (10/10)97%  (232/240)98%  (50/51)
saveOrUpdateExecutionContext (ExecutionContext, Long, String): void 100% (1/1)87%  (52/60)92%  (12/13)
JdbcExecutionContextDao (): void 100% (1/1)100% (8/8)100% (3/3)
getExecutionContext (JobExecution): ExecutionContext 100% (1/1)100% (32/32)100% (5/5)
getExecutionContext (StepExecution): ExecutionContext 100% (1/1)100% (32/32)100% (5/5)
insertExecutionAttribute (Long, String, String, Object, JdbcExecutionContextD... 100% (1/1)100% (21/21)100% (3/3)
saveOrUpdateExecutionContext (JobExecution): void 100% (1/1)100% (18/18)100% (6/6)
saveOrUpdateExecutionContext (StepExecution): void 100% (1/1)100% (18/18)100% (6/6)
setBlob (LobCreator, PreparedStatement, int, Object): void 100% (1/1)100% (15/15)100% (3/3)
setLobHandler (LobHandler): void 100% (1/1)100% (4/4)100% (2/2)
updateExecutionAttribute (Long, String, String, Object, JdbcExecutionContextD... 100% (1/1)100% (32/32)100% (5/5)
     
class JdbcExecutionContextDao$AttributeType100% (1/1)100% (4/4)97%  (68/70)92%  (12/13)
getType (String): JdbcExecutionContextDao$AttributeType 100% (1/1)90%  (19/21)75%  (3/4)
<static initializer> 100% (1/1)100% (40/40)100% (5/5)
JdbcExecutionContextDao$AttributeType (String): void 100% (1/1)100% (6/6)100% (3/3)
toString (): String 100% (1/1)100% (3/3)100% (1/1)

1package org.springframework.batch.core.repository.dao;
2 
3import java.io.ByteArrayInputStream;
4import java.io.Serializable;
5import java.sql.PreparedStatement;
6import java.sql.ResultSet;
7import java.sql.SQLException;
8import java.util.Iterator;
9import java.util.Map.Entry;
10 
11import org.apache.commons.lang.SerializationUtils;
12import org.springframework.batch.core.JobExecution;
13import org.springframework.batch.core.StepExecution;
14import org.springframework.batch.core.UnexpectedJobExecutionException;
15import org.springframework.batch.item.ExecutionContext;
16import org.springframework.dao.DataAccessException;
17import org.springframework.jdbc.core.PreparedStatementCallback;
18import org.springframework.jdbc.core.RowCallbackHandler;
19import org.springframework.jdbc.core.support.AbstractLobCreatingPreparedStatementCallback;
20import org.springframework.jdbc.support.lob.DefaultLobHandler;
21import org.springframework.jdbc.support.lob.LobCreator;
22import org.springframework.jdbc.support.lob.LobHandler;
23import org.springframework.util.Assert;
24 
25/**
26 * JDBC DAO for {@link ExecutionContext}.
27 * 
28 * Stores execution context data related to both Step and Job using
29 * discriminator column to distinguish between the two.
30 * 
31 * @author Lucas Ward
32 * @author Robert Kasanicky
33 */
34class JdbcExecutionContextDao extends AbstractJdbcBatchMetadataDao {
35 
36        private static final String STEP_DISCRIMINATOR = "S";
37 
38        private static final String JOB_DISCRIMINATOR = "J";
39 
40        private static final String FIND_EXECUTION_CONTEXT = "SELECT TYPE_CD, KEY_NAME, STRING_VAL, DOUBLE_VAL, LONG_VAL, OBJECT_VAL "
41                        + "from %PREFIX%EXECUTION_CONTEXT where EXECUTION_ID = ? and DISCRIMINATOR = ?";
42 
43        private static final String INSERT_STEP_EXECUTION_CONTEXT = "INSERT into %PREFIX%EXECUTION_CONTEXT(EXECUTION_ID, DISCRIMINATOR, TYPE_CD,"
44                        + " KEY_NAME, STRING_VAL, DOUBLE_VAL, LONG_VAL, OBJECT_VAL) values(?,?,?,?,?,?,?,?)";
45 
46        private static final String UPDATE_STEP_EXECUTION_CONTEXT = "UPDATE %PREFIX%EXECUTION_CONTEXT set "
47                        + "TYPE_CD = ?, STRING_VAL = ?, DOUBLE_VAL = ?, LONG_VAL = ?, OBJECT_VAL = ? where EXECUTION_ID = ? and KEY_NAME = ?";
48 
49        private LobHandler lobHandler = new DefaultLobHandler();
50 
51        /**
52         * @param jobExecution
53         * @return execution context associated with the given jobExecution.
54         */
55        public ExecutionContext getExecutionContext(JobExecution jobExecution) {
56                final Long executionId = jobExecution.getId();
57                Assert.notNull(executionId, "ExecutionId must not be null.");
58 
59                final ExecutionContext executionContext = new ExecutionContext();
60 
61                getJdbcTemplate().query(getQuery(FIND_EXECUTION_CONTEXT), new Object[] { executionId, JOB_DISCRIMINATOR },
62                                new ExecutionContextRowCallbackHandler(executionContext));
63 
64                return executionContext;
65        }
66 
67        /**
68         * @param stepExecution
69         * @return execution context associated with the given stepExecution.
70         */
71        public ExecutionContext getExecutionContext(StepExecution stepExecution) {
72                final Long executionId = stepExecution.getId();
73                Assert.notNull(executionId, "ExecutionId must not be null.");
74 
75                final ExecutionContext executionContext = new ExecutionContext();
76 
77                getJdbcTemplate().query(getQuery(FIND_EXECUTION_CONTEXT), new Object[] { executionId, STEP_DISCRIMINATOR },
78                                new ExecutionContextRowCallbackHandler(executionContext));
79 
80                return executionContext;
81        }
82 
83        /**
84         * Persist or update the execution context associated with the given
85         * jobExecution
86         * @param jobExecution
87         */
88        public void saveOrUpdateExecutionContext(final JobExecution jobExecution) {
89                Long executionId = jobExecution.getId();
90                ExecutionContext executionContext = jobExecution.getExecutionContext();
91                Assert.notNull(executionId, "ExecutionId must not be null.");
92                Assert.notNull(executionContext, "The ExecutionContext must not be null.");
93 
94                saveOrUpdateExecutionContext(executionContext, executionId, JOB_DISCRIMINATOR);
95        }
96 
97        /**
98         * Persist or update the execution context associated with the given
99         * stepExecution
100         * @param stepExecution
101         */
102        public void saveOrUpdateExecutionContext(final StepExecution stepExecution) {
103 
104                Long executionId = stepExecution.getId();
105                ExecutionContext executionContext = stepExecution.getExecutionContext();
106                Assert.notNull(executionId, "ExecutionId must not be null.");
107                Assert.notNull(executionContext, "The ExecutionContext must not be null.");
108 
109                saveOrUpdateExecutionContext(executionContext, executionId, STEP_DISCRIMINATOR);
110        }
111 
112        /**
113         * Resolves attribute's class to corresponding {@link AttributeType} and
114         * persists or updates the attribute.
115         */
116        private void saveOrUpdateExecutionContext(ExecutionContext ctx, Long executionId, String discriminator) {
117 
118                for (Iterator it = ctx.entrySet().iterator(); it.hasNext();) {
119                        Entry entry = (Entry) it.next();
120                        final String key = entry.getKey().toString();
121                        final Object value = entry.getValue();
122 
123                        if (value instanceof String) {
124                                updateExecutionAttribute(executionId, discriminator, key, value, AttributeType.STRING);
125                        }
126                        else if (value instanceof Double) {
127                                updateExecutionAttribute(executionId, discriminator, key, value, AttributeType.DOUBLE);
128                        }
129                        else if (value instanceof Long) {
130                                updateExecutionAttribute(executionId, discriminator, key, value, AttributeType.LONG);
131                        }
132                        else {
133                                updateExecutionAttribute(executionId, discriminator, key, value, AttributeType.OBJECT);
134                        }
135                }
136        }
137 
138        /**
139         * Creates {@link PreparedStatement} from the provided arguments and tries
140         * to update the attribute - if the attribute does not exist in the database
141         * yet it is inserted.
142         */
143        private void updateExecutionAttribute(final Long executionId, final String discriminator, final String key,
144                        final Object value, final AttributeType type) {
145 
146                PreparedStatementCallback callback = new AbstractLobCreatingPreparedStatementCallback(lobHandler) {
147 
148                        protected void setValues(PreparedStatement ps, LobCreator lobCreator) throws SQLException,
149                                        DataAccessException {
150 
151                                ps.setLong(6, executionId.longValue());
152                                ps.setString(7, key);
153                                if (type == AttributeType.STRING) {
154                                        ps.setString(1, AttributeType.STRING.toString());
155                                        ps.setString(2, value.toString());
156                                        ps.setDouble(3, 0.0);
157                                        ps.setLong(4, 0);
158                                        lobCreator.setBlobAsBytes(ps, 5, null);
159                                }
160                                else if (type == AttributeType.DOUBLE) {
161                                        ps.setString(1, AttributeType.DOUBLE.toString());
162                                        ps.setString(2, null);
163                                        ps.setDouble(3, ((Double) value).doubleValue());
164                                        ps.setLong(4, 0);
165                                        lobCreator.setBlobAsBytes(ps, 5, null);
166                                }
167                                else if (type == AttributeType.LONG) {
168                                        ps.setString(1, AttributeType.LONG.toString());
169                                        ps.setString(2, null);
170                                        ps.setDouble(3, 0.0);
171                                        ps.setLong(4, ((Long) value).longValue());
172                                        lobCreator.setBlobAsBytes(ps, 5, null);
173                                }
174                                else {
175                                        ps.setString(1, AttributeType.OBJECT.toString());
176                                        ps.setString(2, null);
177                                        ps.setDouble(3, 0.0);
178                                        ps.setLong(4, 0);
179                                        setBlob(lobCreator, ps, 5, value);
180                                }
181                        }
182                };
183 
184                // LobCreating callbacks always return the affect row count for SQL DML
185                // statements, if less than 1 row
186                // is affected, then this row is new and should be inserted.
187                Integer affectedRows = (Integer) getJdbcTemplate().execute(getQuery(UPDATE_STEP_EXECUTION_CONTEXT), callback);
188                if (affectedRows.intValue() < 1) {
189                        insertExecutionAttribute(executionId, discriminator, key, value, type);
190                }
191        }
192 
193        /**
194         * Creates {@link PreparedStatement} from provided arguments and inserts new
195         * row for the attribute.
196         */
197        private void insertExecutionAttribute(final Long executionId, final String discriminator, final String key,
198                        final Object value, final AttributeType type) {
199                PreparedStatementCallback callback = new AbstractLobCreatingPreparedStatementCallback(lobHandler) {
200 
201                        protected void setValues(PreparedStatement ps, LobCreator lobCreator) throws SQLException,
202                                        DataAccessException {
203 
204                                ps.setLong(1, executionId.longValue());
205                                ps.setString(2, discriminator);
206                                ps.setString(4, key);
207                                if (type == AttributeType.STRING) {
208                                        ps.setString(3, AttributeType.STRING.toString());
209                                        ps.setString(5, value.toString());
210                                        ps.setDouble(6, 0.0);
211                                        ps.setLong(7, 0);
212                                        lobCreator.setBlobAsBytes(ps, 8, null);
213                                }
214                                else if (type == AttributeType.DOUBLE) {
215                                        ps.setString(3, AttributeType.DOUBLE.toString());
216                                        ps.setString(5, null);
217                                        ps.setDouble(6, ((Double) value).doubleValue());
218                                        ps.setLong(7, 0);
219                                        lobCreator.setBlobAsBytes(ps, 8, null);
220                                }
221                                else if (type == AttributeType.LONG) {
222                                        ps.setString(3, AttributeType.LONG.toString());
223                                        ps.setString(5, null);
224                                        ps.setDouble(6, 0.0);
225                                        ps.setLong(7, ((Long) value).longValue());
226                                        lobCreator.setBlobAsBytes(ps, 8, null);
227                                }
228                                else {
229                                        ps.setString(3, AttributeType.OBJECT.toString());
230                                        ps.setString(5, null);
231                                        ps.setDouble(6, 0.0);
232                                        ps.setLong(7, 0);
233                                        setBlob(lobCreator, ps, 8, value);
234                                }
235                        }
236                };
237                getJdbcTemplate().execute(getQuery(INSERT_STEP_EXECUTION_CONTEXT), callback);
238        }
239 
240        /**
241         * Code used to set BLOB values.  Uses a binary stream since that seems to be the most
242         * compatibile option across database platforms.
243         *
244         * @throws SQLException
245         */
246        private void setBlob(LobCreator lobCreator, PreparedStatement ps, int index, Object value) throws SQLException {
247                byte[] b = SerializationUtils.serialize((Serializable) value);
248                lobCreator.setBlobAsBinaryStream( ps, index, new ByteArrayInputStream(b), b.length);
249        }
250 
251        public void setLobHandler(LobHandler lobHandler) {
252                this.lobHandler = lobHandler;
253        }
254 
255        /**
256         * Attribute types supported by the {@link ExecutionContext}.
257         */
258        private static class AttributeType {
259 
260                private final String type;
261 
262                private AttributeType(String type) {
263                        this.type = type;
264                }
265 
266                public String toString() {
267                        return type;
268                }
269 
270                public static final AttributeType STRING = new AttributeType("STRING");
271 
272                public static final AttributeType LONG = new AttributeType("LONG");
273 
274                public static final AttributeType OBJECT = new AttributeType("OBJECT");
275 
276                public static final AttributeType DOUBLE = new AttributeType("DOUBLE");
277 
278                private static final AttributeType[] VALUES = { STRING, OBJECT, LONG, DOUBLE };
279 
280                public static AttributeType getType(String typeAsString) {
281 
282                        for (int i = 0; i < VALUES.length; i++) {
283                                if (VALUES[i].toString().equals(typeAsString)) {
284                                        return (AttributeType) VALUES[i];
285                                }
286                        }
287 
288                        return null;
289                }
290        }
291 
292        /**
293         * Reads attributes from {@link ResultSet} and puts them into
294         * {@link ExecutionContext}, resolving the attributes' types using the
295         * 'TYPE_CD' column.
296         */
297        private static class ExecutionContextRowCallbackHandler implements RowCallbackHandler {
298 
299                private ExecutionContext executionContext;
300 
301                public ExecutionContextRowCallbackHandler(ExecutionContext ctx) {
302                        executionContext = ctx;
303                }
304 
305                public void processRow(ResultSet rs) throws SQLException {
306 
307                        String typeCd = rs.getString("TYPE_CD");
308                        AttributeType type = AttributeType.getType(typeCd);
309                        String key = rs.getString("KEY_NAME");
310                        if (type == AttributeType.STRING) {
311                                executionContext.putString(key, rs.getString("STRING_VAL"));
312                        }
313                        else if (type == AttributeType.LONG) {
314                                executionContext.putLong(key, rs.getLong("LONG_VAL"));
315                        }
316                        else if (type == AttributeType.DOUBLE) {
317                                executionContext.putDouble(key, rs.getDouble("DOUBLE_VAL"));
318                        }
319                        else if (type == AttributeType.OBJECT) {
320                                executionContext.put(key, SerializationUtils.deserialize(rs.getBinaryStream("OBJECT_VAL")));
321                        }
322                        else {
323                                throw new UnexpectedJobExecutionException("Invalid type found: [" + typeCd + "]");
324                        }
325                }
326        };
327}

[all classes][org.springframework.batch.core.repository.dao]
EMMA 2.0.5312 (C) Vladimir Roubtsov