publicclass MyDefaultWebSessionManager extends DefaultWebSessionManager {
privatestaticfinal Logger log = LoggerFactory.getLogger(MyDefaultWebSessionManager.class);
@Override
protectedvoid onStart(Session session, SessionContext context) {
super.onStart(session, context);
if (!WebUtils.isHttp(context)) {
log.debug("SessionContext argument is not HTTP compatible or does not have an HTTP request/response pair. No session ID cookie will be set.");
} else {
HttpServletRequest request = WebUtils.getHttpRequest(context);
HttpServletResponse response = WebUtils.getHttpResponse(context);
if (this.isSessionIdCookieEnabled()) {
Serializable sessionId = session.getId();
//重写父类方法,使用我们自己定义的cookiethis.storeSessionId(sessionId, request, response);
} else {
log.debug("Session ID cookie is disabled. No cookie has been set for new session with id {}", session.getId());
}
/** * An {@code HttpCookie} subclass with the additional attributes allowed in * the "Set-Cookie" response header. To build an instance use the {@link #from} * static method.
@Override publicint hashCode() { int result = super.hashCode(); result = 31 * result + ObjectUtils.nullSafeHashCode(this.domain); result = 31 * result + ObjectUtils.nullSafeHashCode(this.path); return result; }
@Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append(getName()).append('=').append(getValue()); if (StringUtils.hasText(getPath())) { sb.append("; Path=").append(getPath()); } if (StringUtils.hasText(this.domain)) { sb.append("; Domain=").append(this.domain); } if (!this.maxAge.isNegative()) { sb.append("; Max-Age=").append(this.maxAge.getSeconds()); sb.append("; Expires="); long millis = this.maxAge.getSeconds() > 0 ? System.currentTimeMillis() + this.maxAge.toMillis() : 0; sb.append(HttpHeaders.formatDate(millis)); } if (this.secure) { sb.append("; Secure"); } if (this.httpOnly) { sb.append("; HttpOnly"); } if (StringUtils.hasText(this.sameSite)) { sb.append("; SameSite=").append(this.sameSite); } return sb.toString(); }
/** * Factory method to obtain a builder for a server-defined cookie that starts * with a name-value pair and may also include attributes.
@param name the cookie name
@param value the cookie value
@return a builder to create the cookie with
*/publicstatic ResponseCookieBuilder from(final String name, final String value) {
return from(name, value, false);
}
/** * Factory method to obtain a builder for a server-defined cookie. Unlike * {@link #from(String, String)} this option assumes input from a remote * server, which can be handled more leniently, e.g. ignoring a empty domain * name with double quotes.
/** * A builder for a server-defined HttpCookie with attributes.
*/publicinterface ResponseCookieBuilder {
</span><span>/**</span><span>
* Set the cookie "Max-Age" attribute.
*
* <p>A positive value indicates when the cookie should expire relative
* to the current time. A value of 0 means the cookie should expire
* immediately. A negative value results in no "Max-Age" attribute in
* which case the cookie is removed when the browser is closed.
</span><span>*/</span><span>
ResponseCookieBuilder maxAge(Duration maxAge);
</span><span>/**</span><span>
* Variant of {</span><span>@link</span><span> #maxAge(Duration)} accepting a value in seconds.
</span><span>*/</span><span>
ResponseCookieBuilder maxAge(</span><span>long</span><span> maxAgeSeconds);
</span><span>/**</span><span>
* Set the cookie "Path" attribute.
</span><span>*/</span><span>
ResponseCookieBuilder path(String path);
</span><span>/**</span><span>
* Set the cookie "Domain" attribute.
</span><span>*/</span><span>
ResponseCookieBuilder domain(String domain);
</span><span>/**</span><span>
* Add the "Secure" attribute to the cookie.
</span><span>*/</span><span>
ResponseCookieBuilder secure(</span><span>boolean</span><span> secure);
</span><span>/**</span><span>
* Add the "HttpOnly" attribute to the cookie.
* </span><span>@see</span><span> <a href="</span><span>https://www.owasp.org/index.php/HTTPOnly</span><span>"></span><span>https://www.owasp.org/index.php/HTTPOnly</span><span></a>
</span><span>*/</span><span>
ResponseCookieBuilder httpOnly(</span><span>boolean</span><span> httpOnly);
</span><span>/**</span><span>
* Add the "SameSite" attribute to the cookie.
* <p>This limits the scope of the cookie such that it will only be
* attached to same site requests if {</span><span>@code</span><span> "Strict"} or cross-site
* requests if {</span><span>@code</span><span> "Lax"}.
* </span><span>@since</span><span> 5.1
* </span><span>@see</span><span> <a href="</span><span>https://tools.ietf.org/html/draft-ietf-httpbis-rfc6265bis</span><span>#section-4.1.2.7">RFC6265 bis</a>
</span><span>*/</span><span>
ResponseCookieBuilder sameSite(@Nullable String sameSite);
</span><span>/**</span><span>
* Create the HttpCookie.
</span><span>*/</span><span>
ResponseCookie build();
}
privatestaticclass Rfc6265Utils {
</span><span>private</span> <span>static</span> <span>final</span> String SEPARATOR_CHARS = <span>new</span> String(<span>new</span> <span>char</span><span>[] {
</span>'(', ')', '<', '>', '@', ',', ';', ':', '\\', '"', '/', '[', ']', '?', '=', '{', '}', ' '<span>
});
</span><span>private</span> <span>static</span> <span>final</span> String DOMAIN_CHARS =
"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.-"<span>;
</span><span>public</span> <span>static</span> <span>void</span><span> validateCookieName(String name) {
</span><span>for</span> (<span>int</span> i = 0; i < name.length(); i++<span>) {
</span><span>char</span> c =<span> name.charAt(i);
</span><span>//</span><span> CTL = <US-ASCII control chars (octets 0 - 31) and DEL (127)></span>
<span>if</span> (c <= 0x1F || c == 0x7F<span>) {
</span><span>throw</span> <span>new</span><span> IllegalArgumentException(
name </span>+ ": RFC2616 token cannot have control chars"<span>);
}
</span><span>if</span> (SEPARATOR_CHARS.indexOf(c) >= 0<span>) {
</span><span>throw</span> <span>new</span><span> IllegalArgumentException(
name </span>+ ": RFC2616 token cannot have separator chars such as '" + c + "'"<span>);
}
</span><span>if</span> (c >= 0x80<span>) {
</span><span>throw</span> <span>new</span><span> IllegalArgumentException(
name </span>+ ": RFC2616 token can only have US-ASCII: 0x" +<span> Integer.toHexString(c));
}
}
}
</span><span>public</span> <span>static</span> <span>void</span><span> validateCookieValue(@Nullable String value) {
</span><span>if</span> (value == <span>null</span><span>) {
</span><span>return</span><span>;
}
</span><span>int</span> start = 0<span>;
</span><span>int</span> end =<span> value.length();
</span><span>if</span> (end > 1 && value.charAt(0) == '"' && value.charAt(end - 1) == '"'<span>) {
start </span>= 1<span>;
end</span>--<span>;
}
</span><span>for</span> (<span>int</span> i = start; i < end; i++<span>) {
</span><span>char</span> c =<span> value.charAt(i);
</span><span>if</span> (c < 0x21 || c == 0x22 || c == 0x2c || c == 0x3b || c == 0x5c || c == 0x7f<span>) {
</span><span>throw</span> <span>new</span><span> IllegalArgumentException(
</span>"RFC2616 cookie value cannot have '" + c + "'"<span>);
}
</span><span>if</span> (c >= 0x80<span>) {
</span><span>throw</span> <span>new</span><span> IllegalArgumentException(
</span>"RFC2616 cookie value can only have US-ASCII chars: 0x" +<span> Integer.toHexString(c));
}
}
}
</span><span>public</span> <span>static</span> <span>void</span><span> validateDomain(@Nullable String domain) {
</span><span>if</span> (!<span>StringUtils.hasLength(domain)) {
</span><span>return</span><span>;
}
</span><span>int</span> char1 = domain.charAt(0<span>);
</span><span>int</span> charN = domain.charAt(domain.length() - 1<span>);
</span><span>if</span> (char1 == '-' || charN == '.' || charN == '-'<span>) {
</span><span>throw</span> <span>new</span> IllegalArgumentException("Invalid first/last char in cookie domain: " +<span> domain);
}
</span><span>for</span> (<span>int</span> i = 0, c = -1; i < domain.length(); i++<span>) {
</span><span>int</span> p =<span> c;
c </span>=<span> domain.charAt(i);
</span><span>if</span> (DOMAIN_CHARS.indexOf(c) == -1 || (p == '.' && (c == '.' || c == '-')) || (p == '-' && c == '.'<span>)) {
</span><span>throw</span> <span>new</span> IllegalArgumentException(domain + ": invalid cookie domain char '" + c + "'"<span>);
}
}
}
</span><span>public</span> <span>static</span> <span>void</span><span> validatePath(@Nullable String path) {
</span><span>if</span> (path == <span>null</span><span>) {
</span><span>return</span><span>;
}
</span><span>for</span> (<span>int</span> i = 0; i < path.length(); i++<span>) {
</span><span>char</span> c =<span> path.charAt(i);
</span><span>if</span> (c < 0x20 || c > 0x7E || c == ';'<span>) {
</span><span>throw</span> <span>new</span> IllegalArgumentException(path + ": Invalid cookie path char '" + c + "'"<span>);
}
}
}
}
}
/*
* Copyright 2002-2021 the original author or authors.
* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License.
You may obtain a copy of the License at
*
https://www.apache.org/licenses/LICENSE-2.0
*
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
/** * A data structure representing HTTP request or response headers, mapping String header names * to a list of String values, also offering accessors for common application-level data types.
* *
In addition to the regular methods defined by {@link Map}, this class offers many common * convenience methods, for example: *
*
{
@link #getFirst(String)} returns the first value associated with a given header name *
{
@link #add(String, String)} adds a header value to the list of values for a header name *
{
@link #set(String, String)} sets the header value to a single string value * * *
Note that {
@code HttpHeaders} generally treats header names in a case-insensitive manner.
/** * Set the value of the {@linkplain #AUTHORIZATION Authorization} header to * Basic Authentication based on the given {@linkplain #encodeBasicAuth * encoded credentials}.
Favor this method over {@link #setBasicAuth(String, String)} and
{@link #setBasicAuth(String, String, Charset)} if you wish to cache the
encoded credentials.
@param encodedCredentials the encoded credentials
@throws IllegalArgumentException if supplied credentials string is
{@code null} or blank
@since 5.2
@see #setBasicAuth(String, String)
@see #setBasicAuth(String, String, Charset)
@see #encodeBasicAuth(String, String, Charset)
@seeRFC 7617*/publicvoid setBasicAuth(String encodedCredentials) {
Assert.hasText(encodedCredentials, "'encodedCredentials' must not be null or blank");
set(AUTHORIZATION, "Basic " + encodedCredentials);
}
/** * Set the value of the {@linkplain #AUTHORIZATION Authorization} header to * the given Bearer token.
/** * Return the date and time at which the message was created, as specified * by the {@code Date} header.
The date is returned as the number of milliseconds since
January 1, 1970 GMT. Returns -1 when the date is unknown.
@throws IllegalArgumentException if the value cannot be converted to a date */publiclong getDate() { return getFirstDate(DATE); }
/** * Set the (new) entity tag of the body, as specified by the {@code ETag} header.
*/publicvoid setETag(@Nullable String etag) { if (etag != null) { Assert.isTrue(etag.startsWith("\"") || etag.startsWith("W/"), "Invalid ETag: does not start with W/ or \""); Assert.isTrue(etag.endsWith("\""), "Invalid ETag: does not end with \""); set(ETAG, etag); } else { remove(ETAG); } }
/** * Return the entity tag of the body, as specified by the {@code ETag} header.
*/ @Nullable public String getETag() { return getFirst(ETAG); }
/** * Set the duration after which the message is no longer valid, * as specified by the {@code Expires} header.
/** * Return the value of the {@code Upgrade} header.
*/ @Nullable public String getUpgrade() { return getFirst(UPGRADE); }
/** * Set the request header names (e.g. "Accept-Language") for which the * response is subject to content negotiation and variances based on the * value of those request headers.
/** * Return the request header names subject to content negotiation.
@since 4.3
*/public List getVary() {
return getValuesAsList(VARY);
}
/** * Set the given date under the given header name after formatting it as a string * using the RFC-1123 date-time formatter. The equivalent of * {@link #set(String, String)} but for date headers.
/** * Set the given date under the given header name after formatting it as a string * using the RFC-1123 date-time formatter. The equivalent of * {@link #set(String, String)} but for date headers.
/** * Set the given date under the given header name after formatting it as a string * using the RFC-1123 date-time formatter. The equivalent of * {@link #set(String, String)} but for date headers.
/** * Parse the first header value for the given header name as a date, * return -1 if there is no value, or raise {@link IllegalArgumentException} * if the value cannot be parsed as a date.
/** * Parse the first header value for the given header name as a date, * return -1 if there is no value or also in case of an invalid value * (if {@code rejectInvalid=false}), or raise {@link IllegalArgumentException} * if the value cannot be parsed as a date.
@param headerName the header name
@param rejectInvalid whether to reject invalid values with an
{@link IllegalArgumentException} ({@code true}) or rather return -1
in that case ({@code false})
@return the parsed date header, or -1 if none (or invalid)
/** * Parse the first header value for the given header name as a date, * return {@code null} if there is no value, or raise {@link IllegalArgumentException} * if the value cannot be parsed as a date.
@param headerName the header name
@return the parsed date header, or {@code null} if none
/** * Parse the first header value for the given header name as a date, * return {@code null} if there is no value or also in case of an invalid value * (if {@code rejectInvalid=false}), or raise {@link IllegalArgumentException} * if the value cannot be parsed as a date.
@param headerName the header name
@param rejectInvalid whether to reject invalid values with an
{@link IllegalArgumentException} ({@code true}) or rather return {@code null}
in that case ({@code false})
@return the parsed date header, or {@code null} if none (or invalid) */ @Nullable private ZonedDateTime getFirstZonedDateTime(String headerName, boolean rejectInvalid) { String headerValue = getFirst(headerName); if (headerValue == null) { // No header value sent at allreturnnull; } if (headerValue.length() >= 3) { // Short "0" or "-1" like values are never valid HTTP date headers...
// Let's only bother with DateTimeFormatter parsing for long enough values.
// See https://stackoverflow.com/questions/12626699/if-modified-since-http-header-passed-by-ie9-includes-lengthint parametersIndex = headerValue.indexOf(';'); if (parametersIndex != -1) { headerValue = headerValue.substring(0, parametersIndex); }
}if (rejectInvalid) { thrownew IllegalArgumentException("Cannot parse date value \"" + headerValue + "\" for \"" + headerName + "\" header");}returnnull; }
/** * Return all values of a given header name, * even if this header is set multiple times.
@param headerName the header name
@return all associated values
@since 4.3
*/public List getValuesAsList(String headerName) {
List values = get(headerName);
if (values != null) {
List result = new ArrayList<>();
for (String value : values) {
if (value != null) {
Collections.addAll(result, StringUtils.tokenizeToStringArray(value, ","));
}
}
return result;
}
return Collections.emptyList();
}
/* * Remove the well-known {@code "Content-"} HTTP headers.
Such headers should be cleared from the response if the intended
/** * Retrieve a combined result from the field values of the ETag header.
@param headerName the header name
@return the combined result
@throws IllegalArgumentException if parsing fails
@since 4.3
/protected List getETagValuesAsList(String headerName) {
List values = get(headerName);
if (values != null) {
List result = new ArrayList<>();
for (String value : values) {
if (value != null) {
Matcher matcher = ETAG_HEADER_VALUE_PATTERN.matcher(value);
while (matcher.find()) {
if ("".equals(matcher.group())) {
result.add(matcher.group());
}
else {
result.add(matcher.group(1));
}
}
if (result.isEmpty()) {
thrownew IllegalArgumentException(
"Could not parse header '" + headerName + "' with value '" + value + "'");
}
}
}
return result;
}
return Collections.emptyList();
}
/** * Retrieve a combined result from the field values of multi-valued headers.
/** * Turn the given list of header values into a comma-delimited result.
@param headerValues the list of header values
@return a combined result with comma delimitation
*/protected String toCommaDelimitedString(List headerValues) {
StringJoiner joiner = new StringJoiner(", ");
for (String val : headerValues) {
if (val != null) {
joiner.add(val);
}
}
return joiner.toString();
}
/** * Set the given header value, or remove the header if {@code null}.
@param headerName the header name
@param headerValue the header value, or {@code null} for none
*/privatevoid setOrRemove(String headerName, @Nullable String headerValue) {
if (headerValue != null) {
set(headerName, headerValue);
}
else {
remove(headerName);
}
}
// MultiValueMap implementation
/** * Return the first header value for the given header name, if any.
@param headerName the header name
@return the first header value, or {@code null} if none
*/
@Override
@Nullable
public String getFirst(String headerName) {
returnthis.headers.getFirst(headerName);
}
/** * Add the given, single header value under the given name.
@param headerName the header name
@param headerValue the header value
@throws UnsupportedOperationException if adding headers is not supported
@Override public String toString() { return formatHeaders(this.headers); }
/** * Apply a read-only {@code HttpHeaders} wrapper around the given headers, if necessary.
Also caches the parsed representations of the "Accept" and "Content-Type" headers.
@param headers the headers to expose
@return a read-only variant of the headers, or the original headers as-is
(in case it happens to be a read-only {@code HttpHeaders} instance already)
@since 5.3
// public static HttpHeaders readOnlyHttpHeaders(MultiValueMap headers) {
return (headers instanceof HttpHeaders ?
readOnlyHttpHeaders((HttpHeaders) headers) : new ReadOnlyHttpHeaders(headers));
}*/
/** * Apply a read-only {@code HttpHeaders} wrapper around the given headers, if necessary.
Also caches the parsed representations of the "Accept" and "Content-Type" headers.
@param headers the headers to expose
@return a read-only variant of the headers, or the original headers as-is
// public static HttpHeaders readOnlyHttpHeaders(HttpHeaders headers) {
Assert.notNull(headers, "HttpHeaders must not be null");
return (headers instanceof ReadOnlyHttpHeaders ? headers : new ReadOnlyHttpHeaders(headers.headers));
}*/
/** * Remove any read-only wrapper that may have been previously applied around * the given headers via {@link #readOnlyHttpHeaders(HttpHeaders)}.
@param headers the headers to expose
@return a writable variant of the headers, or the original headers as-is
@since 5.1.1
//public static HttpHeaders writableHttpHeaders(HttpHeaders headers) {
Assert.notNull(headers, "HttpHeaders must not be null");
if (headers == EMPTY) {
return new HttpHeaders();
}
return (headers instanceof ReadOnlyHttpHeaders ? new HttpHeaders(headers.headers) : headers);
}*/
/** * Helps to format HTTP header values, as HTTP header values themselves can * contain comma-separated values, can become confusing with regular * {@link Map} formatting that also uses commas between entries.
/** * Encode the given username and password into Basic Authentication credentials.
The encoded credentials returned by this method can be supplied to
{@link #setBasicAuth(String)} to set the Basic Authentication header.
@param username the username
@param password the password
@param charset the charset to use to convert the credentials into an octet
sequence. Defaults to {@linkplain StandardCharsets#ISO_8859_1 ISO-8859-1}.
@throws IllegalArgumentException if {@code username} or {@code password}
contains characters that cannot be encoded to the given charset
@since 5.2
@see #setBasicAuth(String)
@see #setBasicAuth(String, String)
@see #setBasicAuth(String, String, Charset)
@seeRFC 7617*/publicstatic String encodeBasicAuth(String username, String password, @Nullable Charset charset) { Assert.notNull(username, "Username must not be null"); Assert.doesNotContain(username, ":", "Username must not contain a colon"); Assert.notNull(password, "Password must not be null"); if (charset == null) { charset = StandardCharsets.ISO_8859_1; }
CharsetEncoder encoder = charset.newEncoder();if (!encoder.canEncode(username) || !encoder.canEncode(password)) { thrownew IllegalArgumentException( "Username or password contains characters that cannot be encoded to " + charset.displayName());}
/** * Represents an HTTP cookie as a name-value pair consistent with the content of * the "Cookie" request header. The {@link ResponseCookie} sub-class has the * additional attributes expected in the "Set-Cookie" response header.
public HttpCookie(String name, @com.sun.istack.internal.Nullable String value) { Assert.hasLength(name, "'name' is required and must not be empty."); this.name = name; this.value = (value != null ? value : ""); }
/** * Return the cookie name.
*/public String getName() { returnthis.name; }
/** * Return the cookie value or an empty string (never {@code null}).
/** * A builder for creating "Cache-Control" HTTP response headers.
* *
Adding Cache-Control directives to HTTP responses can significantly improve the client * experience when interacting with a web application. This builder creates opinionated * "Cache-Control" headers with response directives only, with several use cases in mind.
* *
*
Caching HTTP responses with {@code CacheControl cc = CacheControl.maxAge(1, TimeUnit.HOURS)} * will result in {@code Cache-Control: "max-age=3600"}
*
Preventing cache with {@code CacheControl cc = CacheControl.noStore()} * will result in {@code Cache-Control: "no-store"}
*
Advanced cases like {@code CacheControl cc = CacheControl.maxAge(1, TimeUnit.HOURS).noTransform().cachePublic()} * will result in {@code Cache-Control: "max-age=3600, no-transform, public"}
*
* *
Note that to be efficient, Cache-Control headers should be written along HTTP validators * such as "Last-Modified" or "ETag" headers.
/** * Return the "Cache-Control" header value, if any.
@return the header value, or {@code null} if no directive was added
*/
@Nullable
public String getHeaderValue() {
String headerValue = toHeaderValue();
return (StringUtils.hasText(headerValue) ? headerValue : null);
}
/** * Return the "Cache-Control" header value.
@return the header value (potentially empty)
*/private String toHeaderValue() {
StringBuilder headerValue = new StringBuilder();
if (this.maxAge != null) {
appendDirective(headerValue, "max-age=" + this.maxAge.getSeconds());
}
if (this.noCache) {
appendDirective(headerValue, "no-cache");
}
if (this.noStore) {
appendDirective(headerValue, "no-store");
}
if (this.mustRevalidate) {
appendDirective(headerValue, "must-revalidate");
}
if (this.noTransform) {
appendDirective(headerValue, "no-transform");
}
if (this.cachePublic) {
appendDirective(headerValue, "public");
}
if (this.cachePrivate) {
appendDirective(headerValue, "private");
}
if (this.proxyRevalidate) {
appendDirective(headerValue, "proxy-revalidate");
}
if (this.sMaxAge != null) {
appendDirective(headerValue, "s-maxage=" + this.sMaxAge.getSeconds());
}
if (this.staleIfError != null) {
appendDirective(headerValue, "stale-if-error=" + this.staleIfError.getSeconds());
}
if (this.staleWhileRevalidate != null) {
appendDirective(headerValue, "stale-while-revalidate=" + this.staleWhileRevalidate.getSeconds());
}
return headerValue.toString();
}
/** * A common Spring annotation to declare that annotated elements can be {@code null} under * some circumstance.
* *
Leverages JSR-305 meta-annotations to indicate nullability in Java to common * tools with JSR-305 support and used by Kotlin to infer nullability of Spring API.
* *
Should be used at parameter, return value, and field level. Methods override should * repeat parent {@code @Nullable} annotations unless they behave differently.
* *
Can be used in association with {@code @NonNullApi} or {@code @NonNullFields} to * override the default non-nullable semantic to nullable.
* * @author Sebastien Deleuze * @author Juergen Hoeller * @since 5.0 * @see N on NullApi * @see N on NullFields * @see N on Null */@Target({ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD})@Retention(RetentionPolicy.RUNTIME)@Documented@Nonnull(when = When.MAYBE)@TypeQualifierNicknamepublic @interface Nullable {}
package javax.annotation.meta;
/** * Used to describe the relationship between a qualifier T and the set of values * S possible on an annotated element.
* * In particular, an issues should be reported if an ALWAYS or MAYBE value is * used where a NEVER value is required, or if a NEVER or MAYBE value is used * where an ALWAYS value is required.
* *
/publicenum When { / S is a subset of T / ALWAYS, / nothing definitive is known about the relation between S and T */ UNKNOWN, / S intersection T is non empty and S - T is nonempty / MAYBE, / S intersection T is empty / NEVER;
package javax.annotation.meta;
import java.lang.annotation.Documented;import java.lang.annotation.ElementType;import java.lang.annotation.Target;
/** * * This annotation is applied to a annotation, and marks the annotation as being * a qualifier nickname. Applying a nickname annotation X to a element Y should * be interpreted as having the same meaning as applying all of annotations of X * (other than QualifierNickname) to Y.
* *
* Thus, you might define a qualifier SocialSecurityNumber as follows: *
package javax.annotation.meta;
import java.lang.annotation.Documented;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;
/** * This qualifier is applied to an annotation to denote that the annotation * should be treated as a type qualifier.
*/
public @interface TypeQualifier {
/** * Describes the kinds of values the qualifier can be applied to. If a * numeric class is provided (e.g., Number.class or Integer.class) then the * annotation can also be applied to the corresponding primitive numeric * types.
*/ Class<?> applicableTo() default Object.class;