//
// Copyright (C) 2010 United States Government as represented by the
// Administrator of the National Aeronautics and Space Administration
// (NASA). All Rights Reserved.
//
// This software is distributed under the NASA Open Source Agreement
// (NOSA), version 1.3. The NOSA has been approved by the Open Source
// Initiative. See the file NOSA-1.3-JPF at the top of the distribution
// directory tree for the complete NOSA document.
//
// THE SUBJECT SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY OF ANY
// KIND, EITHER EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT
// LIMITED TO, ANY WARRANTY THAT THE SUBJECT SOFTWARE WILL CONFORM TO
// SPECIFICATIONS, ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR
// A PARTICULAR PURPOSE, OR FREEDOM FROM INFRINGEMENT, ANY WARRANTY THAT
// THE SUBJECT SOFTWARE WILL BE ERROR FREE, OR ANY WARRANTY THAT
// DOCUMENTATION, IF PROVIDED, WILL CONFORM TO THE SUBJECT SOFTWARE.
//
package gov.nasa.jpf.vm;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import de.fosd.typechef.featureexpr.FeatureExpr;
import gov.nasa.jpf.Config;
import gov.nasa.jpf.JPF;
import gov.nasa.jpf.JPFConfigException;
import gov.nasa.jpf.annotation.MJI;
import gov.nasa.jpf.util.FileUtils;
import gov.nasa.jpf.util.JPFLogger;
/**
* very simple URLConnection model that can be used for reading static URL contents
*
* The data can be stored in/read from the file system, and is cached
* to avoid DOS by means of model checking
*/
public class JPF_gov_nasa_jpf_CachedROHttpConnection extends NativePeer {
static JPFLogger logger = JPF.getLogger("http");
File cacheDir;
HashMap<String,byte[]> dataCache;
public JPF_gov_nasa_jpf_CachedROHttpConnection (Config conf){
String cacheDirPath = conf.getString("http.cache_dir");
if (cacheDirPath != null){
cacheDir = new File(cacheDirPath);
if (!cacheDir.exists()){
cacheDir.mkdir();
}
if (!cacheDir.isDirectory()){
throw new JPFConfigException("illegal http.cache_dir entry: " + cacheDirPath);
}
}
dataCache = new HashMap<String,byte[]>();
}
private static String getCacheFileName( String url){
String fn = url.replace('/', '^');
fn = fn.replace(':', '%');
return fn;
}
private byte[] getDataFromCachedFile (String url){
byte[] data = null;
String cacheFileName = getCacheFileName(url);
File cacheFile = new File(cacheDir, cacheFileName);
if (cacheFile.isFile()) {
try {
data = FileUtils.getContents(cacheFile);
} catch (IOException iox) {
logger.warning("can't read http data from cached file ", cacheFile.getPath());
}
if (data != null) {
logger.info("reading contents of ", url, " from file ", cacheFile.getPath());
dataCache.put(url, data);
}
}
return data;
}
private byte[] getDataFromURL (String surl){
byte[] data = null;
try {
URL url = new URL(surl);
InputStream is = url.openStream();
if (is != null) {
ByteArrayOutputStream os = new ByteArrayOutputStream(is.available());
byte[] buf = new byte[1024];
for (int n = is.read(buf); n >= 0; n = is.read(buf)) {
os.write(buf, 0, n);
}
is.close();
data = os.toByteArray();
dataCache.put(surl, data);
logger.info("reading contents of ", surl, " from server");
if (cacheDir != null) {
String cacheFileName = getCacheFileName(surl);
File cacheFile = new File(cacheDir, cacheFileName);
try {
FileUtils.setContents(cacheFile, data);
logger.info("storing contents of ", surl, " to file ", cacheFile.getPath());
} catch (IOException iox) {
logger.warning("can't store to cache directory ", cacheFile.getPath());
}
}
return data;
}
} catch (MalformedURLException mux){
logger.warning("mallformed URL ", surl);
} catch (IOException ex) {
logger.warning("reading URL data ", surl, " failed with ", ex.getMessage());
}
return data;
}
@SuppressWarnings("deprecation")
@MJI
public int getContents__Ljava_lang_String_2___3B (MJIEnv env, int objRef, int surlRef, FeatureExpr ctx){
String url = env.getStringObject(ctx, surlRef);
// first we check if it's already cached in memory
byte[] data = dataCache.get(url);
if (data != null){
logger.info("using cached contents of ", url);
} else {
// see if we can get it from the cacheDir
if (cacheDir != null){
data = getDataFromCachedFile(url);
}
// if that didn't produce anything, we have to reach out to the net
if (data == null){
data = getDataFromURL( url);
}
}
if (data != null){
return env.newByteArray(ctx, data);
} else {
return MJIEnv.NULL;
}
}
}