comparison flys-artifacts/src/main/java/de/intevation/flys/artifacts/datacage/Recommendations.java @ 5779:ebec12def170

Datacage: Add a pool of builders to make it multi threadable. XML DOM is not thread safe. Therefore the old implementation only allowed one thread to use the builder at a time. As the complexity of the configuration has increased over time this has become a bottleneck of the whole application because it took quiet some time to build a result. Furthermore the builder code path is visited very frequent. So many concurrent requests were piled up resulting in long waits for the users. To mitigate this problem a round robin pool of builders is used now. Each of the pooled builders has an independent copy of the XML template and can be run in parallel. The number of builders is determined by the system property 'flys.datacage.pool.size'. It defaults to 4.
author Sascha L. Teichmann <teichmann@intevation.de>
date Sun, 21 Apr 2013 12:48:09 +0200
parents cf4cc385e7c6
children
comparison
equal deleted inserted replaced
5778:4a1bd43e7aa6 5779:ebec12def170
34 import de.intevation.flys.backend.SessionHolder; 34 import de.intevation.flys.backend.SessionHolder;
35 35
36 import de.intevation.artifactdatabase.data.StateData; 36 import de.intevation.artifactdatabase.data.StateData;
37 37
38 import de.intevation.flys.artifacts.datacage.templating.Builder; 38 import de.intevation.flys.artifacts.datacage.templating.Builder;
39 import de.intevation.flys.artifacts.datacage.templating.BuilderPool;
39 40
40 41
41 /** 42 /**
42 * Also accessible as Singleton with getInstance(). 43 * Also accessible as Singleton with getInstance().
43 */ 44 */
60 public static final String DEFAULT_TEMPLATE_PATH = 61 public static final String DEFAULT_TEMPLATE_PATH =
61 "${artifacts.config.dir}/meta-data.xml"; 62 "${artifacts.config.dir}/meta-data.xml";
62 63
63 private static Recommendations INSTANCE; 64 private static Recommendations INSTANCE;
64 65
65 public static class BuilderProvider 66 public static class BuilderPoolProvider
66 { 67 {
67 protected Builder builder; 68 protected BuilderPool builderPool;
68 69
69 public BuilderProvider() { 70 public BuilderPoolProvider() {
70 } 71 }
71 72
72 public BuilderProvider(Builder builder) { 73 public BuilderPoolProvider(BuilderPool builderPool) {
73 this.builder = builder; 74 this.builderPool = builderPool;
74 } 75 }
75 76
76 public Builder getBuilder() { 77 public BuilderPool getBuilderPool() {
77 return builder; 78 return builderPool;
78 } 79 }
79 } // class BuilderProvider 80 } // class BuilderProvider
80 81
81 public static class FileBuilderProvider 82 public static class FileBuilderPoolProvider
82 extends BuilderProvider 83 extends BuilderPoolProvider
83 { 84 {
84 protected File file; 85 protected File file;
85 protected long lastModified; 86 protected long lastModified;
86 87
87 public FileBuilderProvider() { 88 public FileBuilderPoolProvider() {
88 } 89 }
89 90
90 public FileBuilderProvider(File file) { 91 public FileBuilderPoolProvider(File file) {
91 this.file = file; 92 this.file = file;
92 lastModified = Long.MIN_VALUE; 93 lastModified = Long.MIN_VALUE;
93 } 94 }
94 95
95 @Override 96 @Override
96 public synchronized Builder getBuilder() { 97 public synchronized BuilderPool getBuilderPool() {
97 long modified = file.lastModified(); 98 long modified = file.lastModified();
98 if (modified > lastModified) { 99 if (modified > lastModified) {
99 lastModified = modified; 100 lastModified = modified;
100 try { 101 try {
101 Document template = loadTemplate(file); 102 Document template = loadTemplate(file);
102 builder = new Builder(template); 103 builderPool = new BuilderPool(template);
103 } 104 }
104 catch (IOException ioe) { 105 catch (IOException ioe) {
105 log.error(ioe); 106 log.error(ioe);
106 } 107 }
107 } 108 }
108 return builder; 109 return builderPool;
109 } 110 }
110 111
111 public BuilderProvider toStaticProvider() { 112 public BuilderPoolProvider toStaticProvider() {
112 return new BuilderProvider(builder); 113 return new BuilderPoolProvider(builderPool);
113 } 114 }
114 } // class BuilderProvider 115 } // class BuilderProvider
115 116
116 protected BuilderProvider builderProvider; 117 protected BuilderPoolProvider builderPoolProvider;
117 118
118 public Recommendations() { 119 public Recommendations() {
119 } 120 }
120 121
121 public Recommendations(BuilderProvider builderProvider) { 122 public Recommendations(BuilderPoolProvider builderPoolProvider) {
122 this.builderProvider = builderProvider; 123 this.builderPoolProvider = builderPoolProvider;
123 } 124 }
124 125
125 public Builder getBuilder() { 126 public BuilderPool getBuilderPool() {
126 return builderProvider.getBuilder(); 127 return builderPoolProvider.getBuilderPool();
127 } 128 }
128 129
129 protected static void artifactToParameters( 130 protected static void artifactToParameters(
130 FLYSArtifact artifact, 131 FLYSArtifact artifact,
131 Map<String, Object> parameters 132 Map<String, Object> parameters
140 } 141 }
141 String key = sd.getName().replace('.', '-').toUpperCase(); 142 String key = sd.getName().replace('.', '-').toUpperCase();
142 parameters.put(key, value); 143 parameters.put(key, value);
143 } 144 }
144 } 145 }
145
146 146
147 /** 147 /**
148 * Put Key/Values from \param src to \param dst, but uppercase 148 * Put Key/Values from \param src to \param dst, but uppercase
149 * both Keys and Values. 149 * both Keys and Values.
150 */ 150 */
268 if (userConnection != null) { 268 if (userConnection != null) {
269 connections.add(new Builder.NamedConnection( 269 connections.add(new Builder.NamedConnection(
270 CONNECTION_USER, userConnection, false)); 270 CONNECTION_USER, userConnection, false));
271 } 271 }
272 272
273 273 getBuilderPool().build(connections, result, parameters);
274 getBuilder().build(connections, result, parameters);
275 } 274 }
276 finally { 275 finally {
277 if (userConnection != null) { 276 if (userConnection != null) {
278 userConnection.close(); 277 userConnection.close();
279 } 278 }
288 return INSTANCE; 287 return INSTANCE;
289 } 288 }
290 289
291 290
292 protected static Document loadTemplate(File file) throws IOException { 291 protected static Document loadTemplate(File file) throws IOException {
293 InputStream in = null; 292 InputStream in = new FileInputStream(file);
294 293
295 try { 294 try {
296 in = new FileInputStream(file);
297
298 Document template = XMLUtils.parseDocument(in); 295 Document template = XMLUtils.parseDocument(in);
299
300 if (template == null) { 296 if (template == null) {
301 throw new IOException("cannot load template"); 297 throw new IOException("cannot load template");
302 } 298 }
303 return template; 299 return template;
304 } 300 }
305 finally { 301 finally {
306 if (in != null) { 302 in.close();
307 try {
308 in.close();
309 }
310 catch (IOException ioe) {
311 log.error(ioe);
312 }
313 }
314 } 303 }
315 } 304 }
316 305
317 public static Recommendations createRecommendations(File file) { 306 public static Recommendations createRecommendations(File file) {
318 log.debug("Recommendations.createBuilder"); 307 log.debug("Recommendations.createBuilder");
320 if (!file.isFile() || !file.canRead()) { 309 if (!file.isFile() || !file.canRead()) {
321 log.error("Cannot open template file '" + file + "'"); 310 log.error("Cannot open template file '" + file + "'");
322 return null; 311 return null;
323 } 312 }
324 313
325 FileBuilderProvider fbp = new FileBuilderProvider(file); 314 FileBuilderPoolProvider fbp = new FileBuilderPoolProvider(file);
326 315
327 if (fbp.getBuilder() == null) { 316 if (fbp.getBuilderPool() == null) {
328 log.error("failed loading builder"); 317 log.error("failed loading builder");
329 return null; 318 return null;
330 } 319 }
331 320
332 BuilderProvider bp = DEVELOPMENT_MODE 321 BuilderPoolProvider bp = DEVELOPMENT_MODE
333 ? fbp 322 ? fbp
334 : fbp.toStaticProvider(); 323 : fbp.toStaticProvider();
335 324
336 return new Recommendations(bp); 325 return new Recommendations(bp);
337 } 326 }

http://dive4elements.wald.intevation.org