1 | /* |
2 | |
3 | Derby - Class org.apache.derby.impl.store.raw.log.LogRecord |
4 | |
5 | Copyright 1997, 2004 The Apache Software Foundation or its licensors, as applicable. |
6 | |
7 | Licensed under the Apache License, Version 2.0 (the "License"); |
8 | you may not use this file except in compliance with the License. |
9 | You may obtain a copy of the License at |
10 | |
11 | http://www.apache.org/licenses/LICENSE-2.0 |
12 | |
13 | Unless required by applicable law or agreed to in writing, software |
14 | distributed under the License is distributed on an "AS IS" BASIS, |
15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
16 | See the License for the specific language governing permissions and |
17 | limitations under the License. |
18 | |
19 | */ |
20 | |
21 | package org.apache.derby.impl.store.raw.log; |
22 | |
23 | import org.apache.derby.iapi.reference.SQLState; |
24 | |
25 | import org.apache.derby.iapi.services.sanity.SanityManager; |
26 | import org.apache.derby.iapi.services.io.FormatIdUtil; |
27 | import org.apache.derby.iapi.services.io.StoredFormatIds; |
28 | import org.apache.derby.iapi.services.io.Formatable; |
29 | |
30 | import org.apache.derby.iapi.error.StandardException; |
31 | |
32 | import org.apache.derby.iapi.store.raw.Loggable; |
33 | import org.apache.derby.iapi.store.raw.Compensation; |
34 | import org.apache.derby.iapi.store.raw.RePreparable; |
35 | import org.apache.derby.iapi.store.raw.Undoable; |
36 | |
37 | import org.apache.derby.iapi.store.raw.xact.TransactionId; |
38 | |
39 | import org.apache.derby.iapi.services.io.CompressedNumber; |
40 | |
41 | import java.io.ObjectOutput; |
42 | import java.io.ObjectInput; |
43 | import java.io.IOException; |
44 | |
45 | |
46 | /** |
47 | The log record written out to disk. This log record includes: |
48 | <P> |
49 | The is a holder object that may be setup using the setValue() and re-used |
50 | rather than creating a new object for each actual log record. |
51 | |
52 | <P> <PRE> |
53 | The format of a log record is |
54 | |
55 | @format_id LOG_RECORD |
56 | the formatId is written by FormatIdOutputStream when this object is |
57 | written out by writeObject |
58 | @purpose The log record described every change to the persistent store |
59 | @upgrade |
60 | @disk_layout |
61 | loggable group(CompressedInt) the loggable's group value |
62 | xactId(TransactionId) The Transaction this log belongs to |
63 | op(Loggable) the log operation |
64 | @end_format |
65 | </PRE> |
66 | |
67 | */ |
68 | public class LogRecord implements Formatable { |
69 | |
70 | private TransactionId xactId; // the transaction Id |
71 | private Loggable op; // the loggable |
72 | private int group; // the loggable's group value |
73 | |
74 | // the objectInput stream that contains the loggable object. The |
75 | // objectification of the transaction Id and the the loggable object is |
76 | // delayed from readExternal time to getTransactionId and getLoggable time |
77 | // to give the log scan an opportunity to discard the loggable based on |
78 | // group value and xactId. |
79 | transient ObjectInput input; |
80 | |
81 | private static final int formatLength = FormatIdUtil.getFormatIdByteLength(StoredFormatIds.LOG_RECORD); |
82 | |
83 | public LogRecord() { |
84 | } |
85 | |
86 | /* |
87 | * Formatable methods |
88 | */ |
89 | |
90 | /** |
91 | Write this out. |
92 | @exception IOException error writing to log stream |
93 | */ |
94 | public void writeExternal(ObjectOutput out) throws IOException |
95 | { |
96 | CompressedNumber.writeInt(out, group); |
97 | out.writeObject(xactId); |
98 | out.writeObject(op); |
99 | } |
100 | |
101 | /** |
102 | Read this in |
103 | @exception IOException error reading from log stream |
104 | @exception ClassNotFoundException corrupted log stream |
105 | */ |
106 | public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException |
107 | { |
108 | group = CompressedNumber.readInt(in); |
109 | input = in; // tie the input to this logRecord |
110 | |
111 | xactId = null; // delay reading these until later |
112 | op = null; |
113 | } |
114 | |
115 | /** |
116 | Return my format identifier. |
117 | */ |
118 | public int getTypeFormatId() { |
119 | return StoredFormatIds.LOG_RECORD; |
120 | } |
121 | |
122 | /* |
123 | * class specific methods |
124 | */ |
125 | public void setValue(TransactionId xactId, Loggable op) |
126 | { |
127 | this.xactId = xactId; |
128 | this.op = op; |
129 | |
130 | this.group = op.group(); |
131 | } |
132 | |
133 | public static int formatOverhead() |
134 | { |
135 | return formatLength; |
136 | } |
137 | |
138 | public static int maxGroupStoredSize() |
139 | { |
140 | return CompressedNumber.MAX_INT_STORED_SIZE; |
141 | } |
142 | |
143 | public static int maxTransactionIdStoredSize(TransactionId tranId) |
144 | { |
145 | return tranId.getMaxStoredSize(); |
146 | } |
147 | |
148 | |
149 | public static int getStoredSize(int group, TransactionId xactId) |
150 | { |
151 | |
152 | if (SanityManager.DEBUG) |
153 | { |
154 | SanityManager.ASSERT(xactId == null, |
155 | "size calculation are based on xactId being null"); |
156 | } |
157 | |
158 | return formatLength + CompressedNumber.sizeInt(group) + |
159 | FormatIdUtil.getFormatIdByteLength(StoredFormatIds.NULL_FORMAT_ID); |
160 | } |
161 | |
162 | |
163 | public TransactionId getTransactionId() |
164 | throws IOException, ClassNotFoundException |
165 | { |
166 | if (xactId != null) |
167 | return xactId; |
168 | |
169 | if (SanityManager.DEBUG) |
170 | SanityManager.ASSERT(input != null, |
171 | "xactId not objectified but object input is not set"); |
172 | |
173 | Object obj = input.readObject(); |
174 | if (SanityManager.DEBUG) |
175 | { |
176 | SanityManager.ASSERT(obj instanceof TransactionId, |
177 | "log record not getting expected TransactionId"); |
178 | } |
179 | xactId = (TransactionId)obj; |
180 | |
181 | return xactId; |
182 | } |
183 | |
184 | public Loggable getLoggable() throws IOException, ClassNotFoundException { |
185 | |
186 | if (op != null) // If log operation is already objectified, |
187 | return op; // then just return it. |
188 | |
189 | if (SanityManager.DEBUG) |
190 | SanityManager.ASSERT(input != null, |
191 | "logop not objectified but object input is not set"); |
192 | |
193 | if (xactId == null) // xactId is not read off yet |
194 | { |
195 | xactId = (TransactionId)input.readObject(); |
196 | } |
197 | |
198 | Object obj = input.readObject(); |
199 | |
200 | if (SanityManager.DEBUG) { |
201 | if ( ! (obj instanceof Loggable)) |
202 | SanityManager.THROWASSERT( |
203 | "log record not getting expected Loggable: got : " + |
204 | obj.getClass().getName()); |
205 | } |
206 | op = (Loggable)obj; |
207 | |
208 | input = null; |
209 | |
210 | return op; |
211 | } |
212 | |
213 | public RePreparable getRePreparable() |
214 | throws IOException, ClassNotFoundException |
215 | { |
216 | return((RePreparable) getLoggable()); |
217 | } |
218 | |
219 | /** |
220 | Skip over the loggable. Set the input stream to point ot after the |
221 | loggable as if the entire log record has been sucked in by the log |
222 | record |
223 | |
224 | @exception StandardException if the loggable is not found, log is corrupt |
225 | */ |
226 | public void skipLoggable() throws StandardException |
227 | { |
228 | if (op != null) // loggable already read off |
229 | return; |
230 | |
231 | try |
232 | { |
233 | if (xactId == null) |
234 | xactId = (TransactionId)input.readObject(); // get rid of the transactionId |
235 | |
236 | if (op == null) |
237 | op = (Loggable)input.readObject(); // get rid of the loggable |
238 | } |
239 | catch(ClassNotFoundException cnfe) |
240 | { |
241 | throw StandardException.newException(SQLState.LOG_CORRUPTED, cnfe); |
242 | } |
243 | catch(IOException ioe) |
244 | { |
245 | throw StandardException.newException(SQLState.LOG_CORRUPTED, ioe); |
246 | } |
247 | } |
248 | |
249 | public Undoable getUndoable() throws IOException, ClassNotFoundException |
250 | { |
251 | if (op == null) |
252 | getLoggable(); // objectify it |
253 | |
254 | if (op instanceof Undoable) |
255 | return (Undoable) op; |
256 | else |
257 | return null; |
258 | } |
259 | |
260 | public boolean isCLR() { |
261 | return ((group & Loggable.COMPENSATION) != 0); |
262 | } |
263 | |
264 | public boolean isFirst() { |
265 | return ((group & Loggable.FIRST) != 0); |
266 | } |
267 | |
268 | public boolean isComplete() { |
269 | return ((group & Loggable.LAST) != 0); |
270 | } |
271 | |
272 | public boolean isPrepare() { |
273 | return ((group & Loggable.PREPARE) != 0); |
274 | } |
275 | |
276 | public boolean requiresPrepareLocks() { |
277 | return ((group & Loggable.XA_NEEDLOCK) != 0); |
278 | } |
279 | |
280 | public boolean isCommit() |
281 | { |
282 | if (SanityManager.DEBUG) |
283 | { |
284 | SanityManager.ASSERT((group & Loggable.LAST) == Loggable.LAST, |
285 | "calling isCommit on log record that is not last"); |
286 | SanityManager.ASSERT((group & (Loggable.COMMIT | Loggable.ABORT)) != 0, |
287 | "calling isCommit on log record before commit status is recorded"); |
288 | } |
289 | return ((group & Loggable.COMMIT) != 0); |
290 | } |
291 | |
292 | public boolean isAbort() |
293 | { |
294 | if (SanityManager.DEBUG) |
295 | { |
296 | SanityManager.ASSERT((group & Loggable.LAST) == Loggable.LAST, |
297 | "calling isAbort on log record that is not last"); |
298 | SanityManager.ASSERT((group & (Loggable.COMMIT | Loggable.ABORT)) != 0, |
299 | "calling isAbort on log record before abort status is recorded"); |
300 | } |
301 | return ((group & Loggable.ABORT) != 0); |
302 | } |
303 | |
304 | public int group() |
305 | { |
306 | return group; |
307 | } |
308 | |
309 | |
310 | public boolean isChecksum() { |
311 | return ((group & Loggable.CHECKSUM) != 0); |
312 | } |
313 | } |