AccessibleContext associated
@@ -9034,6 +9037,13 @@ public abstract class Component implements ImageObserver, MenuContainer,
protected AccessibleAWTComponent() {
}
+ /**
+ * Number of PropertyChangeListener objects registered. It's used
+ * to add/remove ComponentListener and FocusListener to track
+ * target Component's state.
+ */
+ private volatile transient int propertyListenersCount = 0;
+
protected ComponentListener accessibleAWTComponentHandler = null;
protected FocusListener accessibleAWTFocusHandler = null;
@@ -9098,10 +9108,12 @@ public abstract class Component implements ImageObserver, MenuContainer,
public void addPropertyChangeListener(PropertyChangeListener listener) {
if (accessibleAWTComponentHandler == null) {
accessibleAWTComponentHandler = new AccessibleAWTComponentHandler();
- Component.this.addComponentListener(accessibleAWTComponentHandler);
}
if (accessibleAWTFocusHandler == null) {
accessibleAWTFocusHandler = new AccessibleAWTFocusHandler();
+ }
+ if (propertyListenersCount++ == 0) {
+ Component.this.addComponentListener(accessibleAWTComponentHandler);
Component.this.addFocusListener(accessibleAWTFocusHandler);
}
super.addPropertyChangeListener(listener);
@@ -9115,13 +9127,9 @@ public abstract class Component implements ImageObserver, MenuContainer,
* @param listener The PropertyChangeListener to be removed
*/
public void removePropertyChangeListener(PropertyChangeListener listener) {
- if (accessibleAWTComponentHandler != null) {
+ if (--propertyListenersCount == 0) {
Component.this.removeComponentListener(accessibleAWTComponentHandler);
- accessibleAWTComponentHandler = null;
- }
- if (accessibleAWTFocusHandler != null) {
Component.this.removeFocusListener(accessibleAWTFocusHandler);
- accessibleAWTFocusHandler = null;
}
super.removePropertyChangeListener(listener);
}
diff --git a/src/share/classes/java/awt/Container.java b/src/share/classes/java/awt/Container.java
index 78af6b10c62e827cc43f0ceeb1542a762c121065..1118852f0e9dc3e2d4f8a834ec1e87450ff1de20 100644
--- a/src/share/classes/java/awt/Container.java
+++ b/src/share/classes/java/awt/Container.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1995, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -3824,6 +3824,12 @@ public class Container extends Component {
return Container.this.getAccessibleAt(p);
}
+ /**
+ * Number of PropertyChangeListener objects registered. It's used
+ * to add/remove ContainerListener to track target Container's state.
+ */
+ private volatile transient int propertyListenersCount = 0;
+
protected ContainerListener accessibleContainerHandler = null;
/**
@@ -3859,11 +3865,27 @@ public class Container extends Component {
public void addPropertyChangeListener(PropertyChangeListener listener) {
if (accessibleContainerHandler == null) {
accessibleContainerHandler = new AccessibleContainerHandler();
+ }
+ if (propertyListenersCount++ == 0) {
Container.this.addContainerListener(accessibleContainerHandler);
}
super.addPropertyChangeListener(listener);
}
+ /**
+ * Remove a PropertyChangeListener from the listener list.
+ * This removes a PropertyChangeListener that was registered
+ * for all properties.
+ *
+ * @param listener the PropertyChangeListener to be removed
+ */
+ public void removePropertyChangeListener(PropertyChangeListener listener) {
+ if (--propertyListenersCount == 0) {
+ Container.this.removeContainerListener(accessibleContainerHandler);
+ }
+ super.removePropertyChangeListener(listener);
+ }
+
} // inner class AccessibleAWTContainer
/**
diff --git a/src/share/classes/java/lang/Class.java b/src/share/classes/java/lang/Class.java
index 094f0cf26d656214c017815a42c90f0fa5bb4a2c..dab6c98e47a781933e72a480cb7dcca5fdbc6d74 100644
--- a/src/share/classes/java/lang/Class.java
+++ b/src/share/classes/java/lang/Class.java
@@ -29,12 +29,14 @@ import java.lang.reflect.Array;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.Member;
import java.lang.reflect.Field;
+import java.lang.reflect.Executable;
import java.lang.reflect.Method;
import java.lang.reflect.Constructor;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.AnnotatedType;
import java.lang.ref.SoftReference;
import java.io.InputStream;
import java.io.ObjectStreamField;
@@ -2325,6 +2327,11 @@ public final
// Annotations handling
private native byte[] getRawAnnotations();
+ // Since 1.8
+ native byte[] getRawTypeAnnotations();
+ static byte[] getExecutableTypeAnnotationBytes(Executable ex) {
+ return getReflectionFactory().getExecutableTypeAnnotationBytes(ex);
+ }
native ConstantPool getConstantPool();
@@ -3068,21 +3075,12 @@ public final
* @throws NullPointerException {@inheritDoc}
* @since 1.5
*/
+ @SuppressWarnings("unchecked")
public A getAnnotation(Class annotationClass) {
Objects.requireNonNull(annotationClass);
initAnnotationsIfNecessary();
- return AnnotationSupport.getOneAnnotation(annotations, annotationClass);
- }
-
- /**
- * @throws NullPointerException {@inheritDoc}
- * @since 1.5
- */
- public boolean isAnnotationPresent(Class extends Annotation> annotationClass) {
- Objects.requireNonNull(annotationClass);
-
- return getAnnotation(annotationClass) != null;
+ return (A) annotations.get(annotationClass);
}
/**
@@ -3101,18 +3099,19 @@ public final
*/
public Annotation[] getAnnotations() {
initAnnotationsIfNecessary();
- return AnnotationSupport.unpackToArray(annotations);
+ return AnnotationParser.toArray(annotations);
}
/**
* @throws NullPointerException {@inheritDoc}
* @since 1.8
*/
+ @SuppressWarnings("unchecked")
public A getDeclaredAnnotation(Class annotationClass) {
Objects.requireNonNull(annotationClass);
initAnnotationsIfNecessary();
- return AnnotationSupport.getOneAnnotation(declaredAnnotations, annotationClass);
+ return (A) declaredAnnotations.get(annotationClass);
}
/**
@@ -3131,17 +3130,7 @@ public final
*/
public Annotation[] getDeclaredAnnotations() {
initAnnotationsIfNecessary();
- return AnnotationSupport.unpackToArray(declaredAnnotations);
- }
-
- /** Returns one "directly" present annotation or null */
- A getDirectDeclaredAnnotation(Class annotationClass) {
- Objects.requireNonNull(annotationClass);
-
- initAnnotationsIfNecessary();
- @SuppressWarnings("unchecked") // TODO check safe
- A ret = (A)declaredAnnotations.get(annotationClass);
- return ret;
+ return AnnotationParser.toArray(declaredAnnotations);
}
// Annotations cache
@@ -3196,4 +3185,53 @@ public final
* Maintained by the ClassValue class.
*/
transient ClassValue.ClassValueMap classValueMap;
+
+ /**
+ * Returns an AnnotatedType object that represents the use of a type to specify
+ * the superclass of the entity represented by this Class. (The use of type
+ * Foo to specify the superclass in '... extends Foo' is distinct from the
+ * declaration of type Foo.)
+ *
+ * If this Class represents a class type whose declaration does not explicitly
+ * indicate an annotated superclass, the return value is null.
+ *
+ * If this Class represents either the Object class, an interface type, an
+ * array type, a primitive type, or void, the return value is null.
+ *
+ * @since 1.8
+ */
+ public AnnotatedType getAnnotatedSuperclass() {
+ return TypeAnnotationParser.buildAnnotatedSuperclass(getRawTypeAnnotations(), getConstantPool(), this);
+}
+
+ /**
+ * Returns an array of AnnotatedType objects that represent the use of types to
+ * specify superinterfaces of the entity represented by this Class. (The use
+ * of type Foo to specify a superinterface in '... implements Foo' is
+ * distinct from the declaration of type Foo.)
+ *
+ * If this Class represents a class, the return value is an array
+ * containing objects representing the uses of interface types to specify
+ * interfaces implemented by the class. The order of the objects in the
+ * array corresponds to the order of the interface types used in the
+ * 'implements' clause of the declaration of this Class.
+ *
+ * If this Class represents an interface, the return value is an array
+ * containing objects representing the uses of interface types to specify
+ * interfaces directly extended by the interface. The order of the objects in
+ * the array corresponds to the order of the interface types used in the
+ * 'extends' clause of the declaration of this Class.
+ *
+ * If this Class represents a class or interface whose declaration does not
+ * explicitly indicate any annotated superinterfaces, the return value is an
+ * array of length 0.
+ *
+ * If this Class represents either the Object class, an array type, a
+ * primitive type, or void, the return value is an array of length 0.
+ *
+ * @since 1.8
+ */
+ public AnnotatedType[] getAnnotatedInterfaces() {
+ return TypeAnnotationParser.buildAnnotatedInterfaces(getRawTypeAnnotations(), getConstantPool(), this);
+ }
}
diff --git a/src/share/classes/java/lang/Double.java b/src/share/classes/java/lang/Double.java
index f79980f4bb876c45847c1bcf07811516f34cd0b1..9bdb0ca46794baf5c4e19979f37965d2bbfbf2ce 100644
--- a/src/share/classes/java/lang/Double.java
+++ b/src/share/classes/java/lang/Double.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1994, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1994, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -289,7 +289,7 @@ public final class Double extends Number implements ComparableThe pair of annotation types {@code @ContainedBy} and - * {@link java.lang.annotation.ContainerFor @ContainerFor} are used to - * indicate that annotation types are repeatable. Specifically: - * - *
- * An inconsistent pair of {@code @ContainedBy} and - * {@code @ContainerFor} annotations on a repeatable annotation type - * and its containing annotation type (JLS 9.6) will lead to - * compile-time errors and runtime exceptions when using reflection to - * read annotations of a repeatable type. - * - * @see java.lang.annotation.ContainerFor - * @since 1.8 - * @jls 9.6 Annotation Types - * @jls 9.7 Annotations - */ -@Documented -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.ANNOTATION_TYPE) -public @interface ContainedBy { - /** - * Indicates the containing annotation type for the - * repeatable annotation type. - */ - Class extends Annotation> value(); -} diff --git a/src/share/classes/java/lang/annotation/ContainerFor.java b/src/share/classes/java/lang/annotation/ContainerFor.java deleted file mode 100644 index 62f3446e021ff476d3e79a4b41fd4a5b6ba9b482..0000000000000000000000000000000000000000 --- a/src/share/classes/java/lang/annotation/ContainerFor.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package java.lang.annotation; - -/** - * The annotation type {@code java.lang.annotation.ContainerFor} is - * used to indicate that the annotation type whose declaration it - * (meta-)annotates is a containing annotation type. The - * value of {@code @ContainerFor} indicates the repeatable - * annotation type for the containing annotation type. - * - *
The pair of annotation types {@link - * java.lang.annotation.ContainedBy @ContainedBy} and - * {@code @ContainerFor} are used to indicate that annotation types - * are repeatable. Specifically: - * - *
- * An inconsistent pair of {@code @ContainedBy} and - * {@code @ContainerFor} annotations on a repeatable annotation type - * and its containing annotation type (JLS 9.6) will lead to - * compile-time errors and runtime exceptions when using reflection to - * read annotations of a repeatable type. - * - * @see java.lang.annotation.ContainedBy - * @since 1.8 - * @jls 9.6 Annotation Types - * @jls 9.7 Annotations - */ -@Documented -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.ANNOTATION_TYPE) -public @interface ContainerFor { - - /** - * Indicates the repeatable annotation type for the containing - * annotation type. - */ - Class extends Annotation> value(); -} diff --git a/src/share/classes/java/lang/annotation/InvalidContainerAnnotationError.java b/src/share/classes/java/lang/annotation/InvalidContainerAnnotationError.java index b24e3034179ae7c417c42f3586a11e6e87454d6f..37d00a98b40e85eaa3fe76af39e59bb821370e50 100644 --- a/src/share/classes/java/lang/annotation/InvalidContainerAnnotationError.java +++ b/src/share/classes/java/lang/annotation/InvalidContainerAnnotationError.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,10 +27,9 @@ package java.lang.annotation; import java.util.Objects; /** - * Thrown to indicate that an annotation type whose declaration is - * (meta-)annotated with a {@link ContainerFor} annotation is not, in - * fact, the containing annotation type of the type named by {@link - * ContainerFor}. + * Thrown to indicate that an annotation type expected to act as a + * container for another annotation type by virture of an @Repeatable + * annotation, does not act as a container. * * @see java.lang.reflect.AnnotatedElement * @since 1.8 diff --git a/src/share/classes/java/lang/reflect/AccessibleObject.java b/src/share/classes/java/lang/reflect/AccessibleObject.java index baaec298a8555f4ee160a0d9d87af87667783866..9986aef6cf77c03addf76c579e1024198c4c68b9 100644 --- a/src/share/classes/java/lang/reflect/AccessibleObject.java +++ b/src/share/classes/java/lang/reflect/AccessibleObject.java @@ -180,14 +180,6 @@ public class AccessibleObject implements AnnotatedElement { throw new AssertionError("All subclasses should override this method"); } - /** - * @throws NullPointerException {@inheritDoc} - * @since 1.5 - */ - public boolean isAnnotationPresent(Class extends Annotation> annotationClass) { - return getAnnotation(annotationClass) != null; - } - /** * @throws NullPointerException {@inheritDoc} * @since 1.8 diff --git a/src/share/classes/java/lang/reflect/AnnotatedArrayType.java b/src/share/classes/java/lang/reflect/AnnotatedArrayType.java new file mode 100644 index 0000000000000000000000000000000000000000..e84a3360fddce0c6f2ca2dd4791b32fc3040914a --- /dev/null +++ b/src/share/classes/java/lang/reflect/AnnotatedArrayType.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.lang.reflect; + + +/** + * AnnotatedArrayType represents the use of an array type, whose component + * type may itself represent the annotated use of a type. + * + * @since 1.8 + */ +public interface AnnotatedArrayType extends AnnotatedType { + + /** + * Returns the annotated generic component type of this array type. + * + * @return the annotated generic component type of this array type + */ + AnnotatedType getAnnotatedGenericComponentType(); +} diff --git a/src/share/classes/java/lang/reflect/AnnotatedElement.java b/src/share/classes/java/lang/reflect/AnnotatedElement.java index 58a07350f363951307c9b8b8793250488893249c..85472ff5b00d3f2f1694b36280cf01591541d606 100644 --- a/src/share/classes/java/lang/reflect/AnnotatedElement.java +++ b/src/share/classes/java/lang/reflect/AnnotatedElement.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -35,6 +35,24 @@ import java.lang.annotation.Annotation; * arrays returned by accessors for array-valued enum members; it will * have no affect on the arrays returned to other callers. * + *
An annotation A is directly present on an element E if the + * RuntimeVisibleAnnotations or RuntimeVisibleParameterAnnotations attribute + * associated with E either: + *
An annotation A is present on an element E if either: + *
If an annotation returned by a method in this interface contains * (directly or indirectly) a {@link Class}-valued member referring to * a class that is not accessible in this VM, attempting to read the class @@ -50,7 +68,7 @@ import java.lang.annotation.Annotation; * containing annotation type of T will result in an * InvalidContainerAnnotationError. * - *
Finally, Attempting to read a member whose definition has evolved + *
Finally, attempting to read a member whose definition has evolved * incompatibly will result in a {@link * java.lang.annotation.AnnotationTypeMismatchException} or an * {@link java.lang.annotation.IncompleteAnnotationException}. @@ -70,6 +88,12 @@ public interface AnnotatedElement { * is present on this element, else false. This method * is designed primarily for convenient access to marker annotations. * + *
The truth value returned by this method is equivalent to: + * {@code getAnnotation(annotationClass) != null} + * + *
The body of the default method is specified to be the code
+ * above.
+ *
* @param annotationClass the Class object corresponding to the
* annotation type
* @return true if an annotation for the specified annotation
@@ -77,7 +101,9 @@ public interface AnnotatedElement {
* @throws NullPointerException if the given annotation class is null
* @since 1.5
*/
- boolean isAnnotationPresent(Class extends Annotation> annotationClass);
+ default boolean isAnnotationPresent(Class extends Annotation> annotationClass) {
+ return getAnnotation(annotationClass) != null;
+ }
/**
* Returns this element's annotation for the specified type if
@@ -110,12 +136,15 @@ public interface AnnotatedElement {
The Base64 padding character {@code '='} is accepted and + * interpreted as the end of the encoded byte data, but is not + * required. So if the final unit of the encoded byte data only has + * two or three Base64 characters (without the corresponding padding + * character(s) padded), they are decoded as if followed by padding + * character(s). + * *
Instances of {@link Decoder} class are safe for use by * multiple concurrent threads. * @@ -695,7 +704,7 @@ public class Base64 { * using the {@link Base64} encoding scheme. * *
An invocation of this method has exactly the same effect as invoking - * {@code return decode(src.getBytes(StandardCharsets.ISO_8859_1))} + * {@code decode(src.getBytes(StandardCharsets.ISO_8859_1))} * * @param src * the string to decode @@ -856,6 +865,9 @@ public class Base64 { /** * Returns an input stream for decoding {@link Base64} encoded byte stream. * + *
The {@code read} methods of the returned {@code InputStream} will + * throw {@code IOException} when reading bytes that cannot be decoded. + * *
Closing the returned input stream will close the underlying * input stream. * @@ -866,6 +878,7 @@ public class Base64 { * byte stream */ public InputStream wrap(InputStream is) { + Objects.requireNonNull(is); return new DecInputStream(is, isURL ? fromBase64URL : fromBase64, isMIME); } @@ -881,13 +894,16 @@ public class Base64 { int dl = dst.arrayOffset() + dst.limit(); int dp0 = dp; int mark = sp; - boolean padding = false; try { while (sp < sl) { int b = sa[sp++] & 0xff; if ((b = base64[b]) < 0) { if (b == -2) { // padding byte - padding = true; + if (shiftto == 6 && (sp == sl || sa[sp++] != '=') || + shiftto == 18) { + throw new IllegalArgumentException( + "Input byte array has wrong 4-byte ending unit"); + } break; } if (isMIME) // skip if for rfc2045 @@ -913,24 +929,23 @@ public class Base64 { if (shiftto == 6) { if (dl - dp < 1) return dp - dp0; - if (padding && (sp + 1 != sl || sa[sp++] != '=')) - throw new IllegalArgumentException( - "Input buffer has wrong 4-byte ending unit"); da[dp++] = (byte)(bits >> 16); - mark = sp; } else if (shiftto == 0) { if (dl - dp < 2) return dp - dp0; - if (padding && sp != sl) - throw new IllegalArgumentException( - "Input buffer has wrong 4-byte ending unit"); da[dp++] = (byte)(bits >> 16); da[dp++] = (byte)(bits >> 8); - mark = sp; - } else if (padding || shiftto != 18) { + } else if (shiftto == 12) { throw new IllegalArgumentException( "Last unit does not have enough valid bits"); } + while (sp < sl) { + if (isMIME && base64[sa[sp++]] < 0) + continue; + throw new IllegalArgumentException( + "Input byte array has incorrect ending byte at " + sp); + } + mark = sp; return dp - dp0; } finally { src.position(mark); @@ -948,14 +963,16 @@ public class Base64 { int dl = dst.limit(); int dp0 = dp; int mark = sp; - boolean padding = false; - try { while (sp < sl) { int b = src.get(sp++) & 0xff; if ((b = base64[b]) < 0) { if (b == -2) { // padding byte - padding = true; + if (shiftto == 6 && (sp == sl || src.get(sp++) != '=') || + shiftto == 18) { + throw new IllegalArgumentException( + "Input byte array has wrong 4-byte ending unit"); + } break; } if (isMIME) // skip if for rfc2045 @@ -981,24 +998,23 @@ public class Base64 { if (shiftto == 6) { if (dl - dp < 1) return dp - dp0; - if (padding && (sp + 1 != sl || src.get(sp++) != '=')) - throw new IllegalArgumentException( - "Input buffer has wrong 4-byte ending unit"); dst.put(dp++, (byte)(bits >> 16)); - mark = sp; } else if (shiftto == 0) { if (dl - dp < 2) return dp - dp0; - if (padding && sp != sl) - throw new IllegalArgumentException( - "Input buffer has wrong 4-byte ending unit"); dst.put(dp++, (byte)(bits >> 16)); dst.put(dp++, (byte)(bits >> 8)); - mark = sp; - } else if (padding || shiftto != 18) { + } else if (shiftto == 12) { throw new IllegalArgumentException( "Last unit does not have enough valid bits"); } + while (sp < sl) { + if (isMIME && base64[src.get(sp++)] < 0) + continue; + throw new IllegalArgumentException( + "Input byte array has incorrect ending byte at " + sp); + } + mark = sp; return dp - dp0; } finally { src.position(mark); @@ -1012,9 +1028,12 @@ public class Base64 { int len = sl - sp; if (len == 0) return 0; - if (len < 2) + if (len < 2) { + if (isMIME && base64[0] == -1) + return 0; throw new IllegalArgumentException( "Input byte[] should at least have 2 bytes for base64 bytes"); + } if (src[sl - 1] == '=') { paddings++; if (src[sl - 2] == '=') @@ -1043,12 +1062,20 @@ public class Base64 { int dp = 0; int bits = 0; int shiftto = 18; // pos of first byte of 4-byte atom - boolean padding = false; while (sp < sl) { int b = src[sp++] & 0xff; if ((b = base64[b]) < 0) { - if (b == -2) { // padding byte - padding = true; + if (b == -2) { // padding byte '=' + // xx= shiftto==6&&sp==sl missing last = + // xx=y shiftto==6 last is not = + // = shiftto==18 unnecessary padding + // x= shiftto==12 be taken care later + // together with single x, invalid anyway + if (shiftto == 6 && (sp == sl || src[sp++] != '=') || + shiftto == 18) { + throw new IllegalArgumentException( + "Input byte array has wrong 4-byte ending unit"); + } break; } if (isMIME) // skip if for rfc2045 @@ -1068,22 +1095,23 @@ public class Base64 { bits = 0; } } - // reach end of byte arry or hit padding '=' characters. - // if '=' presents, they must be the last one or two. - if (shiftto == 6) { // xx== - if (padding && (sp + 1 != sl || src[sp] != '=')) - throw new IllegalArgumentException( - "Input byte array has wrong 4-byte ending unit"); + // reached end of byte array or hit padding '=' characters. + if (shiftto == 6) { dst[dp++] = (byte)(bits >> 16); - } else if (shiftto == 0) { // xxx= - if (padding && sp != sl) - throw new IllegalArgumentException( - "Input byte array has wrong 4-byte ending unit"); + } else if (shiftto == 0) { dst[dp++] = (byte)(bits >> 16); dst[dp++] = (byte)(bits >> 8); - } else if (padding || shiftto != 18) { - throw new IllegalArgumentException( - "last unit does not have enough bytes"); + } else if (shiftto == 12) { + throw new IllegalArgumentException( + "Last unit does not have enough valid bits"); + } + // anything left is invalid, if is not MIME. + // if MIME, ignore all non-base64 character + while (sp < sl) { + if (isMIME && base64[src[sp++]] < 0) + continue; + throw new IllegalArgumentException( + "Input byte array has incorrect ending byte at " + sp); } return dp; } @@ -1247,8 +1275,22 @@ public class Base64 { int v = is.read(); if (v == -1) { eof = true; - if (nextin != 18) - throw new IOException("Base64 stream has un-decoded dangling byte(s)."); + if (nextin != 18) { + if (nextin == 12) + throw new IOException("Base64 stream has one un-decoded dangling byte."); + // treat ending xx/xxx without padding character legal. + // same logic as v == 'v' below + b[off++] = (byte)(bits >> (16)); + len--; + if (nextin == 0) { // only one padding byte + if (len == 0) { // no enough output space + bits >>= 8; // shift to lowest byte + nextout = 0; + } else { + b[off++] = (byte) (bits >> 8); + } + } + } if (off == oldOff) return -1; else diff --git a/src/share/classes/java/util/Formatter.java b/src/share/classes/java/util/Formatter.java index f5f479588c1175d75f249d22597d1aaa2334c216..03a58004918a874f4d5ba5b666c6a08eb962373c 100644 --- a/src/share/classes/java/util/Formatter.java +++ b/src/share/classes/java/util/Formatter.java @@ -351,7 +351,9 @@ import sun.misc.FormattedFloatingDecimal; *
AccessibleContext associated with this
- * JComponent.
- */
- protected AccessibleContext accessibleContext = null;
-
- /**
- * Returns the AccessibleContext associated with this
- * JComponent. The method implemented by this base
- * class returns null. Classes that extend JComponent
- * should implement this method to return the
- * AccessibleContext associated with the subclass.
- *
- * @return the AccessibleContext of this
- * JComponent
- */
- public AccessibleContext getAccessibleContext() {
- return accessibleContext;
- }
-
/**
* Inner class of JComponent used to provide default support for
* accessibility. This class is not meant to be used directly by
@@ -3689,7 +3669,18 @@ public abstract class JComponent extends Container implements Serializable,
super();
}
- protected ContainerListener accessibleContainerHandler = null;
+ /**
+ * Number of PropertyChangeListener objects registered. It's used
+ * to add/remove ContainerListener and FocusListener to track
+ * target JComponent's state
+ */
+ private volatile transient int propertyListenersCount = 0;
+
+ /**
+ * This field duplicates the one in java.awt.Component.AccessibleAWTComponent,
+ * so it has been deprecated.
+ */
+ @Deprecated
protected FocusListener accessibleFocusHandler = null;
/**
@@ -3747,10 +3738,12 @@ public abstract class JComponent extends Container implements Serializable,
public void addPropertyChangeListener(PropertyChangeListener listener) {
if (accessibleFocusHandler == null) {
accessibleFocusHandler = new AccessibleFocusHandler();
- JComponent.this.addFocusListener(accessibleFocusHandler);
}
if (accessibleContainerHandler == null) {
accessibleContainerHandler = new AccessibleContainerHandler();
+ }
+ if (propertyListenersCount++ == 0) {
+ JComponent.this.addFocusListener(accessibleFocusHandler);
JComponent.this.addContainerListener(accessibleContainerHandler);
}
super.addPropertyChangeListener(listener);
@@ -3764,9 +3757,9 @@ public abstract class JComponent extends Container implements Serializable,
* @param listener the PropertyChangeListener to be removed
*/
public void removePropertyChangeListener(PropertyChangeListener listener) {
- if (accessibleFocusHandler != null) {
+ if (--propertyListenersCount == 0) {
JComponent.this.removeFocusListener(accessibleFocusHandler);
- accessibleFocusHandler = null;
+ JComponent.this.removeContainerListener(accessibleContainerHandler);
}
super.removePropertyChangeListener(listener);
}
diff --git a/src/share/classes/sun/management/Agent.java b/src/share/classes/sun/management/Agent.java
index 19d2dedb5b40b71e86352be894daae12d03743ff..feef02ad6bea38bbafe7ed75a1ab60f0ac593522 100644
--- a/src/share/classes/sun/management/Agent.java
+++ b/src/share/classes/sun/management/Agent.java
@@ -22,7 +22,6 @@
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
-
package sun.management;
import java.io.BufferedInputStream;
@@ -31,49 +30,55 @@ import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
-
+import java.lang.management.ManagementFactory;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
-import java.lang.management.ManagementFactory;
-
+import java.net.InetAddress;
+import java.net.Socket;
+import java.net.UnknownHostException;
import java.text.MessageFormat;
-
import java.util.MissingResourceException;
import java.util.Properties;
import java.util.ResourceBundle;
import javax.management.remote.JMXConnectorServer;
+import javax.management.remote.JMXServiceURL;
import static sun.management.AgentConfigurationError.*;
import sun.management.jmxremote.ConnectorBootstrap;
+import sun.management.jdp.JdpController;
+import sun.management.jdp.JdpException;
import sun.misc.VMSupport;
/**
- * This Agent is started by the VM when -Dcom.sun.management.snmp
- * or -Dcom.sun.management.jmxremote is set. This class will be
- * loaded by the system class loader. Also jmx framework could
- * be started by jcmd
+ * This Agent is started by the VM when -Dcom.sun.management.snmp or
+ * -Dcom.sun.management.jmxremote is set. This class will be loaded by the
+ * system class loader. Also jmx framework could be started by jcmd
*/
public class Agent {
// management properties
+
private static Properties mgmtProps;
private static ResourceBundle messageRB;
-
private static final String CONFIG_FILE =
- "com.sun.management.config.file";
+ "com.sun.management.config.file";
private static final String SNMP_PORT =
- "com.sun.management.snmp.port";
+ "com.sun.management.snmp.port";
private static final String JMXREMOTE =
- "com.sun.management.jmxremote";
+ "com.sun.management.jmxremote";
private static final String JMXREMOTE_PORT =
- "com.sun.management.jmxremote.port";
+ "com.sun.management.jmxremote.port";
+ private static final String RMI_PORT =
+ "com.sun.management.jmxremote.rmi.port";
private static final String ENABLE_THREAD_CONTENTION_MONITORING =
- "com.sun.management.enableThreadContentionMonitoring";
+ "com.sun.management.enableThreadContentionMonitoring";
private static final String LOCAL_CONNECTOR_ADDRESS_PROP =
- "com.sun.management.jmxremote.localConnectorAddress";
-
+ "com.sun.management.jmxremote.localConnectorAddress";
private static final String SNMP_ADAPTOR_BOOTSTRAP_CLASS_NAME =
- "sun.management.snmp.AdaptorBootstrap";
+ "sun.management.snmp.AdaptorBootstrap";
+
+ private static final String JDP_DEFAULT_ADDRESS = "239.255.255.225";
+ private static final int JDP_DEFAULT_PORT = 7095;
// The only active agent allowed
private static JMXConnectorServer jmxServer = null;
@@ -81,26 +86,25 @@ public class Agent {
// Parse string com.sun.management.prop=xxx,com.sun.management.prop=yyyy
// and return property set if args is null or empty
// return empty property set
- private static Properties parseString(String args){
+ private static Properties parseString(String args) {
Properties argProps = new Properties();
if (args != null) {
- for (String option : args.split(",")) {
- String s[] = option.split("=", 2);
- String name = s[0].trim();
- String value = (s.length > 1) ? s[1].trim() : "";
+ for (String option : args.split(",")) {
+ String s[] = option.split("=", 2);
+ String name = s[0].trim();
+ String value = (s.length > 1) ? s[1].trim() : "";
- if (!name.startsWith("com.sun.management.")) {
- error(INVALID_OPTION, name);
- }
+ if (!name.startsWith("com.sun.management.")) {
+ error(INVALID_OPTION, name);
+ }
- argProps.setProperty(name, value);
- }
+ argProps.setProperty(name, value);
+ }
}
return argProps;
}
-
// invoked by -javaagent or -Dcom.sun.management.agent.class
public static void premain(String args) throws Exception {
agentmain(args);
@@ -115,18 +119,18 @@ public class Agent {
Properties arg_props = parseString(args);
// Read properties from the config file
- Properties config_props = new Properties();
- String fname = arg_props.getProperty(CONFIG_FILE);
- readConfiguration(fname, config_props);
+ Properties config_props = new Properties();
+ String fname = arg_props.getProperty(CONFIG_FILE);
+ readConfiguration(fname, config_props);
- // Arguments override config file
- config_props.putAll(arg_props);
- startAgent(config_props);
+ // Arguments override config file
+ config_props.putAll(arg_props);
+ startAgent(config_props);
}
// jcmd ManagementAgent.start_local entry point
// Also called due to command-line via startAgent()
- private static synchronized void startLocalManagementAgent(){
+ private static synchronized void startLocalManagementAgent() {
Properties agentProps = VMSupport.getAgentProperties();
// start local connector if not started
@@ -156,7 +160,7 @@ public class Agent {
throw new RuntimeException(getText(INVALID_STATE, "Agent already started"));
}
- Properties argProps = parseString(args);
+ Properties argProps = parseString(args);
Properties configProps = new Properties();
// Load the management properties from the config file
@@ -169,7 +173,7 @@ public class Agent {
// management properties can be overridden by system properties
// which take precedence
Properties sysProps = System.getProperties();
- synchronized(sysProps){
+ synchronized (sysProps) {
configProps.putAll(sysProps);
}
@@ -190,21 +194,26 @@ public class Agent {
// can specify this property inside config file, so enable optional
// monitoring functionality if this property is set
final String enableThreadContentionMonitoring =
- configProps.getProperty(ENABLE_THREAD_CONTENTION_MONITORING);
+ configProps.getProperty(ENABLE_THREAD_CONTENTION_MONITORING);
if (enableThreadContentionMonitoring != null) {
ManagementFactory.getThreadMXBean().
- setThreadContentionMonitoringEnabled(true);
+ setThreadContentionMonitoringEnabled(true);
}
String jmxremotePort = configProps.getProperty(JMXREMOTE_PORT);
if (jmxremotePort != null) {
jmxServer = ConnectorBootstrap.
- startRemoteConnectorServer(jmxremotePort, configProps);
+ startRemoteConnectorServer(jmxremotePort, configProps);
+
+ startDiscoveryService(configProps);
}
}
private static synchronized void stopRemoteManagementAgent() throws Exception {
+
+ JdpController.stopDiscoveryService();
+
if (jmxServer != null) {
ConnectorBootstrap.unexportRegistry();
@@ -222,15 +231,15 @@ public class Agent {
// Enable optional monitoring functionality if requested
final String enableThreadContentionMonitoring =
- props.getProperty(ENABLE_THREAD_CONTENTION_MONITORING);
+ props.getProperty(ENABLE_THREAD_CONTENTION_MONITORING);
if (enableThreadContentionMonitoring != null) {
ManagementFactory.getThreadMXBean().
- setThreadContentionMonitoringEnabled(true);
+ setThreadContentionMonitoringEnabled(true);
}
try {
if (snmpPort != null) {
- loadSnmpAgent(snmpPort, props);
+ loadSnmpAgent(snmpPort, props);
}
/*
@@ -242,13 +251,14 @@ public class Agent {
* of this "local" server is exported as a counter to the jstat
* instrumentation buffer.
*/
- if (jmxremote != null || jmxremotePort != null) {
+ if (jmxremote != null || jmxremotePort != null) {
if (jmxremotePort != null) {
- jmxServer = ConnectorBootstrap.
- startRemoteConnectorServer(jmxremotePort, props);
+ jmxServer = ConnectorBootstrap.
+ startRemoteConnectorServer(jmxremotePort, props);
+ startDiscoveryService(props);
}
startLocalManagementAgent();
- }
+ }
} catch (AgentConfigurationError e) {
error(e.getError(), e.getParams());
@@ -257,6 +267,73 @@ public class Agent {
}
}
+ private static void startDiscoveryService(Properties props)
+ throws IOException {
+ // Start discovery service if requested
+ String discoveryPort = props.getProperty("com.sun.management.jdp.port");
+ String discoveryAddress = props.getProperty("com.sun.management.jdp.address");
+ String discoveryShouldStart = props.getProperty("com.sun.management.jmxremote.autodiscovery");
+
+ // Decide whether we should start autodicovery service.
+ // To start autodiscovery following conditions should be met:
+ // autodiscovery==true OR (autodicovery==null AND jdp.port != NULL)
+
+ boolean shouldStart = false;
+ if (discoveryShouldStart == null){
+ shouldStart = (discoveryPort != null);
+ }
+ else{
+ try{
+ shouldStart = Boolean.parseBoolean(discoveryShouldStart);
+ } catch (NumberFormatException e) {
+ throw new AgentConfigurationError("Couldn't parse autodiscovery argument");
+ }
+ }
+
+ if (shouldStart) {
+ // port and address are required arguments and have no default values
+ InetAddress address;
+ try {
+ address = (discoveryAddress == null) ?
+ InetAddress.getByName(JDP_DEFAULT_ADDRESS) : InetAddress.getByName(discoveryAddress);
+ } catch (UnknownHostException e) {
+ throw new AgentConfigurationError("Unable to broadcast to requested address", e);
+ }
+
+ int port = JDP_DEFAULT_PORT;
+ if (discoveryPort != null) {
+ try {
+ port = Integer.parseInt(discoveryPort);
+ } catch (NumberFormatException e) {
+ throw new AgentConfigurationError("Couldn't parse JDP port argument");
+ }
+ }
+
+ // Rebuilding service URL to broadcast it
+ String jmxremotePort = props.getProperty(JMXREMOTE_PORT);
+ String rmiPort = props.getProperty(RMI_PORT);
+
+ JMXServiceURL url = jmxServer.getAddress();
+ String hostname = url.getHost();
+
+ String jmxUrlStr = (rmiPort != null)
+ ? String.format(
+ "service:jmx:rmi://%s:%s/jndi/rmi://%s:%s/jmxrmi",
+ hostname, rmiPort, hostname, jmxremotePort)
+ : String.format(
+ "service:jmx:rmi:///jndi/rmi://%s:%s/jmxrmi", hostname, jmxremotePort);
+
+ String instanceName = System.getProperty("com.sun.management.jdp.name");
+
+ try{
+ JdpController.startDiscoveryService(address, port, instanceName, jmxUrlStr);
+ }
+ catch(JdpException e){
+ throw new AgentConfigurationError("Couldn't start JDP service", e);
+ }
+ }
+ }
+
public static Properties loadManagementProperties() {
Properties props = new Properties();
@@ -268,22 +345,22 @@ public class Agent {
// management properties can be overridden by system properties
// which take precedence
Properties sysProps = System.getProperties();
- synchronized(sysProps){
+ synchronized (sysProps) {
props.putAll(sysProps);
}
return props;
- }
+ }
- public static synchronized Properties getManagementProperties() {
+ public static synchronized Properties getManagementProperties() {
if (mgmtProps == null) {
String configFile = System.getProperty(CONFIG_FILE);
String snmpPort = System.getProperty(SNMP_PORT);
String jmxremote = System.getProperty(JMXREMOTE);
String jmxremotePort = System.getProperty(JMXREMOTE_PORT);
- if (configFile == null && snmpPort == null &&
- jmxremote == null && jmxremotePort == null) {
+ if (configFile == null && snmpPort == null
+ && jmxremote == null && jmxremotePort == null) {
// return if out-of-the-management option is not specified
return null;
}
@@ -297,22 +374,23 @@ public class Agent {
// invoke the following through reflection:
// AdaptorBootstrap.initialize(snmpPort, props);
final Class> adaptorClass =
- Class.forName(SNMP_ADAPTOR_BOOTSTRAP_CLASS_NAME,true,null);
+ Class.forName(SNMP_ADAPTOR_BOOTSTRAP_CLASS_NAME, true, null);
final Method initializeMethod =
adaptorClass.getMethod("initialize",
- String.class, Properties.class);
- initializeMethod.invoke(null,snmpPort,props);
+ String.class, Properties.class);
+ initializeMethod.invoke(null, snmpPort, props);
} catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException x) {
// snmp runtime doesn't exist - initialization fails
- throw new UnsupportedOperationException("Unsupported management property: " + SNMP_PORT,x);
+ throw new UnsupportedOperationException("Unsupported management property: " + SNMP_PORT, x);
} catch (InvocationTargetException x) {
final Throwable cause = x.getCause();
- if (cause instanceof RuntimeException)
+ if (cause instanceof RuntimeException) {
throw (RuntimeException) cause;
- else if (cause instanceof Error)
+ } else if (cause instanceof Error) {
throw (Error) cause;
+ }
// should not happen...
- throw new UnsupportedOperationException("Unsupported management property: " + SNMP_PORT,cause);
+ throw new UnsupportedOperationException("Unsupported management property: " + SNMP_PORT, cause);
}
}
@@ -353,8 +431,8 @@ public class Agent {
} catch (IOException e) {
error(CONFIG_FILE_CLOSE_FAILED, fname);
}
- }
- }
+ }
+ }
}
public static void startAgent() throws Exception {
@@ -389,9 +467,9 @@ public class Agent {
// invoke the premain(String args) method
Class> clz = ClassLoader.getSystemClassLoader().loadClass(cname);
Method premain = clz.getMethod("premain",
- new Class>[] { String.class });
+ new Class>[]{String.class});
premain.invoke(null, /* static */
- new Object[] { args });
+ new Object[]{args});
} catch (ClassNotFoundException ex) {
error(AGENT_CLASS_NOT_FOUND, "\"" + cname + "\"");
} catch (NoSuchMethodException ex) {
@@ -400,8 +478,8 @@ public class Agent {
error(AGENT_CLASS_ACCESS_DENIED);
} catch (Exception ex) {
String msg = (ex.getCause() == null
- ? ex.getMessage()
- : ex.getCause().getMessage());
+ ? ex.getMessage()
+ : ex.getCause().getMessage());
error(AGENT_CLASS_FAILED, msg);
}
}
@@ -425,7 +503,6 @@ public class Agent {
}
}
-
public static void error(String key, String message) {
String keyText = getText(key);
System.err.print(getText("agent.err.error") + ": " + keyText);
@@ -447,7 +524,7 @@ public class Agent {
private static void initResource() {
try {
messageRB =
- ResourceBundle.getBundle("sun.management.resources.agent");
+ ResourceBundle.getBundle("sun.management.resources.agent");
} catch (MissingResourceException e) {
throw new Error("Fatal: Resource for management agent is missing");
}
@@ -470,10 +547,9 @@ public class Agent {
}
String format = messageRB.getString(key);
if (format == null) {
- format = "missing resource key: key = \"" + key + "\", " +
- "arguments = \"{0}\", \"{1}\", \"{2}\"";
+ format = "missing resource key: key = \"" + key + "\", "
+ + "arguments = \"{0}\", \"{1}\", \"{2}\"";
}
return MessageFormat.format(format, (Object[]) args);
}
-
}
diff --git a/src/share/classes/sun/management/jdp/JdpBroadcaster.java b/src/share/classes/sun/management/jdp/JdpBroadcaster.java
new file mode 100644
index 0000000000000000000000000000000000000000..df5f23458e5bc900960dd0369c19c187a819501a
--- /dev/null
+++ b/src/share/classes/sun/management/jdp/JdpBroadcaster.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package sun.management.jdp;
+
+import java.io.IOException;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.NetworkInterface;
+import java.net.ProtocolFamily;
+import java.net.StandardProtocolFamily;
+import java.net.StandardSocketOptions;
+import java.nio.ByteBuffer;
+import java.nio.channels.DatagramChannel;
+import java.nio.channels.UnsupportedAddressTypeException;
+
+/**
+ * JdpBroadcaster is responsible for sending pre-built JDP packet across a Net
+ *
+ * Multicast group address, port number and ttl have to be chosen on upper + * level and passed to broadcaster constructor. Also it's possible to specify + * source address to broadcast from.
+ * + *JdpBradcaster doesn't perform any validation on a supplied {@code port} and {@code ttl} because + * the allowed values depend on an operating system setup
+ * + */ +public final class JdpBroadcaster { + + private final InetAddress addr; + private final int port; + private final DatagramChannel channel; + + /** + * Create a new broadcaster + * + * @param address - multicast group address + * @param srcAddress - address of interface we should use to broadcast. + * @param port - udp port to use + * @param ttl - packet ttl + * @throws IOException + */ + public JdpBroadcaster(InetAddress address, InetAddress srcAddress, int port, int ttl) + throws IOException, JdpException { + this.addr = address; + this.port = port; + + ProtocolFamily family = (address instanceof Inet6Address) + ? StandardProtocolFamily.INET6 : StandardProtocolFamily.INET; + + channel = DatagramChannel.open(family); + channel.setOption(StandardSocketOptions.SO_REUSEADDR, true); + channel.setOption(StandardSocketOptions.IP_MULTICAST_TTL, ttl); + + // with srcAddress equal to null, this constructor do exactly the same as + // if srcAddress is not passed + if (srcAddress != null) { + // User requests particular interface to bind to + NetworkInterface interf = NetworkInterface.getByInetAddress(srcAddress); + try { + channel.bind(new InetSocketAddress(srcAddress, 0)); + } catch (UnsupportedAddressTypeException ex) { + throw new JdpException("Unable to bind to source address"); + } + channel.setOption(StandardSocketOptions.IP_MULTICAST_IF, interf); + } + } + + /** + * Create a new broadcaster + * + * @param address - multicast group address + * @param port - udp port to use + * @param ttl - packet ttl + * @throws IOException + */ + public JdpBroadcaster(InetAddress address, int port, int ttl) + throws IOException, JdpException { + this(address, null, port, ttl); + } + + /** + * Broadcast pre-built packet + * + * @param packet - instance of JdpPacket + * @throws IOException + */ + public void sendPacket(JdpPacket packet) + throws IOException { + byte[] data = packet.getPacketData(); + // Unlike allocate/put wrap don't need a flip afterward + ByteBuffer b = ByteBuffer.wrap(data); + channel.send(b, new InetSocketAddress(addr, port)); + } + + /** + * Shutdown broadcaster and close underlying socket channel + * + * @throws IOException + */ + public void shutdown() throws IOException { + channel.close(); + } +} diff --git a/src/share/classes/sun/management/jdp/JdpController.java b/src/share/classes/sun/management/jdp/JdpController.java new file mode 100644 index 0000000000000000000000000000000000000000..d8d0ed46930c51de1d1e7d77388f8536fff91802 --- /dev/null +++ b/src/share/classes/sun/management/jdp/JdpController.java @@ -0,0 +1,196 @@ +/* + * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package sun.management.jdp; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.UUID; + +/** + * JdpController is responsible to create and manage a broadcast loop + * + *Other part of code has no access to broadcast loop and have to use + * provided static methods + * {@link #startDiscoveryService(InetAddress,int,String,String) startDiscoveryService} + * and {@link #stopDiscoveryService() stopDiscoveryService}
+ *{@link #startDiscoveryService(InetAddress,int,String,String) startDiscoveryService} could be called multiple + * times as it stops the running service if it is necessary. Call to {@link #stopDiscoveryService() stopDiscoveryService} + * ignored if service isn't run
+ * + * + * + * + *System properties below could be used to control broadcast loop behavior. + * Property below have to be set explicitly in command line. It's not possible to + * set it in management.config file. Careless changes of these properties could + * lead to security or network issues. + *
null parameters values are filtered out on {@link JdpPacketWriter} level and + * corresponding keys are not placed to packet.
+ */ +public final class JdpController { + + private static class JDPControllerRunner implements Runnable { + + private final JdpJmxPacket packet; + private final JdpBroadcaster bcast; + private final int pause; + private volatile boolean shutdown = false; + + private JDPControllerRunner(JdpBroadcaster bcast, JdpJmxPacket packet, int pause) { + this.bcast = bcast; + this.packet = packet; + this.pause = pause; + } + + @Override + public void run() { + try { + while (!shutdown) { + bcast.sendPacket(packet); + try { + Thread.sleep(this.pause); + } catch (InterruptedException e) { + // pass + } + } + + } catch (IOException e) { + // pass; + } + + // It's not possible to re-use controller, + // nevertheless reset shutdown variable + try { + stop(); + bcast.shutdown(); + } catch (IOException ex) { + // pass - ignore IOException during shutdown + } + } + + public void stop() { + shutdown = true; + } + } + private static JDPControllerRunner controller = null; + + private JdpController(){ + // Don't allow to instantiate this class. + } + + // Utility to handle optional system properties + // Parse an integer from string or return default if provided string is null + private static int getInteger(String val, int dflt, String msg) throws JdpException { + try { + return (val == null) ? dflt : Integer.parseInt(val); + } catch (NumberFormatException ex) { + throw new JdpException(msg); + } + } + + // Parse an inet address from string or return default if provided string is null + private static InetAddress getInetAddress(String val, InetAddress dflt, String msg) throws JdpException { + try { + return (val == null) ? dflt : InetAddress.getByName(val); + } catch (UnknownHostException ex) { + throw new JdpException(msg); + } + } + + /** + * Starts discovery service + * + * @param address - multicast group address + * @param port - udp port to use + * @param instanceName - name of running JVM instance + * @param url - JMX service url + * @throws IOException + */ + public static synchronized void startDiscoveryService(InetAddress address, int port, String instanceName, String url) + throws IOException, JdpException { + + // Limit packet to local subnet by default + int ttl = getInteger( + System.getProperty("com.sun.management.jdp.ttl"), 1, + "Invalid jdp packet ttl"); + + // Broadcast once a 5 seconds by default + int pause = getInteger( + System.getProperty("com.sun.management.jdp.pause"), 5, + "Invalid jdp pause"); + + // Converting seconds to milliseconds + pause = pause * 1000; + + // Allow OS to choose broadcast source + InetAddress sourceAddress = getInetAddress( + System.getProperty("com.sun.management.jdp.source_addr"), null, + "Invalid source address provided"); + + // Generate session id + UUID id = UUID.randomUUID(); + + JdpJmxPacket packet = new JdpJmxPacket(id, url); + + // Don't broadcast whole command line for security reason. + // Strip everything after first space + String javaCommand = System.getProperty("sun.java.command"); + if (javaCommand != null) { + String[] arr = javaCommand.split(" ", 2); + packet.setMainClass(arr[0]); + } + + // Put optional explicit java instance name to packet, if user doesn't specify + // it the key is skipped. PacketWriter is responsible to skip keys having null value. + packet.setInstanceName(instanceName); + + JdpBroadcaster bcast = new JdpBroadcaster(address, sourceAddress, port, ttl); + + // Stop discovery service if it's already running + stopDiscoveryService(); + + controller = new JDPControllerRunner(bcast, packet, pause); + + Thread t = new Thread(controller, "JDP broadcaster"); + t.setDaemon(true); + t.start(); + } + + /** + * Stop running discovery service, + * it's safe to attempt to stop not started service + */ + public static synchronized void stopDiscoveryService() { + if ( controller != null ){ + controller.stop(); + controller = null; + } + } +} diff --git a/src/share/classes/sun/management/jdp/JdpException.java b/src/share/classes/sun/management/jdp/JdpException.java new file mode 100644 index 0000000000000000000000000000000000000000..03404223e940e18793b58b9084b477dc57ac2956 --- /dev/null +++ b/src/share/classes/sun/management/jdp/JdpException.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package sun.management.jdp; + +/** + * An Exception thrown if a JDP implementation encounters a problem. + */ +public final class JdpException extends Exception { + + private static final long serialVersionUID = 1L; + + /** + * Construct a new JDP exception with a meaningful message + * + * @param msg - message + */ + public JdpException(String msg) { + super(msg); + } +} diff --git a/src/share/classes/sun/management/jdp/JdpGenericPacket.java b/src/share/classes/sun/management/jdp/JdpGenericPacket.java new file mode 100644 index 0000000000000000000000000000000000000000..8e88b1482657d128f6b519536078866d1e51c134 --- /dev/null +++ b/src/share/classes/sun/management/jdp/JdpGenericPacket.java @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package sun.management.jdp; + +/** + * JdpGenericPacket responsible to provide fields + * common for all Jdp packets + */ +public abstract class JdpGenericPacket implements JdpPacket { + + /** + * JDP protocol magic. Magic allows a reader to quickly select + * JDP packets from a bunch of broadcast packets addressed to the same port + * and broadcast group. Any packet intended to be parsed by JDP client + * has to start from this magic. + */ + private static final int MAGIC = 0xC0FFEE42; + + /** + * Current version of protocol. Any implementation of this protocol has to + * conform with the packet structure and the flow described in JEP-168 + */ + private static final short PROTOCOL_VERSION = 1; + + /** + * Default do-nothing constructor + */ + protected JdpGenericPacket(){ + // do nothing + } + + + /** + * Validate protocol header magic field + * + * @param magic - value to validate + * @throws JdpException + */ + public static void checkMagic(int magic) + throws JdpException { + if (magic != MAGIC) { + throw new JdpException("Invalid JDP magic header: " + magic); + } + } + + /** + * Validate protocol header version field + * + * @param version - value to validate + * @throws JdpException + */ + public static void checkVersion(short version) + throws JdpException { + + if (version > PROTOCOL_VERSION) { + throw new JdpException("Unsupported protocol version: " + version); + } + } + + /** + * + * @return protocol magic + */ + public static int getMagic() { + return MAGIC; + } + + /** + * + * @return current protocol version + */ + public static short getVersion() { + return PROTOCOL_VERSION; + } +} diff --git a/src/share/classes/sun/management/jdp/JdpJmxPacket.java b/src/share/classes/sun/management/jdp/JdpJmxPacket.java new file mode 100644 index 0000000000000000000000000000000000000000..7d5ccc2f892562691cb8f5e66f5dd58036ea3ba2 --- /dev/null +++ b/src/share/classes/sun/management/jdp/JdpJmxPacket.java @@ -0,0 +1,196 @@ +/* + * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package sun.management.jdp; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.UUID; + +/** + * A packet to broadcasts JMX URL + * + * Fields: + * + *Each packet have to contain MAGIC and PROTOCOL_VERSION in order to be + * recognized as a valid JDP packet.
+ * + *Default implementation build packet as a set of UTF-8 encoded Key/Value pairs + * are stored as an ordered list of values, and are sent to the server + * in that order.
+ * + *+ * Packet structure: + * + * 4 bytes JDP magic (0xC0FFE42) + * 2 bytes JDP protocol version (01) + * + * 2 bytes size of key + * x bytes key (UTF-8 encoded) + * 2 bytes size of value + * x bytes value (UTF-8 encoded) + * + * repeat as many times as necessary ... + *
+ */ +public interface JdpPacket { + + /** + * This method responsible to assemble packet and return a byte array + * ready to be sent across a Net. + * + * @return assembled packet as an array of bytes + * @throws IOException + */ + public byte[] getPacketData() throws IOException; + +} diff --git a/src/share/classes/sun/management/jdp/JdpPacketReader.java b/src/share/classes/sun/management/jdp/JdpPacketReader.java new file mode 100644 index 0000000000000000000000000000000000000000..9f3957ab5ce454fba358389f658219953925343e --- /dev/null +++ b/src/share/classes/sun/management/jdp/JdpPacketReader.java @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package sun.management.jdp; + +import java.io.ByteArrayInputStream; +import java.io.DataInputStream; +import java.io.EOFException; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +/** + * JdpPacketReader responsible for reading a packetThis class gets a byte + * array as it came from a Net, validates it and breaks a part
+ */ +public final class JdpPacketReader { + + private final DataInputStream pkt; + private MapThis class assembles a set of key/value pairs to valid JDP packet, + * ready to be sent across a Net
+ */ +public final class JdpPacketWriter { + + private final ByteArrayOutputStream baos; + private final DataOutputStream pkt; + + /** + * Create a JDP packet, add mandatory magic and version headers + * + * @throws IOException + */ + public JdpPacketWriter() + throws IOException { + baos = new ByteArrayOutputStream(); + pkt = new DataOutputStream(baos); + + pkt.writeInt(JdpGenericPacket.getMagic()); + pkt.writeShort(JdpGenericPacket.getVersion()); + } + + /** + * Put string entry to packet + * + * @param entry - string to put (utf-8 encoded) + * @throws IOException + */ + public void addEntry(String entry) + throws IOException { + pkt.writeShort(entry.length()); + byte[] b = entry.getBytes("UTF-8"); + pkt.write(b); + } + + /** + * Put key/value pair to packet + * + * @param key - key to put (utf-8 encoded) + * @param val - value to put (utf-8 encoded) + * @throws IOException + */ + public void addEntry(String key, String val) + throws IOException { + /* Silently skip key if value is null. + * We don't need to distinguish between key missing + * and key has no value cases + */ + if (val != null) { + addEntry(key); + addEntry(val); + } + } + + /** + * Return assembled packet as a byte array + * + * @return packet bytes + */ + public byte[] getPacketBytes() { + return baos.toByteArray(); + } +} diff --git a/src/share/classes/sun/management/jdp/package-info.java b/src/share/classes/sun/management/jdp/package-info.java new file mode 100644 index 0000000000000000000000000000000000000000..e21e461214fb261e2cbdaa7b98d7b4f5f754932d --- /dev/null +++ b/src/share/classes/sun/management/jdp/package-info.java @@ -0,0 +1,55 @@ +/** + * Summary + * ------- + * + * Define a lightweight network protocol for discovering running and + * manageable Java processes within a network subnet. + * + * + * Description + * ----------- + * + * The protocol is lightweight multicast based, and works like a beacon, + * broadcasting the JMXService URL needed to connect to the external JMX + * agent if an application is started with appropriate parameters. + * + * The payload is structured like this: + * + * 4 bytes JDP magic (0xC0FFEE42) + * 2 bytes JDP protocol version (1) + * 2 bytes size of the next entry + * x bytes next entry (UTF-8 encoded) + * 2 bytes size of next entry + * ... Rinse and repeat... + * + * The payload will be parsed as even entries being keys, odd entries being + * values. + * + * The standard JDP packet contains four entries: + * + * - `DISCOVERABLE_SESSION_UUID` -- Unique id of the instance; this id changes every time + * the discovery protocol starts and stops + * + * - `MAIN_CLASS` -- The value of the `sun.java.command` property + * + * - `JMX_SERVICE_URL` -- The URL to connect to the JMX agent + * + * - `INSTANCE_NAME` -- The user-provided name of the running instance + * + * The protocol sends packets to 239.255.255.225:7095 by default. + * + * The protocol uses system properties to control it's behaviour: + * - `com.sun.management.jdp.port` -- override default port + * + * - `com.sun.management.jdp.address` -- override default address + * + * - `com.sun.management.jmxremote.autodiscovery` -- whether we should start autodiscovery or + * not. Autodiscovery starts if and only if following conditions are met: (autodiscovery is + * true OR (autodiscovery is not set AND jdp.port is set)) + * + * - `com.sun.management.jdp.ttl` -- set ttl for broadcast packet, default is 1 + * - `com.sun.management.jdp.pause` -- set broadcast interval in seconds default is 5 + * - `com.sun.management.jdp.source_addr` -- an address of interface to use for broadcast + */ + +package sun.management.jdp; diff --git a/src/share/classes/sun/misc/JavaLangAccess.java b/src/share/classes/sun/misc/JavaLangAccess.java index 9506194c707b7699ef560b8a76e41af1a00e24e6..db8d8213179bee2c0d142dbc8e657dc5ac64eac7 100644 --- a/src/share/classes/sun/misc/JavaLangAccess.java +++ b/src/share/classes/sun/misc/JavaLangAccess.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,6 +26,7 @@ package sun.misc; import java.lang.annotation.Annotation; +import java.lang.reflect.Executable; import sun.reflect.ConstantPool; import sun.reflect.annotation.AnnotationType; import sun.nio.ch.Interruptible; @@ -46,6 +47,18 @@ public interface JavaLangAccess { */ AnnotationType getAnnotationType(Class> klass); + /** + * Get the array of bytes that is the class-file representation + * of this Class' type annotations. + */ + byte[] getRawClassTypeAnnotations(Class> klass); + + /** + * Get the array of bytes that is the class-file representation + * of this Executable's type annotations. + */ + byte[] getRawExecutableTypeAnnotations(Executable executable); + /** * Returns the elements of an enum class or null if the * Class object does not represent an enum type; @@ -84,9 +97,4 @@ public interface JavaLangAccess { * Returns the ith StackTraceElement for the given throwable. */ StackTraceElement getStackTraceElement(Throwable t, int i); - - /** - * Returns a directly present annotation. - */ - public A getDirectDeclaredAnnotation(Class> klass, Class anno); } diff --git a/src/share/classes/sun/reflect/LangReflectAccess.java b/src/share/classes/sun/reflect/LangReflectAccess.java index fba1d31808690f79d2f6dbd8c66e3b31a72e39d1..3c3b275741093b6e1f0a6c4d720dd118070e9fa0 100644 --- a/src/share/classes/sun/reflect/LangReflectAccess.java +++ b/src/share/classes/sun/reflect/LangReflectAccess.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2004, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -81,6 +81,9 @@ public interface LangReflectAccess { public void setConstructorAccessor(Constructor> c, ConstructorAccessor accessor); + /** Gets the byte[] that encodes TypeAnnotations on an Executable. */ + public byte[] getExecutableTypeAnnotationBytes(Executable ex); + /** Gets the "slot" field from a Constructor (used for serialization) */ public int getConstructorSlot(Constructor> c); diff --git a/src/share/classes/sun/reflect/ReflectionFactory.java b/src/share/classes/sun/reflect/ReflectionFactory.java index ea5323b8b9aa2297bd6f857a9b5af7906e369fcb..de97dfc700125eeb30400c2c0ff31459717b93be 100644 --- a/src/share/classes/sun/reflect/ReflectionFactory.java +++ b/src/share/classes/sun/reflect/ReflectionFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,6 +26,7 @@ package sun.reflect; import java.lang.reflect.Field; +import java.lang.reflect.Executable; import java.lang.reflect.Method; import java.lang.reflect.Constructor; import java.lang.reflect.Modifier; @@ -314,6 +315,12 @@ public class ReflectionFactory { return langReflectAccess().copyConstructor(arg); } + /** Gets the byte[] that encodes TypeAnnotations on an executable. + */ + public byte[] getExecutableTypeAnnotationBytes(Executable ex) { + return langReflectAccess().getExecutableTypeAnnotationBytes(ex); + } + //-------------------------------------------------------------------------- // // Routines used by serialization diff --git a/src/share/classes/sun/reflect/annotation/AnnotatedTypeFactory.java b/src/share/classes/sun/reflect/annotation/AnnotatedTypeFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..e0524e849dc420577ccf67eab2334013e89d6bab --- /dev/null +++ b/src/share/classes/sun/reflect/annotation/AnnotatedTypeFactory.java @@ -0,0 +1,315 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.reflect.annotation; + +import java.lang.annotation.*; +import java.lang.reflect.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +import static sun.reflect.annotation.TypeAnnotation.*; + +public class AnnotatedTypeFactory { + /** + * Create an AnnotatedType. + * + * @param type the type this AnnotatedType corresponds to + * @param currentLoc the location this AnnotatedType corresponds to + * @param actualTypeAnnos the type annotations this AnnotatedType has + * @param allOnSameTarget all type annotation on the same TypeAnnotationTarget + * as the AnnotatedType being built + * @param decl the declaration having the type use this AnnotatedType + * corresponds to + */ + public static AnnotatedType buildAnnotatedType(Type type, + LocationInfo currentLoc, + TypeAnnotation[] actualTypeAnnos, + TypeAnnotation[] allOnSameTarget, + AnnotatedElement decl) { + if (type == null) { + return EMPTY_ANNOTATED_TYPE; + } + if (isArray(type)) + return new AnnotatedArrayTypeImpl(type, + currentLoc, + actualTypeAnnos, + allOnSameTarget, + decl); + if (type instanceof Class) { + return new AnnotatedTypeBaseImpl(type, + addNesting(type, currentLoc), + actualTypeAnnos, + allOnSameTarget, + decl); + } else if (type instanceof TypeVariable) { + return new AnnotatedTypeVariableImpl((TypeVariable)type, + currentLoc, + actualTypeAnnos, + allOnSameTarget, + decl); + } else if (type instanceof ParameterizedType) { + return new AnnotatedParameterizedTypeImpl((ParameterizedType)type, + addNesting(type, currentLoc), + actualTypeAnnos, + allOnSameTarget, + decl); + } else if (type instanceof WildcardType) { + return new AnnotatedWildcardTypeImpl((WildcardType) type, + currentLoc, + actualTypeAnnos, + allOnSameTarget, + decl); + } + throw new AssertionError("Unknown instance of Type: " + type + "\nThis should not happen."); + } + + private static LocationInfo addNesting(Type type, LocationInfo addTo) { + if (isArray(type)) + return addTo; + if (type instanceof Class) { + Class> clz = (Class)type; + if (clz.getEnclosingClass() == null) + return addTo; + return addNesting(clz.getEnclosingClass(), addTo.pushInner()); + } else if (type instanceof ParameterizedType) { + ParameterizedType t = (ParameterizedType)type; + if (t.getOwnerType() == null) + return addTo; + return addNesting(t.getOwnerType(), addTo.pushInner()); + } + return addTo; + } + + private static boolean isArray(Type t) { + if (t instanceof Class) { + Class> c = (Class)t; + if (c.isArray()) + return true; + } else if (t instanceof GenericArrayType) { + return true; + } + return false; + } + + static final AnnotatedType EMPTY_ANNOTATED_TYPE = new AnnotatedTypeBaseImpl(null, LocationInfo.BASE_LOCATION, + new TypeAnnotation[0], new TypeAnnotation[0], null); + + private static class AnnotatedTypeBaseImpl implements AnnotatedType { + private final Type type; + private final AnnotatedElement decl; + private final LocationInfo location; + private final TypeAnnotation[] allOnSameTargetTypeAnnotations; + private final Map