I have created a method to dynamically build rest URI based on Bean properties, initially it was imperative then I have refactored it to functional style, it's my first time doing functional programming.
both imperative and functional are working as expected, but I am not happy by the functional readability, functional seams an over kill for this method or it could be because i am still a novice functional programmer!
How would you refactor this method to more clean functional way?
Or would you keep it Imperative?
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.lang.reflect.Method;
import org.springframework.beans.BeanUtils;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.web.util.UriComponentsBuilder;
public String functionalBuildRestUri() throws Exception {
final UriComponentsBuilder uriBuilder = UriComponentsBuilder.newInstance().scheme("https")
.host("foo.com").path("/offers");
//here is the functional
List<PropertyDescriptor> propDescList = Arrays.asList(BeanUtils.getPropertyDescriptors(getClass()));
//this part is readable and precis, but to enable it had to add 4 methods
propDescList.stream().filter(notClassProp())
.filter(notNullPropValue())
.collect(Collectors.toMap(PropertyDescriptor::getName, propValue()))//conversion to map doesn't feel good to me how can I avoid it?
.forEach(buildRestParam(uriBuilder));
return uriBuilder.build().toUriString();
}
public String imperativeBuildRestUri() throws Exception {
final UriComponentsBuilder uriBuilder = UriComponentsBuilder.newInstance().scheme("https")
.host("foo.com").path("/offers");
PropertyDescriptor[] propDescArray = BeanUtils.getPropertyDescriptors(getClass());
for (PropertyDescriptor propDesc : propDescArray) {
String propName = propDesc.getName();
if (!propName.equals("class")) {
Method getPropMethod = propDesc.getReadMethod();
Object propValue = getPropMethod.invoke(this);
if (propValue != null) {
if(propValue instanceof Date){
String dateStr = new SimpleDateFormat(DATE_FORMAT).format((Date)propValue);
uriBuilder.queryParam(propName, ":"+dateStr);
}else{
uriBuilder.queryParam(propName, propValue);
}
}
}
}
return uriBuilder.build().toUriString();
}
All Those methods has been added after functional refactoring
// I couldn't avoid being imperative here, how can we refactor it to more functional style
private BiConsumer<String, Object> buildRestParam(final UriComponentsBuilder uriBuilder) {
return (propName, propValue) -> {
if (propValue instanceof Date) {
String dateStr = new SimpleDateFormat(DATE_FORMAT).format((Date) propValue);
uriBuilder.queryParam(propName, ":" + dateStr);
} else {
uriBuilder.queryParam(propName, propValue);
}
};
}
private Predicate<? super PropertyDescriptor> notNullPropValue() {
return propDesc -> {
return propValue().apply(propDesc) != null;
};
}
private Predicate<? super PropertyDescriptor> notClassProp() {
return propDesc -> {
return !propDesc.getName().equals("class");
};
}
private Function<? super PropertyDescriptor, ? extends Object> propValue() {
return (propDesc) -> {
try {
return propDesc.getReadMethod().invoke(HotelOfferSearchCommand.this);
} catch (IllegalAccessException e) {
e.printStackTrace();
throw new RuntimeException(e);
} catch (IllegalArgumentException e) {
e.printStackTrace();
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
};
}
Most of the verbosity of the new code has nothing to do with functional programming. You have refactored the code to put every lambda expression into a method of it’s own, which, of course, destroys one of the main advantages of lambda expressions, the compactness. Even if code is complex enough to justify the creation of a method, that method should perform actual work, then, you could use a method reference where a function is required.
The methods further suffer from an unnecessary (even discouraged, as being in a return type) use of wild cards. You also used the verbose syntax parameter -> { return expression; } where parameter -> expression would be possible.
There are other issues, like unnecessarily creating a distinct catch clause for each exception type, when all do the same or wrapping the array into a List before creating the Stream instead of streaming over the array directly or having code duplication, the last point applies to both, the imperative variant and the functional one.
You can just write:
public String functionalBuildRestUri() throws Exception {
final UriComponentsBuilder uriBuilder = UriComponentsBuilder.newInstance()
.scheme("https").host("foo.com").path("/offers");
Function<PropertyDescriptor, Object> propValue = propDesc -> {
try { return propDesc.getReadMethod().invoke(HotelOfferSearchCommand.this); }
catch(ReflectiveOperationException e) { throw new RuntimeException(e); }
};
Arrays.stream(BeanUtils.getPropertyDescriptors(getClass()))
.filter(propDesc -> !propDesc.getName().equals("class"))
.filter(propDesc -> propValue.apply(propDesc) != null)
.forEach(propDesc -> {
Object value = propValue.apply(propDesc);
if (value instanceof Date)
value = ":"+new SimpleDateFormat(DATE_FORMAT).format(value);
uriBuilder.queryParam(propDesc.getName(), value);
});
return uriBuilder.build().toUriString();
}
without any extra method.
This might not be the best option, as there is indeed one flaw, the absence of a tuple or pair type to hold two values to be passed through the stream. By using Map.Entry as a stand-in, but not populating a Map, we can express the operation as
public String functionalBuildRestUri() throws Exception {
final UriComponentsBuilder uriBuilder = UriComponentsBuilder.newInstance()
.scheme("https").host("foo.com").path("/offers");
Function<PropertyDescriptor, Object> propValue = propDesc -> {
try { return propDesc.getReadMethod().invoke(HotelOfferSearchCommand.this); }
catch(ReflectiveOperationException e) { throw new RuntimeException(e); }
};
Arrays.stream(BeanUtils.getPropertyDescriptors(getClass()))
.filter(propDesc -> !propDesc.getName().equals("class"))
.map(propDesc -> new AbstractMap.SimpleImmutableEntry<>(
propDesc.getName(), propValue.apply(propDesc)))
.filter(entry -> entry.getValue() != null)
.forEach(entry -> {
Object value = entry.getKey();
if (value instanceof Date)
value = ":"+new SimpleDateFormat(DATE_FORMAT).format(value);
uriBuilder.queryParam(entry.getKey(), value);
});
return uriBuilder.build().toUriString();
}
or, alternatively
Arrays.stream(BeanUtils.getPropertyDescriptors(getClass()))
.filter(propDesc -> !propDesc.getName().equals("class"))
.map(propDesc -> new AbstractMap.SimpleImmutableEntry<>(
propDesc.getName(), propValue.apply(propDesc)))
.filter(entry -> entry.getValue() != null)
.map(e -> e.getValue() instanceof Date?
new AbstractMap.SimpleImmutableEntry<>(e.getKey(),
":"+new SimpleDateFormat(DATE_FORMAT).format(e.getValue())):
e)
.forEach(entry -> uriBuilder.queryParam(entry.getKey(), entry.getValue()));
With these two variants, the propValue function is evaluated only once per element instead of two times as in the first variant and your original code, where both, the check for null property value and the terminal operation evaluated it.
Note that there’s still room for improvement, e.g. there’s no reason to add the ":" after the format operation when you could make the colon a part of the format pattern string in the first place.
Whether this is an improvement over the loop, is something you have to decide yourself. Not every code has to be rewritten to a functional style. At least, as shown by the the examples above, it doesn’t have to be bigger than the imperative code…
Related
New to WebFlux, reactive, and handlers. I've got things "working", but am not understanding why following code is returning "okay" with empty body, vice "not found".
Clarification: The issue-of-concern is in the final return statement of DemoPOJOHandler.getById(). The "short-circuit" code works as expected (i.e., returns "Bad Request" status), but the "switchIfEmpty" path of the final return statement does not appear to get exercised if a DemoPOJORepo.getById(int) returns Mono.empty().
(Note: I've hacked up a list-based "repo" to avoid dealing with database while figuring out handlers and http return types.)
Router implementation ("/v1" is a set of annotation based RESTful endpoints)...
#Configuration
public class DemoPOJORouter {
#Bean
public RouterFunction<ServerResponse> route(DemoPOJOHandler requestHandler) {
return nest(path("/v2"),
nest(accept(APPLICATION_JSON),
RouterFunctions.route(RequestPredicates.GET("/DemoPOJO"), requestHandler::getAll)
.andRoute(RequestPredicates.GET("/DemoPOJO/{id}"), requestHandler::getById)
.andRoute(RequestPredicates.POST("/DemoPOJO"), requestHandler::add)));
}
}
Handler implementation has been "stripped down" to only the code in question. I have a feeling that much of the style is "still imperative", but I've attempted to put the reactive stuff where it "makes the most sense".
If I supply a bad value on the URI (i.e., "foo"), then I get the http "bad request" returned. But, never seem to get the "not found" that should be generated by "switchIfEmpty" if a validly formatted int value is supplied, but it does not map to an entry in the repo.
#Component
public class DemoPOJOHandler {
public static final String PATH_VAR_ID = "id";
private DemoPOJORepo repo = null;
public Mono<ServerResponse> getById(ServerRequest request) {
Mono<DemoPOJO> monoDemoPOJO = null;
Map<String, String> pathVariables = request.pathVariables();
int id = -1;
checkRepoRef(); // part of the list hack
// short-circuit if request doesn't contain id (should never happen)
if ((pathVariables == null)
|| (!pathVariables.containsKey(PATH_VAR_ID))) {
return ServerResponse.badRequest().build();
}
// short-circuit if bad id value
try {
id = Integer.parseInt(pathVariables.get(PATH_VAR_ID));
} catch(NumberFormatException e) {
return ServerResponse.badRequest().build();
}
// get entity by keyValue
monoDemoPOJO = repo.getById(id);
return monoDemoPOJO
.flatMap(demoPOJO -> ServerResponse.ok()
.contentType(MediaType.APPLICATION_JSON)
.syncBody(demoPOJO)
.switchIfEmpty(ServerResponse.notFound().build()));
}
}
Hack of a list-based repo to avoid dealing with data/APIs while working on handlers and http return types.
// local hack to avoid a database for testing
public class DemoPOJORepo {
private static DemoPOJORepo fpRepo = null;
private static int NUM_ROWS = 100;
private Map<Integer, DemoPOJO> fooPOJOMap;
private DemoPOJORepo() {
initMap();
}
public static DemoPOJORepo getInstance() {
if (fpRepo == null) {
fpRepo = new DemoPOJORepo();
}
return fpRepo;
}
public Mono<DemoPOJO> getById(int id) {
Mono<DemoPOJO> monoDP;
if (fooPOJOMap.containsKey(id)) {
monoDP = Mono.just(fooPOJOMap.get(id));
} else {
monoDP = Mono.empty();
}
return monoDP;
}
private Mono<Void> initMap() {
fooPOJOMap = new TreeMap<Integer, DemoPOJO>();
int offset = -1;
for(int ndx=0; ndx<NUM_ROWS; ndx++) {
offset = ndx + 1;
fooPOJOMap.put(offset, new DemoPOJO(offset, "foo_" + offset, offset+100));
}
return Mono.empty();
}
}
Your brackets are in the wrong place causing the swithIfEmpy to apply to the ServerResponse.ok() publisher not the monoDemoPOJO, replace the return with this and it should work:
return monoDemoPOJO
.flatMap(demoPOJO -> ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).syncBody(demoPOJO))
.switchIfEmpty(ServerResponse.notFound().build());
As I can see the code is right. The response code is Bad request because you are trying to convert "foo" to Integer, and when it throws an exception you are returning a Bad request response, so I think it works perfectly fine.
If you use an Integer id that is not present in your database then the answer must be a not found response
I think I need some feedback on my collection classes - still learning typescript and javascript and these implementations can surely be improved. I am looking forward to any suggestion. I think I do use the generic types in a useful way, any advice here would be appreciated.
The answer I am looking for most is removing the duplicate IHashTable definition from the end of both snippets and moving it to its own file, I cannot get that done it seems. I am even unsure if this IS an interface in the first place. It compiles and works this way, as far as I can see.
The collection types are incomplete and only define the basic most function at the moment. Once I am sure I use the language and its features correct the other functions should not be too difficult.
Here is my HashSet:
import { IHashable } from "./IHashable"
export class HashSet<T extends IHashable> {
private _items: HashTable<T>;
public constructor() {
this._items = {};
}
public Add(key: T): void {
let str: string = key.GetHash();
if (this._items[str] == null) {
this._items[str] = key;
}
else {
throw new RangeError("Key '" + str + "' already exists.");
}
}
public Contains(key: T): boolean {
let str: string = key.GetHash();
return this._items[str] != null;
}
}
interface HashTable<T> {
[key: string]: T;
}
I wonder if I can avoid the checking-before-adding in a way. The javascript-dictionary this relies on does allow duplicates, so to avoid them there is no other way than to check myself?
This is my Dictionary:
import { IHashable } from "./IHashable"
export class Dictionary<T1 extends IHashable, T2> {
private _items: HashTable<KeyValuePair<T1, T2>>;
public constructor() {
this._items = {};
}
public Add(key: T1, value: T2) {
let str: string = key.GetHash();
if (this._items[str] == null) {
let kvp: KeyValuePair<T1, T2> = new KeyValuePair(key, value);
this._items[str] = kvp;
}
else {
throw new RangeError("Key '" + str + "' already exists.");
}
}
public ContainsKey(key: T1): boolean {
let str: string = key.GetHash();
return this._items[str] != null;
}
public Get(key: T1): T2 {
let str: string = key.GetHash();
let kvp: KeyValuePair<T1, T2> = this._items[str];
if (kvp == null) throw new RangeError("Key '" + str + "' not found")
return kvp.Value;
}
}
export class KeyValuePair<T1 extends IHashable, T2> {
private _key: T1;
private _value: T2;
public get Key(): T1 { return this._key; }
public get Value(): T2 { return this._value; }
public constructor(key: T1, value: T2) {
this._key = key;
this._value = value;
}
}
interface HashTable<T> {
[key: string]: T;
}
Both rely on a definition of IHashable (hashABLE and hashTABLE: I should find other names.)
export interface IHashable {
GetHash(): string;
}
The dictionary looks a bit strange, it "wraps" my dictionary into a new type KeyValuePair and then uses this in the javascript dictionary. What I hope to gain by doing this is get my own type for key, in and out, as long as it offers a string by which it can be indexed. - No idea if that makes sense or is completly wrong.
What I am missing is the count of items in the collection, a way to remove items, and a way to iterate over the keys and the values.
Regarding iterating over I will post another question with my implementation of a list and a ForEach over it, hoping iterating the keys or values might be possible in the same way.
Probably the most important question I forgot here: How could the GetHash-Method be build for an own class? I was going to have a static number on my classes, and count up by 1 in the constructor before assign this number to each instance. This would guarantee uniqueness... is there something better?
Thanks for any tip!
Ralf
I am implementing a method using Vertx to check the existence of certain value in the database and use Handler with AsyncResult.
I would like to know which one is the best practice:
Option 1: When nothing found, Handler is with succeededFuture but with result as FALSE:
public void checkExistence (..., String itemToFind, Handler<AsyncResult<Boolean>> resultHandler) {
// ....
doQuery(..., queryHandler -> {
if (queryHandler.succeeded()) {
List<JsonObject> results = queryHandler.result();
boolean foundIt = false;
for (JsonObject json: results) {
if (json.getString("someKey").equals(itemToFind)) {
foundIt = true;
break;
}
}
resultHandler.handle(Future.succeededFuture(foundIt));
} else {
resultHandler.handle(Future.failedFuture(queryHandler.cause().toString()));
}
});
}
Option 2: When nothing found, Handler is with failedFuture:
public void checkExistence (..., String itemToFind, Handler<AsyncResult<Void>> resultHandler) {
// ....
doQuery(..., queryHandler -> {
if (queryHandler.succeeded()) {
List<JsonObject> results = queryHandler.result();
boolean foundIt = false;
for (JsonObject json: results) {
if (json.getString("someKey").equals(itemToFind)) {
foundIt = true;
break;
}
}
// HERE IS THE DIFFERENCE!!!
if (foundIt) {
resultHandler.handle(Future.succeededFuture());
} else {
resultHandler.handle(Future.failedFuture("Item " + itemToFind + " not found!"));
}
} else {
resultHandler.handle(Future.failedFuture(queryHandler.cause().toString()));
}
});
}
UPDATE:
Let's say I have another example, instead of checking the existence, I would like to get all the results. Do I check the Empty results? Do I treat Empty as failure or success?
Option 1: only output them when it's not null or empty, otherwise, fail it
public void getItems(..., String itemType, Handler<AsyncResult<List<Item>>> resultHandler) {
// ....
doQuery(..., queryHandler -> {
if (queryHandler.succeeded()) {
List<Item> items = queryHandler.result();
if (items != null && !items.empty()) {
resultHandler.handle(Future.succeededFuture(items));
} else {
resultHandler.handle(Future.failedFuture("No items found!"));
}
} else {
resultHandler.handle(Future.failedFuture(queryHandler.cause().toString()));
}
});
}
Option 2: output results I got, even though it could be empty or null
public void getItems(..., String itemType, Handler<AsyncResult<List<Item>>> resultHandler) {
// ....
doQuery(..., queryHandler -> {
if (queryHandler.succeeded()) {
List<Item> items = queryHandler.result();
resultHandler.handle(Future.succeededFuture(items));
} else {
resultHandler.handle(Future.failedFuture(queryHandler.cause().toString()));
}
});
}
The 1st one option is better, because you can clearly say, that checkExistence returned True or False and completed successfully or it failed with some exception (database issue, etc.).
But lets say, you've decided to stick with 2nd option. Then, imagine you have another method:
void getEntity(int id, Handler<AsyncResult<Entity>> resultHandler);
If entity with provided id doesn't exists, will you throw exception (using Future.failedFuture) or return null (using Future.succeededFuture)? I think, you should throw exception to make your methods logic similar to each other. But again, is that exceptional situation?
For case with returning list of entities you can just return empty list, if there are no entities. Same goes to single entity: it's better to return Optional<Entity> instead of Entity, because in this way you avoid NullPointerException and don't have nullable variables in the code. What's better: Optional<List<Entity>> or empty List<Entity>, it's open question.
Particularly if you're writing this as reusable code, then definitely go with your first option. This method is simply determining whether an item exists, and so should simply return whether it does or not. How is this particular method to know whether it's an error condition that the item doesn't exist?
Some caller might determine that it is indeed an error; it that's the case, then it will throw an appropriate exception if the Future returns with false. But another caller might simply need to know whether the item exists before proceeding; in that case, you'll find yourself using exception handling to compose your business logic.
Say I have an enum
public enum E {A,B,C}
Is it possible to add another value, say D, by AspectJ?
After googling around, it seems that there used to be a way to hack the private static field $VALUES, then call the constructor(String, int) by reflection, but seems not working with 1.7 anymore.
Here are several links:
http://www.javaspecialists.eu/archive/Issue161.html (provided by #WimDeblauwe )
and this: http://www.jroller.com/VelkaVrana/entry/modify_enum_with_reflection
Actually, I recommend you to refactor the source code, maybe adding a collection of valid region IDs to each enumeration value. This should be straightforward enough for subsequent merging if you use Git and not some old-school SCM tool like SVN.
Maybe it would even make sense to use a dynamic data structure altogether instead of an enum if it is clear that in the future the list of commands is dynamic. But that should go into the upstream code base. I am sure the devs will accept a good patch or pull request if prepared cleanly.
Remember: Trying to avoid refactoring is usually a bad smell, a symptom of an illness, not a solution. I prefer solutions to symptomatic workarounds. Clean code rules and software craftsmanship attitude demand that.
Having said the above, now here is what you can do. It should work under JDK 7/8 and I found it on Jérôme Kehrli's blog (please be sure to add the bugfix mentioned in one of the comments below the article).
Enum extender utility:
package de.scrum_master.util;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import sun.reflect.ConstructorAccessor;
import sun.reflect.FieldAccessor;
import sun.reflect.ReflectionFactory;
public class DynamicEnumExtender {
private static ReflectionFactory reflectionFactory =
ReflectionFactory.getReflectionFactory();
private static void setFailsafeFieldValue(Field field, Object target, Object value)
throws NoSuchFieldException, IllegalAccessException
{
// let's make the field accessible
field.setAccessible(true);
// next we change the modifier in the Field instance to
// not be final anymore, thus tricking reflection into
// letting us modify the static final field
Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
int modifiers = modifiersField.getInt(field);
// blank out the final bit in the modifiers int
modifiers &= ~Modifier.FINAL;
modifiersField.setInt(field, modifiers);
FieldAccessor fa = reflectionFactory.newFieldAccessor(field, false);
fa.set(target, value);
}
private static void blankField(Class<?> enumClass, String fieldName)
throws NoSuchFieldException, IllegalAccessException
{
for (Field field : Class.class.getDeclaredFields()) {
if (field.getName().contains(fieldName)) {
AccessibleObject.setAccessible(new Field[] { field }, true);
setFailsafeFieldValue(field, enumClass, null);
break;
}
}
}
private static void cleanEnumCache(Class<?> enumClass)
throws NoSuchFieldException, IllegalAccessException
{
blankField(enumClass, "enumConstantDirectory"); // Sun (Oracle?!?) JDK 1.5/6
blankField(enumClass, "enumConstants"); // IBM JDK
}
private static ConstructorAccessor getConstructorAccessor(Class<?> enumClass, Class<?>[] additionalParameterTypes)
throws NoSuchMethodException
{
Class<?>[] parameterTypes = new Class[additionalParameterTypes.length + 2];
parameterTypes[0] = String.class;
parameterTypes[1] = int.class;
System.arraycopy(additionalParameterTypes, 0, parameterTypes, 2, additionalParameterTypes.length);
return reflectionFactory.newConstructorAccessor(enumClass .getDeclaredConstructor(parameterTypes));
}
private static Object makeEnum(Class<?> enumClass, String value, int ordinal, Class<?>[] additionalTypes, Object[] additionalValues)
throws Exception
{
Object[] parms = new Object[additionalValues.length + 2];
parms[0] = value;
parms[1] = Integer.valueOf(ordinal);
System.arraycopy(additionalValues, 0, parms, 2, additionalValues.length);
return enumClass.cast(getConstructorAccessor(enumClass, additionalTypes).newInstance(parms));
}
/**
* Add an enum instance to the enum class given as argument
*
* #param <T> the type of the enum (implicit)
* #param enumType the class of the enum to be modified
* #param enumName the name of the new enum instance to be added to the class
*/
#SuppressWarnings("unchecked")
public static <T extends Enum<?>> void addEnum(Class<T> enumType, String enumName) {
// 0. Sanity checks
if (!Enum.class.isAssignableFrom(enumType))
throw new RuntimeException("class " + enumType + " is not an instance of Enum");
// 1. Lookup "$VALUES" holder in enum class and get previous enum
// instances
Field valuesField = null;
Field[] fields = enumType.getDeclaredFields();
for (Field field : fields) {
if (field.getName().contains("$VALUES")) {
valuesField = field;
break;
}
}
AccessibleObject.setAccessible(new Field[] { valuesField }, true);
try {
// 2. Copy it
T[] previousValues = (T[]) valuesField.get(enumType);
List<T> values = new ArrayList<T>(Arrays.asList(previousValues));
// 3. build new enum
T newValue = (T) makeEnum(
enumType, // The target enum class
enumName, // THE NEW ENUM INSTANCE TO BE DYNAMICALLY ADDED
values.size(), new Class<?>[] {}, // could be used to pass values to the enum constuctor if needed
new Object[] {} // could be used to pass values to the enum constuctor if needed
);
// 4. add new value
values.add(newValue);
// 5. Set new values field
setFailsafeFieldValue(valuesField, null, values.toArray((T[]) Array.newInstance(enumType, 0)));
// 6. Clean enum cache
cleanEnumCache(enumType);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e.getMessage(), e);
}
}
}
Sample application & enum:
package de.scrum_master.app;
/** In honour of "The Secret of Monkey Island"... ;-) */
public enum Command {
OPEN, CLOSE, PUSH, PULL, WALK_TO, PICK_UP, TALK_TO, GIVE, USE, LOOK_AT, TURN_ON, TURN_OFF
}
package de.scrum_master.app;
public class Server {
public void executeCommand(Command command) {
System.out.println("Executing command " + command);
}
}
package de.scrum_master.app;
public class Client {
private Server server;
public Client(Server server) {
this.server = server;
}
public void issueCommand(String command) {
server.executeCommand(
Command.valueOf(
command.toUpperCase().replace(' ', '_')
)
);
}
public static void main(String[] args) {
Client client = new Client(new Server());
client.issueCommand("use");
client.issueCommand("walk to");
client.issueCommand("undress");
client.issueCommand("sleep");
}
}
Console output with original enum:
Executing command USE
Executing command WALK_TO
Exception in thread "main" java.lang.IllegalArgumentException: No enum constant de.scrum_master.app.Command.UNDRESS
at java.lang.Enum.valueOf(Enum.java:236)
at de.scrum_master.app.Command.valueOf(Command.java:1)
at de.scrum_master.app.Client.issueCommand(Client.java:12)
at de.scrum_master.app.Client.main(Client.java:22)
Now you can either add an aspect with an advice executed after the enum class was loaded or just call this manually in your application before extended enum values are to be used for the first time. Here I am showing how it can be done in an aspect.
Enum extender aspect:
package de.scrum_master.aspect;
import de.scrum_master.app.Command;
import de.scrum_master.util.DynamicEnumExtender;
public aspect CommandExtender {
after() : staticinitialization(Command) {
System.out.println(thisJoinPoint);
DynamicEnumExtender.addEnum(Command.class, "UNDRESS");
DynamicEnumExtender.addEnum(Command.class, "SLEEP");
DynamicEnumExtender.addEnum(Command.class, "WAKE_UP");
DynamicEnumExtender.addEnum(Command.class, "DRESS");
}
}
Console output with extended enum:
staticinitialization(de.scrum_master.app.Command.<clinit>)
Executing command USE
Executing command WALK_TO
Executing command UNDRESS
Executing command SLEEP
Et voilà! ;-)
Groovy offers some really neat language features for dealing with and implementing Java interfaces, but I seem kind of stuck.
I want to dynamically implement an Interface on a Groovy class and intercept all method calls on that interface using GroovyInterceptable.invokeMethod. Here what I tried so far:
public interface TestInterface
{
public void doBla();
public String hello(String world);
}
import groovy.lang.GroovyInterceptable;
class GormInterfaceDispatcher implements GroovyInterceptable
{
def invokeMethod(String name, args) {
System.out.println ("Beginning $name with $args")
def metaMethod = metaClass.getMetaMethod(name, args)
def result = null
if(!metaMethod)
{
// Do something cool here with the method call
}
else
result = metaMethod.invoke(this, args)
System.out.println ("Completed $name")
return result
}
TestInterface getFromClosure()
{
// This works, but how do I get the method name from here?
// I find that even more elegant than using invokeMethod
return { Object[] args -> System.out.println "An unknown method called with $args" }.asType(TestInterface.class)
}
TestInterface getThisAsInterface()
{
// I'm using asType because I won't know the interfaces
// This returns null
return this.asType(TestInterface.class)
}
public static void main(String[] args)
{
def gid = new GormInterfaceDispatcher()
TestInterface ti = gid.getFromClosure()
assert ti != null
ti.doBla() // Works
TestInterface ti2 = gid.getThisAsInterface()
assert ti2 != null // Assertion failed
ti2.doBla()
}
}
Returning the Closure works fine, but I couldn't figure a way to find out the name of the method being called there.
Trying to make a Proxy to the this reference itself (so that method calls will call invokeMethod) returns null.
You could use the Map coercion feature of Groovy to dynamically generate a Map that represents the given interface:
TestInterface getMapAsInterface() {
def map = [:]
TestInterface.class.methods.each() { method ->
map."$method.name" = { Object[] args->
println "Called method ${method.name} with ${args}"
}
}
return map.asType(TestInterface.class)
}
To complete the response of Christoph, as stated by this page, you can implement an interface with a closure. For example:
def map = [doBla: { println 'Bla!'}, hello: {world -> "Hello $world".toString()}] as TestInterface
map.hello 'Groovy' // returns 'Hello Groovy'