LOG.finest("Using search scope '"+searchScope+"'");
}
// the rest of the uri is the location of the gedcom individual
//String gedcomURL = URLDecoder.decode(uri.toString(), "UTF-8");
GLinkURL gedcomURL = new GLinkURL(URLDecoder.decode(uri.toString(), "UTF-8"));
LOG.finest("The GedapiServlet gedcom active individual is: "+gedcomURL);
LOG.finest("The individualId is: "+gedcomURL.getIndividualId());
String eTag = request.getHeader("If-None-Match");
LOG.finest("The eTag header is: "+eTag);
boolean networkReload = Boolean.valueOf(request.getHeader("X-Network-Reload"));
LOG.finest("The X-Network-Reload header is: "+networkReload);
try
{
File forceNetworkReload = gedcomURL.getForceNetworkReloadFile();
if(forceNetworkReload.exists())
{
networkReload = true;
forceNetworkReload.delete();
gedcomURL.deleteAllCachedFiles();
}
}
catch(Exception ex)
{
if(LOG.isLoggable(Level.FINEST))
{
ex.printStackTrace();
}
}
// DONE: Need to normalize the searchCriteria String to not contain characters that will mess up filenames,
// namely: spaces, commas, apostrophes, equals, etc; basically any non-alphanumeric character needs
// to be mapped to it's own alphanumeric character; i.e. space=a, comma=b, apostrophe=c, equals=d, etc.
// Easy, use Base64 encoding and then the following mapping: +=_, ==-, /=$
String searchCriteriaPath = null;
if(searchCriteria != null)
{
searchCriteriaPath = new String(Base64.encodeBase64(searchCriteria.getBytes("UTF-8")));
searchCriteriaPath = searchCriteriaPath.replace('+', '_').replace('=', '-').replace('/', '$');
}
// TODO: Could make it so that the json response is always read from the disk cache rather
// than being built on the fly, this way the HTTP response is very fast but the data being
// displayed will lag a certain interval behind the latest changes; this could be acceptable
// but the end user would not control at their convenience when the latest data was checked
// for; probably still need to allow the end user to snag the latest data at their convenience
// AND in addition have a thread that runs in the background as a cron job that keeps the disk
// cache uptodate with all of the changes that have occured on the network!!!!
//even if a networkReload is requested the json string that is returned should come from the disk
//cache unless it has been at least 60 seconds since the last time a networkReload has been requested
//which resulted in the writing of the json string to the disk cache
String eTagVersion = GLinkURL.JSON_ETAG_VERSION;
if(eTag != null)
{
eTagVersion = eTag.substring(0, eTag.indexOf('/'));
}
if(eTag != null && !networkReload && GLinkURL.JSON_ETAG_VERSION.equals(eTagVersion) &&
gedcomURL.getCachedJSONFile(view, searchCriteria, searchCriteriaPath).exists())
{
LOG.finest("Sending the request back the 304 status to tell it to use it's cache: "+eTag);
//Status=Not Modified - 304
//Server=Apache-Coyote/1.1
//Etag=W/"82-1214507740000"
//Date=Fri, 27 Jun 2008 19:15:41 GMT
response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
response.setHeader("ETag", eTag);
return;
}
else
{
// TODO: read the jsonObjectString and response headers from disk cache
if(gedcomURL.isValidIndividualId() || (searchCriteria != null && ViewEnum.GEDCOM_SEARCH_VIEW.equals(view)) ||
ViewEnum.SUBMITTER_VIEW.equals(view) || ViewEnum.ALL_GLINKS_VIEW.equals(view))
{
File cachedJSONFile = gedcomURL.getCachedJSONFile(view, searchCriteria, searchCriteriaPath);
if(cachedJSONFile.exists() && ((cachedJSONFile.lastModified() + MIN_RELOAD_WAIT) >= System.currentTimeMillis() ||
!networkReload))
{
// TODO: I think that there is a problem inside this if statement during the unit tests!!!
//LOG.finest("This is where the 0 content problem starts!!");
//if the file exists and the timestamp on the file was written to less than 60 seconds
//ago then use the contents of the file otherwise allow a networkReload if one
//was requested and if one was not requested then also return the contents of the file
ByteArrayOutputStream jsonOutput = new ByteArrayOutputStream();
synchronized(this)
{
BufferedInputStream input = new BufferedInputStream(new FileInputStream(cachedJSONFile));
byte[] b = new byte[10240];
int bytesRead;
while((bytesRead = input.read(b, 0, b.length)) != -1)
{
jsonOutput.write(b, 0, bytesRead);
}
input.close();
}
String jsonObjectString = jsonOutput.toString();
// DONE: if the X-Downloading-GEDCOM header has been set then do not allow the
// browser to cache the returned content
response.setHeader("Cache-Control", "max-age=0");
Object downloadRunner = request.getAttribute("X-Downloading-GEDCOM");
if(downloadRunner != null)
{
response.setHeader("X-Downloading-GEDCOM", "true");
LOG.finest("Setting the X-Downloading-GEDCOM header to true!!");
}
else
{
// TODO: The jsonObjectString is of length zero (0) and the jsonLastModified String is empty
// only under heavy load from lots of threads; this happens because the cachedJSONFile is being
// written to simultaneously by another thread while the thread in this part of the code
// is trying to read from the cachedJSONFile. The way to fix this is to make this read code
// and the write code (latter on in this servlet) acquire a lock on the file before proceeding!!
// **** For multiple JVMs this is still a problem but the 'synchronized(this)' block around
// the file I/O operations for the cachedJSONFile fix it for multiple threads calling
// this code in the same JVM
long jsonLastModified = cachedJSONFile.lastModified();
response.setHeader("ETag", GLinkURL.JSON_ETAG_VERSION+"/\""+jsonObjectString.length()+"-"+jsonLastModified+"\"");
response.setDateHeader("Last-Modified", jsonLastModified);
}
response.setContentType("application/json");
LOG.finest("The cached json for this view is: "+jsonObjectString);
PrintWriter printWriter = response.getWriter();
printWriter.print(jsonObjectString);
printWriter.flush();
printWriter.close();
return;
}
}
LOG.finest("Doing a network reload!!! The network reload parameter is: "+networkReload);
}
// the last part of the gedcomURL should be the individual id
// if the gedcomURL does not end in an individual id then the
// data for the first person in the gedcom is retrieved
Gedcom gedcom = gedcomURL.loadGedcom(networkReload, request);
Indi indi;
if(gedcomURL.isValidIndividualId())
{
indi = (Indi)gedcom.getEntity(Gedcom.INDI, gedcomURL.getIndividualId());
if(indi == null)
{
throw new ServletException(ErrorCodes.NONEXISTANT_INDI.getErrorMessage(new Object[] {gedcomURL.getIndividualId(), gedcomURL.getGedcomURL()}));
}
}
else
{
indi = (Indi)gedcom.getFirstEntity(Gedcom.INDI);
if(indi == null)
{
throw new ServletException(ErrorCodes.NO_INDIVIDUALS.getErrorMessage(new Object[] {gedcomURL.getGedcomURL()}));
}
gedcomURL.setIndividualId(indi.getId());
}
//switch on the view to know how to get the correct view data into X-JSON format
BaseView baseView = null;
switch(view)