1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.opensaml.xml.parse;
18
19 import java.io.File;
20 import java.io.IOException;
21 import java.io.InputStream;
22 import java.io.Reader;
23 import java.lang.ref.SoftReference;
24 import java.util.Collections;
25 import java.util.EmptyStackException;
26 import java.util.HashMap;
27 import java.util.Map;
28 import java.util.Stack;
29
30 import javax.xml.parsers.DocumentBuilder;
31 import javax.xml.parsers.DocumentBuilderFactory;
32 import javax.xml.parsers.ParserConfigurationException;
33 import javax.xml.validation.Schema;
34
35 import org.opensaml.xml.Configuration;
36 import org.slf4j.Logger;
37 import org.slf4j.LoggerFactory;
38 import org.w3c.dom.DOMImplementation;
39 import org.w3c.dom.Document;
40 import org.xml.sax.EntityResolver;
41 import org.xml.sax.ErrorHandler;
42 import org.xml.sax.InputSource;
43 import org.xml.sax.SAXException;
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60 public class BasicParserPool implements ParserPool {
61
62
63 private final Logger log = LoggerFactory.getLogger(BasicParserPool.class);
64
65
66 private long poolVersion;
67
68
69 private boolean createBuildersAtPoolLimit;
70
71
72 private boolean dirtyBuilderConfiguration;
73
74
75 private DocumentBuilderFactory builderFactory;
76
77
78 private Stack<SoftReference<DocumentBuilder>> builderPool;
79
80
81 private int maxPoolSize;
82
83
84 private Map<String, Object> builderAttributes;
85
86
87 private boolean coalescing;
88
89
90 private boolean expandEntityReferences;
91
92
93 private Map<String, Boolean> builderFeatures;
94
95
96 private boolean ignoreComments;
97
98
99 private boolean ignoreElementContentWhitespace;
100
101
102 private boolean namespaceAware;
103
104
105 private Schema schema;
106
107
108 private boolean dtdValidating;
109
110
111 private boolean xincludeAware;
112
113
114 private EntityResolver entityResolver;
115
116
117 private ErrorHandler errorHandler;
118
119
120 public BasicParserPool() {
121 Configuration.validateNonSunJAXP();
122 maxPoolSize = 5;
123 builderPool = new Stack<SoftReference<DocumentBuilder>>();
124 builderAttributes = new HashMap<String, Object>();
125 coalescing = true;
126 expandEntityReferences = true;
127 builderFeatures = new HashMap<String, Boolean>();
128 ignoreComments = true;
129 ignoreElementContentWhitespace = true;
130 namespaceAware = true;
131 schema = null;
132 dtdValidating = false;
133 xincludeAware = false;
134 errorHandler = new LoggingErrorHandler(log);
135
136 try {
137 dirtyBuilderConfiguration = true;
138 initializePool();
139 } catch (XMLParserException e) {
140
141 }
142 }
143
144
145 public DocumentBuilder getBuilder() throws XMLParserException {
146 DocumentBuilder builder = null;
147
148 try {
149 if (dirtyBuilderConfiguration) {
150 initializePool();
151 }
152 builder = builderPool.pop().get();
153 } catch (EmptyStackException e) {
154
155
156
157 }
158
159 if (builder == null) {
160 if (builderPool.size() < maxPoolSize || createBuildersAtPoolLimit) {
161 builder = createBuilder();
162 }
163 }
164
165 if (builder != null) {
166 return new DocumentBuilderProxy(builder, this);
167 }
168
169 return null;
170 }
171
172
173 public void returnBuilder(DocumentBuilder builder) {
174 if (!(builder instanceof DocumentBuilderProxy)) {
175 return;
176 }
177
178 DocumentBuilderProxy proxiedBuilder = (DocumentBuilderProxy) builder;
179 if (proxiedBuilder.getOwningPool() != this || proxiedBuilder.getPoolVersion() != poolVersion) {
180 return;
181 }
182
183 DocumentBuilder unwrappedBuilder = proxiedBuilder.getProxiedBuilder();
184 unwrappedBuilder.reset();
185 SoftReference<DocumentBuilder> builderReference = new SoftReference<DocumentBuilder>(unwrappedBuilder);
186
187 synchronized (this) {
188 if (builderPool.size() < maxPoolSize) {
189 builderPool.push(builderReference);
190 }
191 }
192 }
193
194
195 public Document newDocument() throws XMLParserException {
196 DocumentBuilder builder = getBuilder();
197 Document document = builder.newDocument();
198 returnBuilder(builder);
199 return document;
200 }
201
202
203 public Document parse(InputStream input) throws XMLParserException {
204 DocumentBuilder builder = getBuilder();
205 try {
206 Document document = builder.parse(input);
207 return document;
208 } catch (SAXException e) {
209 throw new XMLParserException("Invalid XML", e);
210 } catch (IOException e) {
211 throw new XMLParserException("Unable to read XML from input stream", e);
212 } finally {
213 returnBuilder(builder);
214 }
215 }
216
217
218 public Document parse(Reader input) throws XMLParserException {
219 DocumentBuilder builder = getBuilder();
220 try {
221 Document document = builder.parse(new InputSource(input));
222 return document;
223 } catch (SAXException e) {
224 throw new XMLParserException("Invalid XML", e);
225 } catch (IOException e) {
226 throw new XMLParserException("Unable to read XML from input stream", e);
227 } finally {
228 returnBuilder(builder);
229 }
230 }
231
232
233
234
235
236
237 public int getMaxPoolSize() {
238 return maxPoolSize;
239 }
240
241
242
243
244
245
246 public void setMaxPoolSize(int newSize) {
247 maxPoolSize = newSize;
248 }
249
250
251
252
253
254
255 public boolean getCreateBuildersAtPoolLimit() {
256 return createBuildersAtPoolLimit;
257 }
258
259
260
261
262
263
264 public void setCreateBuildersAtPoolLimit(boolean createBuilders) {
265 createBuildersAtPoolLimit = createBuilders;
266 }
267
268
269
270
271
272
273 public Map<String, Object> getBuilderAttributes() {
274 return Collections.unmodifiableMap(builderAttributes);
275 }
276
277
278
279
280
281
282 public void setBuilderAttributes(Map<String, Object> newAttributes) {
283 builderAttributes = newAttributes;
284 dirtyBuilderConfiguration = true;
285 }
286
287
288
289
290
291
292 public boolean isCoalescing() {
293 return coalescing;
294 }
295
296
297
298
299
300
301 public void setCoalescing(boolean isCoalescing) {
302 coalescing = isCoalescing;
303 dirtyBuilderConfiguration = true;
304 }
305
306
307
308
309
310
311 public boolean isExpandEntityReferences() {
312 return expandEntityReferences;
313 }
314
315
316
317
318
319
320 public void setExpandEntityReferences(boolean expand) {
321 expandEntityReferences = expand;
322 dirtyBuilderConfiguration = true;
323 }
324
325
326
327
328
329
330 public Map<String, Boolean> getBuilderFeatures() {
331 return Collections.unmodifiableMap(builderFeatures);
332 }
333
334
335
336
337
338
339 public void setBuilderFeatures(Map<String, Boolean> newFeatures) {
340 builderFeatures = newFeatures;
341 dirtyBuilderConfiguration = true;
342 }
343
344
345
346
347
348
349 public boolean getIgnoreComments() {
350 return ignoreComments;
351 }
352
353
354
355
356
357
358 public void setIgnoreComments(boolean ignore) {
359 ignoreComments = ignore;
360 dirtyBuilderConfiguration = true;
361 }
362
363
364
365
366
367
368 public boolean isIgnoreElementContentWhitespace() {
369 return ignoreElementContentWhitespace;
370 }
371
372
373
374
375
376
377 public void setIgnoreElementContentWhitespace(boolean ignore) {
378 ignoreElementContentWhitespace = ignore;
379 dirtyBuilderConfiguration = true;
380 }
381
382
383
384
385
386
387 public boolean isNamespaceAware() {
388 return namespaceAware;
389 }
390
391
392
393
394
395
396 public void setNamespaceAware(boolean isNamespaceAware) {
397 namespaceAware = isNamespaceAware;
398 dirtyBuilderConfiguration = true;
399 }
400
401
402 public Schema getSchema() {
403 return schema;
404 }
405
406
407 public void setSchema(Schema newSchema) {
408 schema = newSchema;
409 if (schema != null) {
410 setNamespaceAware(true);
411 builderAttributes.remove("http://java.sun.com/xml/jaxp/properties/schemaSource");
412 builderAttributes.remove("http://java.sun.com/xml/jaxp/properties/schemaLanguage");
413 }
414
415 dirtyBuilderConfiguration = true;
416 }
417
418
419
420
421
422
423 public boolean isDTDValidating() {
424 return dtdValidating;
425 }
426
427
428
429
430
431
432 public void setDTDValidating(boolean isValidating) {
433 dtdValidating = isValidating;
434 dirtyBuilderConfiguration = true;
435 }
436
437
438
439
440
441
442 public boolean isXincludeAware() {
443 return xincludeAware;
444 }
445
446
447
448
449
450
451 public void setXincludeAware(boolean isXIncludeAware) {
452 xincludeAware = isXIncludeAware;
453 dirtyBuilderConfiguration = true;
454 }
455
456
457
458
459
460
461 protected long getPoolVersion() {
462 return poolVersion;
463 }
464
465
466
467
468
469
470 protected synchronized void initializePool() throws XMLParserException {
471 if (!dirtyBuilderConfiguration) {
472
473 return;
474 }
475
476 try {
477 DocumentBuilderFactory newFactory = DocumentBuilderFactory.newInstance();
478
479 for (Map.Entry<String, Object> attribute : builderAttributes.entrySet()) {
480 newFactory.setAttribute(attribute.getKey(), attribute.getValue());
481 }
482
483 for (Map.Entry<String, Boolean> feature : builderFeatures.entrySet()) {
484 if (feature.getKey() != null) {
485 newFactory.setFeature(feature.getKey(), feature.getValue().booleanValue());
486 }
487 }
488
489 newFactory.setCoalescing(coalescing);
490 newFactory.setExpandEntityReferences(expandEntityReferences);
491 newFactory.setIgnoringComments(ignoreComments);
492 newFactory.setIgnoringElementContentWhitespace(ignoreElementContentWhitespace);
493 newFactory.setNamespaceAware(namespaceAware);
494 newFactory.setSchema(schema);
495 newFactory.setValidating(dtdValidating);
496 newFactory.setXIncludeAware(xincludeAware);
497
498 poolVersion++;
499 dirtyBuilderConfiguration = false;
500 builderFactory = newFactory;
501 builderPool.clear();
502
503 } catch (ParserConfigurationException e) {
504 throw new XMLParserException("Unable to configure builder factory", e);
505 }
506 }
507
508
509
510
511
512
513
514
515 protected DocumentBuilder createBuilder() throws XMLParserException {
516 try {
517 DocumentBuilder builder = builderFactory.newDocumentBuilder();
518
519 if (entityResolver != null) {
520 builder.setEntityResolver(entityResolver);
521 }
522
523 if (errorHandler != null) {
524 builder.setErrorHandler(errorHandler);
525 }
526
527 return builder;
528 } catch (ParserConfigurationException e) {
529 log.error("Unable to create new document builder", e);
530 throw new XMLParserException("Unable to create new document builder", e);
531 }
532 }
533
534
535
536
537 protected class DocumentBuilderProxy extends DocumentBuilder {
538
539
540 private DocumentBuilder builder;
541
542
543 private ParserPool owningPool;
544
545
546 private long owningPoolVersion;
547
548
549
550
551
552
553
554 public DocumentBuilderProxy(DocumentBuilder target, BasicParserPool owner) {
555 owningPoolVersion = owner.getPoolVersion();
556 owningPool = owner;
557 builder = target;
558 }
559
560
561 public DOMImplementation getDOMImplementation() {
562 return builder.getDOMImplementation();
563 }
564
565
566 public Schema getSchema() {
567 return builder.getSchema();
568 }
569
570
571 public boolean isNamespaceAware() {
572 return builder.isNamespaceAware();
573 }
574
575
576 public boolean isValidating() {
577 return builder.isValidating();
578 }
579
580
581 public boolean isXIncludeAware() {
582 return builder.isXIncludeAware();
583 }
584
585
586 public Document newDocument() {
587 return builder.newDocument();
588 }
589
590
591 public Document parse(File f) throws SAXException, IOException {
592 return builder.parse(f);
593 }
594
595
596 public Document parse(InputSource is) throws SAXException, IOException {
597 return builder.parse(is);
598 }
599
600
601 public Document parse(InputStream is) throws SAXException, IOException {
602 return builder.parse(is);
603 }
604
605
606 public Document parse(InputStream is, String systemId) throws SAXException, IOException {
607 return builder.parse(is, systemId);
608 }
609
610
611 public Document parse(String uri) throws SAXException, IOException {
612 return builder.parse(uri);
613 }
614
615
616 public void reset() {
617
618 }
619
620
621 public void setEntityResolver(EntityResolver er) {
622 return;
623 }
624
625
626 public void setErrorHandler(ErrorHandler eh) {
627 return;
628 }
629
630
631
632
633
634
635 protected ParserPool getOwningPool() {
636 return owningPool;
637 }
638
639
640
641
642
643
644 protected long getPoolVersion() {
645 return owningPoolVersion;
646 }
647
648
649
650
651
652
653 protected DocumentBuilder getProxiedBuilder() {
654 return builder;
655 }
656
657
658 protected void finalize() throws Throwable {
659 super.finalize();
660 owningPool.returnBuilder(this);
661 }
662 }
663 }