My image files are stored in database (I know they shouldn't be, but can't help).
To be able to render them on clients, I've implemented an async servlet that helps read the binary stream off the database column and write on to the Output Stream of Servlet Response. Traditional IO works just fine here.
When I thought of trying the non blocking IO with async servlet (to test performance), my binary data returned in the response keeps getting corrupted.
Starting with the Oracle Blog, I've seen various examples of file upload with async NIO servlet, but no help with my issue.
Here's the servlet code:
#WebServlet(asyncSupported = true, urlPatterns = "/myDownloadServlet")
public class FileRetrievalServletAsyncNIO extends HttpServlet
{
private static final long serialVersionUID = -6914766655133758332L;
#Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
Queue<byte[]> containerQueue = new LinkedList<byte[]>();
AsyncContext asyncContext = request.startAsync();
asyncContext.addListener(new AsyncListenerImpl());
asyncContext.setTimeout(120000);
try
{
long attachmentId = Long.valueOf(request.getParameter("id"));
MyAttachmentDataObject retObj = ServletUtils.fetchAttachmentHeaders(attachmentId);
response = (HttpServletResponse) asyncContext.getResponse();
response.setHeader("Content-Length", String.valueOf(retObj.getContentLength()));
if (Boolean.valueOf(request.getParameter(ServletConstants.REQ_PARAM_ENABLE_DOWNLOAD)))
response.setHeader("Content-disposition", "attachment; filename=" + retObj.getName());
response.setContentType(retObj.getContentType());
ServletOutputStream sos = response.getOutputStream();
ServletUtils.fetchContentStreamInChunks(attachmentId, containerQueue); // reads from database and adds to the queue in chunks
sos.setWriteListener(new WriteListenerImpl(sos, containerQueue, asyncContext));
}
catch (NumberFormatException | IOException exc)
{
exc.printStackTrace();
request.setAttribute("message", "Failed");
}
}
}
Here's the write listener implementation
public class WriteListenerImpl implements WriteListener
{
private ServletOutputStream output = null;
private Queue<byte[]> queue = null;
private AsyncContext asyncContext = null;
private HttpServletRequest request = null;
private HttpServletResponse response = null;
public WriteListenerImpl(ServletOutputStream sos, Queue<byte[]> q, AsyncContext aCtx)
{
output = sos;
queue = q;
asyncContext = aCtx;
request = (HttpServletRequest) asyncContext.getRequest();
}
#Override
public void onWritePossible() throws IOException
{
while(output.isReady())
{
while (!queue.isEmpty())
{
byte[] temp = queue.poll();
output.write(temp, 0, temp.length);
}
asyncContext.complete();
request.setAttribute("message", "Success");
}
}
#Override
public void onError(Throwable t)
{
System.err.println(t);
try
{
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
}
catch (IOException exc)
{
exc.printStackTrace();
}
request.setAttribute("message", "Failure");
asyncContext.complete();
}
}
The response data looks like this:
What am I doing wrong?
Not sure exactly what you expect the output to look like but in terms of async i/o you should check output.isReady() before every write. So your onWritePossible code should be:
while(output.isReady() && !queue.isEmpty())
{
byte[] temp = queue.poll();
output.write(temp, 0, temp.length);
}
if (queue.isEmpty()) {
asyncContext.complete();
request.setAttribute("message", "Success");
}
This allows onWritePossible() to return if writing becomes blocked which is the basically the point of async I/O.
If you write when writing is blocked (output.isReady() would return false) different implementations may either ignore the write or throw an exception. Either way your output data would be either missing some writes in the middle or truncated.
Related
We have implemented a custom ExceptionHandler that attempts to intercept any NonexistentConversationException that are thrown if a user requests a URL with an old cid in it (eg. bookmarked link etc.).
The handler simply removed the cid from the URL and performs a redirect to the updated URL.
This solution seems to work in most cases, we have a number of different user 'roles' within the App each with their own login areas and for one of these user types it seems that fc.getExternalContext().isResponseCommitted() is always true when the ExceptionHandler fires which means we are unable to perform the redirect. All other user types it works ok.
I am not sure exactly what the difference is with this user type for this to happen, I am guessing some CDI bean we using setup differently
Is there a way to unsure the ExceptionHandler kicks in earlier before the response is committed?
Below is the handler...
public class ConversationExpiredExceptionHandler
extends ExceptionHandlerWrapper {
final static Logger log = LoggerFactory.getLogger(ConversationExpiredExceptionHandler.class);
private ExceptionHandler wrapped;
private static String CONVERSATION_PARAM = "cid";
public ConversationExpiredExceptionHandler(ExceptionHandler wrapped) {
this.wrapped = wrapped;
}
#Override
public ExceptionHandler getWrapped() {
return this.wrapped;
}
#Override
public void handle() throws FacesException {
for (Iterator<ExceptionQueuedEvent> i =
getUnhandledExceptionQueuedEvents().iterator();
i.hasNext();) {
ExceptionQueuedEvent event = i.next();
ExceptionQueuedEventContext context = (ExceptionQueuedEventContext) event.getSource();
Throwable t = context.getException();
if (t instanceof javax.enterprise.context.NonexistentConversationException
|| t instanceof org.jboss.weld.contexts.NonexistentConversationException
|| t.getCause() instanceof org.jboss.weld.contexts.NonexistentConversationException /* can be wrapped in FacesException */) {
final FacesContext fc = FacesContext.getCurrentInstance();
try {
if (!fc.getExternalContext().isResponseCommitted()) {
if(Faces.getRequestQueryStringMap().containsKey(CONVERSATION_PARAM)) {
String requestedUrl = Faces.getRequestURLWithQueryString();
String updatedUrl = this.removeQueryParameter(requestedUrl,CONVERSATION_PARAM);
log.debug("No conversation active for {}, redirecting to {}",requestedUrl,updatedUrl);
fc.getExternalContext().redirect(updatedUrl);
}
}
fc.renderResponse();
break;
} catch (Exception ex) {
throw new FacesException(ex);
} finally {
i.remove();
}
}
}
getWrapped().handle();
}
private String removeQueryParameter(String url, String parameterName) throws URISyntaxException {
URIBuilder uriBuilder = new URIBuilder(url);
List<NameValuePair> queryParameters = uriBuilder.getQueryParams();
for (Iterator<NameValuePair> queryParameterItr = queryParameters.iterator(); queryParameterItr.hasNext();) {
NameValuePair queryParameter = queryParameterItr.next();
if (queryParameter.getName().equals(parameterName)) {
queryParameterItr.remove();
}
}
uriBuilder.setParameters(queryParameters);
return uriBuilder.build().toString();
}
It's probably pretty obvious, but I'm completely new to programming or asking a question at stackoverflow, so I apologize in advance if I can't explain myself properly. Also, there are some parts I have no idea what they are for anymore since the code is basically a mix of tutorials.
What I need the app to do is for it to keep doing what it's doing (the handler part), but while it's is closed (not minimized). But instead of changing the background, I need it to send a notification instead.
In other words, every 10 minutes, if the value of temperBU is 19, I get a notification even if the app is closed.
For that, if I'm not mistaken, what I need is a service, but I don't understand what type is better for this situation. I tried some tutorials, but nothing seems to work, and if it's possible to start the service as soon as the app gets started.
public class MainActivity extends AppCompatActivity {
ConstraintLayout layout;
class Weather extends AsyncTask<String, Void, String> {
#Override
protected String doInBackground(String... address) {
try {
URL url = new URL(address[0]);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.connect();
InputStream is = connection.getInputStream();
InputStreamReader isr = new InputStreamReader(is);
int data = isr.read();
String content = "";
char ch;
while (data != -1) {
ch = (char) data;
content = content + ch;
data = isr.read();
}
Log.i("Content", content);
return content;
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
String content;
Weather weather = new Weather();
{
{
try {
content = weather.execute("https://api.openweathermap.org/data/2.5/weather?q=budapest,hu&units=metric&appid=ce2fd10cdcc8ab209f979f6a41c27cfe").get();
JSONObject jsonObject = new JSONObject(content);
String mainData = jsonObject.getString("main");
Log.i("mainData", mainData);
JSONObject object = new JSONObject(mainData);
Double temp = object.getDouble("temp");
Log.i("temp", String.valueOf(temp));
int temperBU = (int) Math.round(temp);
Log.i("temperBU", String.valueOf(temperBU));
layout = findViewById(R.id.hs_n);
if (temperBU == 19)
layout.setBackgroundResource(R.drawable.hungry_summer_premium_yes_simple);
else layout.setBackgroundResource(R.drawable.hungry_summer_premium_no_simple);
Handler handler = new Handler();
Runnable r = new Runnable() {
public void run() {
String content;
Weather weather = new Weather();
try {
content = weather.execute("https://api.openweathermap.org/data/2.5/weather?q=budapest,hu&units=metric&appid=ce2fd10cdcc8ab209f979f6a41c27cfe").get();
JSONObject jsonObject = new JSONObject(content);
String mainData = jsonObject.getString("main");
Log.i("mainData", mainData);//*
JSONObject object = new JSONObject(mainData);
Double temp = object.getDouble("temp");
Log.i("temp", String.valueOf(temp));
int temperBU = (int) Math.round(temp);
Log.i("temperBU", String.valueOf(temperBU));//*
layout = findViewById(R.id.hs_n);
if (temperBU == 19)
layout.setBackgroundResource(R.drawable.hungry_summer_premium_yes_simple);
else
layout.setBackgroundResource(R.drawable.hungry_summer_premium_no_simple);
} catch (Exception e) {
e.printStackTrace();
}
handler.postDelayed(this::run, 600000);
}
};
handler.postDelayed(r, 600000);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
Thank you so much for the help.
Please note that AsyncTask is deprecated, so use the following to do a background work:
Android AsyncTask API deprecating in Android 11.What are the alternatives?
In order to continue doing something after the user closed your app try using foreground service, like this:
in Android manifest, add
uses-permission android:name="android.permission.FOREGROUND_SERVICE"
this inside the application tag:
service android:name=".services.WorkerSvc"
add this class:
class WorkerSvc : Service() {
override fun onBind(intent: Intent?): IBinder? {
return null
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
LogUtil.i("onStartCommand")
startForeground(
NotificationUtil.NOTIFICATION_ID,
NotificationUtil.makeForeGroundNotification(getString(R.string.please_wait))
)
processIntent(intent)
return START_STICKY
}
private fun processIntent(intent: Intent?) {
if (intent == null) {
stopSelf()
} else {
// DO YOUR WORK HERE. USE INTENT EXTRAS TO PASS DATA TO SERVICE
// NOTE THIS IS EXECUTED IN MAIN THREAD SO USE ONE OF THE SOLUTION PROVIDED IN A LINK ABOVE
}
}
}
To start the service:
val svcIntent = Intent(App.instance, WorkerSvc::class.java)
svcIntent.putExtra(
//DATA TO PASS TO SERVICE
)
if (context != null) {
ContextCompat.startForegroundService(context, svcIntent)
}
There is a field called "metadata" (not to be confused with GRPC metadata) that is present in every request proto that comes to the GRPC service:
message MyRequest {
RequestResponseMetadata metadata = 1;
...
}
And the same field is also present in all responses:
message MyResponse {
RequestResponseMetadata metadata = 1;
...
}
I am trying to write a ServerInterceptor (or something else, if it works) to read the "metadata" field from the request, keep it somewhere, and then set it in the response once done processing the request.
Attempt 1: ThreadLocal
public class ServerInterceptor implements io.grpc.ServerInterceptor {
private ThreadLocal<RequestResponseMetadata> metadataThreadLocal = new ThreadLocal<>();
#Override
public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(
ServerCall<ReqT, RespT> call,
final Metadata requestHeaders,
ServerCallHandler<ReqT, RespT> next) {
return new SimpleForwardingServerCallListener<ReqT>(
next.startCall(
new SimpleForwardingServerCall<ReqT, RespT>(call) {
#Override
public void sendMessage(RespT message) {
super.sendMessage(
(RespT)
MetadataUtils.setMetadata(
(GeneratedMessageV3) message, metadataThreadLocal.get()));
metadataThreadLocal.remove();
}
},
requestHeaders)) {
#Override
public void onMessage(ReqT request) {
// todo nava see if ReqT can extend GenericV3Message
var metadata = MetadataUtils.getMetadata((GeneratedMessageV3) request);
metadataThreadLocal.set(metadata);
super.onMessage(request);
}
};
}
}
I tried to use ThreadLocal, to later realise that sendMessage and onMessage need not necessary to be on the same thread.
Attempt 2: GRPC Context
public class ServerInterceptor implements io.grpc.ServerInterceptor {
public static final Context.Key<RequestResponseMetadata> METADATA_KEY = Context.key("metadata");
#Override
public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(
ServerCall<ReqT, RespT> call,
final Metadata requestHeaders,
ServerCallHandler<ReqT, RespT> next) {
return new SimpleForwardingServerCallListener<ReqT>(
next.startCall(
new SimpleForwardingServerCall<ReqT, RespT>(call) {
#Override
public void sendMessage(RespT message) {
super.sendMessage(
(RespT)
MetadataUtils.setMetadata(
(GeneratedMessageV3) message, METADATA_KEY.get()));
}
},
requestHeaders)) {
#Override
public void onMessage(ReqT request) {
var metadata = MetadataUtils.getMetadata((GeneratedMessageV3) request);
var newContext = Context.current().withValue(METADATA_KEY, metadata);
oldContext = newContext.attach();
super.onMessage(request);
}
};
}
}
I am planning to detach the context in a onComplete(), but before it reaches there itself, METADATA_KEY.get() in sendMessage returns null, while I was expecting it to return the data.
Even before hitting the sendMessage() function, I get this in the console, indicating that I am doing something wrong:
3289640 [grpc-default-executor-0] ERROR i.g.ThreadLocalContextStorage - Context was not attached when detaching
java.lang.Throwable: null
at io.grpc.ThreadLocalContextStorage.detach(ThreadLocalContextStorage.java:48)
at io.grpc.Context.detach(Context.java:421)
at io.grpc.Context$CancellableContext.detach(Context.java:761)
at io.grpc.internal.ContextRunnable.run(ContextRunnable.java:39)
at io.grpc.internal.SerializingExecutor.run(SerializingExecutor.java:123)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
at java.base/java.lang.Thread.run(Thread.java:834)
How do I read data when a request is received, store it somewhere and use it when the response is send back?
You can use Metadata to pass values from the request to the response:
public class MetadataServerInterceptor implements ServerInterceptor {
public static final Metadata.Key<byte[]> METADATA_KEY = Metadata.Key.of("metadata-bin", Metadata.BINARY_BYTE_MARSHALLER);
#Override
public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(ServerCall<ReqT, RespT> call, Metadata headers, ServerCallHandler<ReqT, RespT> next) {
var serverCall = new ForwardingServerCall.SimpleForwardingServerCall<>(call) {
#Override
public void sendMessage(RespT message) {
byte[] metadata = headers.get(METADATA_KEY);
message = (RespT) MetadataUtils.setMetadata((GeneratedMessageV3) message, metadata);
super.sendMessage(message);
}
};
ServerCall.Listener<ReqT> listenerWithContext = Contexts.interceptCall(Context.current(), serverCall, headers, next);
return new ForwardingServerCallListener.SimpleForwardingServerCallListener<>(listenerWithContext) {
#Override
public void onMessage(ReqT message) {
byte[] metadata = MetadataUtils.getMetadata((GeneratedMessageV3) message);
headers.put(METADATA_KEY, metadata);
super.onMessage(message);
}
};
}
}
Note: Since it is not possible to put the instance of RequestResponseMetadata in the metadata (at least without implementing a custom marshaller), you can save it there as a byte array. You can use toByteArray() on your RequestResponseMetadata object to get byte[] and RequestResponseMetadata.#parseFrom(byte[]) to get the object from byte[].
Trying to fetch the response using spring web client, but the result response is getting truncated as the response size is more than that of string class. Is there any other way to get the response without being truncated?
LinkedMultiValueMap<String, Object> requestMap = new LinkedMultiValueMap<String, Object>();
//String response = "";
try{
File tempFile = File.createTempFile("ccda", "File");
FileOutputStream out = new FileOutputStream(tempFile);
IOUtils.copy(ccdaFile.getInputStream(), out);
requestMap.add("ccdaFile", new FileSystemResource(tempFile));
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
HttpEntity<LinkedMultiValueMap<String, Object>> requestEntity =
new HttpEntity<LinkedMultiValueMap<String, Object>>(requestMap, headers);
RestTemplate restTemplate = new RestTemplate();
FormHttpMessageConverter formConverter = new FormHttpMessageConverter();
formConverter.setCharset(Charset.forName("UTF8"));
restTemplate.getMessageConverters().add(formConverter);
restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
String response = restTemplate.postForObject("localhost:8080",
requestEntity, String.class);
tempFile.delete();
}catch(Exception exc)
{
exc.printStackTrace();
}
You may use an InputStream
I think you may do in this way:
InputStream is = rt.execute("localhost:8080", HttpMethod.POST, requestCallback, responseExtractor);
Where requestCallback is an implementation of org.springframework.web.client.RequestCallback like this one
public class MyRequestCallback implements RequestCallback
{
#Override
public void doWithRequest(ClientHttpRequest request) throws IOException
{
request.getHeaders().add(HttpHeaders.CONTENT_TYPE, MimeTypeUtils.APPLICATION_JSON_VALUE);
request.getHeaders().add(HttpHeaders.ACCEPT, MimeTypeUtils.APPLICATION_JSON_VALUE);
}
}
While responseExtractor may by an instance of this class
public class MyResponseExtractor implements ResponseExtractor<InputStream>
{
#Override
public Boolean extractData(ClientHttpResponse response) throws IOException
{
HttpStatus status = response.getStatusCode();
switch (status)
{
case OK:
return response.getBody();
default:
return null;
}
}
}
Once obtained the InputStream you can manage it
Hope it is usefull
I am using the jsp-servlet in my application. and deployed the war on jboss 7.0.2 server. i have servlet have code related to database and that is being called many time in sec (say 500 times). but it is falling over for such many threads, jboss 7.0.2 will not able to handle this threads. server (jboss7.0.2) throws an exception.
java.lang.OutOfMemoryError: unable to create new native thread
at java.lang.Thread.start0(Native Method)
at java.lang.Thread.start(Unknown Source)
Here is my servlet
public class Test extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
processRequest(request, response);
}
protected void doPost(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
processRequest(request, response);
}
public void processRequest(HttpServletRequest request,
HttpServletResponse response) {
Logger log=LoggerFactory.getLogger(Test.class);
/* here is my code to insert the data in database. */
TestClass testobj = new TestClass();
testobj.setparam("");
smsmanager1.add(sms);
smsmanager1 = null;
sms = null;
}
}
code for add method
public void add(T obj) {
SessionFactory sessionFactory = HibernateUtil.getSessionFactory();
Session session=sessionFactory.openSession();
Transaction transaction = null;
try {
transaction = session.beginTransaction();
session.save(obj);
transaction.commit();
session.flush();
} catch (HibernateException e) {
if(transaction!=null){
transaction.rollback();}
e.printStackTrace();
} finally {
if(session!=null){
session.close();}
session = null;
transaction = null;
}
i have tested for the blank servlet that has the only one console printing statement. it works fine but it not work for above servlet.
am i on the right track here?
how the server will handle the such servlet for above 500-800 threads?