Mercurial > dive4elements > framework
comparison artifact-database/src/main/java/de/intevation/artifactdatabase/Backend.java @ 90:68285f7bc476
More javadoc.
artifacts/trunk@846 c6561f87-3c4e-4783-a992-168aeb5c3f6f
author | Sascha L. Teichmann <sascha.teichmann@intevation.de> |
---|---|
date | Fri, 26 Mar 2010 17:59:50 +0000 |
parents | b2e0cb83631c |
children | e27cf9c84eb8 |
comparison
equal
deleted
inserted
replaced
89:d348fe1fd822 | 90:68285f7bc476 |
---|---|
13 import de.intevation.artifacts.ArtifactSerializer; | 13 import de.intevation.artifacts.ArtifactSerializer; |
14 | 14 |
15 import org.apache.log4j.Logger; | 15 import org.apache.log4j.Logger; |
16 | 16 |
17 /** | 17 /** |
18 * The backend implements the low level layer used to store artifacts | |
19 * in a SQL database. | |
20 * | |
18 * @author <a href="mailto:sascha.teichmann@intevation.de">Sascha L. Teichmann</a> | 21 * @author <a href="mailto:sascha.teichmann@intevation.de">Sascha L. Teichmann</a> |
19 */ | 22 */ |
20 public class Backend | 23 public class Backend |
21 implements DatabaseCleaner.ArtifactReviver | 24 implements DatabaseCleaner.ArtifactReviver |
22 { | 25 { |
23 private static Logger logger = Logger.getLogger(Backend.class); | 26 private static Logger logger = Logger.getLogger(Backend.class); |
24 | 27 |
28 /** | |
29 * The SQL statement to create new artifact id inside the database. | |
30 */ | |
25 public static final String SQL_NEXT_ID = | 31 public static final String SQL_NEXT_ID = |
26 SQL.get("artifacts.id.nextval"); | 32 SQL.get("artifacts.id.nextval"); |
27 | 33 |
34 /** | |
35 * The SQL statement to insert an artifact into the database. | |
36 */ | |
28 public static final String SQL_INSERT = | 37 public static final String SQL_INSERT = |
29 SQL.get("artifacts.insert"); | 38 SQL.get("artifacts.insert"); |
30 | 39 |
40 /** | |
41 * The SQL statement to update some columns of an existing | |
42 * artifact in the database. | |
43 */ | |
31 public static final String SQL_UPDATE = | 44 public static final String SQL_UPDATE = |
32 SQL.get("artifacts.update"); | 45 SQL.get("artifacts.update"); |
33 | 46 |
47 /** | |
48 * The SQL statement to touch the access time of an | |
49 * artifact inside the database. | |
50 */ | |
34 public static final String SQL_TOUCH = | 51 public static final String SQL_TOUCH = |
35 SQL.get("artifacts.touch"); | 52 SQL.get("artifacts.touch"); |
36 | 53 |
54 /** | |
55 * The SQL statement to load an artifact by a given | |
56 * identifier from the database. | |
57 */ | |
37 public static final String SQL_LOAD_BY_GID = | 58 public static final String SQL_LOAD_BY_GID = |
38 SQL.get("artifacts.select.gid"); | 59 SQL.get("artifacts.select.gid"); |
39 | 60 |
61 /** | |
62 * The SQL statement to get the database id of an artifact | |
63 * identified by the identifier. | |
64 */ | |
40 public static final String SQL_GET_ID = | 65 public static final String SQL_GET_ID = |
41 SQL.get("artifacts.get.id"); | 66 SQL.get("artifacts.get.id"); |
42 | 67 |
68 /** | |
69 * The SQL statement to replace the content of an | |
70 * existing artifact inside the database. | |
71 */ | |
43 public static final String SQL_REPLACE = | 72 public static final String SQL_REPLACE = |
44 SQL.get("artifacts.replace"); | 73 SQL.get("artifacts.replace"); |
45 | 74 |
75 /** | |
76 * The database cleaner. Reference is stored here because | |
77 * the cleaner is woken up if the backend finds an outdated | |
78 * artifact. This artifact should be removed as soon as | |
79 * possible. | |
80 */ | |
46 protected DatabaseCleaner cleaner; | 81 protected DatabaseCleaner cleaner; |
47 | 82 |
83 /** | |
84 * To revive an artifact from the bytes coming from the database | |
85 * we need the artifact factory which references the artifact | |
86 * serializer which is able to do the reviving job. | |
87 */ | |
48 protected FactoryLookup factoryLookup; | 88 protected FactoryLookup factoryLookup; |
49 | 89 |
90 /** | |
91 * Little helper interface to decouple the ArtifactDatabase | |
92 * from the Backend. A ArtifactDatabase should depend on a | |
93 * Backend but a Backend not from an ArtifactDatabase. | |
94 */ | |
50 public interface FactoryLookup { | 95 public interface FactoryLookup { |
51 | 96 |
97 /** | |
98 * Returns an ArtifactFactory which is bound to a given name. | |
99 * @param factoryName The name of the artifact factory. | |
100 * @return The ArtifactFactory bound to the factory name or | |
101 * null if not matching factory is found. | |
102 */ | |
52 ArtifactFactory getArtifactFactory(String factoryName); | 103 ArtifactFactory getArtifactFactory(String factoryName); |
53 | 104 |
54 } // interface FactoryLookup | 105 } // interface FactoryLookup |
55 | 106 |
107 /** | |
108 * Inner class that brigdes between the persisten form of the | |
109 * artifact and the living one inside the artifact database. | |
110 * After the describe(), feed(), advance() and out() operations | |
111 * of the artifact it must be possible to write to modified artifact | |
112 * back into the database. | |
113 */ | |
56 public final class PersistentArtifact | 114 public final class PersistentArtifact |
57 extends Id | 115 extends Id |
58 { | 116 { |
59 private Artifact artifact; | 117 private Artifact artifact; |
60 private ArtifactSerializer serializer; | 118 private ArtifactSerializer serializer; |
61 private Long ttl; | 119 private Long ttl; |
62 | 120 |
121 /** | |
122 * Cronstructor to create a persistent artifact. | |
123 * @param artifact The living artifact. | |
124 * @param serializer The serializer to store the artifact | |
125 * after the operations. | |
126 * @param ttl The time to life of the artifact. | |
127 * @param id The database id of the artifact. | |
128 */ | |
63 public PersistentArtifact( | 129 public PersistentArtifact( |
64 Artifact artifact, | 130 Artifact artifact, |
65 ArtifactSerializer serializer, | 131 ArtifactSerializer serializer, |
66 Long ttl, | 132 Long ttl, |
67 int id | 133 int id |
70 this.artifact = artifact; | 136 this.artifact = artifact; |
71 this.serializer = serializer; | 137 this.serializer = serializer; |
72 this.ttl = ttl; | 138 this.ttl = ttl; |
73 } | 139 } |
74 | 140 |
141 /** | |
142 * Returns the wrapped living artifact. | |
143 * @return the living artifact. | |
144 */ | |
75 public Artifact getArtifact() { | 145 public Artifact getArtifact() { |
76 return artifact; | 146 return artifact; |
77 } | 147 } |
78 | 148 |
149 /** | |
150 * Returns the serialized which is able to write a | |
151 * modified artifact back into the database. | |
152 * @return The serializer. | |
153 */ | |
79 public ArtifactSerializer getSerializer() { | 154 public ArtifactSerializer getSerializer() { |
80 return serializer; | 155 return serializer; |
81 } | 156 } |
82 | 157 |
158 /** | |
159 * The time to life of the artifact. | |
160 * @return The time to live. | |
161 */ | |
83 public Long getTTL() { | 162 public Long getTTL() { |
84 return ttl; | 163 return ttl; |
85 } | 164 } |
86 | 165 |
166 /** | |
167 * Stores the living artifact back into the database. | |
168 */ | |
87 public void store() { | 169 public void store() { |
88 if (logger.isDebugEnabled()) { | 170 if (logger.isDebugEnabled()) { |
89 logger.debug("storing artifact id = " + getId()); | 171 logger.debug("storing artifact id = " + getId()); |
90 } | 172 } |
91 Backend.this.store(this); | 173 Backend.this.store(this); |
92 } | 174 } |
93 | 175 |
176 /** | |
177 * Only touches the access time of the artifact. | |
178 */ | |
94 public void touch() { | 179 public void touch() { |
95 if (logger.isDebugEnabled()) { | 180 if (logger.isDebugEnabled()) { |
96 logger.debug("touching artifact id = " + getId()); | 181 logger.debug("touching artifact id = " + getId()); |
97 } | 182 } |
98 Backend.this.touch(this); | 183 Backend.this.touch(this); |
99 } | 184 } |
100 } // class ArtifactWithId | 185 } // class ArtifactWithId |
101 | 186 |
187 /** | |
188 * Default constructor | |
189 */ | |
102 public Backend() { | 190 public Backend() { |
103 } | 191 } |
104 | 192 |
193 /** | |
194 * Constructor to create a backend with a link to the database cleaner. | |
195 * @param cleaner The clean which periodically removes outdated | |
196 * artifacts from the database. | |
197 */ | |
105 public Backend(DatabaseCleaner cleaner) { | 198 public Backend(DatabaseCleaner cleaner) { |
106 this.cleaner = cleaner; | 199 this.cleaner = cleaner; |
107 } | 200 } |
108 | 201 |
202 /** | |
203 * Sets the factory lookup mechanism to decouple ArtifactDatabase | |
204 * and Backend. | |
205 * @param factoryLookup | |
206 */ | |
109 public void setFactoryLookup(FactoryLookup factoryLookup) { | 207 public void setFactoryLookup(FactoryLookup factoryLookup) { |
110 this.factoryLookup = factoryLookup; | 208 this.factoryLookup = factoryLookup; |
111 } | 209 } |
112 | 210 |
211 /** | |
212 * Sets the database cleaner explicitly. | |
213 * @param cleaner The database cleaner | |
214 */ | |
113 public void setCleaner(DatabaseCleaner cleaner) { | 215 public void setCleaner(DatabaseCleaner cleaner) { |
114 this.cleaner = cleaner; | 216 this.cleaner = cleaner; |
115 } | 217 } |
116 | 218 |
219 /** | |
220 * Returns a new unique identifier to external identify | |
221 * the artifact across the system. This implementation | |
222 * uses random UUIDs v4 to achieve this target. | |
223 * @return the new identifier | |
224 */ | |
117 public String newIdentifier() { | 225 public String newIdentifier() { |
118 // TODO: check database for collisions. | 226 // TODO: check database for collisions. |
119 return StringUtils.newUUID(); | 227 return StringUtils.newUUID(); |
120 } | 228 } |
121 | 229 |
230 /** | |
231 * Stores a new artifact into the database. | |
232 * @param artifact The artifact to be stored | |
233 * @param factory The factory which build the artifact | |
234 * @param ttl The initial time to life of the artifact. | |
235 * @return A persistent wrapper around the living | |
236 * artifact to be able to write modification later. | |
237 * @throws Exception Thrown if something went wrong with the | |
238 * storage process. | |
239 */ | |
122 public PersistentArtifact storeInitially( | 240 public PersistentArtifact storeInitially( |
123 Artifact artifact, | 241 Artifact artifact, |
124 ArtifactFactory factory, | 242 ArtifactFactory factory, |
125 Long ttl | 243 Long ttl |
126 ) | 244 ) |
131 factory.getSerializer(), | 249 factory.getSerializer(), |
132 ttl, | 250 ttl, |
133 insertDatabase(artifact, factory, ttl)); | 251 insertDatabase(artifact, factory, ttl)); |
134 } | 252 } |
135 | 253 |
254 /** | |
255 * Stores an artifact into database if it does not exist there. | |
256 * If it exists there it is only updated. | |
257 * @param artifact The artifact to store/update. | |
258 * @param factory The factory which created the artifact. | |
259 * @param ttl The initial time to live of the artifact. | |
260 * @return A persistent version of the artifact to be able | |
261 * to store a modification later. | |
262 * @throws Exception Thrown if something went wrong during | |
263 * storing/updating. | |
264 */ | |
136 public PersistentArtifact storeOrReplace( | 265 public PersistentArtifact storeOrReplace( |
137 Artifact artifact, | 266 Artifact artifact, |
138 ArtifactFactory factory, | 267 ArtifactFactory factory, |
139 Long ttl | 268 Long ttl |
140 ) | 269 ) |
145 factory.getSerializer(), | 274 factory.getSerializer(), |
146 ttl, | 275 ttl, |
147 storeOrReplaceDatabase(artifact, factory, ttl)); | 276 storeOrReplaceDatabase(artifact, factory, ttl)); |
148 } | 277 } |
149 | 278 |
279 /** | |
280 * Implementors of this interface are able to process the raw | |
281 * artifact data from the database for loading. | |
282 */ | |
150 public interface ArtifactLoader { | 283 public interface ArtifactLoader { |
151 | 284 |
285 /** | |
286 * Creates a custom object from the raw artifact database data. | |
287 * @param factory The factory that created this artifact. | |
288 * @param ttl The current time to life of the artifact. | |
289 * @param bytes The raw artifact bytes from the database. | |
290 * @param id The database id of the artifact. | |
291 * @return The custom object created by the implementation. | |
292 */ | |
152 Object load(ArtifactFactory factory, Long ttl, byte [] bytes, int id); | 293 Object load(ArtifactFactory factory, Long ttl, byte [] bytes, int id); |
153 | 294 |
154 } // interface ArtifactLoader | 295 } // interface ArtifactLoader |
155 | 296 |
297 /** | |
298 * Fetches an artifact from the database identified by the | |
299 * given identifier. | |
300 * @param identifer The identifier of the artifact. | |
301 * @return A persistent wrapper around the found artifact | |
302 * to be able to write back a modifaction later or null | |
303 * if no artifact is found for this identifier. | |
304 */ | |
156 public PersistentArtifact getArtifact(String identifer) { | 305 public PersistentArtifact getArtifact(String identifer) { |
157 | 306 |
158 return (PersistentArtifact)loadArtifact( | 307 return (PersistentArtifact)loadArtifact( |
159 identifer, | 308 identifer, |
160 new ArtifactLoader() { | 309 new ArtifactLoader() { |
174 : new PersistentArtifact(artifact, serializer, ttl, id); | 323 : new PersistentArtifact(artifact, serializer, ttl, id); |
175 } | 324 } |
176 }); | 325 }); |
177 } | 326 } |
178 | 327 |
328 /** | |
329 * More general loading mechanism for artifacts. The concrete | |
330 * load processing is delegated to the given loader. | |
331 * @param identifer The identifier of the artifact. | |
332 * @param loader The loader which processes the raw database data. | |
333 * @return The object created by the loader. | |
334 */ | |
179 public Object loadArtifact(String identifer, ArtifactLoader loader) { | 335 public Object loadArtifact(String identifer, ArtifactLoader loader) { |
180 | 336 |
181 if (!StringUtils.checkUUID(identifer)) { | 337 if (!StringUtils.checkUUID(identifer)) { |
182 return null; | 338 return null; |
183 } | 339 } |
248 } | 404 } |
249 } | 405 } |
250 return null; | 406 return null; |
251 } | 407 } |
252 | 408 |
409 /** | |
410 * Called if the load mechanism found an outdated artifact. | |
411 * It wakes up the database cleaner. | |
412 * @param id The id of the outdated artifact. | |
413 */ | |
253 protected void artifactOutdated(int id) { | 414 protected void artifactOutdated(int id) { |
254 if (logger.isDebugEnabled()) { | 415 if (logger.isDebugEnabled()) { |
255 logger.info("artifactOutdated: id = " + id); | 416 logger.info("artifactOutdated: id = " + id); |
256 } | 417 } |
257 if (cleaner != null) { | 418 if (cleaner != null) { |
266 } | 427 } |
267 ArtifactFactory factory = factoryLookup | 428 ArtifactFactory factory = factoryLookup |
268 .getArtifactFactory(factoryName); | 429 .getArtifactFactory(factoryName); |
269 | 430 |
270 if (factory == null) { | 431 if (factory == null) { |
271 logger.error("reviveArtifact: no factory '" + factoryName + "' found"); | 432 logger.error( |
433 "reviveArtifact: no factory '" + factoryName + "' found"); | |
272 return null; | 434 return null; |
273 } | 435 } |
274 | 436 |
275 ArtifactSerializer serializer = factory.getSerializer(); | 437 ArtifactSerializer serializer = factory.getSerializer(); |
276 | 438 |
277 return serializer.fromBytes(bytes); | 439 return serializer.fromBytes(bytes); |
278 } | 440 } |
279 | 441 |
442 /** | |
443 * Internal method to store/replace an artifact inside the database. | |
444 * If an artifact with the given identifier does not exists it is | |
445 * created else only the content data is updated. | |
446 * @param artifact The artifact to be store/update inside the database. | |
447 * @param factory The factory that created the artifact. | |
448 * @param ttl The initial time to life of the artifact. | |
449 * @return The database id of the stored/updated artifact. | |
450 */ | |
280 protected int storeOrReplaceDatabase( | 451 protected int storeOrReplaceDatabase( |
281 Artifact artifact, | 452 Artifact artifact, |
282 ArtifactFactory factory, | 453 ArtifactFactory factory, |
283 Long ttl | 454 Long ttl |
284 ) { | 455 ) { |
389 } | 560 } |
390 } | 561 } |
391 throw new RuntimeException("failed insert artifact into database"); | 562 throw new RuntimeException("failed insert artifact into database"); |
392 } | 563 } |
393 | 564 |
565 /** | |
566 * Internal method to store an artifact inside the database. | |
567 * @param artifact The artifact to be stored. | |
568 * @param factory The factory which created the artifact. | |
569 * @param ttl The initial time to live of the artifact. | |
570 * @return The database id of the stored artifact. | |
571 */ | |
394 protected int insertDatabase( | 572 protected int insertDatabase( |
395 Artifact artifact, | 573 Artifact artifact, |
396 ArtifactFactory factory, | 574 ArtifactFactory factory, |
397 Long ttl | 575 Long ttl |
398 ) { | 576 ) { |
468 } | 646 } |
469 } | 647 } |
470 throw new RuntimeException("failed insert artifact into database"); | 648 throw new RuntimeException("failed insert artifact into database"); |
471 } | 649 } |
472 | 650 |
651 /** | |
652 * Touches the access timestamp of a given artifact to prevent | |
653 * that it will be removed from the database by the database cleaner. | |
654 * @param artifact The persistent wrapper around the living artifact. | |
655 */ | |
473 public void touch(PersistentArtifact artifact) { | 656 public void touch(PersistentArtifact artifact) { |
474 | 657 |
475 try { | 658 try { |
476 Connection connection = null; | 659 Connection connection = null; |
477 PreparedStatement stmnt_touch = null; | 660 PreparedStatement stmnt_touch = null; |
506 catch (Exception e) { | 689 catch (Exception e) { |
507 logger.error(e.getLocalizedMessage(), e); | 690 logger.error(e.getLocalizedMessage(), e); |
508 } | 691 } |
509 } | 692 } |
510 | 693 |
694 /** | |
695 * Writes modification of an artifact back to the database. | |
696 * @param artifact The persistent wrapper around a living | |
697 * artifact. | |
698 */ | |
511 public void store(PersistentArtifact artifact) { | 699 public void store(PersistentArtifact artifact) { |
512 | 700 |
513 try { | 701 try { |
514 Connection connection = null; | 702 Connection connection = null; |
515 PreparedStatement stmnt_update = null; | 703 PreparedStatement stmnt_update = null; |