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;

http://dive4elements.wald.intevation.org