I have a simple, but incredibly ugly looking method.
The issue I am having is that I feel this can be done a million times more elegantly. In addition, I would also like to scan a method for more than one annotation, and not just the Rest endpoint declarations.
I feel this can be done through a stream of Annotations[] (method.getDeclaredAnnotations()) and filtered by a List<Annotation> restEndpointAnnotationsAndOtherAnnotations, but I cannot seem to get it to work.
Any help would be greatly appreciated. I think it's probably a fairly fun challenge for some people. The primary issue I am getting (I think) is trying to convert Class<? extends Annotation> to Annotation, but perhaps I am missing the mark.
public RestEndpoint mapToRestEndpoint(Method method) {
String url = null;
if (method.isAnnotationPresent(GetMapping.class)) {
url = method.getAnnotation(GetMapping.class).value()[0];
} else
if (method.isAnnotationPresent(PutMapping.class)) {
url = method.getAnnotation(PutMapping.class).value()[0];
} else
if (method.isAnnotationPresent(PostMapping.class)) {
url = method.getAnnotation(PostMapping.class).value()[0];
} else
if (method.isAnnotationPresent(PatchMapping.class)) {
url = method.getAnnotation(PatchMapping.class).value()[0];
} else
if (method.isAnnotationPresent(DeleteMapping.class)) {
url = method.getAnnotation(DeleteMapping.class).value()[0];
} else
if (method.isAnnotationPresent(RequestMapping.class)) {
url = method.getAnnotation(RequestMapping.class).value()[0];
} else return null;
return new RestEndpoint(url, true);
}
Where RestEndpoint is a simple POJO
#Value
public class RestEndpoint {
#NonNull String endpoint;
boolean isPublic;
}
I can actually find where it matches the Rest mapping using streams, but I cannot then apply the .value() method to it (since it doesn't know what annotation it is, and would be just as tedious to then cast to multiple annotation types)
EDIT:
This is a pretty handy way of getting the information on methods if anyone is interested.
ApplicationContext context = ((ContextRefreshedEvent) event).getApplicationContext();
context.getBean(RequestMappingHandlerMapping.class).getHandlerMethods();
Problem is in getAnnotation as it need concrete annotation class to know that it has somethings like value(). You can create helper method that try to invoke value() on given object and do other parsing.
private String getUrl(Method method, Class<? extends Annotation> annotationClass){
Annotation annotation = method.getAnnotation(annotationClass);
String[] value;
try {
value = (String[])annotationClass.getMethod("value").invoke(annotation);
} catch (IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
return null;
}
return value[0];
}
Then use it like this:
String url = Stream.of(
GetMapping.class, PutMapping.class, PostMapping.class, PatchMapping.class, DeleteMapping.class, RequestMapping.class)
.filter(clazz -> method.isAnnotationPresent(clazz))
.map(clazz -> getUrl(method, clazz))
.findFirst().orElse(null);
Related
I'm pretty new to Java so bear with me. I can't for the life of me figure out why I'm getting
a cannot find symbol error on resourceResolver.resolve. When on the line above it I'm defining the variable.
Maybe this is something simple I'm missing but I can't figure this out and I feel like I've
stared at this way to long.
private static final String ROOTCHILD = "rootChild";
public void setResource(Resource resource) {
this.resource = resource;
}
public void setProperties(ValueMap properties) {
this.properties = properties;
}
public Page getRootPage() {
ResourceResolver resourceResolver = getResource().getResourceResolver();
return (this.properties != null)
? resourceResolver.resolve(
properties.get( ROOTCHILD,currentPage.getPath())).adaptTo(Page.class)
: null;
}
My guess here (never worked with sling and haven't used Java for a while):
I think the problem is you initialized the ValueMap properties so that it doesn't contain Strings or HttpServletRequests, but something else. The .resolve() method only accepts either a String or an HttpServletRequest. (Or two parameters, but you're only passing one, so that one can't be the case.) There is no .resolve() method found accepting the parameters you try to give it, so that symbol is not found!
To see the true error, rewrite your code and compile it:
public Page getRootPage() {
if( properties == null ) {
return null;
}
YYYYYY resource = getResource();
ResourceResolver resourceResolver = resource.getResourceResolver();
String path = currentPage.getPath();
String rootChild = properties.get( ROOTCHILD, path );
XXXXXX rc = resourceResolver.resolve( rootChild );
return rc.adaptTo( Page.class );
}
I was wondering if this approach was correct :
public ITask getState()
{
statePredicate[Some predicate definition];
ITask nextRunnable = null;
try {
nextRunnable = Iterables.find((Iterable)queue, statePredicate);
}
catch (NoSuchElementException e)
{}
return nextRunnable;
}
The points on which I am wondering are :
should the predicate be cached as a member of the class ?
I do nothing with the catch, I do not even log it because it is
normal for my app to not find anything.
t return null because I do a final return.
Thank you for your input !
-
1) If the predicate is always the same, I would make it a static final class member.
2) There is also a version of Iterables.find that you can specify a default value to (assuming you're using Google Guava). Then you don't need to deal with the NoSuchElementException at all.
3) Is there a reason to cast queue to Iterable? If this is not necessary, then don't cast.
class MyClass {
private static final Predicate STATE_PREDICATE = new Predicate<ITask>() {
#Override
public boolean apply(ITask input) {
// ... your code here
}
};
public ITask getState() {
return Iterables.find(queue, STATE_PREDICATE, null);
}
}
If the exception is really the usual case in your approach than you should put at least a comment into the catch area to make clear for everyone who reads the code that it was intentional and not a mistake. In my opinion returning Null is something different, but it some circumstanced not avoidable.
I'm using BeanUtils to manipulate Java objects created via JAXB, and I've run into an interesting issue. Sometimes, JAXB will create a Java object like this:
public class Bean {
protected Boolean happy;
public Boolean isHappy() {
return happy;
}
public void setHappy(Boolean happy) {
this.happy = happy;
}
}
The following code works just fine:
Bean bean = new Bean();
BeanUtils.setProperty(bean, "happy", true);
However, attempting to get the happy property like so:
Bean bean = new Bean();
BeanUtils.getProperty(bean, "happy");
Results in this exception:
Exception in thread "main" java.lang.NoSuchMethodException: Property 'happy' has no getter method in class 'class Bean'
Changing everything to a primitive boolean allows both the set and get call to work. I don't have this option, however, since these are generated classes. I assume this happens because the Java Bean libraries only consider an is<name> method to represent a property if the return type is a primitive boolean, and not the wrapper type Boolean. Does anyone have a suggestion as to how to access properties like these through BeanUtils? Is there some kind of workaround I can use?
Finally I've found legal confirmation:
8.3.2 Boolean properties
In addition, for boolean properties, we allow a getter method to match the pattern:
public boolean is<PropertyName>();
From JavaBeans specification. Are you sure you haven't came across JAXB-131 bug?
Workaround to handle Boolean isFooBar() case with BeanUtils
Create new BeanIntrospector
private static class BooleanIntrospector implements BeanIntrospector{
#Override
public void introspect(IntrospectionContext icontext) throws IntrospectionException {
for (Method m : icontext.getTargetClass().getMethods()) {
if (m.getName().startsWith("is") && Boolean.class.equals(m.getReturnType())) {
String propertyName = getPropertyName(m);
PropertyDescriptor pd = icontext.getPropertyDescriptor(propertyName);
if (pd == null)
icontext.addPropertyDescriptor(new PropertyDescriptor(propertyName, m, getWriteMethod(icontext.getTargetClass(), propertyName)));
else if (pd.getReadMethod() == null)
pd.setReadMethod(m);
}
}
}
private String getPropertyName(Method m){
return WordUtils.uncapitalize(m.getName().substring(2, m.getName().length()));
}
private Method getWriteMethod(Class<?> clazz, String propertyName){
try {
return clazz.getMethod("get" + WordUtils.capitalize(propertyName));
} catch (NoSuchMethodException e) {
return null;
}
}
}
Register BooleanIntrospector:
BeanUtilsBean.getInstance().getPropertyUtils().addBeanIntrospector(new BooleanIntrospector());
you can just create second getter with SET - sufix as workaround :)
This is the second time I found myself writing this kind of code, and decided that there must be a more readable way to accomplish this:
My code tries to figure something out, that's not exactly well defined, or there are many ways to accomplish it. I want my code to try out several ways to figure it out, until it succeeds, or it runs out of strategies. But I haven't found a way to make this neat and readable.
My particular case: I need to find a particular type of method from an interface. It can be annotated for explicitness, but it can also be the only suitable method around (per its arguments).
So, my code currently reads like so:
Method candidateMethod = getMethodByAnnotation(clazz);
if (candidateMethod == null) {
candidateMethod = getMethodByBeingOnlyMethod(clazz);
}
if (candidateMethod == null) {
candidateMethod = getMethodByBeingOnlySuitableMethod(clazz);
}
if (candidateMethod == null) {
throw new NoSuitableMethodFoundException(clazz);
}
There must be a better way…
Edit: The methods return a method if found, null otherwise. I could switch that to try/catch logic, but that hardly makes it more readable.
Edit2: Unfortunately, I can accept only one answer :(
To me it is readable and understandable. I'd simply extract the ugly part of the code to a separate method (following some basic principles from "Robert C.Martin: Clean Code") and add some javadoc (and apologies, if necessary) like that:
//...
try {
Method method = MethodFinder.findMethodIn(clazz);
catch (NoSuitableMethodException oops) {
// handle exception
}
and later on in MethodFinder.java
/**
* Will find the most suitable method in the given class or throw an exception if
* no such method exists (...)
*/
public static Method findMethodIn(Class<?> clazz) throws NoSuitableMethodException {
// all your effort to get a method is hidden here,
// protected with unit tests and no need for anyone to read it
// in order to understand the 'main' part of the algorithm.
}
I think for a small set of methods what you're doing is fine.
For a larger set, I might be inclined to build a Chain of Responsibility, which captures the base concept of trying a sequence of things until one works.
I don't think that this is such a bad way of doing it. It is a bit verbose, but it clearly conveys what you are doing, and is easy to change.
Still, if you want to make it more concise, you can wrap the methods getMethod* into a class which implements an interface ("IMethodFinder") or similar:
public interface IMethodFinder{
public Method findMethod(...);
}
Then you can create instances of you class, put them into a collection and loop over it:
...
Method candidateMethod;
findLoop:
for (IMethodFinder mf: myMethodFinders){
candidateMethod = mf.findMethod(clazz);
if (candidateMethod!=null){
break findLoop;
}
}
if (candidateMethod!=null){
// method found
} else {
// not found :-(
}
While arguably somewhat more complicated, this will be easier to handle if you e.g. need to do more work between calling the findMethods* methods (such as more verification that the method is appropriate), or if the list of ways to find methods is configurable at runtime...
Still, your approach is probably OK as well.
I'm sorry to say, but the method you use seems to be the widely accepted one. I see a lot of code like that in the code base of large libraries like Spring, Maven etc.
However, an alternative would be to introduce a helper interface that can convert from a given input to a given output. Something like this:
public interface Converter<I, O> {
boolean canConvert(I input);
O convert(I input);
}
and a helper method
public static <I, O> O getDataFromConverters(
final I input,
final Converter<I, O>... converters
){
O result = null;
for(final Converter<I, O> converter : converters){
if(converter.canConvert(input)){
result = converter.convert(input);
break;
}
}
return result;
}
So then you could write reusable converters that implement your logic. Each of the converters would have to implement the canConvert(input) method to decide whether it's conversion routines will be used.
Actually: what your request reminds me of is the Try.these(a,b,c) method in Prototype (Javascript).
Usage example for your case:
Let's say you have some beans that have validation methods. There are several strategies to find these validation methods. First we'll check whether this annotation is present on the type:
// retention, target etc. stripped
public #interface ValidationMethod {
String value();
}
Then we'll check whether there's a method called "validate". To make things easier I assume, that all methods define a single parameter of type Object. You may choose a different pattern. Anyway, here's sample code:
// converter using the annotation
public static final class ValidationMethodAnnotationConverter implements
Converter<Class<?>, Method>{
#Override
public boolean canConvert(final Class<?> input){
return input.isAnnotationPresent(ValidationMethod.class);
}
#Override
public Method convert(final Class<?> input){
final String methodName =
input.getAnnotation(ValidationMethod.class).value();
try{
return input.getDeclaredMethod(methodName, Object.class);
} catch(final Exception e){
throw new IllegalStateException(e);
}
}
}
// converter using the method name convention
public static class MethodNameConventionConverter implements
Converter<Class<?>, Method>{
private static final String METHOD_NAME = "validate";
#Override
public boolean canConvert(final Class<?> input){
return findMethod(input) != null;
}
private Method findMethod(final Class<?> input){
try{
return input.getDeclaredMethod(METHOD_NAME, Object.class);
} catch(final SecurityException e){
throw new IllegalStateException(e);
} catch(final NoSuchMethodException e){
return null;
}
}
#Override
public Method convert(final Class<?> input){
return findMethod(input);
}
}
// find the validation method on a class using the two above converters
public static Method findValidationMethod(final Class<?> beanClass){
return getDataFromConverters(beanClass,
new ValidationMethodAnnotationConverter(),
new MethodNameConventionConverter()
);
}
// example bean class with validation method found by annotation
#ValidationMethod("doValidate")
public class BeanA{
public void doValidate(final Object input){
}
}
// example bean class with validation method found by convention
public class BeanB{
public void validate(final Object input){
}
}
You may use Decorator Design Pattern to accomplish different ways of finding out how to find something.
public interface FindMethod
{
public Method get(Class clazz);
}
public class FindMethodByAnnotation implements FindMethod
{
private final FindMethod findMethod;
public FindMethodByAnnotation(FindMethod findMethod)
{
this.findMethod = findMethod;
}
private Method findByAnnotation(Class clazz)
{
return getMethodByAnnotation(clazz);
}
public Method get(Class clazz)
{
Method r = null == findMethod ? null : findMethod.get(clazz);
return r == null ? findByAnnotation(clazz) : r;
}
}
public class FindMethodByOnlyMethod implements FindMethod
{
private final FindMethod findMethod;
public FindMethodByOnlyMethod(FindMethod findMethod)
{
this.findMethod = findMethod;
}
private Method findByOnlyMethod(Class clazz)
{
return getMethodOnlyMethod(clazz);
}
public Method get(Class clazz)
{
Method r = null == findMethod ? null : findMethod.get(clazz);
return r == null ? findByOnlyMethod(clazz) : r;
}
}
Usage is quite simple
FindMethod finder = new FindMethodByOnlyMethod(new FindMethodByAnnotation(null));
finder.get(clazz);
... I could switch that to try/catch logic, but that hardly makes it more readable.
Changing the signature of the get... methods so you can use try / catch would be a really bad idea. Exceptions are expensive and should only be used for "exceptional" conditions. And as you say, the code would be less readable.
What is bothering you is the repeating pattern used for flow control--and it should bother you--but there isn't too much to be done about it in Java.
I get really annoyed at repeated code & patterns like this, so for me it would probably be worth it to extract the repeated copy & paste control code and put it in it's own method:
public Method findMethod(Class clazz)
int i=0;
Method candidateMethod = null;
while(candidateMethod == null) {
switch(i++) {
case 0:
candidateMethod = getMethodByAnnotation(clazz);
break;
case 1:
candidateMethod = getMethodByBeingOnlyMethod(clazz);
break;
case 2:
candidateMethod = getMethodByBeingOnlySuitableMethod(clazz);
break;
default:
throw new NoSuitableMethodFoundException(clazz);
}
return clazz;
}
Which has the disadvantage of being unconventional and possibly more verbose, but the advantage of not having as much repeated code (less typos) and reads easier because of there being a little less clutter in the "Meat".
Besides, once the logic has been extracted into it's own class, verbose doesn't matter at all, it's clarity for reading/editing and for me this gives that (once you understand what the while loop is doing)
I do have this nasty desire to do this:
case 0: candidateMethod = getMethodByAnnotation(clazz); break;
case 1: candidateMethod = getMethodByBeingOnlyMethod(clazz); break;
case 2: candidateMethod = getMethodByBeingOnlySuitableMethod(clazz); break;
default: throw new NoSuitableMethodFoundException(clazz);
To highlight what's actually being done (in order), but in Java this is completely unacceptable--you'd actually find it common or preferred in some other languages.
PS. This would be downright elegant (damn I hate that word) in groovy:
actualMethod = getMethodByAnnotation(clazz) ?:
getMethodByBeingOnlyMethod(clazz) ?:
getMethodByBeingOnlySuitableMethod(clazz) ?:
throw new NoSuitableMethodFoundException(clazz) ;
The elvis operator rules. Note, the last line may not actually work, but it would be a trivial patch if it doesn't.
I would like to know what's the best approach to test the method "pushEvent()" in the following class with a jUnit test.
My problem is, that the private method "callWebsite()" always requires a connection to the network. How can I avoid this requirement or refactor my class that I can test it without a connection to the network?
class MyClass {
public String pushEvent (Event event) {
//do something here
String url = constructURL (event); //construct the website url
String response = callWebsite (url);
return response;
}
private String callWebsite (String url) {
try {
URL requestURL = new URL (url);
HttpURLConnection connection = null;
connection = (HttpURLConnection) requestURL.openConnection ();
String responseMessage = responseParser.getResponseMessage (connection);
return responseMessage;
} catch (MalformedURLException e) {
e.printStackTrace ();
return e.getMessage ();
} catch (IOException e) {
e.printStackTrace ();
return e.getMessage ();
}
}
}
Stubbing
You'll need a test double (stub) to allow isolated, easy, unit testing. The following is non tested, but demonstrates the idea. The use of Dependency Injection will allow you to inject at test time, a test version of your HttpURLConnection.
public class MyClass()
{
private IHttpURLConnection httpUrlConnection;
public MyClass(IHttpURLConnection httpUrlConnection)
{
this.httpUrlConnection = httpUrlConnection;
}
public String pushEvent(Event event)
{
String url = constructURL(event);
String response = callWebsite(url);
return response;
}
}
Then you create a stub (sometimes referred to as a mock object) to be the stand in for the concrete instance.
class TestHttpURLConnection : IHttpURLConnection { /* Methods */ }
You'll also construct a concrete version, for your production code to use.
class MyHttpURLConnection : IHttpURLConnection { /* Methods */ }
Using your test class (an adapter) you are able to specifiy what should happen during your test. A mocking framework will enable you to do this with less code, or you can manually wire this up. The end result of this for your test is that you'll set your expectations for your test, for example, in this case you may set OpenConnection to return a true boolean (This is just an example by the way). Your test will then assert that when this value is true, the return value of your PushEvent method matches some expected result. I've not touched Java properly for a while, but here are some recommended mocking frameworks as specified by StackOverflow members.
Possible solution: You can extend this class, override callWebsite (you have to make it protected for this purpose) - and the override method write some stub method implementation.
Approaching things from a slightly different angle...
I'd worry less about testing this specific class. The code in it is extremely simple and, while a functional test to make sure it's working with a connection would be helpful, a unit level test "may" not be necessary.
Instead, I'd focus on testing the methods it calls that appear to actually do something. Specifically...
I'd test constructURL method from this line:
String url = constructURL (event);
making sure that it can construct a URL properly from different Events, and throws Exceptions when it should (possibly on an invalid Event or null).
And I'd test the method from the following line:
String responseMessage = responseParser.getResponseMessage (connection);
Possibly pulling out any "get information out of the connection" logic into one proc, and leaving only "parse said information" in the original one:
String responseMessage = responseParser.getResponseMessage(responseParser.getResponseFromConnection(connection));
or something along those lines.
The idea being to put any "must deal with external data sources" code in one method, and any code logic in separate methods that can be easily tested.
As an alternative to Finglas's helpful answer with respect to mocking, consider a stubbed approach where we override the functionality of callWebsite(). This works quite well in the case where we aren't so interested in the logic of callWebsite as that of the other logic called within pushEvent(). One important thing to check is that callWebsite is calledwith the correct URL. So, first change is to the method signature of callWebsite() to become:
protected String callWebsite(String url){...}
Now we create a stubbed class like this:
class MyClassStub extends MyClass {
private String callWebsiteUrl;
public static final String RESPONSE = "Response from callWebsite()";
protected String callWebsite(String url) {
//don't actually call the website, just hold onto the url it was going to use
callWebsiteUrl = url;
return RESPONSE;
}
public String getCallWebsiteUrl() {
return callWebsiteUrl;
}
}
And finally in our JUnit test:
public class MyClassTest extends TestCase {
private MyClass classUnderTest;
protected void setUp() {
classUnderTest = new MyClassStub();
}
public void testPushEvent() { //could do with a more descriptive name
//create some Event object 'event' here
String response = classUnderTest.pushEvent(event);
//possibly have other assertions here
assertEquals("http://some.url",
(MyClassStub)classUnderTest.getCallWebsiteUrl());
//finally, check that the response from the callWebsite() hasn't been
//modified before being returned back from pushEvent()
assertEquals(MyClassStub.RESPONSE, response);
}
}
Create an abstract class WebsiteCaller which would be a parent of ConcreteWebsiteCaller and WebsiteCallerStub.
This class should have one method callWebsite (String url). Move your callWebsite method from MyClass to ConcreteWebsiteCaller. And MyClass will look like:
class MyClass {
private WebsiteCaller caller;
public MyClass (WebsiteCaller caller) {
this.caller = caller;
}
public String pushEvent (Event event) {
//do something here
String url = constructURL (event); //construct the website url
String response = caller.callWebsite (url);
return response;
}
}
and implement method callWebsite in your WebsiteCallerStub in some way appropriate for testing.
Then in your unit test do something like this:
#Test
public void testPushEvent() {
MyClass mc = new MyClass (new WebsiteCallerStub());
mc.pushEvent (new Event(...));
}