I wonder if there is an easy way to get a String with the result of a sling content exporter in AEM.
In my current usecase I need the content of a component's .model.json output in the component's htl file and sending an additional request is obviously not a good idea.
Any hints on how I can get the data?
After some reading and experimenting, I found a way to do it:
Add a dependency to the following package in your pom:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.5</version>
</dependency>
Then create a method in your model that does the serialization:
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
public String getJson() {
ObjectMapper objectMapper = new ObjectMapper();
String tStr = "";
try {
tStr = objectMapper.writeValueAsString(this);
logger.error(tStr);
}
catch (JsonProcessingException ex) {
logger.error("Cannot do it: {}", ex.getMessage());
}
return tStr;
}
Now you can call this method from inside a HTL script or any other code fragment that has access to the model.
Related
I can use String object in JAX-RS rest service but not able to use POJO object. How I should configure a POJO class to enable it to be used as a resource in JAX-RS rest service?
DTO class
public class RestServiceDTO {
private String groupId;
public String getGroupId() {
return groupId;
}
public void setGroupId(String groupId)
this.groupId = groupId;
}
#Override
public String toString() {
return "RestServiceDTO [groupId=" + groupId + "]";
}
}
Rest service:
#Component(
immediate = true,
property = {
JaxrsWhiteboardConstants.JAX_RS_APPLICATION_BASE + "=/greetings",
},
service = Application.class
)
public class RestServiceApplication extends Application {
public Set<Object> getSingletons() {
return Collections.<Object>singleton(this);
}
#POST
#Path("/post")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public String test(RestServiceDTO dto) {
String groupid = dto.getGroupId();
return "{'groupid':'" + groupid + "'}";
}
}
Error:
2019-02-12 13:33:58.021 ERROR [http-nio-8080-exec-1][JAXRSUtils:83] No
message body reader has been found for class com.dto.RestServiceDTO,
ContentType: application/json
Since version 7.1 Liferay supports the OSGi JAX-RS Whiteboard specification, which means support for JAX-RS 2.1 using CXF, which also mean that there is support for JAXB annotated POJOs.
If you need to return a simple POJO you would normally be OK just by annotating your POJO class with #XmlRootElement.
Make sure you get JAXB runtime support attached to your application by requiring it on your application component configuration putting the property osgi.jaxrs.extension.select=(osgi.jaxrs.name=jaxb-json) on your application component.
This property will instruct the JAX-RS whiteboard to not start your application until the extension is present and ready for your application.
Since Liferay 7.2 the JAXB default implementation has been changed to Jackson. There is no configuration change needed, but now every POJO can be serialized to JSON even if the POJO is not annotated. Just make sure the jaxb runtime support is attached to your application the same as above.
In both 7.1 and 7.2 you can check the status of your JAX-RS applications, and attached extensions, using the gogo shell command jaxrs:check
Liferay uses the Apache CXF implementation of JAX-RS. As #dkb mentioned in the comments you need to provide the annotation that you have in the sample code.
You need to add the dependencies. See the list below and note that some are provided by the platform but some needs to be included in your jar and don't forget the transitive dependencies.
<dependency>
<groupId>javax.ws.rs</groupId>
<artifactId>javax.ws.rs-api</artifactId>
<version>2.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-core</artifactId>
<version>3.0.3</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-frontend-jaxrs</artifactId>
<version>3.0.3</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-rs-extension-providers</artifactId>
<version>3.0.3</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.jaxrs</groupId>
<artifactId>jackson-jaxrs-json-provider</artifactId>
<version>2.7.9</version>
</dependency>
The last thing is. You need to register your Jackson provider within the JAX-RS app. It is done in teh applicaiton class for example like this (there are more ways how to do it).
#Override
public Set<Object> getSingletons() {
Set<Object> singletons = new HashSet<>();
singletons.add(getJacksonProvider());
return Collections.unmodifiableSet(singletons);
}
Add dependency
for MAVEN:
<dependency>
<groupId>com.fasterxml.jackson.jaxrs</groupId>
<artifactId>jackson-jaxrs-json-provider</artifactId>
<version>2.9.10</version>
</dependency>
for GRADLE:
compileOnly group: 'com.fasterxml.jackson.jaxrs', name: 'jackson-jaxrs-json-provider', version: '2.9.10'
now add this method in your Apllication class
private JacksonJsonProvider getJacksonJsonProvider() {
JacksonJsonProvider jacksonJsonProvider = new JacksonJsonProvider();
ObjectMapper objectMapper = new ObjectMapper();
// Prevent serialization of null and empty string values
objectMapper.setSerializationInclusion(Include.NON_EMPTY);
jacksonJsonProvider.setMapper(objectMapper);
jacksonJsonProvider.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
return jacksonJsonProvider;
}
now change the code of getSingletons() method of your Application class to
#Override
public Set<Object> getSingletons() {
Set<Object> singletons = new HashSet<>();
singletons.add(this);
singletons.add(getJacksonJsonProvider());
return Collections.unmodifiableSet(singletons);
}
now I think you have to change your return statement to
return Response.ok(JSONFactoryUtil.looseSerializeDeep("{'groupid':'" + groupid + "'}"), MediaType.APPLICATION_JSON).build();
but I am not sure that you have to change your return statement or yours one will run ok
I am getting the error:
"Method has too many Body parameters"
when trying to generate a Spring Feign client
#RequestMapping(value="/media", method=RequestMethod.POST)
String uploadMedia(#RequestHeader("Authentication") String token,
#RequestPart("media") MultipartFile audio,
#RequestPart("a-json-object") SomeClass someClazz,
#RequestPart("another-json-object") AnotherClass anotherClazz);
I found the following solution, which works when using regular Feign annotations, but not with Spring MVC annotations:
'Too many body parameters' Exception on Feign Client
It should be possible now. Add the following dependencies:
<dependencies>
...
<dependency>
<groupId>io.github.openfeign.form</groupId>
<artifactId>feign-form</artifactId>
<version>2.2.0</version>
</dependency>
<dependency>
<groupId>io.github.openfeign.form</groupId>
<artifactId>feign-form-spring</artifactId>
<version>2.2.0</version>
</dependency>
...
and use this client configuration:
#FeignClient(name = "file-upload-service", configuration = FileUploadServiceClient.MultipartSupportConfig.class)
public interface FileUploadServiceClient extends IFileUploadServiceClient {
#Configuration
public class MultipartSupportConfig {
#Bean
#Primary
#Scope("prototype")
public Encoder feignFormEncoder() {
return new SpringFormEncoder();
}
}
}
Example was taken from: feign-form docs
I would like to have my JaxRs resource to take a custom method argument that is built from some parameter in the request.
Something to be used in conjunction with another object created from the body.
Something like:
#Resource
public class MyResource {
#Path("/resource")
public Object resource(MyResourceDTO body, AConfiguration conf){
}
}
For which the AConfiguration is created from some headers in the request.
How can I achive it?
I need something like th spring webargumentresovler: http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/bind/support/WebArgumentResolver.html
For my case MyResource is a subresource, the method should work also in this case...
If you add a DTO as parameter of your resource method your JAX-RS runtime will try to convert the body of the request into this type. You can additionally add any of the #xParam parameters like #QueryParam as parameters of your resource method. (The only exception is #FormParam as they are found in the body).
If you want to encapsulate multiple of your Params in one object you can use #BeanParam. Your Configuration class could look like this:
public class Configuration {
#QueryParam("foo")
private String foo;
#HeaderParam("bar")
private String bar;
// getters + setters
}
And can be used like this:
#POST
public Response someMethod(Dto dto, #BeanParam Configuration conf) {}
You can use something like below. Your conf object have be sent as json from the client. If the parameters in conf object have to change dynamically you have to follow the second approach.
#Resource
public class MyResource {
#POST
#Consumes("application/json")
#Path("/resource")
public Object resource(AConfiguration conf){
// This method can receive multiple objects here. Currently it receives
// conf object only as the payload of the post method.
}
}
To change the conf object dynamically, You can send json String.
public Object resource(String confJson){
// Collect parameters manually here.
}
In your pom.xml, you should include,
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jackson-provider</artifactId>
<version>2.3.1.GA</version>
</dependency>
Edit:
You can set a json string as a header param (But, not the best practice.) Or you can set different headers at you will and access them using HttpHeaders. Here is an example.
public Object resource(#Context HttpHeaders confHeaders){
// Collect parameters manually.
String confJson = confHeaders.getRequestHeader("confJson").get(0);
// cast your `confJson` to `AConfiguration aConf` here.
// process query params and set them to aConf here.
}
I have implemented web service:
#WebServiceClient(//parameters//)
#HandlerChain(file = "handlers.xml")
public class MyWebServiceImpl {...}
Also I have implemented ObjectFactory with list of classes for creating my requests and responses. For Example class Test.
I need to get xml of response.
I try to use JAX-WS SOAP handler, so I add this #HandlerChain(file = "handlers.xml") anotation.
My handlers.xml looks like:
<?xml version="1.0" encoding="UTF-8"?>
<handler-chains xmlns="http://java.sun.com/xml/ns/javaee">
<handler-chain>
<handler>
<handler-class>java.com.webservice.service.LoggingHandler</handler-class>
</handler>
</handler-chain>
</handler-chains>
My LoggingHandler class is:
import java.io.PrintWriter;
import java.util.Set;
import javax.xml.namespace.QName;
import javax.xml.soap.SOAPMessage;
import javax.xml.ws.handler.MessageContext;
import javax.xml.ws.handler.soap.SOAPMessageContext;
public class LoggingHandler implements javax.xml.ws.handler.soap.SOAPHandler<SOAPMessageContext> {
public void close(MessageContext messagecontext) {
}
public Set<QName> getHeaders() {
return null;
}
public boolean handleFault(SOAPMessageContext messagecontext) {
return true;
}
public boolean handleMessage(SOAPMessageContext smc) {
Boolean outboundProperty = (Boolean) smc.get (MessageContext.MESSAGE_OUTBOUND_PROPERTY);
if (outboundProperty.booleanValue()) {
System.out.println("\nOutbound message:");
} else {
System.out.println("\nInbound message:");
}
SOAPMessage message = smc.getMessage();
try {
PrintWriter writer = new PrintWriter("soap_responce" + System.currentTimeMillis(), "UTF-8");
writer.println(message);
writer.close();
message.writeTo(System.out);
System.out.println(""); // just to add a newline
} catch (Exception e) {
System.out.println("Exception in handler: " + e);
}
return outboundProperty;
}
}
I have test class which creates request, here are part of code:
MyWebServiceImpl impl = new MyWebServiceImpl(url, qName);
ws = impl.getMyWebServicePort();
Test req = new Test();
I suppose to get xml response in file "soap_responce" + System.currentTimeMillis(). But such file isn't even created. Please suggest how to get xml response, I'm new to web services and may do something wrong. Thanks
Using SOAP handlers is IMHO perfectly fine for such a task. I would approach it the same way.
I was able to use your configuration with minor modifications to get the example running. As a result I am able to see generated files. If you can't see them please check whether you check correct path, e.g. using:
File file = new File("soap_responce" + System.currentTimeMillis());
System.out.println(file.getAbsolutePath());
What I changed is:
package from java.com.webservice.service.LoggingHandler to com.webservice.service.LoggingHandler as packages starting with java are forbidden
Complete project can be found here:
https://github.com/destin/SO-answers/tree/master/SO-how-get-xml-responce-using-jax-ws-soap-handler
org.dpytel.jaxws.jaxws_java_first_jboss.client.Main class shows how I get and execute the web service.
BTW. you don't need to implement client stub and object factory etc when you have WSDL file. You can use wsimport tool. You can check how to use it in mentioned project.
Here is what I am trying to do:
#POST
#Path("/MyPath")
#Produces("text/xml")
#RolesAllowed({"user"})
public Output myMethod (#QueryParam("itemId") long ItemId,
#QueryParam("plannedstartdate") Calendar plannedStartDate,
#QueryParam("plannedreturndate") Calendar plannedReturnDate)
I am using JBoss AS7. As far as I understan, resteasy is integrated into JBoss AS7. I am able to run simple rest services.
The only documentation I found about passing dates is at the link :
http://docs.jboss.org/resteasy/2.0.0.GA/userguide/html/StringConverter.html#StringParamUnmarshaller
I am not able to follow this and fix the issue as the instructions are not clear.
When I try to create an annotation DateFormat as given in the example, it does not recognize StringParamUnmarshaller. I don't know where to get it from. If resteasy is already integrated into JBoss AS7, is this not supposed to be recognized?
My pom.xml has the following dependency:
<!-- Import the JAX-RS API, we use provided scope as the API is included
in JBoss AS 7 -->
<dependency>
<groupId>org.jboss.spec.javax.ws.rs</groupId>
<artifactId>jboss-jaxrs-api_1.1_spec</artifactId>
<scope>provided</scope>
</dependency>
The calls to this method fail as the String to Calendar conversion does not happen. I dont want to pass String instead of Calendar as there are other clients that make java call directly. Can anyone help with how I can pass dates to Rest Calls?
Thanks
Veer
This issue is resolved. See the following code.
Create an Annotation class CalendarFormat.java:
#Retention(RUNTIME)
#StringParameterUnmarshallerBinder(CalendarFormatter.class)
public #interface CalendarFormat {
String value();
}
Add a class CalendarFormatter.java:
public class CalendarFormatter implements StringParameterUnmarshaller<Calendar> {
private SimpleDateFormat formatter;
public void setAnnotations(Annotation[] annotations) {
CalendarFormat format = FindAnnotation.findAnnotation(annotations, CalendarFormat.class);
formatter = new SimpleDateFormat(format.value());
}
public Calendar fromString(String str) {
try {
Calendar cal = Calendar.getInstance();
cal.setTime(formatter.parse(str));
return cal;
} catch (ParseException e) {
throw new RuntimeException(e);
}
}
}
Add to POM
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxrs</artifactId>
<version>2.3.3.Final</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-multipart-provider</artifactId>
<version>2.3.4.Final</version>
<exclusions>
<exclusion>
<artifactId>resteasy-jaxrs</artifactId>
<groupId>org.jboss.resteasy</groupId>
</exclusion>
</exclusions>
</dependency>
Change the method signature to use the annotation
#POST
#Path("/MyPath")
#Produces("text/xml")
#RolesAllowed({"user"})
public Output myMethod(#QueryParam("itemId") long ItemId,
#QueryParam("plannedstartdate") #CalendarFormat("MM-dd-yyyy") Calendar plannedStartDate,
#QueryParam("plannedreturndate") #CalendarFormat("MM-dd-yyyy") Calendar plannedReturnDate)
That's it.
It's not possible to use java.util.Calendar as an argument of #QueryParam, because it doesn't have a one-arg constructor that accepts String. The only option you have is to introduce a new class QueryCalendar, which will have one-arg constructor and will return Calendar (or inherit it).
More information about who can be an argument: http://docs.oracle.com/javaee/6/api/javax/ws/rs/QueryParam.html