Skip to content

Commit 83f5f5a

Browse files
committed
Merge trackjdk/REL1_6_STABLE/xmlpropnames into REL1_6_STABLE
Merges PR #452.
2 parents da68ef3 + 57e9609 commit 83f5f5a

File tree

3 files changed

+385
-146
lines changed

3 files changed

+385
-146
lines changed

pljava-api/src/main/java/org/postgresql/pljava/Adjusting.java

Lines changed: 182 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2019-2020 Tada AB and other contributors, as listed below.
2+
* Copyright (c) 2019-2023 Tada AB and other contributors, as listed below.
33
*
44
* All rights reserved. This program and the accompanying materials
55
* are made available under the terms of the The BSD 3-Clause License
@@ -14,6 +14,9 @@
1414
import java.io.Reader;
1515
import java.sql.SQLException;
1616
import java.sql.SQLXML;
17+
import java.util.List;
18+
import static java.util.Objects.requireNonNull;
19+
import java.util.function.Consumer;
1720
import javax.xml.stream.XMLInputFactory; // for javadoc
1821
import javax.xml.stream.XMLResolver; // for javadoc
1922
import javax.xml.stream.XMLStreamReader;
@@ -126,16 +129,139 @@ public static final class XML
126129
{
127130
private XML() { } // no instances
128131

132+
/**
133+
* Attempts a given action (typically to set something) using a given
134+
* value, trying one or more supplied keys in order until the action
135+
* succeeds with no exception.
136+
*<p>
137+
* This logic is common to the
138+
* {@link Parsing#setFirstSupportedFeature setFirstSupportedFeature}
139+
* and
140+
* {@link Parsing#setFirstSupportedProperty setFirstSupportedProperty}
141+
* methods, and is exposed here because it may be useful for other
142+
* tasks in Java's XML APIs, such as configuring {@code Transformer}s.
143+
*<p>
144+
* If any attempt succeeds, null is returned. If no attempt
145+
* succeeds, the first exception caught is returned, with any
146+
* exceptions from the subsequent attempts retrievable from it with
147+
* {@link Exception#getSuppressed getSuppressed}. The return is
148+
* immediate, without any remaining names being tried, if an exception
149+
* is caught that is not assignable to a class in the
150+
* <var>expected</var> list. Such an exception will be passed to the
151+
* <var>onUnexpected</var> handler if that is non-null; otherwise,
152+
* it will be returned (or added to the suppressed list of the
153+
* exception to be returned) just as expected exceptions are.
154+
* @param setter typically a method reference for a method that
155+
* takes a string key and some value.
156+
* @param value the value to pass to the setter
157+
* @param expected a list of exception classes that can be foreseen
158+
* to indicate that a key was not recognized, and the operation
159+
* should be retried with the next possible key.
160+
* @param onUnexpected invoked, if non-null, on an {@code Exception}
161+
* that is caught and matches nothing in the expected list, instead
162+
* of returning it. If this parameter is null, such an exception is
163+
* returned (or added to the suppressed list of the exception to be
164+
* returned), just as for expected exceptions, but the return is
165+
* immediate, without trying remaining names, if any.
166+
* @param names one or more String keys to be tried in order until
167+
* the action succeeds.
168+
* @return null if any attempt succeeded, otherwise an exception,
169+
* which may have further exceptions in its suppressed list.
170+
*/
171+
public static <T, V extends T> Exception setFirstSupported(
172+
SetMethod<? super T> setter, V value,
173+
List<Class<? extends Exception>> expected,
174+
Consumer<? super Exception> onUnexpected, String... names)
175+
{
176+
requireNonNull(expected);
177+
Exception caught = null;
178+
for ( String name : names )
179+
{
180+
try
181+
{
182+
setter.set(name, value);
183+
return null;
184+
}
185+
catch ( Exception e )
186+
{
187+
boolean benign =
188+
expected.stream().anyMatch(c -> c.isInstance(e));
189+
190+
if ( benign || null == onUnexpected )
191+
{
192+
if ( null == caught )
193+
caught = e;
194+
else
195+
caught.addSuppressed(e);
196+
}
197+
else
198+
onUnexpected.accept(e);
199+
200+
if ( ! benign )
201+
break;
202+
}
203+
}
204+
return caught;
205+
}
206+
207+
/**
208+
* A functional interface fitting various {@code setFeature} or
209+
* {@code setProperty} methods in Java XML APIs.
210+
*<p>
211+
* The XML APIs have a number of methods on various interfaces that can
212+
* be used to set some property or feature, and can generally be
213+
* assigned to this functional interface by bound method reference, and
214+
* used with {@link #setFirstSupported setFirstSupported}.
215+
*/
216+
@FunctionalInterface
217+
public interface SetMethod<T>
218+
{
219+
void set(String key, T value) throws Exception;
220+
}
221+
129222
/**
130223
* Interface with methods to adjust the restrictions on XML parsing
131224
* that are commonly considered when XML content might be from untrusted
132225
* sources.
133226
*<p>
134-
* The adjusting methods are best-effort and do not provide an
135-
* indication of whether the requested adjustment was made. Not all of
227+
* The adjusting methods are best-effort; not all of
136228
* the adjustments are available for all flavors of {@code Source} or
137229
* {@code Result} or for all parser implementations or versions the Java
138-
* runtime may supply.
230+
* runtime may supply. Cases where a requested adjustment has not been
231+
* made are handled as follows:
232+
*<p>
233+
* Any sequence of adjustment calls will ultimately be followed by a
234+
* {@code get}. During the sequence of adjustments, exceptions caught
235+
* are added to a signaling list or to a quiet list, where "added to"
236+
* means that if either list has a first exception, any caught later are
237+
* attached to that exception with
238+
* {@link Exception#addSuppressed addSuppressed}.
239+
*<p>
240+
* For each adjustment (and depending on the type of underlying
241+
* {@code Source} or {@code Result}), one or more exception types will
242+
* be 'expected' as indications that an identifying key or value for
243+
* that adjustment was not recognized. This implementation may continue
244+
* trying to apply the adjustment, using other keys that have at times
245+
* been used to identify it. Expected exceptions caught during these
246+
* attempts form a temporary list (a first exception and those attached
247+
* to it by {@code addSuppressed}). Once any such attempt succeeds, the
248+
* adjustment is considered made, and any temporary expected exceptions
249+
* list from the adjustment is discarded. If no attempt succeeded, the
250+
* temporary list is retained, by adding its head exception to the quiet
251+
* list.
252+
*<p>
253+
* Any exceptions caught that are not instances of any of the 'expected'
254+
* types are added to the signaling list.
255+
*<p>
256+
* When {@code get} is called, the head exception on the signaling list,
257+
* if any, is thrown. Otherwise, the head exception on the quiet list,
258+
* if any, is logged at [@code WARNING} level.
259+
*<p>
260+
* During a chain of adjustments, {@link #lax lax()} can be called to
261+
* tailor the handling of the quiet list. A {@code lax()} call applies
262+
* to whatever exceptions have been added to the quiet list up to that
263+
* point. To discard them, call {@code lax(true)}; to move them to the
264+
* signaling list, call {@code lax(false)}.
139265
*/
140266
public interface Parsing<T extends Parsing<T>>
141267
{
@@ -173,14 +299,14 @@ public interface Parsing<T extends Parsing<T>>
173299

174300
/**
175301
* For a feature that may have been identified by more than one URI
176-
* in different parsers or versions, try passing the supplied
302+
* in different parsers or versions, tries passing the supplied
177303
* <em>value</em> with each URI from <em>names</em> in order until
178304
* one is not rejected by the underlying parser.
179305
*/
180306
T setFirstSupportedFeature(boolean value, String... names);
181307

182308
/**
183-
* Make a best effort to apply the recommended, restrictive
309+
* Makes a best effort to apply the recommended, restrictive
184310
* defaults from the OWASP cheat sheet, to the extent they are
185311
* supported by the underlying parser, runtime, and version.
186312
*<p>
@@ -196,7 +322,7 @@ public interface Parsing<T extends Parsing<T>>
196322
/**
197323
* For a parser property (in DOM parlance, attribute) that may have
198324
* been identified by more than one URI in different parsers or
199-
* versions, try passing the supplied <em>value</em> with each URI
325+
* versions, tries passing the supplied <em>value</em> with each URI
200326
* from <em>names</em> in order until one is not rejected by the
201327
* underlying parser.
202328
*<p>
@@ -278,7 +404,7 @@ public interface Parsing<T extends Parsing<T>>
278404
T accessExternalSchema(String protocols);
279405

280406
/**
281-
* Set an {@link EntityResolver} of the type used by SAX and DOM
407+
* Sets an {@link EntityResolver} of the type used by SAX and DOM
282408
* <em>(optional operation)</em>.
283409
*<p>
284410
* This method only succeeds for a {@code SAXSource} or
@@ -297,7 +423,7 @@ public interface Parsing<T extends Parsing<T>>
297423
T entityResolver(EntityResolver resolver);
298424

299425
/**
300-
* Set a {@link Schema} to be applied during SAX or DOM parsing
426+
* Sets a {@link Schema} to be applied during SAX or DOM parsing
301427
*<em>(optional operation)</em>.
302428
*<p>
303429
* This method only succeeds for a {@code SAXSource} or
@@ -316,6 +442,31 @@ public interface Parsing<T extends Parsing<T>>
316442
* already.
317443
*/
318444
T schema(Schema schema);
445+
446+
/**
447+
* Tailors the treatment of 'quiet' exceptions during a chain of
448+
* best-effort adjustments.
449+
*<p>
450+
* See {@link Parsing the class description} for an explanation of
451+
* the signaling and quiet lists.
452+
*<p>
453+
* This method applies to whatever exceptions may have been added to
454+
* the quiet list by best-effort adjustments made up to that point.
455+
* They can be moved to the signaling list with {@code lax(false)},
456+
* or simply discarded with {@code lax(true)}. In either case, the
457+
* quiet list is left empty when {@code lax} returns.
458+
*<p>
459+
* At the time a {@code get} method is later called, any exception
460+
* at the head of the signaling list will be thrown (possibly
461+
* wrapped in an exception permitted by {@code get}'s {@code throws}
462+
* clause), with any later exceptions on that list retrievable from
463+
* the head exception with
464+
* {@link Exception#getSuppressed getSuppressed}. Otherwise, any
465+
* exception at the head of the quiet list (again with any later
466+
* ones attached as its suppressed list) will be logged at
467+
* {@code WARNING} level.
468+
*/
469+
T lax(boolean discard);
319470
}
320471

321472
/**
@@ -347,12 +498,17 @@ public interface Source<T extends javax.xml.transform.Source>
347498
extends Parsing<Source<T>>, javax.xml.transform.Source
348499
{
349500
/**
350-
* Return an object of the expected {@code Source} subtype
501+
* Returns an object of the expected {@code Source} subtype
351502
* reflecting any adjustments made with the other methods.
503+
*<p>
504+
* Refer to {@link Parsing the {@code Parsing} class description}
505+
* and the {@link Parsing#lax lax()} method for how any exceptions
506+
* caught while applying best-effort adjustments are handled.
352507
* @return an implementing object of the expected Source subtype
353508
* @throws SQLException for any reason that {@code getSource} might
354509
* have thrown when supplying the corresponding non-Adjusting
355-
* subtype of Source.
510+
* subtype of Source, or for reasons saved while applying
511+
* adjustments.
356512
*/
357513
T get() throws SQLException;
358514
}
@@ -392,12 +548,16 @@ public interface Result<T extends javax.xml.transform.Result>
392548
extends Parsing<Result<T>>, javax.xml.transform.Result
393549
{
394550
/**
395-
* Return an object of the expected {@code Result} subtype
551+
* Returns an object of the expected {@code Result} subtype
396552
* reflecting any adjustments made with the other methods.
553+
* Refer to {@link Parsing the {@code Parsing} class description}
554+
* and the {@link Parsing#lax lax()} method for how any exceptions
555+
* caught while applying best-effort adjustments are handled.
397556
* @return an implementing object of the expected Result subtype
398557
* @throws SQLException for any reason that {@code getResult} might
399558
* have thrown when supplying the corresponding non-Adjusting
400-
* subtype of Result.
559+
* subtype of Result, or for reasons saved while applying
560+
* adjustments.
401561
*/
402562
T get() throws SQLException;
403563
}
@@ -428,7 +588,7 @@ public interface Result<T extends javax.xml.transform.Result>
428588
public interface SourceResult extends Result<SourceResult>
429589
{
430590
/**
431-
* Supply the {@code Source} instance that is the source of the
591+
* Supplies the {@code Source} instance that is the source of the
432592
* content.
433593
*<p>
434594
* This method must be called before any of the inherited adjustment
@@ -484,7 +644,8 @@ SourceResult set(javax.xml.transform.stax.StAXSource source)
484644
throws SQLException;
485645

486646
/**
487-
* Provide the content to be copied in the form of a {@code String}.
647+
* Provides the content to be copied in the form of a
648+
* {@code String}.
488649
*<p>
489650
* An exception from the pattern of {@code Source}-typed arguments,
490651
* this method simplifies retrofitting adjustments into code that
@@ -507,11 +668,14 @@ SourceResult set(javax.xml.transform.dom.DOMSource source)
507668
throws SQLException;
508669

509670
/**
510-
* Return the result {@code SQLXML} instance ready for handing off
671+
* Returns the result {@code SQLXML} instance ready for handing off
511672
* to PostgreSQL.
512673
*<p>
513-
* This method must be called after any of the inherited adjustment
514-
* methods.
674+
* The handling/logging of exceptions normally handled in a
675+
* {@code get} method happens here for a {@code SourceResult}.
676+
*<p>
677+
* Any necessary calls of the inherited adjustment methods must be
678+
* made before this method is called.
515679
*/
516680
SQLXML getSQLXML() throws SQLException;
517681
}

0 commit comments

Comments
 (0)