Thursday, March 17, 2022

Java Tutorial: Reflection

Chapters

Overview

Reflection is a feature in the Java programming language. It allows an executing Java program to examine or "introspect" upon itself, and manipulate internal properties of the program. For example, it's possible for a Java class to obtain the names of all its members and display them.

The ability to examine and manipulate a Java class from within itself may not sound like very much, but in other programming languages this feature simply doesn't exist. For example, there is no way in a Pascal, C, or C++ program to obtain information about the functions defined within that program.

One tangible use of reflection is in JavaBeans, where software components can be manipulated visually via a builder tool. The tool uses reflection to obtain the properties of Java components (classes) as they are dynamically loaded.

Reflection is powerful, but should not be used indiscriminately. If it is possible to perform an operation without using reflection, then it is preferable to avoid using it. In this tutorial, I'm gonna demonstrate some essential methods of java.lang.Class and some essential classes of java.lang.reflect package like Field, Method and Constructor.

Retrieving Class Object

The entry point for all reflection operations is java.lang.Class. With the exception of java.lang.reflect.ReflectPermission, none of the classes in java.lang.reflect have public constructors. Most Class objects refer to .class files of classes.

If there's an available instance and it's related to Object, we can use getClass method. This example demonstrates getClass method.
public class SampleClass{

  public static void main(String[] args){
    String str = "String";
    
    Class<?> classObj = str.getClass();
    System.out.println(classObj);
    
  }
}

Result 
class java.lang.String
If there's no available instance, we can use the .class instance. This syntax requires name of type followed by ".class". This feature is also the easiest way of getting Class object for primitive types. This example demonstrates .class syntax.
public class SampleClass{

  public static void main(String[] args){
    
    Class<?> classObj = boolean.class;
    System.out.println(classObj);
    classObj = String.class;
    System.out.println(classObj);
  }
}

Result
boolean
class java.lang.String
when .class syntax is used with primitive type. It returns a Class object that corresponds to the primitive type.

Another way of getting Class object is using Class.forName method. This static method verifies the fully-qualified class name in its argument. If the Class name is available, this method will return the corresponding Class object. This method has multiple forms. Also, this method can't be used with primitive type.

getName method returns the Class name of a Class object. This example demonstrates Class.forName(String className) method.
public class SampleClass{

  public static void main(String[] args){
    Class<?> classObj = Class.forName("java.lang.String");
    System.out.println(classObj.getName());
  }
}
For wrapper classes like Integer, Boolean and others; we can use the TYPE fields. Each wrapper class has TYPE field that returns a Class object of the specified wrapper class. This example demonstrates TYPE fields.
public class SampleClass{

  public static void main(String[] args){
    
    Class<?> classObj = Boolean.TYPE;
    System.out.println(classObj.getName());
  }
}

Result
boolean
There are other methods from the Class class that we can use to get nested Class objects. Although, these methods are not static. Thus, we need to have a Class object first. This example demonstrates getSuperClass, getClasses and getClasses methods.
public class SampleClass{

  public static void main(String[] args){
    Class<?> classObj = javax.swing.JButton.class;
    System.out.println("Super Class");
    System.out.println(classObj.getSuperclass().getName());
    System.out.println();
    
    Class<?>[] classes = Character.class.getClasses();
    System.out.println("Classes");
    for(Class<?> c : classes)
      System.out.println(c.getName());
    System.out.println();
    
    System.out.println("Declared Classes");
    for(Class<?> c : Character.class.getDeclaredClasses()){
      System.out.println(c.getName());
    }
    
  }
}

Result
Super Class
class javax.swing.AbstractButton

Classes
class java.lang.Character$UnicodeBlock
class java.lang.Character$UnicodeScript
class java.lang.Character$Subset

Declared Classes
class java.lang.Character$CharacterCache
class java.lang.Character$UnicodeBlock
class java.lang.Character$UnicodeScript
class java.lang.Character$Subset
getSuperclass method returns superclass of the given class. In the example above, javax.swing.JButton is the given class and getSuperclass method returns javax.swing.AbstractButton which is the superclass of JButton.

getClasses method returns returns all the public classes, interfaces, and enums that are members of the class including inherited members. getDeclaredClasses returns all public and non-public classes, interfaces, and enums.

In the example above, CharacterCache is a private class in Character class. It's included in the result of getDeclaredClasses method but not included in the result of getClasses method.

To get declaring class, we use getDeclaringClass method. This example demonstrate that method.
import java.lang.reflect.*;

public class SampleClass{

  public static void main(String[] args) 
                     throws NoSuchFieldException{
    //Field class stores reflected information
    //regarding the specified field in a class
    Field f = System.class.getField("out");
    
    Class<?> classObj = f.getDeclaringClass();
    System.out.println(classObj.getName());
  }
}

Result
java.lang.System
out is a standard output stream in java and it's declared in the System class in java.lang package. Anonymous class is not considered as "declared" in a class. Although, we can still get the class where an anonymous class resides by invoking getEnclosingClass method. Take a look at this example.
public class SampleClass{
  static Object o = new Object(){};
  
  public static void main(String[] args){
    Class<?> classObj = o.getClass().getDeclaringClass();
    System.out.println(classObj);
    classObj = o.getClass().getEnclosingClass();
    System.out.println(classObj);
  }
}

Result
null
class SampleClass

Accessing Fields

A field is a class, interface, or enum with an associated value. Methods in the Field class can retrieve information about the field, such as its name, type, modifiers, and annotations.

This example demonstrates retrieving name, type, modifiers and annotations of a field.
import java.lang.reflect.*;
import java.lang.annotation.Annotation;

public class SampleClass{
  
  @Deprecated
  public static java.util.ArrayList<String> arlist;
  
  public static void main(String[] args)
                     throws NoSuchFieldException{
    //getField method returns a Field object where
    //information about the given field is stored.
    Field field = SampleClass.class.getField("arlist");
    System.out.println("Name: " + field.getName());
    System.out.println();
    
    Class<?> type = field.getType();
    System.out.println("Type: " + type.getName());
    System.out.println();
    
    Type genericType = field.getGenericType();
    System.out.println("Generic Type: " + genericType);
    System.out.println();
    
    int modifiers = field.getModifiers();
    System.out.println("Modifiers: " + modifiers);
    System.out.println("Public field? " + 
    Modifier.isPublic(modifiers));
    System.out.println("Static field? " + 
    Modifier.isStatic(modifiers));
    System.out.println();
    
    System.out.println("Annotations");
    System.out.println(field.getAnnotation(Deprecated.class));
    System.out.println(
    field.getAnnotation(SuppressWarnings.class));
    
  }
}

Result
Name: arList

Type: java.util.ArrayList

Generic Type: java.util.ArrayList<java.lang.String>

Modifiers: 9
Public field? true
Static field? true

Annotations
@java.lang.Deprecated(forRemoval=false, since="")
null
getName method in Field class returns the name of the field. getType method in Field class returns a Class object that stores the type of the field. getGenericType method returns a Type object that represents a generic type of the field if it's present in the Signature attribute of the Class object.Otherwise, this method will rollback to getType method. getModifiers returns an integer value that represents one or more modifiers.

Modifier class provides static methods and constants to decode class and member access modifiers. The sets of modifiers are represented as integers with distinct bit positions representing different modifiers. Bits that represent modifiers can be computed by using the OR(|) operator.

For example, Modifier.PUBLIC is represented by integer value of 8. Modifier.STATIC is represented by integer value of 1. 8|1 = 9. getAnnotation method returns an Annotation or one of its subclasses that holds the specified annotation if such annotation is applied to the field. Otherwise, the method returns null. Note that any annotation returned by this method is a declaration annotation. Also, getAnnotation only returns annotation with RetentionPolicy.RUNTIME.

Getting and Setting Field Values

We can get and set field values by using Field's getter and setter methods. This example demonstrates getting and setting field values via reflection.
import java.lang.reflect.*;

public class SampleClass{
  public static int num = 5;
  public String str = "String";
  
  public static void main(String[] args)
                     throws NoSuchFieldException,
                            IllegalAccessException{
    Field primitive = SampleClass.class.getField("num");
    int value = primitive.getInt(primitive);
    System.out.println("Integer Original Value: " + value);
    System.out.println();
    
    Field object = SampleClass.class.getField("str");
    SampleClass sc = new SampleClass();
    
    String s = (String)object.get(sc);
    System.out.println("String Original Value: " + s);
    System.out.println();
    
    System.out.println("Setting values...");
    System.out.println();
    
    primitive.setInt(primitive, 10);
    value = primitive.getInt(primitive);
    System.out.println("Integer Current Value: " + value);
    System.out.println();
    
    object.set(sc, new String("MyString"));
    s = (String)object.get(sc);
    System.out.println("String Current Value: " + s);
    System.out.println();
  }
}

Result
Integer Original Value: 5

String Original Value: String

Setting Values...

Integer Current Value: 10

String Current Value: MyString
Use get(Object obj) method when getting object-type fields. obj parameter is the given field which value will be returned by the method. For primitive-type fields, It's preferrable to use get methods that correspond to the type of the field.

In the example above, primitive variable is an int primitive. Thus, I used getInt method. If we use the regular get method with a primitive field, the return value will be wrapped into the corresponding wrapper class.

If we're getting a value from a non-static field, we need to instantiate the class where the field resides and then we can get the value of the field.

Use set(Object obj, Object value) method when setting object-type values. For primitive-type values, It's preferrable to use set methods that correspond to the type of the field.

If we're setting a value to a non-static field, we need to instantiate the class where the field resides and then we can set the value to the field.

If we wanna get/set private field value, we need to invoke getDeclaredField method instead of getField method. getDeclaredField method returns public and non-public declared(but not inherited fields) fields in the Class object whereas getField method only returns public fields(including inherited fields). This example demonstrates getting/setting private field value.
import java.lang.reflect.*;

public class SampleClass{
  private static int num = 5;
  
  public static void main(String[] args)
                     throws NoSuchFieldException,
                            IllegalAccessException{
    Field field = SampleClass.class.getDeclaredField("num");
    
    System.out.println("Original Value: " + field.getInt(field));
    field.setInt(field, 10);
    System.out.println("Current Value: " + field.getInt(field));
  }
}
To get all public fields in a class, use getFields method. To get all public and non-public fields, use getDeclaredFields method. Read their descriptions in the documentation. To know more about Field class, you can read this documentation.

If you're having a problem accessing fields. You may try to check the accessibility of a field by invoking canAccess(Object obj) method. If it returns false, we need to set the accessiblity of the field to true by invoking setAccessible(boolean flag) method. Here's an example:
SampleClass sc = new SampleClass(); Field field = sc.getClass().getField("field");
if(!field.canAccess(sc)) field.setAccessible(true); System.out.println(field.getInt(sc));

Accessing Methods

A method declaration includes the name, modifiers, parameters, return type, and list of throwable exceptions. The java.lang.reflect.Method class provides a way to obtain this information. This example demonstrates retrieving name, return type, modifiers, parameters and annotations of a method.
import java.lang.reflect.*;
import java.lang.annotation.Annotation;

public class SampleClass{
  
  @Deprecated
  public static <T extends String>
  String message(T msg1, T msg2)
         throws NullPointerException{
    return msg1.concat(msg2);
  }
  
  public static void main(String[] args){
    Method[] methods = SampleClass.class.getMethods();
    
    for(Method m : methods){
      if(!m.getName().equals("message"))
         continue;
      System.out.println("Name: " + m.getName());
      System.out.println();
      
      System.out.println("Annotations");
      for(Annotation a : m.getDeclaredAnnotations())
        System.out.println(a);
      System.out.println();
      int modifiers = m.getModifiers();
      
      System.out.println("Modifiers: "+modifiers);
      System.out.println("Is public? " + 
      Modifier.isPublic(modifiers));
      System.out.println("Is static? " + 
      Modifier.isStatic(modifiers));
      System.out.println();
      
      System.out.println("Parameter Count: " + 
      m.getParameterCount());
      System.out.println("Parameter Types");
      for(Class<?> param : m.getParameterTypes())
        System.out.println(param.getTypeName());
      System.out.println("Type Parameters");
      for(TypeVariable tv : m.getTypeParameters())
        System.out.println(tv.getName());
      System.out.println();
      
      System.out.println("Return Type: " + 
      m.getReturnType().getName());
      System.out.println();
      
      System.out.println("Exceptions");
      for(Class<?> exp : m.getExceptionTypes())
        System.out.println(exp.getName());
      
    }
  }
}

Result
Name: message

Annotations
@java.lang.Deprecated(forRemoval=false, since="")

Modifiers: 9
Is public? true
Is static? true

Parameter Count: 2
Parameter Types
java.lang.String
java.lang.String
Type Parameters
T

Return Type: java.lang.String

Exceptions
java.lang.NullPointerException
getMethods returns all methods including inherited methods. If you wanna only access declared method in a class, use getDeclaredMethods method. getName method in Method class returns the name of the method.

getDeclaredAnnotations method returns annotations that are directly present on this element. This method ignores inherited annotations. If you wanna include inherited annotations, use getAnnotations method.

getModifiers returns an integer value that represents one or more modifiers. Modifier class provides static methods and constants to decode class and member access modifiers. The sets of modifiers are represented as integers with distinct bit positions representing different modifiers. Bits that represent modifiers can be computed by using the OR(|) operator.

For example, Modifier.PUBLIC is represented by integer value of 8. Modifier.STATIC is represented by integer value of 1. 8|1 = 9. getParameterCount method returns the total number of parameters in a method.

m.getParameterTypes returns types of method parameters that are wrapped into Class objects. getTypeParameters method returns type parameters in a method that are wrapped into TypeVariable interface.

getReturnType method returns the return type of a method wrapped into a Class object. getExceptionTypes returns exceptions thrown in a method.

To get parameter names and other information regarding parameters, we can use the getParameters method. This method returns parameter information wrapped into a Parameter object. This example demonstrates getParameters method.
import java.lang.reflect.*;

public class SampleClass{
  
  public void message(String str, int num){
    System.out.println(str);
    System.out.println(num);
  }
  
  public static void main(String[] args){
    
    for(Method m : SampleClass.class.getMethods()){
      if(!m.getName().equals("message"))
        continue;
      System.out.println("Method Name: " + m.getName());
      for(Parameter param : m.getParameters()){
        System.out.println("Param Name: " + param.getName());
        System.out.println("Param Type: " + 
        param.getType().getName());
      }
    }
  }
}

Result
Method name: message
Param Name: arg0
Param Type: java.lang.String
Param Name: arg1
Param Type: int
In the result above, the names that we get are different from the parameter names in our source code. That's because .class files do not store formal parameter names by default. According to oracle:

However, .class files do not store formal parameter names by default. This is because many tools that produce and consume class files may not expect the larger static and dynamic footprint of .class files that contain parameter names. In particular, these tools would have to handle larger .class files, and the Java Virtual Machine (JVM) would use more memory. In addition, some parameter names, such as secret or password, may expose information about security-sensitive methods.

To store formal parameter names in a particular .class file, and thus enable the Reflection API to retrieve formal parameter names, compile the source file with the -parameters option to the javac compiler.


If we compile our source code above with -parameters flag like this:
javac SampleClass.java -parameters
The result is going to be:
Method name: message
Param Name: str
Param Type: java.lang.String
Param Name: num
Param Type: int

The "arg0" and "arg1" that we see in the result above are synthesized parameter names of the form argN, where N is the index of the parameter in the descriptor of the method which declares the parameter.

Another way of getting method is invoking getMethod method. This example demonstrates getMethod.
import java.lang.reflect.*;

public class SampleClass{
  
  public void meth(String msg){
    System.out.println(msg);
  }
  
  public static void main(String[] args)
                throws NoSuchMethodException{
    Method m = 
    SampleClass.class.getMethod("meth", String.class);
    System.out.println("Method Name: " + m.getName());
  }
}

Result: Method Name: meth
getMethod(String name, Class<?>... parameterTypes) has two parameters. name parameter is the name of the method that we are looking for. parameterTypes is used to determine the number and types of parameters of the method that we're looking for. getMethod can obtain a public method(inherited or declared in the specified class).

If you wanna access a non-public method, use getDeclaredMethod method. Unlike getMethod, getDeclaredMethod can obtain a non-public method(declared in the specified class only).

Invoking Method

We can invoke a method via reflection by using invoke method. This example demonstrates invoke method.
import java.lang.reflect.*;

public class SampleClass{
  
  String invokeMe(String msg1, String msg2){
    System.out.println("invokeMe Method!");
    System.out.println("message1: " + msg1);
    System.out.println("message2: " + msg2);
    return msg1+msg2;
  }
  
  static void noParam(){
    System.out.println("noParam Method!");
  }
  
  public static void main(String[] args)
                throws IllegalAccessException,
                       InvocationTargetException{
    for(Method m : SampleClass.class.getDeclaredMethods()){
      if(m.getName().equals("noParam")){
        System.out.println("Invoke noParam");
        Object o = m.invoke(m);
        System.out.println("return value");
        System.out.println(o);
        System.out.println();
      }
      else if(m.getName().equals("invokeMe")){
        System.out.println("Invoke invokeMe");
        Object o = m.invoke(new SampleClass(),
                            "Hello","Everyone");
        System.out.println("return value");
        System.out.println(o);
        System.out.println();
      }
    }
      
  }
}

Result
Invoke noParam
noParam Method!
return value
null

Invoke invokeMe
invokeMe Method!
message1: Hello
message2: Everyone
return value
HelloEveryone
invoke(Object obj, Object... args) has two parameters. obj parameter is the object where the method we're going to invoke resides. args parameter is a varargs that accepts objects as method arguments. Number of arguments in args parameter must match the number of arguments in the method.

If you're having a problem accessing methods. You may try to set the accessibility of a method by invoking canAccess(Object obj) method. If it returns false, we need to set the accessiblity of the method to true by invoking setAccessible(boolean flag) method. Here's an example:
SampleClass sc = new SampleClass();
Method[] m = sc.getClass().getDeclaredMethods()
if(m.length != 0){ if(!m[0].canAccess(sc)) m[0].setAccessible(true); m[0].invoke(sc); }
Accessing Constructors

A constructor is used in the creation of an object that is an instance of a class. Typically it performs operations required to initialize the class before methods are invoked or fields are accessed. Constructors are never inherited.

Similar to methods, reflection provides APIs to discover and retrieve the constructors of a class and obtain declaration information such as the modifiers, parameters, annotations, and thrown exceptions.The java.lang.reflect.Constructor class provides a way to obtain this information.

This example demonstrates obtaining information regarding constructors such as modifiers, parameters, annotations and thrown exceptions.
import java.lang.reflect.*;
import java.lang.annotation.Annotation;

public class SampleClass{
  
  @Deprecated
  public SampleClass(String s1, Integer i1)
               throws IllegalStateException{
  }
  
  public static void main(String[] args){
    for(Constructor c : SampleClass.class.getConstructors()){
      System.out.println("Name: " + c.getName());
      System.out.println();
      
      System.out.println("Parameters");
      for(Parameter p : c.getParameters()){
        System.out.println("Param Name: " + p.getName());
        System.out.println("Param Type: " + 
        p.getType().getName());
      }
      System.out.println();
      
      System.out.println("Modifiers");
      System.out.println(c.getModifiers());
      System.out.println();
      
      System.out.println("Exceptions");
      for(Class<?> ex : c.getExceptionTypes())
        System.out.println(ex.getName());
      System.out.println();
      
      System.out.println("Annotations");
      for(Annotation a : c.getDeclaredAnnotations())
        System.out.println(a);
    }
  }
}

Result
Name: SampleClass

Parameters
Param Name: arg0
Param Type: java.lang.String
Param Name: arg1
Param Type: java.lang.Integer

Modifiers
1

Exceptions
java.lang.IllegalStateException

Annotations
@java.lang.Deprecated(forRemoval=false, since="")
getConstructors returns all public constructors in a class. If you wanna obtain non-public constructors, use getDeclaredConstructors method. getName method in Constructor class returns the name of the constructor. Other methods that I used here like getParameters, getExceptionTypes, etc. are explained in the "Accessing Methods" and "Invoking Methods" topics. You should read those topic first before reading this topic.

Another way of getting class's constructor is to use getConstructor method. This example demonstrates getConstructor method.
import java.lang.reflect.*;

public class SampleClass{
  String str;
  
  public SampleClass(String str){
    this.str = str;
  }
  
  public static void main(String[] args)
                throws NoSuchMethodException{
    Constructor c = 
    SampleClass.class.getConstructor(String.class);
    
    System.out.println("Parameters");
      for(Parameter p : c.getParameters()){
        System.out.println("Param Name: " + p.getName());
        System.out.println("Param Type: " + 
        p.getType().getName());
      }
  }
}

Result
Parameters
Param Name: args0
Param Type: java.lang.String
getConstructor(Class<?>... parameterType) has a varargs parameter that is used to identify specific constructor that we wanna access. In the example ebove, I put String.class as argument. Meaning, I'm looking for a constructor in SampleClass with only a String stype parameter.

Creating New Instance

We can instantiate an object of a class via reflection by using newInstance(Object... initargs) method. This method invokes a constructor that has the same parameter signature as the parameters in the method. This example demonstrates newInstance method.
import java.lang.reflect.*;

public class SampleClass{
  private String str;
  private int num;
  
  SampleClass(String str, int num){
    this.str = str;
    this.num = num;
  }
  
  void display(){
    System.out.println(str + " | " + num);
  }
  
  public static void main(String[] args)
                throws InstantiationException,
                       IllegalAccessException,
                       InvocationTargetException{
    
    Constructor[] cons = 
    SampleClass.class.getDeclaredConstructors();
    
    SampleClass sc = null;
    if(cons.length != 0){
      sc = (SampleClass)cons[0].newInstance("Hello!",5);
    }
    else throw new NullPointerException();
    sc.display();
  }
}

Result
Hello! | 5
If you're having a problem accessing constructors. You may try to set the accessibility of a constructor by invoking setAccessible(boolean flag) method. Here's an example:
Constructor[] cons =
SampleClass.class.getDeclaredConstructors()
if(cons.length != 0){ cons[0].setAccessible(true); SampleClass sc = cons[0].newInstance("Hello!", 5); sc.display(); }
Accessing Arrays

An array is an object of reference type which contains a fixed number of components of the same type; the length of an array is immutable. Creating an instance of an array requires knowledge of the length and component type. Each component may be a primitive type (such as byte, int, or double), a reference type (such as String or Object), or an array. Multi-dimensional arrays are really just arrays which contain components of array type.

Arrays are implemented in the Java virtual machine. The only methods on arrays are those inherited from Object. The length of an array is not part of its type; arrays have a length field which is accessible via getLength() in java.lang.reflect.Array class.

This example demonstrates creating a new array, getting an array and accessing array elements via reflection.
import java.lang.reflect.*;

public class SampleClass{
  static String[] strArr;
  
  public static void main(String[] args)
                throws NoSuchFieldException,
                       IllegalAccessException{
    Field field = 
    SampleClass.class.getDeclaredField("strArr");
    
    Class<?> type = field.getType();
    if(type.isArray()){
      System.out.println("Array Name: " + 
      field.getName());
      System.out.println("Array Type: " + 
      type.getComponentType());
      
      //create new array
      Object o = 
      Array.newInstance(type.getComponentType(), 3);
      //set elements
      for(int i = 0; i < 3; i++)
        Array.set(o, i, String.valueOf("a"+i));
      //get elements
      System.out.println("Reflection");
      System.out.println("length: " + Array.getLength(o));
      System.out.println("Elements");
      for(int i = 0; i < 3; i++)
        System.out.println(Array.get(o, i));
      System.out.println();
     //assign our newly created array to strArr
     field.set(field,o);
     
     System.out.println("strArr");
     System.out.println("length: " + strArr.length);
     System.out.println("Elements");
     for(String s : strArr)
       System.out.println(s);
     System.out.println();
     
     //get array field
     String[] strArr2 = (String[])field.get(field);
     System.out.println("strArr2");
     System.out.println("length: " + strArr2.length);
     System.out.println("Elements");
     for(String s : strArr2)
       System.out.println(s);
    }
  }
}

Result
Array Name: strArr
Array Type: class java.lang.String
Reflection
length: 3
Elements
a0
a1
a2

strArr
length: 3
Elements
a0
a1
a2

strArr2
length: 3
Elements
a0
a1
a2
isArray() verifies if a type is an array type. Returns true if the type is an array. Otherwise, returns false. getComponentType(interchangeable with componentType method) method returns a Class object with component type of an array. Otherwise, returns null. ComponentType of an array is the type of element that we can put in the array. In the example above, the component type of strArr array is java.lang.String.

If you wanna obtain array type, use getType() method. This method returns array type in this format: braces+L+component-type. Example, [Ljava.lang.String represents a one-dimensional array with java.lang.String as its component type. [[Ljava.lang.String represents a two-dimensional array with java.lang.String as its component type.

newInstance(Class<?> componentType, int length) in set(Object array, int index, Object value) method in Array class creates a new array. componentType is the component type of the array and length is the number of elements in array.

set(Object array, int index, Object value) method in Array class sets a value in a specified index. array parameter is the array where we wanna set a value, index parameter is where the value is going to be placed and value is the value to be placed in the specified index in the specified array.

If you're setting primitive value to a primitive-type array, use the setter methods in Array class that correspond to primitives like setInt, setBoolean and others.

getLength in Array class returns the total length of an array. get(Object array, int index) method retrives a value in an array. array parameter is the array where the value is to be obtained. index parameter is the location of the value to be obtained. If you're getting primitive value from a primitive-type array, use getter methods in Arrayclass that correspond to primitives like getInt, getBoolean and others.

Working on Multi-Dimensional Arrays

Working on multi-dimensional arrays is similar to working on one-dimensional array. Note that multi-dimensional array is just an array inside an array. This example demonstrates creating and accessing multi-dimensional array.
import java.lang.reflect.*;

public class SampleClass{
  
  public static void main(String[] args){
    //2x1 array
    Object o = Array.newInstance(String[].class,2);
    Array.set(o,0,new String[]{"a","b"});
    Array.set(o,1,new String[]{"c","d"});
    
    Object row0 = Array.get(o,0);
    Object row1 = Array.get(o,1);
    
    System.out.println("Row1");
    for(String s : (String[])row0)
      System.out.println(s);
    System.out.println();
    
    System.out.println("Row2");
    for(String s : (String[])row1)
      System.out.println(s);
    System.out.println();
    
  }
}

Result
Row1
a
b

Row2
c
d
Another way of creating multi-dimensional array is by invoking the second form of newInstance method. newInstance(Class<?> componentType, int... dimensions) Creates a new array with the specified component type and dimensions. If componentType represents a non-array class or interface, the new array has dimensions.length dimensions and componentType as its component type.

If componentType represents an array class, the number of dimensions of the new array is equal to the sum of dimensions.length and the number of dimensions of componentType. In this case, the component type of the new array is the component type of componentType.

This example demonstrates the method.
import java.lang.reflect.*;

public class SampleClass{

  public static void main(String[] args){
    //2x2 array
    Object o = Array.newInstance(int.class,2,2);
    
    Object row1 = Array.get(o,0);
    Array.set(row1,0,2);
    Array.set(row1,1,4);
    
    Object row2 = Array.get(o,1);
    Array.set(row2,0,6);
    Array.set(row2,1,8);
    
    System.out.println("Row1");
    for(int i : (int[])row1)
      System.out.println(i);
    System.out.println();
    
    System.out.println("Row2");
    for(int i : (int[])row2)
      System.out.println(i);
    System.out.println();
  }
}
Result
Row1
2
4

Row2
6
8
Another example.
import java.lang.reflect.*;

public class SampleClass{
  public static String[] str =
  new String[1];
  
  public static void main(String[] args)
                throws NoSuchFieldException{
    Field field = SampleClass.class.getField("str");
    //1x2 array
    Object o = Array.newInstance(field.getType(),1,2);
    System.out.println(Array.getLength(o));
    
    Object o1 = Array.get(o,0);
    System.out.println(Array.getLength(o1));
    Array.set(o1,0,new String[]{"a","b","c"});
    Array.set(o1,1,new String[]{"d","e"});
    
    for(String s : (String[])Array.get(o1,0))
      System.out.println(s);
    for(String s : (String[])Array.get(o1,1))
      System.out.println(s);
    
  }
}

Result
1
2
a
b
c
d
e
Accessing Enums

An enum is a language construct that is used to define type-safe enumerations which can be used when a fixed set of named values is desired. All enums implicitly extend java.lang.Enum. Enums may contain one or more enum constants, which define unique instances of the enum type. An enum declaration defines an enum type which is very similar to a class in that it may have members such as fields, methods, and constructors (with some restrictions).

This example demonstrates accessing enums via reflection.
import java.lang.reflect.*;

enum Days{
MONDAY, TUESDAY, WEDNESDAY, 
THURSDAY, FRIDAY, SATURDAY, SUNDAY;

final boolean field1 = false;
final int field2 = 1;
}

public class SampleClass{
  public static Days day = Days.WEDNESDAY;
  
  public static void main(String[] args)
                throws ClassNotFoundException,
                       NoSuchFieldException,
                       IllegalAccessException{
    Class<?> c = Class.forName("Days");
    
    if(c.isEnum()){
      System.out.println("enum name: " + c.getName());
      
      int mod = c.getModifiers();
      System.out.println("Modifiers: " + mod);
      System.out.println("Is public? " + 
      Modifier.isPublic(mod));
      System.out.println("Is static? " + 
      Modifier.isStatic(mod));
      System.out.println("Is final? " + 
      Modifier.isFinal(mod));
      System.out.println();
      
      System.out.println("Constants");
      for(Object o : c.getEnumConstants())
        System.out.println(o);
      System.out.println();
      
      System.out.println("Fields");
      for(Field f : c.getDeclaredFields())
        if(f.isEnumConstant())
          System.out.println("Constant: " + f.getName());
        else
          System.out.println("Field: " + f.getName());
      System.out.println();
      
      Class<?> sc = SampleClass.class;
      Field enumField = sc.getField("day");
      if(enumField.getType().isEnum()){
        System.out.println("Enum field value");
        System.out.println(enumField.get(enumField));
        System.out.println("Set value");
        enumField.set(enumField, Days.SATURDAY);
        System.out.println("Enum field value");
        System.out.println(enumField.get(enumField));
      }
      
    }
  }
}

Result
enum name: Days
Modifiers: 16400
Is public? false
Is static? false
Is final? true

Constants
MONDAY
TUESDAY
WEDNESDAY
THURSDAY
FRIDAY
SATURDAY
SUNDAY

Fields
Constant: MONDAY
Constant: TUESDAY
Constant: WEDNESDAY
Constant: THURSDAY
Constant: FRIDAY
Constant: SATURDAY
Constant: SUNDAY
Field: field1
Field: field2
Field: $VALUES

Enum field value
WEDNESDAY
Set value
Enum field value
SATURDAY
isEnum method returns true if a Class object is an enum. Otherwise, returns false. getEnumConstants returns enum constants in the order they're declared in source code.

Take note that getDeclaredFields and getFields methods don't guarantee the order of enum constants. use getEnumConstants if enum constants are needed to be ordered.

isEnumConstant in Field class returns true if the field is an enum constants. Otherwise, returns false. We can also get and set fields with enum values by using get and set methods of Field class.

Java Reflection's Nest-Based Access Control Feature

Nest-Based feature is introduced in java 11. This feature links nested classes without creating bridge methods. Instead, this feature creates nest-based attributes in class files to determine the relationship between nested classes. Java adds access controls to java reflection in order to deal with the new feature.

a nest consists of a nest host and nest mates. Typically, nest host is the top-level class and nest mates are classes enclosed within a nest host. This example demonstrates methods that can access nest-based attributes.
import java.util.ArrayList;

public class SampleClass{

  public static void main(String[] args){
    
    Class<?> hostClass = Host.class;
    ArrayList<Class<?>> list =
    new ArrayList<>();
    
    System.out.println
    (hostClass.getName() + 
    " class nest mates/members");
    for(Class<?> c : hostClass.getNestMembers()){
      System.out.println(c.getName());
      list.add(c);
    }
    System.out.println();
    
    for(Class<?> c : list){
      System.out.println("Nest host of " + c.getName());
      System.out.println(c.getNestHost().getName());
      System.out.println
      (c.getName()+" nest mate of " + hostClass.getName());
      System.out.println(c.isNestmateOf(hostClass));
      System.out.println();
    }
  }
}

class Host{
  class ClassA{
    class ClassC{
    }
  }
  
  class ClassB{
  }
}

Host class nest mates/members
Host
Host$ClassB
Host$ClassA
Host$ClassA$ClassC

Nest host of Host
Host nest mate of Host
true

Nest host of Host$ClassB
Host
Host$ClassB nest mate of Host
true

Nest host of Host$ClassA
Host
Host$ClassA nest mate of Host
true

Nest host of Host$ClassA$ClassC
Host
Host$ClassA$ClassC nest mate of Host
true

getNestMembers returns an array of nest mates of a nest host. There are interesting things in the result above. First off, the "root" nest host(Host class) considers itself as its nest mate. This could mean that the nest host can access itself which is pretty normal.

Another interesting thing is that ClassC is a nest mate of Host class. You might ask why ClassA is not the nest host of ClassC. Note that inheritance is different from nest-based feature. Nest-based feature is about links or communication lines, if you will, between nested classes whereas inheritance is about relationships between classes.

That's why ClassC is not nest mate of ClassA even ClassC is a subclass of ClassA. The nest host of ClassC is Host because, in my opinion, ClassC requires to link to Host first and then Host links ClassC to ClassA.

getNestHost returns the nest host of a Class object that invokes this method. isNestmateOf method tests if the Class object that invokes this method is a nest mate of the given Class object in the argument. Returns true if the test is successful. Otherwise, returns false.

If we change the "root" nest host to a subclass of Host class, the result will change. Try changing this line:
Class<?> hostClass = Host.class;
To this:
Class<?> hostClass = ClassA.class;
And the result will be:
ClassA nest mates/members
ClassA

Nest host of ClassA
ClassA nest mate of ClassA
true
In the example above, the nest host and nest mate of ClassA is itself. That's because ClassA is the "root" nest host and "root" host considers itself as its nest host and nest mate. Although, ClassC is still not a nest mate of ClassA because in the class file of ClassC, Host class is the nest host of ClassC.

Troubleshooting

In this chapter, I'm gonna discuss some exceptions that we may encounter when using java reflection and how to avoid them.

Fields

Getter and setter methods of Field class methods throw IllegalArgumentException if an argument can't be referenced to a designated field. This example demonstrates this scenario.
import java.lang.reflect.*;

public class SampleClass{
  public static Integer num;
  
  public static void main(String[] args)
                throws NoSuchFieldException,
                       IllegalAccessException{
    Field f = SampleClass.class.getField("num");
    f.setInt(f,4);
  }
}

Result
IllegalArgumentException...
To avoid this problem, use the appropriate set method for a specific type. Try changing this:
f.setInt(f,4);
To this:
f.set(f, Integer.valueOf(4));
And IllegalArgumentException in the example above will be gone. Integer is an Object. Thus, we should use set method.

Getter and setter methods of Field class support upcasting(widening) but don't support downcasting(narrowing). An exception will be thrown if we try to do downcasting. Take a look at this example.
import java.lang.reflect.*;

public class SampleClass{
  public static int num = 0;
  
  public static void main(String[] args)
                        throws Exception{
    Field f = SampleClass.class.getField("num");
    //upcasting works
    f.setShort(f,(short)4);
    System.out.println(SampleClass.num);
    //downcasting doesn't work
    //IllegalArgumentException
    f.setLong(f,2L);
  }
}

Result
4
IllegalArgumentException...
If we try to obtain field from a non-public Field object using getField method, java will throw NoSuchFieldException. Take a look at this example.
import java.lang.reflect.*;

public class SampleClass{
  private static Integer num;
  
  public static void main(String[] args)
                throws NoSuchFieldException,
                       IllegalAccessException{
    Field f = SampleClass.class.getField("num");
    f.set(f, Integer.valueOf(4));
  }
}

Result
NoSuchFieldException...
Use getDeclaredField method when accessing non-public fields.

IllegalAccessException may be thrown if an inaccessible private field is being accessed. In java versions prior to java 11, it is recommended to enable field accessibility by invoking setAccessible method. This method accepts a boolean value as an argument. set the argument to true to enable accessibility. Otherwise, set it to false.

IllegalAccessException or IllegalArgumentException may be thrown if a final public or non-public field value is accessed. Take a look at this example.
import java.lang.reflect.*;

public class SampleClass{
  public final int num = 4;
  
  public static void main(String[] args)
                throws NoSuchFieldException,
                       IllegalAccessException{
    Field f = SampleClass.class.getField("num");
    f.setInt(f, 4);
    //This will throw an exception too
    //Object o = f.getInt(f);
  }
}

Result
IllegalArgumentException...
Methods

NoSuchMethodException is thrown if we try to look for a method with Non-reifiable parameterized type. Take a look at this example.
import java.lang.reflect.*;

public class SampleClass{
  
  public static void main(String[] args)
                throws NoSuchMethodException{
    Class<?> c = 
    new ClassA<String>().getClass();
    Method m = c.getMethod("meth",String.class);
  }
}

class ClassA<T>{
  public void meth(T o){
    System.out.println(o);
  }
}

Result
NoSuchMethodException...
Remember that non-reifiable type like T and the type that is going to replace T which is String will be erased due to type erasure. Instead, T will be replaced with its upper bound which is Object type.

Try changing this:
Method m = c.getMethod("meth",String.class);
To this:
Method m = c.getMethod("meth",Object.class);
And NoSuchMethodException in the example above will be gone.

Reflection follows methods access restriction that is applied to direct invocation of methods. For example, private methods can only be accessed by its enclosing class and that enclosing class members. Take a look at this example.
import java.lang.reflect.*;

public class SampleClass{

  public static void main(String[] args)
                throws NoSuchMethodException,
                       IllegalAccessException,
                       InvocationTargetException{
    ClassA<String> ca = new ClassA<>();
    Class<?> c = ca.getClass();
    Method m = c.getDeclaredMethod("meth",Object.class);
    m.invoke(ca,"Hello");
  }
}

class ClassA<T>{
  private void meth(T o){
    System.out.println(o);
  }
}

Result
IllegalAccessException...
meth can't be invoked outside ClassA because meth is a private method. We can use setAccessible method to suppress IllegalAccessException in this situation. Add this line of code before invoking the method:
m.setAccessible(true)
Put it like this:
m.setAccessible(true);
m.invoke(ca,"Hello");
If it succeeds, java will print(without quotes) "Hello" on the console.

invoke method has been retrofitted to be a variable-arity method(method that takes one or more arguments). Although, sometimes, this can cause ambiguity. Take a look at this example.
Source: The Java™ Tutorials

import java.lang.reflect.Method;

public class MethodTroubleToo {
    public void ping() { System.out.format("PONG!%n"); }

    public static void main(String... args) {
	try {
	    MethodTroubleToo mtt = new MethodTroubleToo();
	    Method m = MethodTroubleToo.class.getMethod("ping");

 	    switch(Integer.parseInt(args[0])) {
        
        // works
	    case 0:
  		m.invoke(mtt);
		break;
        
        // works (expect compiler warning)
	    case 1:
 		m.invoke(mtt, null); 
		break;
        
        // IllegalArgumentException
	    case 2:
		Object arg2 = null;
		m.invoke(mtt, arg2);
		break;
        
        // IllegalArgumentException
	    case 3:
		Object arg4 = new Object[0];
		m.invoke(mtt, arg4);           
		break;
        
        // works
	    case 4:
		m.invoke(mtt, new Object[0]);
		break;
        
	    default:
		System.out.format("Test not found%n");
	    }

        // production code should 
        //handle these exceptions more gracefully
	} catch (Exception x) {
	    x.printStackTrace();
	}
    }
}
In the example above, case 0 works fine because it met the right criteria for invoking ping method. case 1 works but it's kinda ambiguous because of the null argument. This case works because we're assigning a null value to a varargs parameter.

When we directly assign a null value to a varargs parameter, the varargs is "ambiguously zero-length". Thus, java doesn't consider a null value as varargs. Moreover, java still thinks something is wrong.

case 2 doesn't work because null is assigned to an Object-type variable. Once we put a variable in the argument-list of a method, this variable will be considered as a regular argument even the value of that variable is null. This explanation is also applicable to case 3.

case 4 works because we're assigning a zero-length array to varargs. Zero-length array is equivalent to zero-length varargs.

An InvocationTargetException wraps all exceptions (checked and unchecked) produced during the invocation of a method. Take a look at this example.
import java.lang.reflect.*;

public class SampleClass{

  public float compute(int num){
    System.out.println("Invoke compute method");
    return num/0;
  }
  
  public static void main(String[] args)
                throws NoSuchMethodException,
                       IllegalAccessException,
                       InvocationTargetException{
    Class<?> c = SampleClass.class;
    Method m = c.getMethod("compute", int.class);
    Object f = m.invoke(new SampleClass(),5);
    System.out.println("result: " + f);
  }
}

Result
Invoke compute method
InvocationTargetException...
...
Caused by: java.lang.ArithmeticException: / by zero
...
If an InvocationTargetException is thrown, the method was invoked. Diagnosis of the problem would be the same as if the method was called directly and threw the exception. This exception does not indicate a problem with the reflection package or its usage.

Constructors

Any exception(checked or unchecked) that is thrown during object instantiation using newInstance method will be wrapped into an InvocationTargetException.
import java.lang.reflect.*;

public class SampleClass{
  int num = 0;
  
  public SampleClass(int num){
    this.num = num/0;
  }
  
  public static void main(String[] args)
                throws InstantiationException,
                       NoSuchMethodException,
                       IllegalAccessException,
                       InvocationTargetException{
    Constructor c = 
    SampleClass.class.getConstructor(int.class);
    SampleClass sc = (SampleClass)c.newInstance(5);
  }
}

Result
InvocationTargetException...
...
Caused by: java.lang.ArithmeticException: /by zero
...
If an InvocationTargetException is thrown, the constructor was invoked. Diagnosis of the problem would be the same as if the constructor was called directly and threw the exception. This exception does not indicate a problem with the reflection package or its usage.

Sometimes, we may have problems regarding locating and invoking constructor. This example demonstrates some of the problems that we may encounter.
public class SampleClass{
  
  SampleClass(){
    System.out.println
    ("Constructor with 0 parameters");
  }
  
  SampleClass(Integer num){
    System.out.println("Integer: " + num);
  }
  
  SampleClass(Object obj){
    System.out.println("Object: " + obj);
  }
  
  SampleClass(String str){
    System.out.println("String: " + str);
  }
  
  public static void main(String[] args)
                throws Exception{
    
    Class<?> c = SampleClass.class;
    
    int num = 0;
    switch(num){
      case 0:
      //IllegalArgumentException: wrong 
      //number of arguments
      c.getDeclaredConstructor().
      newInstance("Hello");
      break;
      
      case 1:
      //NoSuchMethodException
      //no constructor with int parameter
      c.getDeclaredConstructor(int.class).
      newInstance(4);
      break;
      
      case 2:
      //Result: Object: Hi
      //Doesn't perform method/constructor
      //resolution
      c.getDeclaredConstructor(Object.class).
      newInstance("Hi");
      break;
      
      default:
      System.err.println("Invalid number!");
      break;
    }
  }
}
In case 0, We looked for a constructor without parameter. Then, we attempted to invoke a constructor with parameter. IllegalArgumentException is thrown because we tried to invoke a constructor that didn't correspond with the constructor that was returned by getConstructor method.

In case 1, we attemped to look for a constructor with int parameter. NoSuchMethodException is thrown because such constructor doesn't exist. Remember that int primitive is different from Integer class. If you expect java to autobox int value and invokes a constructor with Integer parameter, if won't happen because autoboxing doesn't work with reflection.

Try directly invoking the constructor like this:
new SampleClass(4);
and the result will be:
Integer: 4
This example shows one of the differences between invoking constructor directly and invoking constructor via reflection.

In case 2, constructor with Object parameter was invoked. Method/Constructor resolution doesn't work with reflection. Method/Constructor resolution is a mechanism that looks for a method/constructor that is mostly suitable for the given arguments. In the example above, the constructor with String parameter should have been invoked if the Method/Constructor resolution was working with reflection.

Try directly invoking the constructor like this:
new SampleClass("Hi");
and the result will be:
String: Hi
This example shows another difference between invoking constructor directly against invoking constructor via reflection.

Reflection follows constructor access restriction that is applied to direct invocation of constructors. For example, private constructors can only be accessed by its enclosing class and that enclosing class members. Take a look at this example.
public class SampleClass{
  
  public static void main(String[] args)
                        throws Exception{
    ClassA.class.getDeclaredConstructor().
    newInstance();
  }
}

class ClassA{
  private ClassA(){
    System.out.println
    ("ClassA Private Constructor!");
  }
}

Result
IllegalAccessException...
Class constructor can't be invoked outside ClassA because the constructor is a private method. We can use setAccessible method to suppress IllegalAccessException in this situation. Add this line of code before invoking the constructor:
m.setAccessible(true)
Put it like this:
Constructor c = ClassA.class.getConstructor();
c.setAccessible(true);
c.newInstance();
If it succeeds, the message in the constructor will be printed.

Arrays

Just like in Field class, Getter and setter methods of Array class methods throw IllegalArgumentException if an argument can't be placed in a particular array. This example demonstrates an attempt to set an int value to an index in an Integer-type array.
import java.lang.reflect.Array;

public class SampleClass{

  public static void main(String[] args)
                        throws Exception{
    Integer[] arr = new Integer[2];
    Array.setInt(arr,0,4);
  }
}

Result
IllegalArgumentException...
To solve this problem, use the appropriate method for a specific array-type. change this line of codes:
Array.setInt(arr,0,4);
To this:
Array.set(arr,0,Integer.valueOf(4));
And IllegalArgumentException will be gone. Integer is an Object. Thus, we should use set method.

Just like in Field class, Getter and setter methods of Array class support upcasting(widening) but don't support downcasting(narrowing). An exception will be thrown if we try to do downcasting. Take a look at this example.
import java.lang.reflect.Array;

public class SampleClass{

  public static void main(String[] args){
    int[] nums = new int[1];
    //upcasting works
    Array.setShort(nums,0,(short)2);
    System.out.println(nums[0]);
    //downcasting doesn't work
    //IllegalArgumentException
    Array.setLong(nums,0,4L);
  }
}

Result
2
IllegalArgumentException...
In reflection, If we get/set a value of an zero-length array, ArrayIndexOutOfBoundsException will be thrown. Take a look at this example.
import java.lang.reflect.Array;

public class SampleClass{

  public static void main(String[] args){
    int[] nums = new int[0];
    int num = Array.getInt(nums,0);
    System.out.println(num);
  }
}

Result
ArrayIndexOutOfBoundsException...
In common code, this problem occurs in a few cases. In reflection, you may encounter this in several cases. If you encounter ArrayIndexOutOfBoundsException, you may check the length of the array you're accessing by invoking getLength method in order to know if you're dealing with zero-length array or not.

Enums

Instantiation of enum types explicitly is forbidden in common coding and reflection. One of reasons why we can't is to preserve the uniqueness of enums. If we try to explicitly instantiate an enum type in reflection, IllegalArgumentException will be thrown. Take a look at this example.
import java.lang.reflect.*;

public class SampleClass{

  public static void main(String[] args)
                        throws Exception{
    Constructor[] cons = 
    Weekends.class.getDeclaredConstructors();
    
    for(Constructor c : cons){
      System.out.println("Name: " + c.getName());
      c.setAccessible(true);
      c.newInstance();
    }
  }
}

enum Weekends{
  SATURDAY, SUNDAY;
  
  Weekends(){
    System.out.println("Invoking...");
  }
}

Result
Name: Weekends
...IllegalArgumentException...
To avoid this problem, check first if the Class object that you wanna instantiate is an enum type by invoking isEnum method.

Another problem that we may encounter is setting an incompatible enum constant to a enum variable. This will throw IllegalArgumentException. Take a look at this example.
import java.lang.reflect.Field;

enum A1{
  A,B
}

enum A2{
  C,D
}

public class SampleClass{
  private static A1 a1 = A1.A;
  
  public static void main(String[] args)
                        throws Exception{
    Field f = 
    SampleClass.class.getDeclaredField("a1");
    f.set(f,A2.C);
  }
}

Result
IllegalArgumentException...
To avoid this problem, we can check if an enum constant can be assigned to an enum variable by invoking isAssignableFrom method. This method returns true if the given object type can be assigned to the object type in Field object. Otherwise, returns false. Let's modify the example above.
import java.lang.reflect.Field;

enum A1{
  A,B
}

enum A2{
  C,D
}

public class SampleClass{
  private static A1 a1 = A1.A;
  
  public static void main(String[] args)
                        throws Exception{
    Field f = 
    SampleClass.class.getDeclaredField("a1");
    if(A2.class.isAssignableFrom(A1.class))
      f.set(f,A2.C);
    else
      System.out.println("Incompatible!");
  }
}

Result
Incompatible!

Saturday, February 12, 2022

Java Tutorial: Exploring java.net.http Package

Chapters

Overview

java.net.http package provides high-level client interfaces to HTTP (versions 1.1 and 2) and low-level client interfaces to WebSocket. This package has classes that can be alternatives to URLConnection, HttpURLConnection and HttpsURLConnection.

I discussed HttpURLConnection and HttpsURLConnection in this article. One of the advantages of this package over URLConnection is that this package can asynchronously handle HTTP requests and responses.

HttpRequest, HttpClient and HttpResponse

HttpRequest is used to create a request that is sent to a server. HttpClient is used to send request to a server and get server's response to the request. HttpResponse holds response that has been sent by server to a client.

First off, we need to create a HttpRequest and HttpClient. HttpResponse can't be created directly. It's created once HttpClient retrieves server's response. To create a HttpRequest and HttpClient, we need to created builders by invoking newBuilder() method. Once we're done building them, we invoke their build() methods.

If you wanna try the example below, you need a web server like apache web server. You can install XAMPP to easily install apache web server. Once you have apache web server, start the server, create a directory and name it as "test" in your document root and create a php file there. Name the php file as "url_con_test_get.php". Then, put these codes in the php file:
<?php
echo str_shuffle($_GET['text']);
?>
This example demonstrates HttpRequest, HttpClient and HttpResponse.
import java.net.http.*;
import java.net.*;
import java.io.IOException;

public class SampleClass{

  public static void main(String[] args)
                        throws Exception{
    //Request
    HttpRequest request = HttpRequest.
    newBuilder(new URI("http://localhost/"+
    "test/url_con_test_get.php?text=myText")).
    version(HttpClient.Version.HTTP_2).
    headers("Content-Type", 
            "text/plain;charset=UTF-8").
    GET().
    build();
    
    //Request-response handler
    HttpClient handler = HttpClient.
    newBuilder().build();
    
    HttpResponse<String> response = 
    handler.send
    (request, 
     HttpResponse.BodyHandlers.ofString());
    System.out.println("response: " + response.body());
  }
}

Result(may vary)
response: temyxT
First off, to create a HttpRequest, we invoke HttpRequest.newBuilder method. This method returns HttpRequest.Builder instance. newBuilder has three forms:

newBuilder() - Creates an HttpRequest builder.
newBuilder(HttpRequest request, BiPredicate<String,String> filter) - Creates a Builder whose initial state is copied from an existing HttpRequest.
newBuilder(URI uri) - Creates an HttpRequest builder with the given URI.


In the example above, I used the third form of newBuilder method. version method in HttpRequest.Builder sets the preferred HttpClient.Version for this request. This method returns HttpRequest.Builder with the specified HttpClient.Version. Take note that if the specified version is HttpClient.Version.HTTP_2 and HTTP version 2 is not supported, the specified version will rollback to HTTP version 1.1. Adding version in HttpRequest is optional.

headers method in HttpRequest.Builder adds the given key-value pairs to the set of headers for the request. HTTP header consists of a key-value pair. key denotes header name and value denotes the value of the header. headers method has another variant which is the header method. header method only accepts a single key-value pair. Although, it can be invoked multiple times. Adding headers in HttpRequest is optional.

GET method Sets the request method of builder to GET. build() builds an HttpRequest with the specified version, headers, request method, etc. Just a reminder, a GET query with a single value looks like this: ?name=value
a GET query with multiple values looks like this: ?name1=value1&name2=value2
Request method is optional.

Next, to create a HttpClient, we invoke HttpClient.newBuilder method. This method returns HttpClient.Builder instance. If we just want to create a HttpClient with default settings, we can invoke HttpClient.newHttpClient method. This syntax HttpClient.newBuilder().build() is equivalent to invoking HttpClient.newHttpClient.

Next, once we have created a HttpRequest and HttpClient, we can invoke send method. This method requires HttpRequest and HttpResponse.BodyHandler arguments. HttpResponse.BodyHandler<T> is a handler for response bodies.

The class HttpResponse.BodyHandlers provides implementations of many common body handlers. HttpResponse.BodyHandlers.ofString() method indicatees that the response body is going to be String type.

send method has another variant which is the sendAsync method. sendAsync method is used for asynchronous request-response tasks. send method is used for synchronous request-response task.

Next, once send method returns a HttpResponse, we can get the response body by invoking the body() method of HttpResponse.

Next, let's change the request method to POST. If you wanna try the example below, you need a web server like apache web server. You can install XAMPP to easily install apache web server. Once you have apache web server, start the server, create a directory and name it as "test" in your document root and create a php file there. Name the php file as "url_con_test_post.php". Then, put these codes in the php file:
<?php
echo str_shuffle($_POST['text']);
?>
This example demonstrates POST request method.
import java.net.http.*;
import java.net.*;
import java.io.IOException;
import java.time.Duration;

public class SampleClass{

  public static void main(String[] args)
                        throws Exception{
    //Request
    HttpRequest request = HttpRequest.
    newBuilder(new URI("http://localhost/"+
    "test/url_con_test_post.php")).
    timeout(Duration.ofSeconds(5)).
    version(HttpClient.Version.HTTP_2).
    headers("Content-Type", 
            "application/x-www-form-urlencoded").
    //or "text1=String1&text2=String2"
    //for multiple key-value pairs
    POST(HttpRequest.BodyPublishers.
         ofString("text=String")).
    build();
    
    //Request-response handler
    HttpClient handler = HttpClient.
    newBuilder().build();
    
    HttpResponse<String> response = 
    handler.send
    (request, 
     HttpResponse.BodyHandlers.ofString());
    System.out.println("response: " + response.body());
  }
}

Result(may vary)
ngSrti
application/x-www-form-urlencoded is used to handle POST data. There's another Content-Type value that handles POST data and that's the multipart/form-data. I think this discussion has a good explanation regarding the difference of these two Content-Type values.

timeout method sets a timeout for this request. If the response is not received within the specified timeout then an HttpTimeoutException is thrown from HttpClient::send or HttpClient::sendAsync completes exceptionally with an HttpTimeoutException. Setting timeout is optional.

HttpRequest.BodyPublishers contains implementations of BodyPublisher that implement various useful publishers, such as publishing the request body from a String, or from a file. Request body is not limited to String, we can create a request body for bytes, input stream or even a file. You can read the examples about request body types in the documentation.

Handling Redirects

Handling redirects here is similar to handling redirects in HttpURLConnection. You may wanna look at this article regarding handling redirects in HttpURLConnection.

This example demonstrates handling redirects manually.
import java.net.http.*;
import java.net.*;
import java.io.IOException;
import java.time.Duration;
import java.util.Optional;

public class SampleClass{

  public static void main(String[] args)
                        throws Exception{
    URI uri = URI.create("http://www.twitter.com");
    boolean checkRedirect = true;
    
    HttpRequest request = null;
    HttpClient handler = null;
    HttpResponse<Void> response = null;
    while(checkRedirect){
      //Request
      request = HttpRequest.
      newBuilder(uri).
      timeout(Duration.ofSeconds(5)).
      build();
      
      //HttpClient
      handler = HttpClient.
      newBuilder().
      followRedirects(HttpClient.Redirect.NEVER).
      build();
      
      response = handler.send(request,
      HttpResponse.BodyHandlers.discarding());
      
      if(response.statusCode() == 
         HttpURLConnection.HTTP_MOVED_PERM ||
         response.statusCode() ==
         HttpURLConnection.HTTP_MOVED_TEMP ||
         response.statusCode() == 
         HttpURLConnection.HTTP_SEE_OTHER){
         
         Optional<String> redirect =
         response.headers().firstValue("Location");
         System.out.println
         ("Redirect URL: " + redirect.get());
         uri = URI.create(redirect.get());
         
      }else checkRedirect = false;
    } 
  }
}

Result
Redirect URL: https://www.twitter.com
Redirect URL: https://twitter.com
followRedirects in HttpClient.Builder specifies whether requests will automatically follow redirects issued by the server. HttpClient has three policies regarding following redirects and they can be found in HttpClient.Redirect enum.

HttpClient.Redirect.NEVER means that HttpClient will never automatically follow redirects. If you wanna automatically follow redirects you can use HttpClient.Redirect.ALWAYS. Careful thougn, Following redirects can be dangerous.

HttpResponse.BodyHandlers.discarding method returns a response body handler that discards the response body. Since we don't need response body in the example above, it's alright to discard it. headers method in HttpResponse returns the response headers. This method return a HttpHeaders instance.

firstValue method returns the first value of a header. Some HTTP headers can have multiple values. Location header only has one value which is the redirect URL.

Sending HTTP Requests Asynchronously

If you wanna try the example below, you need a web server like apache web server. You can install XAMPP to easily install apache web server. Once you have apache web server, start the server, create a directory and name it as "test" in your document root and create a php file there. Name the php file as "url_con_test_get.php". Then, put these codes in the php file:
<?php
echo str_shuffle($_GET['text']);
?>
To send requests asynchronously, we will use the sendAsync method from HttpClient. This example demonstrates sendAsync method.
import java.net.http.*;
import java.net.*;
import java.io.IOException;
import java.time.Duration;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.ArrayList;

public class SampleClass{

  public static void main(String[] args)
                     throws InterruptedException,
                            ExecutionException{
    int ln = 2;
    
    URI[] uris = new URI[ln];
    
    uris[0] = URI.create("http://localhost/"+
    "test/url_con_test_get.php?text=String1");
    uris[1] = URI.create("http://localhost/"+
    "test/url_con_test_get.php?text=String2");
    
    HttpRequest[] requests = new HttpRequest[ln];
    
    //Request-response handler
    HttpClient handler = HttpClient.
    newHttpClient();
    
    ArrayList
    <CompletableFuture<HttpResponse
    <String>>> responses =
    new ArrayList<>();
    
    //requests
    for(int i = 0; i < ln; i++){
      requests[i] = 
      HttpRequest.newBuilder(uris[i]).
      headers("Content-Type", 
            "text/plain;charset=UTF-8").
      GET().build();
    }
    
    //responses
    for(int i = 0; i < ln; i++){
      responses.add
      (handler.sendAsync(requests[i],
      HttpResponse.BodyHandlers.ofString()));
    }
    
    for(
    CompletableFuture
    <HttpResponse<String>> r : responses)
      System.out.println(r.get().body());
  }
}

Result(may vary)
irn1tSg
rSgin2t
If you're not familiar with CompletableFuture class, I explained the class in this article. If you wanna implement your own executor, invoke executor method of HttpClient.Builder. For example:
ExecutorService myExecutor...
...
HttpClient handler = HttpClient.
newBuilder().
executor(myExecutor).
build();
The default executor that HttpClient uses is java.util.concurrent.Executors.newCachedThreadPool(). Also, Take note that Flow API has been implemented to HttpRequest, HttpClient and HttpResponse. HttpRequest has BodyPublisher whereas HttpResponse has BodySubsriber. HttpClient is acting like Flow.processor. I explained Flow API in article.

Java provides HttpRequest.BodyPublishers and HttpResponse.BodySubscribers that contain useful implementations of BodyPublisher and BodySubscriber. Although, you can create your own implementations if the implementations that java provide don't suffice.

Handling Cookies

To handle cookies, first off, we need to create a CookieManager. Then, invoke cookieHandler method of HttpClient and put your cookie manager there as argument. You should read this article regarding handling of cookies using URLConnection because I'm gonna implement a similar approach to this example.
import java.net.http.*;
import java.net.*;
import java.time.Duration;
import java.util.List;

public class SampleClass{
  
  public static void main(String[] args)
                        throws Exception{
    URI uri = URI.create("https://www.google.com");
    
    HttpRequest request = 
    HttpRequest.newBuilder(uri).
    timeout(Duration.ofSeconds(5)).
    build();
    
    HttpClient handler =
    HttpClient.newBuilder().
    cookieHandler(new CookieManager()).
    build();
    
    HttpResponse response = handler.send(request,
    HttpResponse.BodyHandlers.discarding());
    
    System.out.println("Response Status Code: "
    + response.statusCode());
    System.out.println();
    
    CookieManager cm = 
    (CookieManager)handler.
    cookieHandler().get();
    
    CookieStore store = cm.getCookieStore();
    List<HttpCookie> cookieList =
    store.getCookies();
    
    for(HttpCookie cookie : cookieList){
      System.out.println
      ("Domain: " + cookie.getDomain());
      System.out.println
      ("Cookie Name: " + cookie.getName());
      System.out.println
      ("Max Age: " + cookie.getMaxAge());
      System.out.println();
    }
    
  }
}

Result(may vary)
Response Status Code: 200

Domain: .google.com
Cookie Name: 1P_JAR
Max Age: 2592003

Domain: .google.com
Cookie Name: NID
Max Age: 15811203
cookieHander method returns an Optional<CookieHandler> instance. The CookieHandler in the Optional instance is the CookieManager that we assigned in cookieHandler of HttpClient.Builder.

HTTP/2 Push Promise

Push Promise is a new feature in HTTP/2(HTTP version 2) that allows server to push resources to the browser without making clients request for them. Push promise increases performance such as page loading. Java supports handling Push Promises via sendAsync method. I think this article has a good explanation regarding push promise. You should read it.

This example demonstrates handling push promise.
import java.net.http.*;
import java.net.*;
import java.time.Duration;
import java.util.function.Function;
import java.util.concurrent.CompletableFuture;

public class SampleClass{

  public static void main(String[] args)
                        throws Exception{
    URI uri = 
    URI.create("https://http2.golang.org/serverpush");
  
    HttpRequest pageRequest = 
    HttpRequest.newBuilder(uri).
    timeout(Duration.ofSeconds(5)).
    build();
    
    HttpClient handler =
    HttpClient.newBuilder().
    version(HttpClient.Version.HTTP_2).
    build();
    
    HttpResponse.PushPromiseHandler<String> pp =
    (HttpRequest initiatingRequest, 
     HttpRequest pushPromiseRequest, 
     Function<HttpResponse.BodyHandler<String>,
     CompletableFuture<HttpResponse<String>>> acceptor)
     -> {
      acceptor.apply(HttpResponse.BodyHandlers.ofString()).
      thenAccept(respo -> {
        System.out.println("Pushed Response: " + respo.uri());
        System.out.println("Headers: " + respo.headers());
        System.out.println();
      });
      
      System.out.println
      ("Promise Request: " + pushPromiseRequest.uri());
      System.out.println
      ("Request Headers: " + pushPromiseRequest.headers());
    };
    
    CompletableFuture<HttpResponse<String>> response = 
    handler.sendAsync(pageRequest,
    HttpResponse.BodyHandlers.ofString(),pp);
    
    System.out.println
    ("Response Status Code: " + response.get().statusCode());
    System.out.println("Requested Page");
    System.out.println(response.get().body());
    
  }
}
HttpResponse.PushPromiseHandler has two methods. applyPushPromise method is an abstract method where push promise resources is being received. This is the method that is defined in pp variable.

of method is a static method that returns a push promise handler that accumulates push promises, and their responses, into the given map.

Tuesday, February 8, 2022

Java Tutorial: HttpURLConnection and HttpsURLConnection

Chapters

HttpURLConnection

HttpURLConnection is a URLConnection with support for HTTP-specific features. See the spec for details. This class is a subclass of URLConnection. You should be knowledgeable about URLConnection first before reading this tutorial. I already discussed URLConnection in this article.

This example demonstrates HttpURLConnection.
import java.net.*;

public class SampleClass{

  public static void main(String[] args)
                        throws Exception{
                        
    URL url = new URL("https://www.google.com");
    HttpURLConnection huc = 
    (HttpURLConnection)url.openConnection();
    
    System.out.println
    ("Request Method: " + huc.getRequestMethod());
    System.out.println
    ("Response Code: " + huc.getResponseCode());
    System.out.println
    ("Response Message: " + huc.getResponseMessage());
    System.out.println
    ("Using Proxy? " + huc.usingProxy());
    
  }
}

Result
Request Method: GET
Response Code: 200
Response Message: OK
Using Proxy? False
The methods that I used above are pretty straightforward to understand. getRequestMethod() returns the request method of the domain like POST, GET, PUT, etc. You can find more about HTTP request methods in this article.

getResponseCode() returns the HTTP response code of the domain like 404, 302, 200, etc. getResponseMessage() returns the message associated with response code. You can find more information about response codes and messages in this article. usingProxy() returns a boolean value that denotes if the domain uses proxy or not.

Handling Redirects

We can use HttpURLConnection to handle redirects. Typically, redirects return response codes of Found(302), Moved Permanently(301) and See Other(303). More information about redirect response codes can be found in this article.

This example demonstrates on how to handle redirects manually.
import java.net.*;

public class SampleClass{

  public static void main(String[] args)
                      throws Exception{
    URL url = 
    new URL("http://www.twitter.com");
    HttpURLConnection huc = 
    (HttpURLConnection)url.openConnection();
    
    HttpURLConnection.
    setFollowRedirects(false);
    huc.setInstanceFollowRedirects(true);
    System.out.println
    ("Original URL: " +huc.getURL());
    
    int responseCode = huc.getResponseCode();
    while(
    responseCode == HttpURLConnection.HTTP_MOVED_PERM ||
    responseCode == HttpURLConnection.HTTP_MOVED_TEMP ||
    responseCode == HttpURLConnection.HTTP_SEE_OTHER){
      
      URL redirectURL = 
      new URL(huc.getHeaderField("Location"));
      
      huc = (HttpURLConnection)redirectURL.openConnection();
      
      System.out.println("Redirect URL: " + huc.getURL());
      
      responseCode = huc.getResponseCode();
    }
  }
}

Result
Original URL: http://www.twitter.com
Redirect URL: https://www.twitter.com
Redirect URL: https://twitter.com
In the example above, we see that http://www.twitter.com has two redirects. getHeaderField("Location") returns the "Location" http header field value which is the redirect URL. setFollowRedirects(boolean set) is a static method that enables/disables automatic redirect follow of every HttpURLConnection in our program.

In the example above, I set it to false because I want to manually handle redirects. Manually handling redirects is good when dealing with a redirect from http to https and vice versa. setInstanceFollowRedirects(boolean followRedirects) determines if the instance that calls the method follows redirects. I set huc follow redirects flag to true because I wanna this instance to follow redirects.

Sometimes, a URLConnection will follow a redirect even we set setFollowRedirects() to false. Take a look at this example.
import java.net.*;

public class SampleClass{

  public static void main(String[] args)
                      throws Exception{
    URL url = new URL("https://dropapk.to");
    HttpURLConnection huc = 
    (HttpURLConnection)url.openConnection();
    
    HttpURLConnection.
    setFollowRedirects(false);
    huc.setInstanceFollowRedirects(true);
    
    System.out.println
    ("Original URL: " +huc.getURL());
    System.out.println
    ("Response Code: " + huc.getResponseCode());
    System.out.println
    ("Redirect URL: " +huc.getURL());
    
  }
}

Result
Original URL: https://dropapk.to
Response Code: 200
Redirect URL: https://drop.download
To prevent automatic redirect in this case, we need to set setInstanceFollowRedirects() method to false. Take a look at this example.
import java.net.*;

public class SampleClass{

  public static void main(String[] args)
                      throws Exception{
    URL url = new URL("https://dropapk.to");
    HttpURLConnection huc = 
    (HttpURLConnection)url.openConnection();
    huc.setInstanceFollowRedirects(false);
    
    System.out.println
    ("Original URL: " +huc.getURL());
    System.out.println
    ("Response Code: " + huc.getResponseCode());
    System.out.println
    ("Redirect URL: " +huc.getURL());
    
  }
}

Result
Original URL: https://dropapk.to
Response Code: 301
Redirect URL: https://dropapk.to
Even we set setFollowRedirects() to true, huc won't automatically follow redirects. Moreover, we can still get redirects manually since the response code is 301(Permanent Redirect).

Note that not every redirect has redirection response codes. For example, some fle hosting site returns 404 not found response code if the requested file is not found and redirects clients to another URL.


Reading From and Writing to a URL

Before testing the example below, assuming you're using apache server, create a directory in your document root directory and name it test. Then, create a php file and name it url_con_test.php. Then, put this in your php file.
<?php
echo str_shuffle($_GET['text']);
?>
This example demonstrates reading from and writing to a URL.
import java.net.*;
import java.io.*;

public class SampleClass{

  public static void main(String[] args)
                throws IOException,
                       MalformedURLException{
    URL url = new URL(
    "http://localhost/test/url_con_test.php?text=String");
    
    HttpURLConnection uc = 
    (HttpURLConnection)url.openConnection();
    
    uc.setRequestMethod("GET");
    uc.setDoOutput(true);
    uc.setConnectTimeout(5000);
    uc.setReadTimeout(3000);
    
    uc.addRequestProperty
    ("Accept-Language","en-US, en-BR; q=0.8");
    
    String stringToShuffle = 
    new String("Brainy Ghosts".getBytes(), "UTF-8");
    
    OutputStreamWriter out =
    new OutputStreamWriter(uc.getOutputStream());
    out.write("text="+stringToShuffle);
    out.close();
    
    BufferedReader in = 
    new BufferedReader(
      new InputStreamReader(
        uc.getInputStream()));
    
    String result = null;
    while ((result = in.readLine()) != null)
      System.out.println
      (new String(result.getBytes(), "UTF-8"));
    in.close();
  }
}

Result(may vary)
trngSi
setRequestMethod method sets the method for the URL request, one of: GET, POST, HEAD, OPTIONS, PUT, DELETE, TRACE are legal, subject to protocol restrictions. The default method is GET. The example above is similar to the example in this article that I've written. You can see the full explanation of the example there.

HttpsURLConnection

HttpsURLConnection extends HttpURLConnection with support for https-specific features. This class is recommended to be used when opening a connection with a URL that has HTTPS protocol. See this article and RFC 2818 for more details on the https specification.

This example demonstrates HttpsURLConnection.
import java.io.IOException;
import java.net.*;
import java.security.cert.Certificate;

import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLPeerUnverifiedException;

public class SampleClass{

  public static void main(String[] args)
                throws MalformedURLException,
                       SSLPeerUnverifiedException,
                       IOException{
  
    URL url = new URL("https://www.google.com");
    HttpsURLConnection https = 
    (HttpsURLConnection)url.openConnection();
    
    System.out.println
    ("URL: " +https.getURL());
    System.out.println
    ("Response Code: " + https.getResponseCode());
    System.out.println
    ("Cipher Suite: " + https.getCipherSuite());
    
    System.out.println();
    //dump domain certificates
    Certificate[] certs = https.getServerCertificates();
	for(Certificate cert : certs){
	   System.out.println
       ("Cert Type: " + cert.getType());
	   System.out.println
       ("Cert Hash Code: " + cert.hashCode());
	   System.out.println
       ("Cert Public Key Algorithm: " 
       + cert.getPublicKey().getAlgorithm());
	   System.out.println
       ("Cert Public Key Format: " 
       + cert.getPublicKey().getFormat());
	   System.out.println();
	}
  }
}

Result
URL: https://www.google.com
Response Code: 200
Cipher Suite: TLS_AES_256_GCM_SHA384

Cert Type: X.509
Cert Hash Code: 1129315267
Cert Public Key Algorithm: EC
Cert Public Key Format: X.509

Cert Type: X.509
Cert Hash Code: 266225904
Cert Public Key Algorithm: RSA
Cert Public Key Format: X.509

Cert Type: X.509
Cert Hash Code: 1081005260
Cert Public Key Algorithm: RSA
Cert Public Key Format: X.509