How should I handle caching? - javascript

I have a SPA application which is written in Websocket, Polymer, ES6, HTML5. This is served by a Jetty 9 backend which is packaged as a runnable jar with absolutely everything in the JAR.
I'd like to have it such that when a new version of the JAR is deployed, I push a message down to the client to force it to do a cache-less refresh of all the resources.
I have a custom HttpServlet to serve my SPA so I can handle the URL "rewriting":
private static final Path ROOT = getDevelopmentWebRoot();
#Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String requestPath = req.getPathInfo();
if (requestPath.equals("/")) {
requestPath = "index.html";
} else {
requestPath = requestPath.substring(1);
}
Path resource = ROOT.resolve(requestPath);
resp.setHeader("Cache-Control", "max-age=31536000");
resp.setContentType(MimeTypes.getDefaultMimeByExtension(resource.toString()));
try {
Files.copy(resource, resp.getOutputStream());
} catch (NoSuchFileException e) {
Files.copy(ROOT.resolve("index.html"), resp.getOutputStream());
}
}
I guess my question is, how can I force a cache-less refresh in javascript?

You can set up a cache with the headers "Last-Modified" and "Cache-Control" with "must-revalidate". This should force the browser to do a "If-Modified-Since" request everytime.
Then in your frontend you can just reload the page every X seconds, and if nothing as been modified you should get it from the cache (instantaneous) else it will load the new page.
I haven't tested it but I think it should work like you want it to.

Related

Android webview local file setting root

I am loading a local web application using a webview in Android:
view.loadUrl("https://appassets.androidplatform.net/assets/dist/index.html")
However when I check the current route in my JavaScript it says https://appassets.androidplatform.net/assets/dist/index.html
This makes stuff like routing and file loading kind of a pain - is there a way to set the root of the loaded application, so I can avoid the whole /assets/ ?
Have a look at WebViewAssetLoader the code snippet there does exactly what you need. So you basically just need to forward all the requests which can be handled by the assetsLoader to it, and use the response that it returns.
final WebViewAssetLoader assetLoader = new WebViewAssetLoader.Builder()
.addPathHandler("/assets/", new AssetsPathHandler(this))
.build();
webView.setWebViewClient(new WebViewClient() {
#Override
#RequiresApi(21)
public WebResourceResponse shouldInterceptRequest(WebView view,
WebResourceRequest request) {
return assetLoader.shouldInterceptRequest(request.getUrl());
}
#Override
#SuppressWarnings("deprecation") // for API < 21
public WebResourceResponse shouldInterceptRequest(WebView view,
WebResourceRequest request) {
return assetLoader.shouldInterceptRequest(Uri.parse(request));
}
});
WebSettings webViewSettings = webView.getSettings();
// Setting this off for security. Off by default for SDK versions >= 16.
webViewSettings.setAllowFileAccessFromFileURLs(false);
// Off by default, deprecated for SDK versions >= 30.
webViewSettings.setAllowUniversalAccessFromFileURLs(false);
// Keeping these off is less critical but still a good idea, especially if your app is not
// using file:// or content:// URLs.
webViewSettings.setAllowFileAccess(false);
webViewSettings.setAllowContentAccess(false);
// Assets are hosted under http(s)://appassets.androidplatform.net/assets/... .
// If the application's assets are in the "main/assets" folder this will read the file
// from "main/assets/www/index.html" and load it as if it were hosted on:
// https://appassets.androidplatform.net/assets/www/index.html
webview.loadUrl("https://appassets.androidplatform.net/assets/www/index.html");

Html report opened in Safari from Java application displays as if Javascript not enabled

I have a Java application that as part of it's output creates an html report ,then opens the html reports in users browser. On Firefox and Google Chrome this works but on Safari it opens the report as if Javascript was not enabled, even though it is. However if you reopen the report by clicking on a link from another webpage (which lists all reports) then it opens fine in Safari.
What do I need to do to trigger Safari to open the report with Javascript enabled.
Console shows some errors, but I dont understand them
This is related issue https://apple.stackexchange.com/questions/361245/safari-kcferrordomaincfnetwork-error-1-on-local-html-files but doesn't provide a satisafactory answer.
Actually the answer here https://apple.stackexchange.com/questions/366448/safari-giving-kcferrordomaincfnetwork-error-303-when-visiting-a-site about removing my site from Preferecnes:Privacy works but that is no good because the problem occurs on new computer after running the program only a few times so would have to continually do it.
You open the file locally. Browsers usually restrict local file processing for security reasons. You must "Disable local file restrictions" in Safari in the "Developer" menu for making this possible. I am not sure, but it might be necessary to do this each time Safari opens such a file via your application. Embedding all external resources might also help, but I am not sure.
Opening the remote URL should always work. So this would be the best option. As an alternative you could serve the file to the browser via an embedded HTTP server in your application.
Edit: i just saw the previous answer proposed the same workaround, but i think copy paste code is always delicious. As for your problem, i verified this is occuring on safari and nothing else, and for me this fix worked.
Edit 2: According to this answer, it is a bug in Safari that it works when you open it via hyperlink. Without changing the developer settings in every users browser, it won't be working except on a local server. I'm sorry but it seems there is no other in-code workaround except what i already provided.
If you want to open with scripts at any cost, you can to the following workaround:
Write a minimalistic server socket (or copy paste my code):
Index.java:
package index;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
public class Index extends Base {
public byte[] buildResponseBody(String resource) {
try {
System.out.println(resource);
if(resource.contains("?")) return "<!DOCTYPE HTML><html><h1>Error 404: Page not found</h1></html>".getBytes(StandardCharsets.US_ASCII);
return Files.readAllBytes(Paths.get(resource));
} catch (Exception e) {
return "<!DOCTYPE HTML><html><h1>Error 404: Page not found</h1></html>".getBytes(StandardCharsets.US_ASCII);
}
}
public static void main(String[] args) throws IOException {
new Index().loop(80);
}
}
Base.java:
package index;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public abstract class Base {
public abstract byte[] buildResponseBody(String resource);
String receiveRequest(BufferedReader reader) throws IOException {
final Pattern getLinePattern = Pattern.compile("(?i)GET\\s+/(.*?)\\s+HTTP/1\\.[01]");
String resource = null;
try {
for (String line = reader.readLine(); !line.isEmpty(); line = reader.readLine()) {
Matcher matcher = getLinePattern.matcher(line);
if (matcher.matches()) {
resource = matcher.group(1);
}
}
} catch (NullPointerException e) {
return null;
}
return resource;
}
public void sendResponse(String resource, OutputStream output, PrintWriter writer) throws IOException {
byte[] responseBody = buildResponseBody(resource);
if (responseBody == null) {
writer.println("HTTP/1.0 404 Not found");
responseBody = "Not found".getBytes();
} else
writer.println("HTTP/1.0 200 OK");
writer.println("Server: " + getClass().getSimpleName());
writer.println("Content-Length: " + responseBody.length);
writer.println();
writer.flush();
output.write(responseBody);
}
public void loop(int port) throws IOException {
try (ServerSocket serverSocket = new ServerSocket(port)) {
while (true)
try (Socket socket = serverSocket.accept();
InputStream input = socket.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(input));
OutputStream output = socket.getOutputStream();
PrintWriter writer = new PrintWriter(new OutputStreamWriter(output))) {
sendResponse(receiveRequest(reader), output, writer);
}
}
}
}
This enables opening a page at 127.0.0.1:80. If you now make sure the document is accessable to your application, you can make it open a browser window at your localhost:80/file.html. Since it now runs over an actual website not just a file in safari, the scripts should theoretically be working. (At least mine are in this code)
You might want to improve security and remove some bugs of this code. I hope i could help.

Downloading web pages and refreshing it when connection available

So I'm building an app with a lot of web content I plan to release it using Phone Gap build but will host all the content online and will link to it. I was wondering if there is a way that the web pages can be downloaded when there is an active internet connection for offline use and when there is a connection again for the data to be refreshed preferably when the user is using a wifi connection. The site will mostly be in html, js, and php. I will be hosting with bluehost
Is there any way of doing this? Thanks in advance! Littleswany!
PhoneGap apps ARE downloaded to the device, when they are downloaded from the store. They are basically a wrapper around an index.html file, but the app is actually programmed in JavaScript, which is responsible for creating and displaying views etc. The only time you need to check for an internet connection is when you are communicating with your back end (PHP)... If the ajax request fails, the best solution is to provide the user with a button/link to try again when they have regained their internet connection, or set a timer which fires intermittently to keep trying again... NEVER use a while(true) loop in your Phone Gap app - it will just hang.
I am not familiar with java, but i think i can provide the logic to get the job done.
You want to do an infinite loop that checks if the user is on wifi. Then if true, use wget, rsync, or scp to download the website. Something like this.:
while (true){
// do an if statement that checks if user is on wifi. Then do a then statement that uses rsync or wget.
}
Info on how to nest if statements in while loops in java: java loop, if else
I do not know if wget, rsync, or scp can be ran from java. You'll need to look more into it or write your own alternative function to do it. Something like:
function download_file() {
var url = "http://www.example.com/file.doc"
window.location = url;
}
You should be able to do it from your java like this:
String whatToRun = "/usr/local/bin/wget http://insitu.fruitfly.org/insitu_image_storage/img_dir_38/insitu38795.jpe";
Sources:
1. What is the equivalent of wget in javascript to download a file from a given url?
2. Call a command in terminal using Java (OSX)
First Create an Connection filter class
public class Connection_Status{
private static ConnectivityManager connectivityManager;
static boolean connected = false;
public static Boolean isOnline(Context ctx) {
try {
connectivityManager = (ConnectivityManager) ctx.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();
connected = networkInfo != null && networkInfo.isAvailable()&& networkInfo.isConnected();
return connected;
} catch (Exception e) {
System.out.println("CheckConnectivity Exception: " + e.getMessage());
}
return connected;
}
}
And in your Main class
public class Main extends Activity{
private WebView mWebView;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mWebView = (WebView) findViewById(R.id.webview);
mWebView.getSettings().setJavaScriptEnabled(true);
mWebView.getSettings().setBuiltInZoomControls(true);
if(Connection_Status.isOnline(Main.this)){
HttpClient httpclient = new DefaultHttpClient(); // Create HTTP Client
HttpGet httpget = new HttpGet("http://yoururl.com"); // Set the action you want to do
HttpResponse response = httpclient.execute(httpget); // Executeit
HttpEntity entity = response.getEntity();
InputStream is = entity.getContent(); // Create an InputStream with the response
BufferedReader reader = new BufferedReader(new InputStreamReader(is, "iso-8859-1"), 8);
StringBuilder sb = new StringBuilder();
String line = null;
while ((line = reader.readLine()) != null) // Read line by line
sb.append(line + "\n");
String resString = sb.toString(); //
is.close(); // Close the stream
}
}
}
Or you can use cache on it e.g
mWebView.getSettings().setAppCacheMaxSize(1024*1024*8);
mWebView.getSettings().setAppCachePath(""+this.getCacheDir());
mWebView.getSettings().setAppCacheEnabled(true);
mWebView.getSettings().setCacheMode(WebSettings.LOAD_DEFAULT);
Don't forget to add the following permissions
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <!-- for the connection status-->
Sources:
https://stackoverflow.com/a/6503817/1309629

On implementing Http Handler for Js extension javascript on page started to compiling

I had implemented HttpHandler for Js extension for google api hosted jquery script files. because when it is called need to replace http with https. But visual studio started compiling javascript on pages being loaded. how do I suppress this behavior. And most interesting why did it happened.
My Http Handler :
public class HttpToHttpsHandler : IHttpHandler
{
public bool IsReusable
{
get { return false; }
}
public void ProcessRequest(HttpContext context)
{
try
{
if (context.Request.RawUrl.Contains("http:"))
{
context.Response.ContentType = "text/plain";
string newUrl = context.Request.RawUrl.Replace("http", "https");
context.Server.Transfer(newUrl);
}
}
catch (Exception)
{
throw;
}
}
}
What possibly went wrong.
It is because you have used Server.Transfer() which doesn't issue redirect to the browser but changes the execution path on the server - in the result ASP.NET will try to create something out of your JavaScript.
You want to make a simple redirection so just use Response.Redirect()
context.Response.Redirect(newUrl, false);
Also I would like suggest a more safe approach for altering the URL (in case there would be port number in URL etc.):
if (!context.Request.IsSecureConnection)
{
UriBuilder secureUriBBuilder = new UriBuilder(context.Request.Url);
secureUriBBuilder.Scheme = Uri.UriSchemeHttps;
//Ude default port for schema - alter this if your server is using custom port for HTTPS
secureUriBBuilder.Port = -1;
context.Response.Redirect(secureUriBBuilder.Uri.ToString(), false);
}
Also remember that the entire page will remain in non-safe mode if you will load the HTML in HTTP and try to load only JavaScript through HTTPS - you should consider redirection in Global.asax or usage of URL Rewrite module.

Is there any way to know in Servlet is JS is enable or not on browser?

Is there any way to know in Servlet is JS is enable or not on browser? Also want flash version which installed on browser. I want all this information at Servlet only. We need to log this information at our end.
There is a way,
Add a new Servlet with some name like JsServlet
Add a no script tag
< noscript>
< img src="{PATH TO YOUR SERVLET}" >
< /noscript>
Return some blank image from the Servlet
If this Servlet get called you can get that the browser dose not support script.
Edit : The servlet code will be,
public class JsServlet extends HttpServlet{
protected void service(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
/*Write your code that you wan't */
/*You are here means the browser dose not support javaScript*/
BufferedImage buffer = new BufferedImage(1, 1,
BufferedImage.TYPE_INT_RGB);
response.setContentType("image/png");
OutputStream os = response.getOutputStream();
ImageIO.write(bufferedImage, "png", os);
os.close();
}

Categories