Is AOP supported in Helidon MP? - helidon

Like Spring framework, I want to create a Pointcut to execute some logic before executing the method. Is it possible to do that in Helidon MP?
#Pointcut("execution(public * *(..))")
private void anyPublicOperation(String input) {}

Helidon MP, like all MicroProfile implementations, is centered around CDI, which offers, for this purpose, interceptors and decorators.

I have already done that using Interceptor. Thanks!
Here is the example:
Creating a custom annotation with #InterceptorBinding
#InterceptorBinding
#Target({ElementType.TYPE, ElementType.METHOD})
#Retention(RetentionPolicy.RUNTIME)
public #interface SoapSecure {
String tokenParam() default "token";
}
Creating the interceptor
#Priority(1)
#Interceptor
#SoapSecure
#Slf4j
public class SoapAuthenticationInterceptor {
#Inject
private AuthService authService;
#AroundInvoke
public Object validateToken(InvocationContext invocationContext) throws Exception {
Method method = invocationContext.getMethod();
log.info("Validate the token from SOAP APIs: " + method.getName());
String tokenParam = method
.getAnnotation(SoapSecure.class)
.tokenParam();
Parameter[] params = method.getParameters();
String accessToken = null;
for (Parameter p : params) {
if (p.getName().equals(tokenParam)) {
// validate the access token
authService.validateAccessToken(Objects.toString(method.invoke(p)));
}
}
return invocationContext.proceed();
}
}
Then use it:
#SoapSecure
public boolean test(String token){}

Related

Process custom annotation in implementing class of an Resource interface

I am trying to process a custom annotation on a class that implements an external interface that defines a Resource. The setup is the following:
A Resource interface, I can't modify it:
#Path("/v1")
public interface Resource {
#GET
#Path("/foo")
Response foo();
}
An implementation that I can modify:
public class ResourceImpl implements Resource {
#Override
#CustomAnnotation // has Retention.RUNTIME
public Response foo() {
// foo logic
}
}
I've implemented a filter to try and process the #CustomAnnotation on the overriden foo() method:
#Provider
#ServerInterceptor
#Precedence("SECURITY")
public class CustomAnnotationInterceptor implements ContainerRequestFilter {
#Context
ResourceInfo resourceInfo;
#Override
public void filter(ContainerRequestContext containerRequestContext) throws IOException {
// check if the invoked resource method is annotated with #CustomAnnotation and do logic
}
}
However, when I try to get the matched resource class from the ResourceInfo instance, I get the Resource interface, and when I get the matched method, I get the foo() method from the interface which is lacking the #CustomAnnotation. Is there any way around this?
I'm using RESTEasy as an implementation of JAX-RS.
You could implement an interceptor, where you can get hold of the actual resource (method and class) being called. The interceptor should be bound to your annotation using #InterceptorBinding (see 54.2.4 Binding Interceptors to Components).
// Interceptor
#Interceptor
#CustomAnnotation
#Priority(Interceptor.Priority.APPLICATION)
public class CustomAnnotationInterceptor {
#AroundInvoke
public Object interceptCustomAnnotation(InvocationContext ctx) throws Exception {
CustomAnnotation customAnnotation = null;
// The actual method being called
Method method = ctx.getMethod();
if (method != null) {
customAnnotation = method.getAnnotation(CustomAnnotation.class);
}
// ... do stuff with the annotation
return ctx.proceed();
}
}
To get the instance of the class that implements your interface you could use ctx.getMethod().getDeclaringClass() or ctx.getTarget().getClass().getSuperclass().

How can I mock the service class in my Controller Test in Micronaut using JUnit5?

I am writing a JUnit test case for the controller in my micronaut application. The controller has a GET endpoint which invokes a method in my service class. I am getting a NullPointerException so I am assuming that my service class might not be properly mocked however, I am not sure. I am using #Mock (Mockito) for the service.
Am I using the correct annotation to mock the service layer? I have tried to search on google but it hasn't given me much to look into. Thanks.
#MicronautTest
public class FPlanControllerTest {
private static final String url = "dummy_url";
#Inject
FPlanService fplanService;
#Inject
#Client("/")
RxHttpClient client;
#Test
public void testGetLayout() {
FPlanUrl expectedFPlanUrl = new FPlanUrl(url);
when(fplanService.getLayoutUrl(Mockito.anyString(), Mockito.anyString()))
.thenReturn(expectedFPlanUrl);
FPlanUrl actualFPlanUrl = client.toBlocking()
.retrieve(HttpRequest.GET("/layout/1000545").header("layoutId", "7"), FPlanUrl.class);
assertEquals(expectedFPlanUrl , actualFPlanUrl);
}
#MockBean(FPlanService.class)
FPlanService fplanService() {
return mock(FPlanService.class);
}
}
I received the below error.
java.lang.NullPointerException at com.apartment.controller.FPlanControllerTest.testGetLayout(FPlanControllerTest.java:44)
Use #MockBean (io.micronaut.test.annotation.MockBean).
Docs - https://micronaut-projects.github.io/micronaut-test/latest/guide/#junit5
Simply try to mock as below :-
#MockBean(MyService.class)
MyService myService() {
return mock(MyService.class);
}
Now the service can be injected as:-
#Inject
private MyService myService;
Use inside your test method as:-
#Test
public void myServiceTest() {
when(myService.foo(any())).thenReturn(any());
MutableHttpResponse<FooResponse> response = controller.bar(new
MyRequest());
Assertions.assertNotNull(response);
}
I figured out what went wrong. This was giving a NullPointerException because the HTTP response was expecting a String and not the FPlanUrl object. The correct code is as below:
#Test
public void testGetLayout() {
FPlanUrl expectedFPlanUrl = new FPlanUrl("http://dummyurl.com");
when(fplanService.getLayoutUrl(Mockito.anyString(), Mockito.anyString()))
.thenReturn(expectedFPlanUrl);
Assertions.assertEquals("{\"url\":\"http://dummyurl.com\"}", client.toBlocking().retrieve(HttpRequest.GET("/layout/123").header("layoutId", "7"), String.class);
verify(fplanService).getLayoutUrl("123","7");
}

How to use Jackson BeanDeserializerModifier?

I am trying to implement a custom deserializer.
Because I only want to add functionality to the default deserializer, I tried to store in my custom deserializer the default one: I would like to use the default to deserialize the json and then add other information.
I am trying to use BeanDeserializerModifier to register the custom deserializer.
SimpleModule module = new SimpleModule("ModelModule", Version.unknownVersion());
module.setDeserializerModifier(new BeanDeserializerModifier() {
#Override
public JsonDeserializer<?> modifyDeserializer(DeserializationConfig config, BeanDescription beanDesc, JsonDeserializer<?> deserializer) {
JsonDeserializer<?> configuredDeserializer = super.modifyDeserializer(config, beanDesc, deserializer);
if (Document.class.isAssignableFrom(beanDesc.getBeanClass())) {
logger.debug("Returning custom deserializer for documents");
configuredDeserializer = new DocumentDeserializer(configuredDeserializer, (Class<Document>)beanDesc.getBeanClass());
}
return configuredDeserializer;
}
});
As you can see, if the object to generate is a "Document", I am modifying the deserializer returning a custom deserializer. I am passing the default deserializer to the constructor so I can use it later.
When I try to deserialize, Jackson fails with the error:
No _valueDeserializer assigned(..)
I have investigated and it seems that the default deserializer does not have the correct deserializers for its properties: for all the properties, it is using the deserializer FailingDeserializer that, of course, fails and returns the error mentioned above. This deserializer is supposed to be substituted but it is not.
It seems that, after calling the method modifyDeserializer, Jackson completes the configuration.
The custom deserializer that I am using is:
#SuppressWarnings("serial")
public class DocumentDeserializer extends StdDeserializer<Document> {
private JsonDeserializer<?> defaultDeserializer;
private DocumentDeserializer(JsonDeserializer<?> defaultDeserializer, Class<? extends Document> clazz) {
super(clazz);
this.defaultDeserializer = defaultDeserializer;
}
#Override
public Document deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
Document documentDeserialized = (Document) defaultDeserializer.deserialize(jp, ctxt);
/* I want to modify the documentDeserialized before returning it */
return documentDeserialized;
}
}
UPDATE:
I solved the problem using a different Deserializer:
public class CustomDeserializerModifier extends BeanDeserializerModifier {
private static final Logger logger = Logger.getLogger(CustomDeserializerModifier.class);
public CustomDeserializerModifier (Factory factory) {
this.factory = factory;
}
#Override
public JsonDeserializer<?> modifyDeserializer(DeserializationConfig config, BeanDescription beanDesc, JsonDeserializer<?> deserializer) {
JsonDeserializer<?> configuredDeserializer;
if (CustomDeserializedNode.class.isAssignableFrom(beanDesc.getBeanClass())) {
Converter<Object, Object> conv = beanDesc.findDeserializationConverter();
JavaType delegateType = conv.getInputType(config.getTypeFactory());
configuredDeserializer = new CustomDeserializedNodeDeserializer(conv, delegateType, (JsonDeserializer<Document>) deserializer,
(Class<? extends CustomDocument<?>>)beanDesc.getBeanClass());
} else {
configuredDeserializer = super.modifyDeserializer(config, beanDesc, deserializer);
}
return configuredDeserializer;
}
#SuppressWarnings("serial")
public class CustomDeserializedNodeDeserializer extends StdDelegatingDeserializer<Object> {
private Class<? extends CustomDocument<?>> beanClass;
public CustomDeserializedNodeDeserializer(Converter<Object,Object> converter,
JavaType delegateType, JsonDeserializer<Document> delegateDeserializer, Class<? extends CustomDocument<?>> beanClass) {
super(converter, delegateType, delegateDeserializer);
this.beanClass = beanClass;
}
#Override
public CustomDeserializedNode deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
CustomDeserializedNode node = (CustomDeserializedNode)factory.createCustomDocument(beanClass);
CustomDeserializedNode documentDeserialized = (Document) super.deserialize(jp, ctxt, node);
return documentDeserialized;
}
}
}
Probably extending StdDelegatingDeserializer does what #StaxMan is suggesting.
This should be added in a FAQ, but what you need to do is to implement 2 interfaces:
ResolvableDeserializer (method resolve(...))
ContextualDeserializer (method createContextual(...))
and delegate these calls to defaultDeserializer in case it implements one or both interfaces. These are required for deserializer initialization; especially ContextualDeserializer through which property annotations are made available to deserializers.
And ResolvableDeserializer is used by BeanDeserializer to get deserializers for properties it has, if any; this is where _valueDeserializer in question is likely to be fetched.

Can I use both the JAX-RS and RAML extensions in Restlet in the same application?

I am preparing a ReSTful service which I would like to have documented using RAML (and perhaps Swagger as well), but it seems that I cannot implement both JAX-RS and RAML in the same application at the same time.
I have created an Application class for JAX-RS as follows:
public class Application extends javax.ws.rs.core.Application {
#Override
public Set<Class<?>> getClasses() {
// Use the reflections library to scan the current package tree for
// classes annotated with javax.ws.rs.Path and add them to the JAX-RS
// application
Reflections reflections = new Reflections(this.getClass().getPackage().getName());
return reflections.getTypesAnnotatedWith(Path.class);
}
}
I attach the JAX-RS Application object as follows:
Component component = new Component();
Server server = new Server(Protocol.HTTP, PORT);
component.getServers().add(server);
JaxRsApplication jaxRsApplication = new JaxRsApplication(component.getContext().createChildContext());
jaxRsApplication.add(new Application());
jaxRsApplication.setObjectFactory(objectFactory);
component.getDefaultHost().attach("/rest", jaxRsApplication);
And I would also like to implement the RAML extension, but it looks like it is tied to the Restlet Router and having it's own Application class. Is there a way to combine the two?
Indeed the RAML extension of Restlet isn't designed to be used within JAXRS application. That said you can define a resource that provide the RAML content based on classes ApplicationIntrospector of Restlet and RamlEmitter of RAML parser, as described below:
public class RamlResource {
private Definition definition;
#Path("/raml")
#GET
public String getRaml() {
return new RamlEmitter().dump(RamlTranslator
.getRaml(getDefinition()));
}
private synchronized Definition getDefinition() {
if (definition == null) {
synchronized (RamlResource.class) {
definition = ApplicationIntrospector.getDefinition(
Application.getCurrent(),
new Reference("/"), null, false);
}
}
return definition;
}
}
It's the way the RAML extension of Restlet works. You could also use such an approach for Swagger but be careful since Swagger 1.2 requires several resources (a main and several sub ones with each categories). It's not the case anymore for Swagger 2.
You can notice that there is a JAX-RS support for Swagger in the extension org.restlet.ext.swagger.
----- Edited
Perhaps can you make a try with this class that corresponds to a port of the class JaxRsApplicationSwaggerSpecificationRestlet to RAML. It's based on the class JaxRsIntrospector which seems relevant for JAX-RS application:
public class JaxRsApplicationRamlSpecificationRestlet extends Restlet {
private Application application;
private String basePath;
private Reference baseRef;
private Definition definition;
public JaxRsApplicationRamlSpecificationRestlet(Application application) {
this(null, application);
}
public JaxRsApplicationRamlSpecificationRestlet(Context context, Application application) {
super(context);
this.application = application;
}
public void attach(Router router) {
attach(router, "/api-docs");
}
public void attach(Router router, String path) {
router.attach(path, this);
router.attach(path + "/{resource}", this);
}
public Representation getApiDeclaration() {
Raml raml = RamlTranslator.getRaml(
getDefinition());
ObjectMapper mapper = new ObjectMapper(new YAMLFactory());
try {
return new StringRepresentation(
mapper.writeValueAsString(raml),
MediaType.APPLICATION_YAML);
} catch (Exception ex) {
return new StringRepresentation("error");
}
}
public String getBasePath() {
return basePath;
}
private synchronized Definition getDefinition() {
if (definition == null) {
synchronized (JaxRsApplicationRamlSpecificationRestlet.class) {
definition = JaxRsIntrospector.getDefinition(application,
baseRef, false);
}
}
return definition;
}
#Override
public void handle(Request request, Response response) {
super.handle(request, response);
if (Method.GET.equals(request.getMethod())) {
response.setEntity(getApiDeclaration());
} else {
response.setStatus(Status.CLIENT_ERROR_METHOD_NOT_ALLOWED);
}
}
public void setApiInboundRoot(Application application) {
this.application = application;
}
public void setApplication(Application application) {
this.application = application;
}
public void setBasePath(String basePath) {
this.basePath = basePath;
// Process basepath and check validity
this.baseRef = basePath != null ? new Reference(basePath) : null;
}
}
You can use this class like this:
JaxRsApplication application
= new JaxRsApplication(component.getContext());
MyApplication app = new MyApplication();
application.add(app);
new JaxRsApplicationRamlSpecificationRestlet(app);
(...)
There is no need for a dedicated resource. Please note that this code is a bit experimental ;-) I could propose it back for a contribution for the extension raml in Restlet...
Hope it helps you,
Thierry

How do you mock the querystring in a WCF service?

I have a WCF service which has methods that depend on reading values (OData) from the http request's querystring. I'm trying to write unit tests which inject in mock values into the querystring, then when I call the method it would use these mock values rather than erroring due to the request context not being available.
I've tried using WCFMock (which is based on Moq) however I don't see a way to set or get the querystring from the WebOperationContext that it provides.
Any ideas?
I ended up using the IOC pattern to solve this, creating an IQueryStringHelper interface that is passed into the constructor of the service. If it isn't passed in then it'll default to use the "real" QueryStringHelper class. When running test cases, it'll use an overloaded service constructor to pass in the TestQueryStringHelper instance, which lets you set a mock value for the querystring.
Here is the querystring helper code.
public interface IQueryStringHelper {
string[] GetParameters();
}
public class QueryStringHelper : IQueryStringHelper {
public string[] GetParameters() {
var properties = OperationContext.Current.IncomingMessageProperties;
var property = properties[HttpRequestMessageProperty.Name] as HttpRequestMessageProperty;
string queryString = property.QueryString;
return queryString.Split('&');
}
}
public class TestQueryStringHelper : IQueryStringHelper {
private string mockValue;
public TestQueryStringHelper(string value) {
mockValue = value;
}
public string[] GetParameters() {
return mockValue.Split('&');
}
}
And the service implementation:
public partial class RestService : IRestService {
private IAuthenticator _auth;
private IQueryStringHelper _queryStringHelper;
public RestService() : this(new Authenticator(), new QueryStringHelper()) {
}
public RestService(IAuthenticator auth, IQueryStringHelper queryStringHelper = null) {
_auth = auth;
if (queryStringHelper != null) {
_queryStringHelper = queryStringHelper;
}
}
}
And how to consume it from a test case:
string odata = String.Format("$filter=Id eq guid'{0}'", "myguid");
var service = new RestService(m_auth,new TestQueryStringHelper(odata));
var entities = service.ReadAllEntities();
Hopefully this helps someone else.