Reflection-based proxy engine to encapsulate sensitive objects in beans. The core idea is that given some object that contains information relevant in i.e. a script engine but contains sensitive information, you can easily encapsulate it.
For this example, we will only encapsulate one class, RealPerson.
The sensitive class contains some information we want to expose to a script engine, such as the name and address, but for the sake of this example not the age.
public class RealPerson {
public Integer getAge() {
return 19;
}
public String getName() {
return "Name";
}
public RealAddress getAddress() {
return new RealAddress();
}
}We can now create an interface to act as the object that will be initialized by the engine and can be exposed to the script engine.
public interface Person extends Wrappable {
@Wrap("getName")
String whatTheyCallMe();
}Two things are important here:
- The interface must extend Wrappable, this is not a technical limitation but rather a forced convention.
- The annotation on each method denotes which source method of the object we are encapsulating to invoke.
For example, we are invoking
RealPerson#getNameand binding it toPerson#whatTheyCallMe.
Next we need to initialize the engine.
GuardianContext context = new GuardianContext();
context.associate(RealPerson.class, Person.class);Finally, we can encapsulate the object:
Person person = context.wrap(new RealPerson());The underlying concepts work recursively. On top of RealPerson, also consider RealAddress.
On top of the already provided above code, let us define the address as:
public class RealAddress {
public Integer getHouse() {
return 123;
}
public String getStreet() {
return "Street";
}
public String getCity() {
return "City";
}
public RealCountry getCountry() {
return new RealCountry();
}
}Which shall be encapsulated by:
public interface Address extends Wrappable {
@Wrap("getStreet")
String getStreet();
@Wrap("getCity")
String getCity();
@Wrap("getCountry")
Country getCountry();
}After extending the context as shown below, the behaviour will work as expected:
GuardianContext context = new GuardianContext();
context.associate(RealPerson.class, Person.class);
context.associate(RealAddress.class, Address.class);In order to work as expected, these are the specifications.
- Every encapsulating object must be an interface extending
Wrappable. - Every method must be annotated by
@Wrapand the referencing method name must be provided, non-null and non-empty. - Every referenced method must meet the following criteria:
- The method must be
publicor otherwise accessible without the use ofMethod#setAccessible. - The method must return a non-primitive object, be of return type
voidor returnnull. - The method must not return an array of any dimension.
- The method must not have any parameters. By default, the method will be looked up using zero parameters. As such, any method with parameters will simply not be found.
- The method must be
- If the return value of a represented object returns a type that mismatches the return type of the encapsulating object's method, the following will happen:
- If the return value is a registered association in the context, the return value itself will also be transformed recursively.
- If not, an error will be thrown.
Coming soon.
The following features are currently planned, and specified in no order:
- Support for primitive data types as well as arrays.
- Methods that return
CollectionorMaphaving their elements (values forMap) scanned and if applicable mapped to other encapsulated objects. - Ability to choose how the reflection is performed and adding ReflectASM as an option.
- Adding support for setters. The details of this aren't clear yet.