I have a client RPC-JSON in Android and I am trying make a RPC-JSON Server with a library for Java (http://software.dzhuvinov.com/json-rpc-2.0-server.html). This is the official example:
// The JSON-RPC 2.0 Base classes that define the
// JSON-RPC 2.0 protocol messages
import com.thetransactioncompany.jsonrpc2.*;
// The JSON-RPC 2.0 server framework package
import com.thetransactioncompany.jsonrpc2.server.*;
import java.text.*;
import java.util.*;
/**
* Demonstration of the JSON-RPC 2.0 Server framework usage. The request
* handlers are implemented as static nested classes for convenience, but in
* real life applications may be defined as regular classes within their old
* source files.
*
* #author Vladimir Dzhuvinov
* #version 2011-03-05
*/
public class Example {
// Implements a handler for an "echo" JSON-RPC method
public static class EchoHandler implements RequestHandler {
// Reports the method names of the handled requests
public String[] handledRequests() {
return new String[]{"echo"};
}
// Processes the requests
public JSONRPC2Response process(JSONRPC2Request req, MessageContext ctx) {
if (req.getMethod().equals("echo")) {
// Echo first parameter
List params = (List)req.getParams();
Object input = params.get(0);
return new JSONRPC2Response(input, req.getID());
}
else {
// Method name not supported
return new JSONRPC2Response(JSONRPC2Error.METHOD_NOT_FOUND, req.getID());
}
}
}
// Implements a handler for "getDate" and "getTime" JSON-RPC methods
// that return the current date and time
public static class DateTimeHandler implements RequestHandler {
// Reports the method names of the handled requests
public String[] handledRequests() {
return new String[]{"getDate", "getTime"};
}
// Processes the requests
public JSONRPC2Response process(JSONRPC2Request req, MessageContext ctx) {
if (req.getMethod().equals("getDate")) {
DateFormat df = DateFormat.getDateInstance();
String date = df.format(new Date());
return new JSONRPC2Response(date, req.getID());
}
else if (req.getMethod().equals("getTime")) {
DateFormat df = DateFormat.getTimeInstance();
String time = df.format(new Date());
return new JSONRPC2Response(time, req.getID());
}
else {
// Method name not supported
return new JSONRPC2Response(JSONRPC2Error.METHOD_NOT_FOUND, req.getID());
}
}
}
public static void main(String[] args) {
// Create a new JSON-RPC 2.0 request dispatcher
Dispatcher dispatcher = new Dispatcher();
// Register the "echo", "getDate" and "getTime" handlers with it
dispatcher.register(new EchoHandler());
dispatcher.register(new DateTimeHandler());
// Simulate an "echo" JSON-RPC 2.0 request
List echoParam = new LinkedList();
echoParam.add("Hello world!");
JSONRPC2Request req = new JSONRPC2Request("echo", echoParam, "req-id-01");
System.out.println("Request: \n" + req);
JSONRPC2Response resp = dispatcher.process(req, null);
System.out.println("Response: \n" + resp);
// Simulate a "getDate" JSON-RPC 2.0 request
req = new JSONRPC2Request("getDate", "req-id-02");
System.out.println("Request: \n" + req);
resp = dispatcher.process(req, null);
System.out.println("Response: \n" + resp);
// Simulate a "getTime" JSON-RPC 2.0 request
req = new JSONRPC2Request("getTime", "req-id-03");
System.out.println("Request: \n" + req);
resp = dispatcher.process(req, null);
System.out.println("Response: \n" + resp);
}
}
I search in the manual and Google but I don't understand how I can do that the server is waiting for a request and send a response. How I can do it?
Related
A SOAP Web-service, which accepts request in following format -
<?xml version = "1.0"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV = "http://www.w3.org/2001/12/soap-envelope"
xmlns:ns="http://...." xmlns:ns1="http://...." xmlns:ns2="http://...."
xmlns:ns3="http://....">
<SOAP-ENV:Header>
<ns:EMContext>
<messageId>1</messageId>
<refToMessageId>ABC123</refToMessageId>
<session>
<sessionId>3</sessionId>
<sessionSequenceNumber>2021-02-24T00:00:00.000+5:00</sessionSequenceNumber>
</session>
<invokerRef>CRS</invokerRef>
</ns:EMContext>
</SOAP-ENV:Header>
<SOAP-ENV:Body>
<ns1:getEmployee>
<ns:empId>111</ns:empId>
</ns1:getEmployee>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
When trying to make a SOAP request to it using JAXB2, it is giving org.springframework.ws.soap.client.SoapFaultClientException: EMContext Header is missing
I am using
pring-boot-starter
spring-boot-starter-web-services
org.jvnet.jaxb2.maven2 : maven-jaxb2-plugin : 0.14.0
and
Client -
public class MyClient extends WebServiceGatewaySupport {
public GetEmployeeResponse getEmployee(String url, Object request){
GetEmployeeResponse res = (GetEmployeeResponse) getWebServiceTemplate().marshalSendAndReceive(url, request);
return res;
}
}
Configuration -
#Configuration
public class EmpConfig {
#Bean
public Jaxb2Marshaller marshaller(){
Jaxb2Marshaller jaxb2Marshaller = new Jaxb2Marshaller();
jaxb2Marshaller.setContextPath("com.crsardar.java.soap.client.request");
return jaxb2Marshaller;
}
#Bean
public MyClient getClient(Jaxb2Marshaller jaxb2Marshaller){
MyClient myClient = new MyClient();
myClient.setDefaultUri("http://localhost:8080/ws");
myClient.setMarshaller(jaxb2Marshaller);
myClient.setUnmarshaller(jaxb2Marshaller);
return myClient;
}
}
App -
#SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
#Bean
CommandLineRunner lookup(MyClient myClient){
return args -> {
GetEmployeeRequest getEmployeeRequest = new GetEmployeeRequest();
getEmployeeRequest.setId(1);
GetEmployeeResponse employee = myClient.getEmployee("http://localhost:8080/ws", getEmployeeRequest);
System.out.println("Response = " + employee.getEmployeeDetails().getName());
};
}
}
How can I add EMContext Header to the SOAP request?
The server is complaining because your Web Service client is not sending the EMContext SOAP header in your SOAP message.
Unfortunately, currently Spring Web Services lack of support for including SOAP headers in a similar way as the SOAP body information is processed using JAXB, for example.
As a workaround, you can use WebServiceMessageCallback. From the docs:
To accommodate the setting of SOAP headers and other settings on the message, the WebServiceMessageCallback interface gives you access to the message after it has been created, but before it is sent.
In your case, you can use something like:
public class MyClient extends WebServiceGatewaySupport {
public GetEmployeeResponse getEmployee(String url, Object request){
// Obtain the required information
String messageId = "1";
String refToMessageId = "ABC123";
String sessionId = "3";
String sessionSequenceNumber = "2021-02-24T00:00:00.000+5:00";
String invokerRef = "CRS";
GetEmployeeResponse res = (GetEmployeeResponse) this.getWebServiceTemplate().marshalSendAndReceive(url, request, new WebServiceMessageCallback() {
#Override
public void doWithMessage(WebServiceMessage message) throws IOException, TransformerException {
// Include the SOAP header content for EMContext
try {
SoapMessage soapMessage = (SoapMessage)message;
SoapHeader header = soapMessage.getSoapHeader();
StringSource headerSource = new StringSource(
"<EMContext xmlns:ns=\"http://....\">" +
"<messageId>" + messageId + "</messageId>" +
"<refToMessageId>" + refToMessageId + "</refToMessageId>" +
"<session>" +
"<sessionId>" + sessionId + "</sessionId>" +
"<sessionSequenceNumber>" + sessionSequenceNumber + "</sessionSequenceNumber>" +
"</session>" +
"<invokerRef>" + invokerRef + "</invokerRef>" +
"</EMContext>"
);
Transformer transformer = TransformerFactory.newInstance().newTransformer();
transformer.transform(headerSource, header.getResult());
} catch (Exception e) {
// handle the exception as appropriate
e.printStackTrace();
}
}
});
return res;
}
}
Similar questions have been posted in SO. Consider for instance review this or this other.
Has anyone tried integrating there java code from CCU V2 to CCU V3 with fast purge. I read the documentation but unable to understand what needs to be done in case of a java based project. After we have configured the client in Akamai console how do we write the code to access and clear.
Any help is highly appreciated.
Thanks,
Tushar
The general approach is to write some code that makes an HTTP request to the fast purge endpoint.
Here is some an example:
import com.akamai.edgegrid.auth.*;
//other imports
public void callAkamaiFastPurgeForUrls(Set<String> urlsToPurge) throws URISyntaxException, IOException, RequestSigningException {
if(!urlsToPurge.isEmpty()) {
int status;
String json = getPurgeJson(urlsToPurge);
HttpRequest signedRequest = getHttpRequest(json, compProperty);
HttpResponse response = signedRequest.execute();
status = response.getStatusCode();
if (status == 201) {
//handle success responses as you see fit
} else {
//handle non-success responses as you see fit
}
}
}
private static String getPurgeJson(Set<String> pathsToPurge) {
//your code to turn the list of urls into JSON in this format:
//{"objects":["https://www.yourdomain.com/page1.html","https://www.yourdomain.com/page2.html"]}
return //JSON string such as the example above
}
private HttpRequest getHttpRequest(String json, Dictionary<String, Object> dispatcherAndAkamaiServletProperties) throws URISyntaxException, IOException, RequestSigningException {
String hostName = yourCodeToFetchConfigValues("hostname");
String accessToken = yourCodeToFetchConfigValues("accesstoken");
String clientToken = yourCodeToFetchConfigValues("clienttoken");
String clientSecret = yourCodeToFetchConfigValues("clientsecret");
String apiUrl = yourCodeToFetchConfigValues("apiurl");
String proxyServer = yourCodeToFetchConfigValues("proxyhostakamai");
int proxyPort = yourCodeToFetchConfigValues("proxyport");
HttpTransport httpTransport = new NetHttpTransport.Builder()
.setProxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyServer, proxyPort))).build();
HttpRequestFactory requestFactory = httpTransport.createRequestFactory();
URI uri = new URI(HTTPS, hostName, apiUrl, null, null);
HttpContent body = new ByteArrayContent("application/json", json.getBytes());
HttpRequest request = requestFactory.buildPostRequest(new GenericUrl(uri), body);
HttpHeaders headers = request.getHeaders();
headers.set("Host", hostName);
ClientCredential credential = new DefaultCredential(clientToken, accessToken, clientSecret);
RequestSigner signer = new EdgeGridV1Signer(Collections.emptyList(), 1024 * 2);
return signer.sign(request, credential);
}
In addition you will likely need to update your truststore to include the certificates of the Akamai endpoints you are calling so that the SSL communication can happen.
What #Shawn has answered is mostly correct. Though you would like to have more things in plate like your custom replication agent, a custom content builder and a transport handler if you are integrating it with AEM. Also, there can be few dependency issues as well.
If you need help with all these, you can refer to below article:
https://www.linkedin.com/pulse/akamai-cache-purge-aem-through-java-code-shubham-saxena/
For a transport handler you can use below snippet:
package com.myproject.bundle.core.services.impl;
import java.io.IOException;
import java.net.URI;
import java.nio.charset.Charset;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpStatus;
import org.apache.http.entity.ContentType;
import org.apache.jackrabbit.util.Base64;
import org.apache.sling.api.resource.ValueMap;
import org.apache.sling.commons.json.JSONArray;
import org.apache.sling.commons.json.JSONException;
import org.apache.sling.commons.json.JSONObject;
import org.apache.sling.commons.osgi.PropertiesUtil;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.akamai.edgegrid.signer.ClientCredential;
import com.akamai.edgegrid.signer.exceptions.RequestSigningException;
import com.akamai.edgegrid.signer.googlehttpclient.GoogleHttpClientEdgeGridRequestSigner;
import com.day.cq.replication.AgentConfig;
import com.day.cq.replication.ReplicationActionType;
import com.day.cq.replication.ReplicationException;
import com.day.cq.replication.ReplicationResult;
import com.day.cq.replication.ReplicationTransaction;
import com.day.cq.replication.TransportContext;
import com.day.cq.replication.TransportHandler;
import com.myproject.bundle.core.configuration.BaseConfigurationService;
import com.myproject.bundle.core.constants.MyConstants;
import com.myproject.bundle.core.search.services.MyProjectConfigurationService;
import com.google.api.client.http.ByteArrayContent;
import com.google.api.client.http.GenericUrl;
import com.google.api.client.http.HttpHeaders;
import com.google.api.client.http.HttpRequest;
import com.google.api.client.http.HttpRequestFactory;
import com.google.api.client.http.HttpResponse;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.http.apache.ApacheHttpTransport;
/**
* Transport handler to send test and purge requests to Akamai and handle
* responses. The handler sets up basic authentication with the user/pass from
* the replication agent's transport config and sends a GET request as a test
* and POST as purge request. A valid test response is 200 while a valid purge
* response is 201.
*
* The transport handler is triggered by setting your replication agent's
* transport URL's protocol to "akamai://".
*
* The transport handler builds the POST request body in accordance with
* Akamai's Fast Purge REST API {#link https://developer.akamai.com/api/core_features/fast_purge/v3.html}
* using the replication agent properties.
*/
#Component(service = TransportHandler.class, immediate = true)
public class AkamaiTransportHandler implements TransportHandler {
/**The Solr Server Configuration Service.*/
#Reference
MyProjectConfigurationService myProjectConfigurationService;
#Reference
BaseConfigurationService baseConfigurationService;
/**Logger Instantiation for Akamai Transport Handler*/
private static final Logger LOGGER = LoggerFactory.getLogger(AkamaiTransportHandler.class);
/** Protocol for replication agent transport URI that triggers this transport handler. */
private static final String AKAMAI_PROTOCOL = "akamai://";
/**Config Pid for Akamai Flush*/
private static final String AKAMAI_FLUSH_CONFIG_PID = "com.myproject.bundle.core.configuration.AkamaiFlushConfiguration";
/** Replication agent type property name. Valid values are "arl" and "cpcode". */
private static final String PROPERTY_AKAMAI_TYPE = "type";
/** Replication agent multifield CP Code property name.*/
private static final String PROPERTY_AKAMAI_CP_CODES = "4321xxx";
/** Replication agent domain property name. Valid values are "staging" and "production". */
private static final String PROPERTY_AKAMAI_DOMAIN = "domain";
/** Replication agent action property name. Valid values are "remove" and "invalidate". */
private static final String PROPERTY_AKAMAI_ACTION = "action";
/** Replication agent default type value */
private static final String PROPERTY_AKAMAI_TYPE_DEFAULT = "url";
/** Replication agent default domain value */
private static final String PROPERTY_AKAMAI_DOMAIN_DEFAULT = "production";
/** Replication agent default action value */
private static final String PROPERTY_AKAMAI_ACTION_DEFAULT = "invalidate";
/**Transport URI*/
private static final String TRANSPORT_URI = "transportUri";
/**
* {#inheritDoc}
*/
#Override
public boolean canHandle(AgentConfig config) {
final String transportURI = config.getTransportURI();
return (transportURI != null) && (transportURI.toLowerCase().startsWith(AKAMAI_PROTOCOL));
}
/**
* {#inheritDoc}
*/
#Override
public ReplicationResult deliver(TransportContext ctx, ReplicationTransaction tx)
throws ReplicationException {
final ReplicationActionType replicationType = tx.getAction().getType();
if (replicationType == ReplicationActionType.TEST) {
return ReplicationResult.OK;
} else if (replicationType == ReplicationActionType.ACTIVATE ||
replicationType == ReplicationActionType.DEACTIVATE ||
replicationType == ReplicationActionType.DELETE) {
LOGGER.info("Replication Type in Akamai Handler: {}", replicationType);
String resourcePath = tx.getAction().getPath();
if (StringUtils.startsWith(resourcePath, myProjectConfigurationService.getContentpath())
|| StringUtils.startsWith(resourcePath, myProjectConfigurationService.getAssetpath())) {
// checking for my project specific root page and root dam path.
LOGGER.info("Calling activate in Akamai for path: {}", resourcePath);
try {
return doActivate(ctx, tx);
} catch (RequestSigningException e) {
LOGGER.error("Signing ceremony unsuccessful....");
throw new ReplicationException("Signing ceremony unsuccessful: {}", e);
} catch (IOException e) {
LOGGER.error("IO Exception in deliver \n");
throw new ReplicationException("IO Exception in deliver: {}", e);
}
}
return ReplicationResult.OK;
} else {
throw new ReplicationException("Replication action type " + replicationType + " not supported.");
}
}
private String getTransportURI(TransportContext ctx) throws IOException {
LOGGER.info("Entering getTransportURI method.");
final ValueMap properties = ctx.getConfig().getProperties();
final String AKAMAI_HOST = baseConfigurationService.getPropValueFromConfiguration(AKAMAI_FLUSH_CONFIG_PID, "akamaiHost");
final String domain = PropertiesUtil.toString(properties.get(PROPERTY_AKAMAI_DOMAIN), PROPERTY_AKAMAI_DOMAIN_DEFAULT);
final String action = PropertiesUtil.toString(properties.get(PROPERTY_AKAMAI_ACTION), PROPERTY_AKAMAI_ACTION_DEFAULT);
final String type = PropertiesUtil.toString(properties.get(PROPERTY_AKAMAI_TYPE), PROPERTY_AKAMAI_TYPE_DEFAULT);
String defaultTransportUri = MyConstants.HTTPS + AKAMAI_HOST + "/ccu/v3/"
+ action + MyConstants.BACK_SLASH + type + MyConstants.BACK_SLASH + domain;
String transporturi = PropertiesUtil.toString(properties.get(TRANSPORT_URI), defaultTransportUri);
if(StringUtils.isEmpty(transporturi)) {
return defaultTransportUri;
}
if (transporturi.startsWith(AKAMAI_PROTOCOL)) {
transporturi = transporturi.replace(AKAMAI_PROTOCOL, MyConstants.HTTPS);
}
transporturi = transporturi + "/ccu/v3/"
+ action + MyConstants.BACK_SLASH + type + MyConstants.BACK_SLASH + domain;
LOGGER.info("Exiting getTransportURI method of Akamai Transport Handler : {}", transporturi);
return transporturi;
}
/**
* Send purge request to Akamai via a POST request
*
* Akamai will respond with a 201 HTTP status code if the purge request was
* successfully submitted.
*
* #param ctx Transport Context
* #param tx Replication Transaction
* #return ReplicationResult OK if 201 response from Akamai
* #throws ReplicationException
* #throws RequestSigningException
* #throws IOException
* #throws JSONException
*/
private ReplicationResult doActivate(TransportContext ctx, ReplicationTransaction tx)
throws ReplicationException, RequestSigningException, IOException {
LOGGER.info("Inside doActivate of Akamai");
final String AKAMAI_ACCESS_TOKEN = baseConfigurationService.getPropValueFromConfiguration(AKAMAI_FLUSH_CONFIG_PID, "akamaiAccessToken");
final String AKAMAI_CLIENT_TOKEN = baseConfigurationService.getPropValueFromConfiguration(AKAMAI_FLUSH_CONFIG_PID, "akamaiClientToken");
final String AKAMAI_CLIENT_SECRET = baseConfigurationService.getPropValueFromConfiguration(AKAMAI_FLUSH_CONFIG_PID, "akamaiClientSecret");
final String AKAMAI_HOST = baseConfigurationService.getPropValueFromConfiguration(AKAMAI_FLUSH_CONFIG_PID, "akamaiHost");
ClientCredential clientCredential = ClientCredential.builder().accessToken(AKAMAI_ACCESS_TOKEN).
clientToken(AKAMAI_CLIENT_TOKEN).clientSecret(AKAMAI_CLIENT_SECRET).host(AKAMAI_HOST).build();
HttpTransport httpTransport = new ApacheHttpTransport();
HttpRequestFactory httpRequestFactory = httpTransport.createRequestFactory();
JSONObject jsonObject = createPostBody(ctx, tx);
URI uri = URI.create(getTransportURI(ctx));
HttpRequest request = httpRequestFactory.buildPostRequest(new GenericUrl(uri), ByteArrayContent.fromString("application/json", jsonObject.toString()));
final HttpResponse response = sendRequest(request, ctx, clientCredential);
if (response != null) {
final int statusCode = response.getStatusCode();
LOGGER.info("Response code recieved: {}", statusCode);
if (statusCode == HttpStatus.SC_CREATED) {
return ReplicationResult.OK;
}
}
return new ReplicationResult(false, 0, "Replication failed");
}
/**
* Build preemptive basic authentication headers and send request.
*
* #param request The request to send to Akamai
* #param ctx The TransportContext containing the username and password
* #return JSONObject The HTTP response from Akamai
* #throws ReplicationException if a request could not be sent
* #throws RequestSigningException
*/
private HttpResponse sendRequest(final HttpRequest request, final TransportContext ctx,
ClientCredential clientCredential)
throws ReplicationException, RequestSigningException {
LOGGER.info("Inside Send Request method of Akamai");
final String auth = ctx.getConfig().getTransportUser() + ":" + ctx.getConfig().getTransportPassword();
final String encodedAuth = Base64.encode(auth);
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setAuthorization("Basic " + encodedAuth);
httpHeaders.setContentType(ContentType.APPLICATION_JSON.getMimeType());
request.setHeaders(httpHeaders);
GoogleHttpClientEdgeGridRequestSigner requestSigner = new GoogleHttpClientEdgeGridRequestSigner(clientCredential);
requestSigner.sign(request);
HttpResponse response;
try {
response = request.execute();
} catch (IOException e) {
LOGGER.error("IO Exception in sendRequest");
throw new ReplicationException("Could not send replication request.", e);
}
LOGGER.info("Sucessfully executed Send Request for Akamai");
return response;
}
/**
* Build the Akamai purge request body based on the replication agent
* settings and append it to the POST request.
*
* #param request The HTTP POST request to append the request body
* #param ctx TransportContext
* #param tx ReplicationTransaction
* #throws ReplicationException if errors building the request body
*/
private JSONObject createPostBody(final TransportContext ctx,
final ReplicationTransaction tx) throws ReplicationException {
final ValueMap properties = ctx.getConfig().getProperties();
final String type = PropertiesUtil.toString(properties.get(PROPERTY_AKAMAI_TYPE), PROPERTY_AKAMAI_TYPE_DEFAULT);
JSONObject json = new JSONObject();
JSONArray purgeObjects = null;
if (type.equals(PROPERTY_AKAMAI_TYPE_DEFAULT)) {
try {
String content = IOUtils.toString(tx.getContent().getInputStream(), Charset.defaultCharset());
if (StringUtils.isNotBlank(content)) {
LOGGER.info("Content of Akamai is:\n {}", content);
purgeObjects = new JSONArray(content);
}
} catch (JSONException | IOException e) {
throw new ReplicationException("Could not retrieve content from content builder", e);
}
}
if (null != purgeObjects && purgeObjects.length() > 0) {
try {
json.put("objects", purgeObjects);
} catch (JSONException e) {
throw new ReplicationException("Could not build purge request content", e);
}
} else {
throw new ReplicationException("No CP codes or pages to purge");
}
return json;
}
}
Also you would need following dependencies:
<dependency>
<groupId>com.akamai.edgegrid</groupId>
<artifactId>edgegrid-signer-google-http-client</artifactId>
<version>4.0.0</version>
</dependency>
<dependency>
<groupId>com.akamai.edgegrid</groupId>
<artifactId>edgegrid-signer-core</artifactId>
<version>4.0.0</version>
</dependency>
<dependency>
<groupId>com.google.http-client</groupId>
<artifactId>google-http-client</artifactId>
<version>1.22.0</version>
</dependency>
For my case, the bundle did not resolve and I had to add below dependencies to resolve it. It might differ in your case though:
<dependency>
<groupId>io.opencensus</groupId>
<artifactId>opencensus-api</artifactId>
<version>0.24.0</version>
</dependency>
<dependency>
<groupId>io.opencensus</groupId>
<artifactId>opencensus-contrib-http-util</artifactId>
<version>0.24.0</version>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-context</artifactId>
<version>1.24.0</version>
</dependency>
<Import-Package>
javax.annotation;version=0.0.0,
</Import-Package>
For the line : "IOUtils.toString(tx.getContent().getInputStream(), Charset.defaultCharset());", it internally calls your custom content builder (you can refer the article link I provided earlier). However, you can directly make you content objects in transport handler itself as the transport handler is creating it's own request anyways. However, if you need the session you need to implement the ContentBuilder since TransportHandler implementations do not provide that leverage.
Thanks,
Shubham
I have set up a Filter to add crawler support for my GWT web application. The idea is to catch all requests that contain "_escaped_fragment_=" and supply a snapshot for the crawler.
I have set up the Filter using Guice as follows:
filter("/*").through(CrawlerFilter.class);
The following is the code for the CrawlerFilter class (many thanks to Patrick):
#Singleton
public class CrawlerFilter implements Filter {
private static final Logger logger = Logger.getLogger(CrawlerFilter.class.getName());
/**
* Special URL token that gets passed from the crawler to the servlet
* filter. This token is used in case there are already existing query
* parameters.
*/
private static final String ESCAPED_FRAGMENT_FORMAT1 = "_escaped_fragment_=";
private static final int ESCAPED_FRAGMENT_LENGTH1 = ESCAPED_FRAGMENT_FORMAT1.length();
/**
* Special URL token that gets passed from the crawler to the servlet
* filter. This token is used in case there are not already existing query
* parameters.
*/
private static final String ESCAPED_FRAGMENT_FORMAT2 = "&" + ESCAPED_FRAGMENT_FORMAT1;
private static final int ESCAPED_FRAGMENT_LENGTH2 = ESCAPED_FRAGMENT_FORMAT2.length();
private class SyncAllAjaxController extends NicelyResynchronizingAjaxController {
private static final long serialVersionUID = 1L;
#Override
public boolean processSynchron(HtmlPage page, WebRequest request, boolean async) {
return true;
}
}
private WebClient webClient = null;
private static final long _pumpEventLoopTimeoutMillis = 30000;
private static final long _jsTimeoutMillis = 1000;
private static final long _pageWaitMillis = 200;
final int _maxLoopChecks = 2;
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException,
ServletException {
// Grab the request uri and query strings.
final HttpServletRequest httpRequest = (HttpServletRequest) request;
final String requestURI = httpRequest.getRequestURI();
final String queryString = httpRequest.getQueryString();
final HttpServletResponse httpResponse = (HttpServletResponse) response;
if ((queryString != null) && (queryString.contains(ESCAPED_FRAGMENT_FORMAT1))) {
// This is a Googlebot crawler request, let's return a static
// indexable html page post javascript execution, as rendered in the browser.
final String domain = httpRequest.getServerName();
final int port = httpRequest.getServerPort();
// Rewrite the URL back to the original #! version
// -- basically remove _escaped_fragment_ from the query.
// Unescape any %XX characters as need be.
final String urlStringWithHashFragment = requestURI + rewriteQueryString(queryString);
final String scheme = httpRequest.getScheme();
final URL urlWithHashFragment = new URL(scheme, "127.0.0.1", port, urlStringWithHashFragment); // get from localhost
final WebRequest webRequest = new WebRequest(urlWithHashFragment);
// Use the headless browser to obtain an HTML snapshot.
webClient = new WebClient(BrowserVersion.FIREFOX_3_6);
webClient.getCache().clear();
webClient.setJavaScriptEnabled(true);
webClient.setThrowExceptionOnScriptError(false);
webClient.setRedirectEnabled(false);
webClient.setAjaxController(new SyncAllAjaxController());
webClient.setCssErrorHandler(new SilentCssErrorHandler());
if (logger.getLevel() == Level.FINEST)
logger.log(Level.FINEST, "HtmlUnit starting webClient.getPage(webRequest) where webRequest = "
+ webRequest.toString());
final HtmlPage page = webClient.getPage(webRequest);
// Important! Give the headless browser enough time to execute
// JavaScript
// The exact time to wait may depend on your application.
webClient.getJavaScriptEngine().pumpEventLoop(_pumpEventLoopTimeoutMillis);
int waitForBackgroundJavaScript = webClient.waitForBackgroundJavaScript(_jsTimeoutMillis);
int loopCount = 0;
while (waitForBackgroundJavaScript > 0 && loopCount < _maxLoopChecks) {
++loopCount;
waitForBackgroundJavaScript = webClient.waitForBackgroundJavaScript(_jsTimeoutMillis);
if (waitForBackgroundJavaScript == 0) {
if (logger.getLevel() == Level.FINEST)
logger.log(Level.FINEST, "HtmlUnit exits background javascript at loop counter " + loopCount);
break;
}
synchronized (page) {
if (logger.getLevel() == Level.FINEST)
logger.log(Level.FINEST, "HtmlUnit waits for background javascript at loop counter "
+ loopCount);
try {
page.wait(_pageWaitMillis);
}
catch (InterruptedException e) {
logger.log(Level.SEVERE, "HtmlUnit ERROR on page.wait at loop counter " + loopCount);
e.printStackTrace();
}
}
}
webClient.getAjaxController().processSynchron(page, webRequest, false);
if (webClient.getJavaScriptEngine().isScriptRunning()) {
logger.log(Level.WARNING, "HtmlUnit webClient.getJavaScriptEngine().shutdownJavaScriptExecutor()");
webClient.getJavaScriptEngine().shutdownJavaScriptExecutor();
}
// Return the static snapshot.
final String staticSnapshotHtml = page.asXml();
httpResponse.setContentType("text/html;charset=UTF-8");
final PrintWriter out = httpResponse.getWriter();
out.println("<hr />");
out.println("<center><h3>This is a non-interactive snapshot for crawlers. Follow <a href=\"");
out.println(urlWithHashFragment + "\">this link</a> for the interactive application.<br></h3></center>");
out.println("<hr />");
out.println(staticSnapshotHtml);
// Close web client.
webClient.closeAllWindows();
out.println("");
out.flush();
out.close();
if (logger.getLevel() == Level.FINEST)
logger.log(Level.FINEST, "HtmlUnit completed webClient.getPage(webRequest) where webRequest = "
+ webRequest.toString());
}
else {
if (requestURI.contains(".nocache.")) {
// Ensure the gwt nocache bootstrapping file is never cached.
// References:
// https://stackoverflow.com/questions/4274053/how-to-clear-cache-in-gwt
// http://seewah.blogspot.com/2009/02/gwt-tips-2-nocachejs-getting-cached-in.html
//
final Date now = new Date();
httpResponse.setDateHeader("Date", now.getTime());
httpResponse.setDateHeader("Expires", now.getTime() - 86400000L); // One day old.
httpResponse.setHeader("Pragma", "no-cache");
httpResponse.setHeader("Cache-control", "no-cache, no-store, must-revalidate");
}
filterChain.doFilter(request, response);
}
}
/**
* Maps from the query string that contains _escaped_fragment_ to one that
* doesn't, but is instead followed by a hash fragment. It also unescapes
* any characters that were escaped by the crawler. If the query string does
* not contain _escaped_fragment_, it is not modified.
*
* #param queryString
* #return A modified query string followed by a hash fragment if
* applicable. The non-modified query string otherwise.
* #throws UnsupportedEncodingException
*/
private static String rewriteQueryString(String queryString) throws UnsupportedEncodingException {
// Seek the escaped fragment.
int index = queryString.indexOf(ESCAPED_FRAGMENT_FORMAT2);
int length = ESCAPED_FRAGMENT_LENGTH2;
if (index == -1) {
index = queryString.indexOf(ESCAPED_FRAGMENT_FORMAT1);
length = ESCAPED_FRAGMENT_LENGTH1;
}
if (index != -1) {
// Found the escaped fragment, so build back the original decoded
// one.
final StringBuilder queryStringSb = new StringBuilder();
// Add url parameters if any.
if (index > 0) {
queryStringSb.append("?");
queryStringSb.append(queryString.substring(0, index));
}
// Add the hash fragment as a replacement for the escaped fragment.
queryStringSb.append("#!");
// Add the decoded token.
final String token2Decode = queryString.substring(index + length, queryString.length());
final String tokenDecoded = URLDecoder.decode(token2Decode, "UTF-8");
queryStringSb.append(tokenDecoded);
return queryStringSb.toString();
}
return queryString;
}
#Override
public void destroy() {
if (webClient != null)
webClient.closeAllWindows();
}
#Override
public void init(FilterConfig config) throws ServletException {
}
}
It uses HtmlUnit to create the snapshot.
However; the error occurs when I try to access the snapshot using a regular browser. The URL that I enter is of the form:
http://www.myapp.com/?_escaped_fragment_=myobject%3Bid%3D507ac730e4b0e2b7a73b1b81
But the processing by the Filter results in the following error:
Proxy Error
The proxy server received an invalid response from an upstream server.
The proxy server could not handle the request GET /.
Reason: Error reading from remote server
Apache/2.2.22 (Amazon) Server at www.myapp.com Port 80
Any help would be appreciated.
I am trying to invoke a webservice method which takes 2 input parameters and also needs a cookie to authenticate.
PostMethod method = new PostMethod("webservice EP URL");
NameValuePair code = new NameValuePair("Code", "");
NameValuePair revision = new NameValuePair("Rev", "Latest");
NameValuePair targetUri = new NameValuePair("TARGET", "GetObject");
method.setRequestBody(new NameValuePair[] { code, revision,targetUri});
int statusNew = client.executeMethod(method);
I dont know how to achieve it. Above code is what i am doing currently.
Most probably you are dealing with RESTful web services (Just my guess as you are passing parameters as http form params). Here is how to pass cookies
method.setRequestHeader("Cookie", "special-cookie=value");
Here just change "special-cookie=value" to your specific cookie that you are trying to pass.
EDIT: Adding cookie to SOAP request:
The quickest way to do is as follows
(Assuming that the call object you are using is an instance of org.apache.axis.client.Call)
call.setProperty(
org.apache.axis.client.Call.SESSION_MAINTAIN_PROPERTY,
new Boolean(true));
call.setProperty(
org.apache.axis.transport.http.HTTPConstants.HEADER_COOKIE2,
"\r\nCookieName=" + "CookieValue");
Please check "Use a SOAPAction HTTP Header" topic on this link.
Using the SOAP Handler, we can pass the headers in the request and it will do the job.
GetObject_Service_Impl impl = new GetObject_Service_Impl();
// Get Iterator for all service ports
Iterator iter = impl.getPorts();
// Now create a new List of HandlerInfo objects - only one really.
// Our client handler
List handlerChain = new ArrayList();
handlerChain.add(new HandlerInfo(SoapHandler.class, null, null));
// Get Handler Registry
HandlerRegistry registry = impl.getHandlerRegistry();
// Register each port with the handler
while (iter.hasNext())
registry.setHandlerChain((QName) iter.next(), handlerChain);
And Write a new class say SoapHandler.java as below
public class SoapHandler extends GenericHandler {
HandlerInfo hi;
public void init(HandlerInfo info) {
hi = info;
}
public QName[] getHeaders() {
return hi.getHeaders();
}
public boolean handleResponse(MessageContext context) {
return true;
}
/**
* This method is use to add custom headers to existing SAOP request
*/
public boolean handleRequest(MessageContext context) {
System.out.println("response");
try {
SOAPMessageContext smc = (SOAPMessageContext) context;
SOAPMessage message = smc.getMessage();
MimeHeaders hd = message.getMimeHeaders();
hd.addHeader("Authorization", "Basic some credentials");
} catch (Exception e) {
throw new JAXRPCException(e);
}
return true;
}
}
And thats it.....its ready to go.
I have written a REST web service in Netbean IDE using Jersey Framework and Java.
For every request the user needs to provide a username and a password, I know that this authentication is not a best practice (using a curl command like: curl -u username:password -X PUT http://localhsot:8080/user).
Now I want to call a REST web service from an Android Class.
How should I do it?
I have an Android Class which uses DefaultHttpClient and CredentialUsernameAndPassword, but when I run it in Eclipse, sometimes I get a runtime exception or SDK exception.
This is an sample restclient class
public class RestClient
{
public enum RequestMethod
{
GET,
POST
}
public int responseCode=0;
public String message;
public String response;
public void Execute(RequestMethod method,String url,ArrayList<NameValuePair> headers,ArrayList<NameValuePair> params) throws Exception
{
switch (method)
{
case GET:
{
// add parameters
String combinedParams = "";
if (params!=null)
{
combinedParams += "?";
for (NameValuePair p : params)
{
String paramString = p.getName() + "=" + URLEncoder.encode(p.getValue(),"UTF-8");
if (combinedParams.length() > 1)
combinedParams += "&" + paramString;
else
combinedParams += paramString;
}
}
HttpGet request = new HttpGet(url + combinedParams);
// add headers
if (headers!=null)
{
headers=addCommonHeaderField(headers);
for (NameValuePair h : headers)
request.addHeader(h.getName(), h.getValue());
}
executeRequest(request, url);
break;
}
case POST:
{
HttpPost request = new HttpPost(url);
// add headers
if (headers!=null)
{
headers=addCommonHeaderField(headers);
for (NameValuePair h : headers)
request.addHeader(h.getName(), h.getValue());
}
if (params!=null)
request.setEntity(new UrlEncodedFormEntity(params, HTTP.UTF_8));
executeRequest(request, url);
break;
}
}
}
private ArrayList<NameValuePair> addCommonHeaderField(ArrayList<NameValuePair> _header)
{
_header.add(new BasicNameValuePair("Content-Type","application/x-www-form-urlencoded"));
return _header;
}
private void executeRequest(HttpUriRequest request, String url)
{
HttpClient client = new DefaultHttpClient();
HttpResponse httpResponse;
try
{
httpResponse = client.execute(request);
responseCode = httpResponse.getStatusLine().getStatusCode();
message = httpResponse.getStatusLine().getReasonPhrase();
HttpEntity entity = httpResponse.getEntity();
if (entity != null)
{
InputStream instream = entity.getContent();
response = convertStreamToString(instream);
instream.close();
}
}
catch (Exception e)
{ }
}
private static String convertStreamToString(InputStream is)
{
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
StringBuilder sb = new StringBuilder();
String line = null;
try
{
while ((line = reader.readLine()) != null)
{
sb.append(line + "\n");
}
is.close();
}
catch (IOException e)
{ }
return sb.toString();
}
}
Recently discovered that a third party library - Square Retrofit can do the job very well.
Defining REST endpoint
public interface GitHubService {
#GET("/users/{user}/repos")
List<Repo> listRepos(#Path("user") String user,Callback<List<User>> cb);
}
Getting the concrete service
RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint("https://api.github.com")
.build();
GitHubService service = restAdapter.create(GitHubService.class);
Calling the REST endpoint
List<Repo> repos = service.listRepos("octocat",new Callback<List<User>>() {
#Override
public void failure(final RetrofitError error) {
android.util.Log.i("example", "Error, body: " + error.getBody().toString());
}
#Override
public void success(List<User> users, Response response) {
// Do something with the List of Users object returned
// you may populate your adapter here
}
});
The library handles the json serialization and deserailization for you. You may customize the serialization and deserialization too.
Gson gson = new GsonBuilder()
.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
.registerTypeAdapter(Date.class, new DateTypeAdapter())
.create();
RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint("https://api.github.com")
.setConverter(new GsonConverter(gson))
.build();
Stop with whatever you were doing ! :)
Implement the RESTful client as a SERVICE and delegate the intensive network stuff to activity independent component: a SERVICE.
Watch this insightful video
http://www.youtube.com/watch?v=xHXn3Kg2IQE where Virgil Dobjanschi is explaining his approach(es) to this challenge...
Using Spring for Android with RestTemplate
https://spring.io/guides/gs/consuming-rest-android/
// The connection URL
String url = "https://ajax.googleapis.com/ajax/" +
"services/search/web?v=1.0&q={query}";
// Create a new RestTemplate instance
RestTemplate restTemplate = new RestTemplate();
// Add the String message converter
restTemplate.getMessageConverters().add(new StringHttpMessageConverter());
// Make the HTTP GET request, marshaling the response to a String
String result = restTemplate.getForObject(url, String.class, "Android");
I used OkHttpClient to call restful web service. It's very simple.
OkHttpClient httpClient = new OkHttpClient();
Request request = new Request.Builder()
.url(url)
.build();
Response response = httpClient.newCall(request).execute();
String body = response.body().string()
What back-end? If JAVA then you can use REST with Java (JAX-RS) using Jersey.
On the Android side you can use this simple RestClient to work with that REST service.
For JSON <--> Object mapping on both sides (Android, Java back-end) you can use GSON.
Perhaps am late or maybe you've already used it before but there is another one called ksoap and its pretty amazing.. It also includes timeouts and can parse any SOAP based webservice efficiently. I also made a few changes to suit my parsing.. Look it up
Follow the below steps to consume RestFul in android.
Step1
Create a android blank project.
Step2
Need internet access permission. write the below code in AndroidManifest.xml file.
<uses-permission android:name="android.permission.INTERNET">
</uses-permission>
Step3
Need RestFul url which is running in another server or same machine.
Step4
Make a RestFul Client which will extends AsyncTask. See RestFulPost.java.
Step5
Make DTO class for RestFull Request and Response.
RestFulPost.java
package javaant.com.consuming_restful.restclient;
import android.app.ProgressDialog;
import android.content.Context;
import android.os.AsyncTask;
import android.util.Log;
import com.google.gson.Gson;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;
import java.util.Map;
import javaant.com.consuming_restful.util.Util;
/**
* Created by Nirmal Dhara on 29-10-2015.
*/
public class RestFulPost extends AsyncTask<map, void,="" string=""> {
RestFulResult restFulResult = null;
ProgressDialog Asycdialog;
String msg;
String task;
public RestFulPost(RestFulResult restFulResult, Context context, String msg,String task) {
this.restFulResult = restFulResult;
this.task=task;
this.msg = msg;
Asycdialog = new ProgressDialog(context);
}
#Override
protected String doInBackground(Map... params) {
String responseStr = null;
Object dataMap = null;
HttpPost httpost = new HttpPost(params[0].get("url").toString());
try {
dataMap = (Object) params[0].get("data");
Gson gson = new Gson();
Log.d("data map", "data map------" + gson.toJson(dataMap));
httpost.setEntity(new StringEntity(gson.toJson(dataMap)));
httpost.setHeader("Accept", "application/json");
httpost.setHeader("Content-type", "application/json");
DefaultHttpClient httpclient= Util.getClient();
HttpResponse response = httpclient.execute(httpost);
int statusCode = response.getStatusLine().getStatusCode();
Log.d("resonse code", "----------------" + statusCode);
if (statusCode == 200)
responseStr = EntityUtils.toString(response.getEntity());
if (statusCode == 404) {
responseStr = "{\n" +
"\"status\":\"fail\",\n" +
" \"data\":{\n" +
"\"ValidUser\":\"Service not available\",\n" +
"\"code\":\"404\"\n" +
"}\n" +
"}";
}
} catch (Exception e) {
e.printStackTrace();
}
return responseStr;
}
#Override
protected void onPreExecute() {
Asycdialog.setMessage(msg);
//show dialog
Asycdialog.show();
super.onPreExecute();
}
#Override
protected void onPostExecute(String s) {
Asycdialog.dismiss();
restFulResult.onResfulResponse(s,task);
}
}
For more details and complete code please visit http://javaant.com/consume-a-restful-webservice-in-android/#.VwzbipN96Hs
Here is my Library That I have created for simple Webservice Calling,
You can use this by adding a one line gradle dependency -
compile 'com.scantity.ScHttpLibrary:ScHttpLibrary:1.0.0'
Here is the demonstration of using.
https://github.com/vishalchhodwani1992/httpLibrary