synchronizedMap common for different classes - java

I've got web servlet class which stores all the responses for requests in synchronizedMap.
Also I have RabbitMQ message handler (listener) which runs in separate thread. On message receive this message handler sends response and removes request from the map.
I put elements in the map on every request:
#WebServlet({"/create_conf_link", "/invite_participant", "/get_conf_credentials", "/get_web_conf_credentials"})
public class VcmsServlet extends HttpServlet implements AutoCloseable {
private static final long serialVersionUID = 1L;
private final VcmsWsMessageHandler mMessageHandler;
private volatile Map<String, HttpServletResponse> mResponses = Collections.synchronizedMap(new HashMap<String, HttpServletResponse>());
/**
* #see HttpServlet#HttpServlet()
*/
public VcmsServlet()
{
super();
mMessageHandler = new VcmsWsMessageHandler(mResponses);
}
/**
* Processes requests for both HTTP <code>GET</code> and <code>POST</code> methods.
* #param request servlet request
* #param response servlet response
*/
protected void handleRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
String corrID = java.util.UUID.randomUUID().toString();
mResponses.put(corrID, response);
On message handle handle method is called in MessageHandler class
public class VcmsWsMessageHandler implements IMessageHandler
{
public VcmsWsMessageHandler(Map<String, HttpServletResponse> responses)
{
mResponses = responses;
}
#Override
public void handle(final String inMethod, final String inMessage, final BasicProperties inProperties)
{
String corrID = inProperties.getCorrelationId();
LOG.info("VCMS response for " + corrID + ": " + inMessage);
synchronized (mResponses)
{
HttpServletResponse response = mResponses.get(corrID);
if(response == null)
LOG.error("Failed to find response object for corrilcation ID " + corrID);
try
{
response.setStatus(200);
response.setContentType("application/json");
response.setCharacterEncoding("UTF-8");
response.getWriter().write(inMessage);
mResponses.remove(corrID);
}
catch(IOException ex)
{
LOG.error("Error sending response");
response.setStatus(HttpServletResponse.SC_CONFLICT);
}
}
}
private volatile Map<String, HttpServletResponse> mResponses;
}
The problem is in that there is no response for corresponding corrID in MessageHandler class, though map element is added in handleRequest method of servlet. HttpServletResponse response = mResponses.get(corrID); returns null. But in some rare cases mResponses.get(corrID); returns valid response and everything is OK.
How should I work with map to sync it between classes?
Thanks.

Related

Akamai CCU V3 Fast Purge implementation with JAVA project or AEM

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

How to continue request processing after sending response from filter in Jersey?

I have a situation where I need to return an "accepted" response for every request received and publish the actual response later to a separate endpoint outside the service.
To implement the 'accepted' Response I implemented a filter.
public class AcknowledgementFilter implements ContainerRequestFilter{
#Override
public void filter(ContainerRequestContext containerRequestContext) throws IOException {
containerRequestContext.abortWith(Response.accepted().build());
// call Resource method in new Thread() . <------ ?
}
}
Implementation of service endpoints:
#Path("/vendor")
public class VendorHandler {
#POST
public void addVendor(VendorRequest addVendorRequest)){
vendor = new Vendor();
Client.publish(vendor); // publish request to an endpoint
return null;
}
How do I call the addVendor of VendorHandler(or any method depends on request) from the acknowledgement filter?
Is there any other way to implement an accepted response for every request then process the request separately?
You can use AsyncResponse,
#GET
#ManagedAsync
#Produces(MediaType.APPLICATION_JSON)
public void getLives(#Suspended final AsyncResponse asyncResponse,
#DefaultValue("0") #QueryParam("newestid") final int newestId,
#QueryParam("oldestid") final Integer oldestId) {
asyncResponse.setTimeoutHandler(asyncResponse1 -> {
logger.info("reached timeout");
asyncResponse1.resume(Response.ok().build());
});
asyncResponse.setTimeout(5, TimeUnit.MINUTES);
try {
List<Life> lives = oldestId == null ?
Lifes.getLastLives(newestId) : Lifes.getOlderLives(oldestId);
if (lives.size() > 0) {
final GenericEntity<List<Life>> entity = new GenericEntity<List<Life>>(lives) {
};
asyncResponse.resume(entity);
} else LifeProvider.suspend(asyncResponse);
} catch (SQLException e) {
logger.error(e, e);
asyncResponse.resume(new WebApplicationException(Response.Status.INTERNAL_SERVER_ERROR));
}
}
Check this Link for more details.

Database server and public website communication [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 6 years ago.
Improve this question
So, i'm getting slightly familiar with html, css and frameworks in general and i have a fair understanding of Java. However, i can only see how you can make inbuilt functions and computations with Javascript that you add to your html file. But i don't understand how it works with say a Java program on your computer that the website would fetch data and information from. Can anyone explain that? I couldn't find any good answers on the internet.
Say i want to compute the value 2+3 with Java on a server and then fetch that value and display it on the website. How would i do that?
I achieve this functionality by sending an ajax request via javascript to a java servlet on the server here is an example that I use:
Say you have a link:
<a href="#" onclick = 'shipProduct(1)'>Test</a>
When this link is clicked it will look for the corresponding javscript function, which in my case is:
/**
*
* #param {type} action
* #param {type} bundleId
* #returns {undefined}
*/
function shipProduct(bundleId)
{
$.ajax
({
url: "/FlcErp-war/ShipmentServlet", //this is the name of the serverlet in your project
type: "GET", // the type of your request
data: _action + "=addToShipped&bundleId=" + bundleId,
success: function (content)
{
if (content.substring(0, 1) == '_')
{
alert(content);
}
else
{
//rebuild shipment tables to show updated
//information about the item
buildShipmentQueue();
}
},
error: function (xhr, status, error) {
alert(xhr.responseText);
alert(status);
alert(error);
}
});
}
What I did was have have an annotated java servlet to serve my request:
your doPost and doGet will handle your post and get requests
/**
*
* #author samo
*/
#WebServlet("/ShipmentServlet")
public class ShipmentServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
private static final String _addToShipped = "addToShipped";
private static final String _bundleId = "bundleId";
private static final String _shipmentId = "shipmentId";
private static final String _productId = "productId";
private static final String _updateQueue = "updateQueue";
private static final String _unship = "unship";
private static final String _setInSession = "setInSession";
private static final String _externalBundleId = "externalBundleId";
private static final String _plywood = "P";
private static final String _veneer = "V";
private static final String _lumber = "L";
private static final String _bundle = "Bundle";
private static final String _list = "List";
private boolean multipleActions;
private String[] actions;
/**
* Processes requests for both HTTP <code>GET</code> and <code>POST</code>
* methods.
*
* #param request servlet request
* #param response servlet response
* #throws ServletException if a servlet-specific error occurs
* #throws IOException if an I/O error occurs
*/
protected void processBundleRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException, NamingException, CloneNotSupportedException {
response.setContentType("text/html;charset=UTF-8");
try (PrintWriter out = response.getWriter()) {
this.actions = null;
//this is where we will get the actions to process
//if there is more than one action the string will contain
// a comma as a delimiter
String action = request.getParameter("_action");
InitialContext ctx = new InitialContext();
LoadShipmentController lsc = (LoadShipmentController) ctx.lookup("loadShipmentController");
switch (action) {
case _addToShipped:
shipProduct(request, out);
break;
case _updateQueue:
Shipment shipment = lsc.getCurrent();
String type = shipment.getShipmentType();
String shipmentQueue = "";
switch (type) {
case _veneer:
shipmentQueue = lsc.getVeneerShipmentQueue();
break;
case _plywood:
shipmentQueue = lsc.getShipmentQueue();
break;
case _lumber:
shipmentQueue = lsc.getShipmentQueue();
break;
}
out.println(shipmentQueue);
break;
case _unship:
unshipProduct(request, out);
break;
case _setInSession:
String bundleId = request.getParameter(_bundleId);
lsc.setBundleId(bundleId);
break;
case _list:
out.println(lsc.getBundleAndProductListString());
break;
// setSessionVariable(_externalBundleId, bundleId);
}
}
}
public void shipProduct(HttpServletRequest request, PrintWriter out) throws NamingException, CloneNotSupportedException {
Integer bundleId = Integer.valueOf(request.getParameter(_bundleId));
InitialContext ctx = new InitialContext();
ShipmentController shipmentController = (ShipmentController) ctx.lookup("shipmentController");
LoadShipmentController loadShipmentController = (LoadShipmentController) ctx.lookup("loadShipmentController");
// getting the product from the bundle, because that's all we care about
Product product = shipmentController.loadBundle(bundleId).getProduct();
String type = product.getProductType();
//because the way we ships differs depending on the product type I need to
//check first mainly for veneer shipments because their bundle count is not
//predetermined
boolean loaded = false;
switch (type) {
case _veneer:
loaded = loadShipmentController.loadVeneerProduct(product, bundleId);
break;
case _plywood:
loaded = loadShipmentController.loadPlywoodProduct(product, bundleId);
break;
case _lumber:
loaded = loadShipmentController.loadLumberProduct(product, bundleId);
break;
}
if(!loaded)
{
out.println("_" + loadShipmentController.getErrors());
}
}
public void unshipProduct(HttpServletRequest request, PrintWriter out) throws NamingException {
Integer bundleId = Integer.valueOf(request.getParameter(_bundle));
InitialContext ctx = new InitialContext();
LoadShipmentController loadShipmentController = (LoadShipmentController) ctx.lookup("loadShipmentController");
boolean unship = loadShipmentController.unshipByBundleId(bundleId);
if (!unship) {
String error = loadShipmentController.getErrors();
out.println("Error:" + error);
}
}
private void setSessionVariable(String name, String value) {
FacesContext context = FacesContext.getCurrentInstance();
ExternalContext externalContext = context.getExternalContext();
HttpSession session = (HttpSession) externalContext.getSession(false);
session.setAttribute(name, value);
}
// <editor-fold defaultstate="collapsed" desc="HttpServlet methods. Click on the + sign on the left to edit the code.">
/**
* Handles the HTTP <code>GET</code> method.
*
* #param request servlet request
* #param response servlet response
* #throws ServletException if a servlet-specific error occurs
* #throws IOException if an I/O error occurs
*/
#Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
try {
processBundleRequest(request, response);
} catch (NamingException ex) {
Logger.getLogger(ShipmentServlet.class.getName()).log(Level.SEVERE, null, ex);
} catch (CloneNotSupportedException ex) {
Logger.getLogger(ShipmentServlet.class.getName()).log(Level.SEVERE, null, ex);
}
}
/**
* Handles the HTTP <code>POST</code> method.
*
* #param request servlet request
* #param response servlet response
* #throws ServletException if a servlet-specific error occurs
* #throws IOException if an I/O error occurs
*/
#Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
}
}

Requests and response from netty server hangs

I have the following code to create a netty web server based on http server created in the netty's example. My buisness logic is the following.
public class HttpServerHandler extends SimpleChannelInboundHandler<Object> {
private final static Logger LOG = LogManager
.getLogger(HttpServerHandler.class);
private WorkflowService workflowService;
private HttpRequest request;
private final StringBuffer buff = new StringBuffer();
private API avalancheApi;
public HttpServerHandler(WorkflowService workflowService) {
this.workflowService = workflowService;
this.avalancheApi = new API(this.workflowService);
}
#Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
LOG.debug("channelActive");
LOG.debug(ctx.toString());
};
#Override
public void channelReadComplete(ChannelHandlerContext ctx) {
ctx.flush();
}
#Override
public void channelRead0(ChannelHandlerContext ctx, Object msg)
throws IOException {
avalancheApi.setContext(ctx);
if (msg instanceof HttpRequest) {
HttpRequest request = this.request = (HttpRequest) msg;
if (HttpHeaders.is100ContinueExpected(request)) {
send100Continue(ctx);
}
String command = getCommand(request);
LOG.debug(command);
Map<String, List<String>> parameters = getParameters(request);
LOG.debug(parameters);
switch (command) {
case "/login":
ctx = avalancheApi.login(parameters);
break;
case "/test":
ctx = avalancheApi.test();
break;
default:
break;
}
}
if (msg instanceof LastHttpContent) {
LOG.debug("msg is of LastHttpContent");
}
if (!HttpHeaders.isKeepAlive(request)) {
// If keep-alive is off, close the connection once the content is
// fully written.
ctx.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(
ChannelFutureListener.CLOSE);
}
}
public class API {
private static final Logger LOG = LogManager.getLogger(API.class);
private ChannelHandlerContext ctx;
private HttpResponse response;
private WorkflowService workflowService;
public API(WorkflowService workflowService) {
this.workflowService = workflowService;
this.ctx = null;
}
public void setContext(ChannelHandlerContext ctx) {
this.ctx = ctx;
}
public ChannelHandlerContext login(Map<String, List<String>> parameters) {
boolean success;
String username = getUsername(parameters);
String password = getPassword(parameters);
User user = null;
user = workflowService.login(username, password);
success = validateLogin(user);
this.response = writeLoginResponse(success);
this.ctx.write(this.response);
writeLoginContext(success, response);
return this.ctx;
}
private void writeLoginContext(boolean success, HttpResponse response) {
JsonObject jsonResponseMessage = new JsonObject();
jsonResponseMessage.addProperty("result", success);
LOG.debug(jsonResponseMessage.toString());
this.ctx.write(Unpooled.copiedBuffer(jsonResponseMessage.toString(),
CharsetUtil.UTF_8));
this.response.headers().set(HttpHeaders.Names.CONTENT_LENGTH,
jsonResponseMessage.toString().length());
}
private HttpResponse writeLoginResponse(boolean success) {
if (success)
return createSuccessfullLoginResponse();
else
return createLoginFailureResponse();
}
private HttpResponse createLoginFailureResponse() {
return Response.loginFailureResponse();
}
private HttpResponse createSuccessfullLoginResponse() {
return Response.loginSuccessResponse();
}
}
Response class is only creating the response and the content_type which is of application/json. Content Length is set in the API class. Using python client with requests, results in the request made in http://localhost/login?username=name&password=pass works only once. The second time everything works, but it doesn't finish processing the request and send the response object. Api calls get executed normally, and I also get the message of LastHttpContext message getting print. The problem sometimes happens with browser too. Am I missing something? Maybe the content data and the content length doesn't match? Could it be that when making requests from python client, the content of the previous context isn't flushed and the content_length value of the header and content length of the context doesn't match?
Just wild guess
this.response.headers().set(HttpHeaders.Names.CONTENT_LENGTH,
jsonResponseMessage.toString().length());
Instead, shouldn't you be doing jsonResponseMessage.toString().getBytes().length ?? Sometimes, one character is not just one byte.
My guess is that you have overwritten the context in your API class, and as a result, are writing the response to the wrong context. Is your HttpServerHandler marked with #Shareable?

Servlet Filter is Returning "Proxy Error" on AWS

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.

Categories