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