I need to extend an existing Liferay webservice (created with Service Builder) to handle an additional optional parameter.
With Service Builder, you have to specify every parameters inside the method signature:
public String getList(String param1){ .. }
This creates a get-list web service accepting a parameter named param1. You have to specify every parameters when making the call or else the call will fail. If you need optional parameters, just pass an empty value and handle the missing parameter inside the code.
My problem is backward compatibility: this web service is already used by a mobile app and I cannot change the call made by the app. The additional parameter must be handled without changing the method signature.
Taking a look at BaseServiceImpl, I tried to obtain the parameter in this way:
HttpServletRequest request = com.liferay.util.axis.ServletUtil.getRequest();
String value = ParamUtil.getString(request, "param-name");
But it throws a NoClassDefException regarding com.liferay.util.axis.ServletUtil.
Is there a way to actually do this?
To enhance and retain backward compatibility of your code, one way is to overload getList() method which accepts additional parameter.
You can achieve this by following:
Move your general pre-logic code of getList() to getList(String param1) method.
Add filter for param1 in getList(String param1) to handle case when parameter is not null / empty.
Call getList(null) from getList().
While you can call getList(String param1) directly when you require to pass additional parameter.
Original method:
public String getList(){
return getList(null);
}
Overriden method:
public String getList(String param1){
if(param1 != null){
// logic for param1
}
// rest of your general code
}
Related
I need to create a custom classes based on some input. What I have atm is this:
final Class service = ...;
final Method method = ...;
final DynamicType.Unloaded unloadedType = new ByteBuddy()
.subclass(Object.class)
.name(service.getClass().getSimpleName() + "DynamicResolver")
.defineMethod(
endpointName,
resolveReturnType(method),
Modifier.PUBLIC)
.withParameters(parameters)
.intercept(MethodDelegation
.withDefaultConfiguration()
.withBinders(Morph.Binder.install(Morphing.class))
.to(interceptor).andThen(
MethodCall.invoke(method).on(service).withArgument(arguments)
))
.make()
What I am doing here is creating a class with a single method that delegates to provided one. However, the created method and delegate method have a bit different parameters. The created method has one argument more (in parameters). The created method does not take that argument, hence the arguments array with argument indexes (one argument less).
So far it's OK. Now, I need to add additional argument when calling delegation method. For the sake of simplicity of the example, imagine we have to add one more string to delegate call.
As I saw from the documentation, the way to manipulate the arguments is using #Morph. So I did:
public interface Morphing<T> {
T invoke(Object[] args);
}
and my interceptor:
public class Interceptor {
#RuntimeType
public Object invoke(
#Morph final Morphing<Integer> m,
#AllArguments final Object[] args
) {
System.out.println("!!!");
return m.invoke(args);
}
}
Unfortunately, this is not working. When I remove the #Morph argument, the interceptor gets called.
What am I missing here?
EDIT: Is the #Morph used only for subclasses and not when delegating to another instance?
EDIT2: example
Byte Buddy is binding a method of the Object class such that your desired interceptor is no longer triggered. You can add filter(isDeclaredBy(MyInterceptor.class)) after the withDefaultConfiguration() to avoid this. Doing so, you will get an exception that Byte Buddy cannot bind any of your methods.
The reason that #Morph makes the class ineligable is that there is no super method to be called. In your example, you are defining a new method which does not have an original implementation. You'd need to override an existing method to use the annotation.
I overwrote the public Action onRequest(final Http.Request request, Method method) method to check that the mandatory http headers are passed in and valid, i.e.: I extract the apiKey (and other things) and make sure that these are valid (that there's data associated with the apiKey). Then I call return super.onRequest(request, method); and I end up in my controller where I once again have to extract the apiKey and get the associated data from the DB.
Is there a way to pass in the data to my controller's method (for instance: public static Result addUser() ).
Thank you.
I know this question is for Java, but I work with Play in Scala, and this question is more related to Play then it is to Java
In Scala I am able to extend WrappedRequest to make my own custom request type, that has instance variables I want to access in my controller:
// Scala code
case class MyRequest[A](request: Request[A]) extends WrappedRequest(request) {
// This is a public instance variable
val apiKey = request.headers.get("Authorization)
}
Then later on in my controller I can access the action object which now has the type of MyRequest:
// Scala code
def foobar(action: MyRequest[AnyContent]) = Action {
// Do something with the api key
val apiKey = action.apiKey
// Send back a response
Ok("foobar")
}
In Java it looks like you can do something similar using a Wrapped Context
Using the Retrofit REST Client library from Square, is there anyway of providing default/constant values for POST/PUT fields in a call.
I know about including constant query parameters by simply including them in the path, but this work for Body parameters.
I have an API that looks similar to:
POST /api/create
type=constanta&value={value}
POST /api/create
type=constantb&value={value}&otherValue={otherValue}
where the second variant requires an additional argument to be supplied. Rather than having a single java method that took all three arguments, I was hoping to be able to elide the constants from the method call, something like:
create(String value);
create(String value, String otherValue);
and have retrofit inject the type argument constant.
Given that adding #FormUrlEncoded can be added to modify how the body is encoded, if it's not natively supported by Retrofit, is there anyway of adding my own annotation and injecting such default values? ( It doesn't appear that RequestInterceptor allows one to modify the body.. ).
Maybe one option would be to send an object, which encapsulates all your values, instead of all string values separately? The object would implement your default values.
For example, you could create a class:
public class CreateObject {
private String type = "constant";
private String value;
private String otherValue;
public CreateObject(String value, String otherValue) {
this.value = value;
this.otherValue = otherValue;
}
}
Your class handles your constant. You could just set it to a default value "constant", like I did above, or set it on the fly in the constructor.
Now all you've to do is to create the object with the values and make the request with Retrofit. Instead of using the string values directly, just pass the object. Your interface could look like this:
public interface CreateService {
#POST("/api/create")
void create(#Body CreateObject create, Callback<CreateObject> cb);
}
The request implementation like this:
CreateObject create = new CreateObject("value", "otherValue");
createService.create(create, new Callback<CreateObject)() {…});
This should include all three of your values in the request body, if they are set. If a value is null, it won't be included in the request body. Based on your two examples above, you would now only need one interface method. Which values are sent is based on the createObject you pass on. For example, if you set otherValue as null, it won't be part of the request body.
My examples were modified from: https://futurestud.io/blog/retrofit-send-objects-in-request-body/
Is it possible for you to use Guava or Java 8 Optional as second argument in method? Then if that argument will be absent you can just ignore it
I have several APIs which retain a parameter "feature" from the url (path param). To avoid retrieving it in each method endpoint (eg.)
#GET
public void findAll(#PathParam("feature") String feature);
am trying to implement AOP using AspectJ.
Following is the implementation of the Aspect
#Aspect
public class FeatureAOP {
#Pointcut("execution(* x.y.z.rest.ModifiersFacadeWrapper.*(..)) && args(feature)")
public void pointCut(String feature) {
}
#Before("x.y.z.rest.aop.FeatureAOP.pointCut(feature)")
public void parseParams(JoinPoint jp, String feature) {
Object[] x = jp.getArgs();
System.out.println("Feature: " + feature);
}
}
The above method gives me the value of "feature" in the Aspect class but if I change the method findAll to following signature, it doesn't works.
#GET
public void findAll();
What I understand is the control is transferred to the Aspect after the parameters are resolved and removing it from the method definition is failing it.
Doing so, thus takes me to the same point where I have to define all method endpoints with the parameter in its signature. I would like to know if there is a way I can get the PathParams in the Aspect class without having to define my methods with the designated parameters.
I think you could probably do it by putting the resolved params in a globally accessible data structure (e.g. a Singleton having some sort of Map or Set), but
I wouldn't recommend that kind of approach. I don't know why you don't like having all the params in your method signatures, but that is the intended way of declaring rest services, e.g.
#GET
#Path("{feature}")
#Produces("text/plain")
public String getFeature(#PathParam("feature") String feature) {
return feature;
}
This way you don't have to write any code for retrieving the params, the rest library you are using (be it Jersey or a different one) will just do everything for you.
I am new to Mockito, I am trying to verify the attributes of an object which gets created inside a method.
pseudo code below:
class A{
...
public String methodToTest(){
Parameter params = new Parameter(); //param is basically like a hashmap
params.add("action", "submit");
return process(params);
}
...
public String process(Parameter params){
//do some work based on params
return "done";
}
}
I want to test 2 things:
when I called methodToTest, process() method is called
process() method is called with the correct params containing action "submit"
I was able to verify that process() is eventually called easily using Mockito.verify().
However trying to check that params contains action "submit" is very difficult so far.
I have tried the following but it doesn't work :(
BaseMatcher<Parameter> paramIsCorrect = new BaseMatcher<Parameter>(){
#Overrides
public boolean matches(Object param){
return ("submit".equals((Parameter)param.get("action")));
}
//#Overrides description but do nothing
}
A mockA = mock(A);
A realA = new A();
realA.methodToTest();
verify(mockA).process(argThat(paramIsCorrect))
Any suggestion ?
If you have got verify() to work, presumably it is just a case of using an argument matcher to check the contains of params.
http://docs.mockito.googlecode.com/hg/org/mockito/Mockito.html#3
The example given in the above docs is verify(mockedList).get(anyInt()). You can also say verify(mockedList).get(argThat(myCustomMatcher)).
As an aside, it sounds like you are mocking the class under test. I've found that this usually means I haven't thought clearly about either my class or my test or both. In your example, you should be able to test that methodToTest() returns the right result irrespective of whether or not it calls process() because it returns a String. The mockito folk have lots of good documentation about this sort thing, particularly the "monkey island" blog: http://monkeyisland.pl/.
Just pass Parameter in as a constructor argument to a constructor of the class A, then use a mocked instance/implementation of Parameter in your test and verify on the mock. That is how it is normally done - you separate your classes and compose them using constructor injection, that enables you to pass in mocks for testing purposes (it also allows rewiring the application and exchanging some commons a lot easier).
If you need to create Parameter on every function invocation you should use a factory that creates Parameter instances and pass that in. Then you can verify on the factory as well as the object created by the factory.