JavaFx 8 WebEngine - Caching images via user data directory? -
i new javafx , tried implement browser in application. since reloading images on every new startup quite time-consuming, i'd store them in cache directory, have been failing so. tried using setuserdatadirectory(...)
, leads empty folder called localstorage
, empty .lock
file being created.
i found so thread, firstly, not yet allowed comment there , secondly, seems address javafx 2.2. has been posted there still hold true javafx 8? if so: there simple way implement such urlconnection
cache?
many :)
so, after 2 days of digging problem , learning urlconnection
class, able come own (presumably crude) implementation i'd share here in case little knowledge happens stumble on thread. again, basic idea place specific file types in cache, not everything. chose store jpg
, png
, gif
, js
files thought them load-heavy ones, though every other file format should possible, too. first things first: browser.getengine().setuserdatadirectory(...)
not job. still have no clue, for, it's not storing image files %)
instead, did creating 5 classes:
cachedresource
: consists ofbyte[]
array holding resource's raw data , meta info (header fields, lastmodified)resourcecache
: holds cached resource objects.myhttpurlconnection
(extendssun.net.www.protocol.http.httpurlconnection
): wrapper class responsible retrieving file given url points to. network magic.cachedurlconnection
(extendsjava.net.urlconnection
): (almost) empty implementation has data need , waits system call it.myurlconnectionhandler
(extendssun.net.www.protocol.http.handler
): class registered @ application start , decides when useurlconnection
(see below).
the resourcecache
, cachedresource
, cachedurlconnection
classes small , easy write. designed resource cache map resource of url corresponding cachedresource
object, thus: concurrenthashmap<url, cachedresource>
plus getter , addresource(...)
function. added other stuff storing files locally, leads off-topic.
i implemented cachedurlconnection
class follows:
public class cachedurlconnection extends urlconnection { private cachedresource resource; private bytearrayinputstream inputstream; /* constructors */ public cachedurlconnection(url url, cachedresource resource) throws ioexception { super(url); this.resource = resource; this.inputstream = new bytearrayinputstream(resource.getbytedata()); } @override public void connect() throws ioexception { // no need anything. } /* object methods */ /* getters , setters */ @override public string getheaderfield(int index) { ... } @override public string getheaderfield(string key) { ... } @override public map<string, list<string>> getheaderfields() { ... } @override public inputstream getinputstream() throws ioexception { return inputstream; // <---- here, system can grab data. } }
when looking @ sourcecode of urlconnection (for example here), notice of method implementations dummies either return null
or throw unknownserviceexception
.
this important: don't know of these need implement!
in order find out, used myhttpurlconnection
class , added every function
system.out.println("function xyz called!"); super.xyz();
but lazy , didn't check of them. far, seems working fine %)
the next class myhttpurlconnection
. not 100% sure if needed overwrite httpurlconnection
class, did anyway, because has protected
constructor implicitely called new sun.net.www.protocol.http.handler
. handler not follow our http policy, wanted (cf. sourcecode line 801). class looks rather empty:
public class myhttpurlconnection extends httpurlconnection { protected myhttpurlconnection(url url, handler handler) { this(url, null, handler); } public myhttpurlconnection(url url, proxy proxy) { this(url, proxy, new myurlconnectionhandler()); // <--- no way sneaking around^^ } protected myhttpurlconnection(url url, proxy proxy, handler handler) { super(url, proxy, handler); } public myhttpurlconnection(url url, string host, int port) { this(url, new proxy(proxy.type.http, inetsocketaddress.createunresolved(host, port))); // taken on httpurlconnection sourcecode. } }
now comes important part: myurlconnectionhandler
. again, check this thread on put it. class needs overwrite openconnection(url, proxy)
function. before posting code, give run-through of does.
- if given url jpg, png, ... file:
- use
myhttpurlconnection
object resource's last date of modification on server. should call header , not entire resource. otherwise, wouldn't have won anything. credits go this thread. i not sure, though, whether i'm closingurlconnection
here. better double-check if in doubt ;) - if there no resource in cache or resource in cache out of date:
- close mini connection , open "proper" 1 download whole thing.
- create new
cachedresource
object , add cache. - close new connection, too.
- return new
cachedurlconnection
object holds data. might seem bit stupid, have everything, function needs returnurlconnection
. - if there exception or did not deal jpg, png, ... file, return "default"
myhttpurlconnection
object process url normally.
and corresponding code looks follows. note used apache org.apache.commons.io.ioutils
:
@override protected urlconnection openconnection(url url, proxy proxy) throws ioexception { try { // resource we'd cache? if (resourcecache.iscachableurl(url)) { // retrieve whatever in cache first. resourcecache cache = resourcecache.getinstance(); cachedresource resource = cache.getcachedresource(url); // open connection server @ least check last-modified field. myhttpurlconnection conn = new myhttpurlconnection(url, this); // don't use url#openconnection avoid looping! conn.setrequestmethod("head"); conn.connect(); long lastmodified = conn.getlastmodified(); // did last-modified value @ all? if (lastmodified == 0) { throw new exception("no last-modified value read! \n\t" + url); } // resource not cached or out of date? if (resource == null || resource.getlastmodified() < lastmodified) { conn = new myhttpurlconnection(url, this); conn.connect(); inputstream input = conn.getinputstream(); byte[] data = ioutils.tobytearray(input); map<string, list<string>> headerfields = conn.getheaderfields(); ioutils.closequietly(input); resource = new cachedresource(url.getfile(), data, headerfields, lastmodified); // use url.getfile() store file on hard drive. cache.addcachedresource(url, resource); } return new cachedurlconnection(url, resource); } } catch (exception e) { e.printstacktrace(); } // return default httpurlconnection in our wrapper class. return new myhttpurlconnection(url, proxy, this); }
one last thing: on safe side, never use url#openconnection
method inside myurlconnectionhandler#openconnection
function. writing down way makes pretty obvious why, today, took me quite while figure out infinite loop coming %) use constructor , call connect()
instead.
i hope ever anyone, otherwise has been exercise me ^^
Comments
Post a Comment