Merge remote branch 'origin/master'
This commit is contained in:
commit
3c793b5302
10 changed files with 390 additions and 104 deletions
|
|
@ -27,6 +27,7 @@ import org.dynmap.debug.Debug;
|
|||
import org.dynmap.kzedmap.KzedMap;
|
||||
import org.dynmap.kzedmap.KzedMap.KzedBufferedImage;
|
||||
import org.dynmap.MapChunkCache;
|
||||
import org.dynmap.utils.FileLockManager;
|
||||
import org.json.simple.JSONObject;
|
||||
|
||||
public class FlatMap extends MapType {
|
||||
|
|
@ -37,7 +38,8 @@ public class FlatMap extends MapType {
|
|||
private int ambientlight = 15;;
|
||||
private int shadowscale[] = null;
|
||||
private boolean night_and_day; /* If true, render both day (prefix+'-day') and night (prefix) tiles */
|
||||
|
||||
protected boolean transparency;
|
||||
|
||||
public FlatMap(ConfigurationNode configuration) {
|
||||
this.configuration = configuration;
|
||||
prefix = (String) configuration.get("prefix");
|
||||
|
|
@ -68,6 +70,7 @@ public class FlatMap extends MapType {
|
|||
ambientlight = Integer.parseInt(String.valueOf(o));
|
||||
}
|
||||
night_and_day = configuration.getBoolean("night-and-day", false);
|
||||
transparency = configuration.getBoolean("transparency", false); /* Default off */
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -114,8 +117,8 @@ public class FlatMap extends MapType {
|
|||
|
||||
boolean rendered = false;
|
||||
Color rslt = new Color();
|
||||
int[] pixel = new int[3];
|
||||
int[] pixel_day = new int[3];
|
||||
int[] pixel = new int[4];
|
||||
int[] pixel_day = null;
|
||||
KzedBufferedImage im = KzedMap.allocateBufferedImage(t.size, t.size);
|
||||
int[] argb_buf = im.argb_buf;
|
||||
KzedBufferedImage im_day = null;
|
||||
|
|
@ -123,6 +126,7 @@ public class FlatMap extends MapType {
|
|||
if(night_and_day) {
|
||||
im_day = KzedMap.allocateBufferedImage(t.size, t.size);
|
||||
argb_buf_day = im_day.argb_buf;
|
||||
pixel_day = new int[4];
|
||||
}
|
||||
MapChunkCache.MapIterator mapiter = cache.getIterator(t.x * t.size, 127, t.y * t.size);
|
||||
for (int x = 0; x < t.size; x++) {
|
||||
|
|
@ -177,20 +181,27 @@ public class FlatMap extends MapType {
|
|||
pixel[0] = c.getRed();
|
||||
pixel[1] = c.getGreen();
|
||||
pixel[2] = c.getBlue();
|
||||
|
||||
pixel[3] = c.getAlpha();
|
||||
|
||||
/* If transparency needed, process it */
|
||||
if(transparency && (pixel[3] < 255)) {
|
||||
process_transparent(pixel, pixel_day, mapiter);
|
||||
}
|
||||
/* If ambient light less than 15, do scaling */
|
||||
if((shadowscale != null) && (ambientlight < 15)) {
|
||||
else if((shadowscale != null) && (ambientlight < 15)) {
|
||||
if(mapiter.y < 127)
|
||||
mapiter.incrementY();
|
||||
if(night_and_day) { /* Use unscaled color for day (no shadows from above) */
|
||||
pixel_day[0] = pixel[0];
|
||||
pixel_day[1] = pixel[1];
|
||||
pixel_day[2] = pixel[2];
|
||||
pixel_day[3] = 255;
|
||||
}
|
||||
int light = Math.max(ambientlight, mapiter.getBlockEmittedLight());
|
||||
pixel[0] = (pixel[0] * shadowscale[light]) >> 8;
|
||||
pixel[1] = (pixel[1] * shadowscale[light]) >> 8;
|
||||
pixel[2] = (pixel[2] * shadowscale[light]) >> 8;
|
||||
pixel[3] = 255;
|
||||
}
|
||||
else { /* Only do height keying if we're not messing with ambient light */
|
||||
boolean below = mapiter.y < 64;
|
||||
|
|
@ -214,28 +225,32 @@ public class FlatMap extends MapType {
|
|||
pixel[0] -= pixel[0] * scale;
|
||||
pixel[1] -= pixel[1] * scale;
|
||||
pixel[2] -= pixel[2] * scale;
|
||||
pixel[3] = 255;
|
||||
} else {
|
||||
pixel[0] += (255-pixel[0]) * scale;
|
||||
pixel[1] += (255-pixel[1]) * scale;
|
||||
pixel[2] += (255-pixel[2]) * scale;
|
||||
pixel[3] = 255;
|
||||
}
|
||||
if(night_and_day) {
|
||||
pixel_day[0] = pixel[0];
|
||||
pixel_day[1] = pixel[1];
|
||||
pixel_day[2] = pixel[2];
|
||||
pixel_day[3] = 255;
|
||||
}
|
||||
|
||||
}
|
||||
rslt.setRGBA(pixel[0], pixel[1], pixel[2], 255);
|
||||
rslt.setRGBA(pixel[0], pixel[1], pixel[2], pixel[3]);
|
||||
argb_buf[(t.size-y-1) + (x*t.size)] = rslt.getARGB();
|
||||
if(night_and_day) {
|
||||
rslt.setRGBA(pixel_day[0], pixel_day[1], pixel_day[2], 255);
|
||||
rslt.setRGBA(pixel_day[0], pixel_day[1], pixel_day[2], pixel[3]);
|
||||
argb_buf_day[(t.size-y-1) + (x*t.size)] = rslt.getARGB();
|
||||
}
|
||||
rendered = true;
|
||||
}
|
||||
}
|
||||
/* Test to see if we're unchanged from older tile */
|
||||
FileLockManager.getWriteLock(outputFile);
|
||||
TileHashManager hashman = MapManager.mapman.hashman;
|
||||
long crc = hashman.calculateTileHash(argb_buf);
|
||||
boolean tile_update = false;
|
||||
|
|
@ -257,12 +272,13 @@ public class FlatMap extends MapType {
|
|||
Debug.debug("skipping image " + outputFile.getPath() + " - hash match");
|
||||
}
|
||||
KzedMap.freeBufferedImage(im);
|
||||
FileLockManager.releaseWriteLock(outputFile);
|
||||
MapManager.mapman.updateStatistics(tile, null, true, tile_update, !rendered);
|
||||
|
||||
/* If day too, handle it */
|
||||
if(night_and_day) {
|
||||
File dayfile = new File(outputFile.getParent(), tile.getDayFilename());
|
||||
|
||||
FileLockManager.getWriteLock(dayfile);
|
||||
crc = hashman.calculateTileHash(argb_buf_day);
|
||||
if((!dayfile.exists()) || (crc != hashman.getImageHashCode(tile.getKey(), "day", t.x, t.y))) {
|
||||
Debug.debug("saving image " + dayfile.getPath());
|
||||
|
|
@ -282,12 +298,85 @@ public class FlatMap extends MapType {
|
|||
tile_update = false;
|
||||
}
|
||||
KzedMap.freeBufferedImage(im_day);
|
||||
FileLockManager.releaseWriteLock(dayfile);
|
||||
MapManager.mapman.updateStatistics(tile, "day", true, tile_update, !rendered);
|
||||
}
|
||||
|
||||
return rendered;
|
||||
}
|
||||
|
||||
private void process_transparent(int[] pixel, int[] pixel_day, MapChunkCache.MapIterator mapiter) {
|
||||
int r = pixel[0], g = pixel[1], b = pixel[2], a = pixel[3];
|
||||
int r_day = 0, g_day = 0, b_day = 0, a_day = 0;
|
||||
if(pixel_day != null) {
|
||||
r_day = pixel[0]; g_day = pixel[1]; b_day = pixel[2]; a_day = pixel[3];
|
||||
}
|
||||
/* Scale alpha to be proportional to iso view (where we go through 4 blocks to go sqrt(6) or 2.45 units of distance */
|
||||
if(a < 255)
|
||||
a = a_day = 255 - ((255-a)*(255-a) >> 8);
|
||||
/* Handle lighting on cube */
|
||||
if((shadowscale != null) && (ambientlight < 15)) {
|
||||
boolean did_inc = false;
|
||||
if(mapiter.y < 127) {
|
||||
mapiter.incrementY();
|
||||
did_inc = true;
|
||||
}
|
||||
if(night_and_day) { /* Use unscaled color for day (no shadows from above) */
|
||||
r_day = r; g_day = g; b_day = b; a_day = a;
|
||||
}
|
||||
int light = Math.max(ambientlight, mapiter.getBlockEmittedLight());
|
||||
r = (r * shadowscale[light]) >> 8;
|
||||
g = (g * shadowscale[light]) >> 8;
|
||||
b = (b * shadowscale[light]) >> 8;
|
||||
if(did_inc)
|
||||
mapiter.decrementY();
|
||||
}
|
||||
if(a < 255) { /* If not opaque */
|
||||
pixel[0] = pixel[1] = pixel[2] = pixel[3] = 0;
|
||||
if(pixel_day != null)
|
||||
pixel_day[0] = pixel_day[1] = pixel_day[2] = pixel_day[3] = 0;
|
||||
mapiter.decrementY();
|
||||
if(mapiter.y >= 0) {
|
||||
int blockType = mapiter.getBlockTypeID();
|
||||
int data = 0;
|
||||
Color[] colors = colorScheme.colors[blockType];
|
||||
if(colorScheme.datacolors[blockType] != null) {
|
||||
data = mapiter.getBlockData();
|
||||
colors = colorScheme.datacolors[blockType][data];
|
||||
}
|
||||
if (colors != null) {
|
||||
Color c = colors[0];
|
||||
if (c != null) {
|
||||
pixel[0] = c.getRed();
|
||||
pixel[1] = c.getGreen();
|
||||
pixel[2] = c.getBlue();
|
||||
pixel[3] = c.getAlpha();
|
||||
}
|
||||
}
|
||||
/* Recurse to resolve color here */
|
||||
process_transparent(pixel, pixel_day, mapiter);
|
||||
}
|
||||
}
|
||||
/* Blend colors from behind block and block, based on alpha */
|
||||
r *= a;
|
||||
g *= a;
|
||||
b *= a;
|
||||
int na = 255 - a;
|
||||
pixel[0] = (pixel[0] * na + r) >> 8;
|
||||
pixel[1] = (pixel[1] * na + g) >> 8;
|
||||
pixel[2] = (pixel[2] * na + b) >> 8;
|
||||
pixel[3] = 255;
|
||||
if(pixel_day != null) {
|
||||
r_day *= a_day;
|
||||
g_day *= a_day;
|
||||
b_day *= a_day;
|
||||
na = 255 - a_day;
|
||||
pixel_day[0] = (pixel_day[0] * na + r_day) >> 8;
|
||||
pixel_day[1] = (pixel_day[1] * na + g_day) >> 8;
|
||||
pixel_day[2] = (pixel_day[2] * na + b_day) >> 8;
|
||||
pixel_day[3] = 255;
|
||||
}
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return prefix;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import org.dynmap.TileHashManager;
|
|||
import org.dynmap.debug.Debug;
|
||||
import org.dynmap.MapChunkCache;
|
||||
import org.dynmap.kzedmap.KzedMap.KzedBufferedImage;
|
||||
import org.dynmap.utils.FileLockManager;
|
||||
import org.json.simple.JSONObject;
|
||||
|
||||
public class DefaultTileRenderer implements MapTileRenderer {
|
||||
|
|
@ -35,6 +36,7 @@ public class DefaultTileRenderer implements MapTileRenderer {
|
|||
protected int shadowscale[]; /* index=skylight level, value = 256 * scaling value */
|
||||
protected int lightscale[]; /* scale skylight level (light = lightscale[skylight] */
|
||||
protected boolean night_and_day; /* If true, render both day (prefix+'-day') and night (prefix) tiles */
|
||||
protected boolean transparency; /* Is transparency support active? */
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
|
|
@ -77,6 +79,7 @@ public class DefaultTileRenderer implements MapTileRenderer {
|
|||
}
|
||||
colorScheme = ColorScheme.getScheme((String)configuration.get("colorscheme"));
|
||||
night_and_day = configuration.getBoolean("night-and-day", false);
|
||||
transparency = configuration.getBoolean("transparency", true); /* Default on */
|
||||
}
|
||||
|
||||
public boolean render(MapChunkCache cache, KzedMapTile tile, File outputFile) {
|
||||
|
|
@ -228,6 +231,7 @@ public class DefaultTileRenderer implements MapTileRenderer {
|
|||
int oy = (mtile.py == zmtile.getTileY())?0:KzedMap.tileHeight/2;
|
||||
|
||||
/* Test to see if we're unchanged from older tile */
|
||||
FileLockManager.getWriteLock(fname);
|
||||
TileHashManager hashman = MapManager.mapman.hashman;
|
||||
long crc = hashman.calculateTileHash(img.argb_buf);
|
||||
boolean updated_fname = false;
|
||||
|
|
@ -247,6 +251,7 @@ public class DefaultTileRenderer implements MapTileRenderer {
|
|||
updated_fname = true;
|
||||
}
|
||||
KzedMap.freeBufferedImage(img);
|
||||
FileLockManager.releaseWriteLock(fname);
|
||||
MapManager.mapman.updateStatistics(mtile, null, true, updated_fname, !rendered);
|
||||
|
||||
mtile.file = fname;
|
||||
|
|
@ -254,6 +259,7 @@ public class DefaultTileRenderer implements MapTileRenderer {
|
|||
boolean updated_dfname = false;
|
||||
File dfname = new File(fname.getParent(), mtile.getDayFilename());
|
||||
if(img_day != null) {
|
||||
FileLockManager.getWriteLock(dfname);
|
||||
crc = hashman.calculateTileHash(img.argb_buf);
|
||||
if((!dfname.exists()) || (crc != hashman.getImageHashCode(mtile.getKey(), "day", tx, ty))) {
|
||||
Debug.debug("saving image " + dfname.getPath());
|
||||
|
|
@ -269,12 +275,14 @@ public class DefaultTileRenderer implements MapTileRenderer {
|
|||
updated_dfname = true;
|
||||
}
|
||||
KzedMap.freeBufferedImage(img_day);
|
||||
FileLockManager.releaseWriteLock(dfname);
|
||||
MapManager.mapman.updateStatistics(mtile, "day", true, updated_dfname, !rendered);
|
||||
}
|
||||
|
||||
// Since we've already got the new tile, and we're on an async thread, just
|
||||
// make the zoomed tile here
|
||||
boolean ztile_updated = false;
|
||||
FileLockManager.getWriteLock(zoomFile);
|
||||
if(updated_fname || (!zoomFile.exists())) {
|
||||
saveZoomedTile(zmtile, zoomFile, zimg, ox, oy);
|
||||
MapManager.mapman.pushUpdate(zmtile.getWorld(),
|
||||
|
|
@ -282,11 +290,13 @@ public class DefaultTileRenderer implements MapTileRenderer {
|
|||
ztile_updated = true;
|
||||
}
|
||||
KzedMap.freeBufferedImage(zimg);
|
||||
FileLockManager.releaseWriteLock(zoomFile);
|
||||
MapManager.mapman.updateStatistics(zmtile, null, true, ztile_updated, !rendered);
|
||||
|
||||
if(zimg_day != null) {
|
||||
File zoomFile_day = new File(zoomFile.getParent(), zmtile.getDayFilename());
|
||||
ztile_updated = false;
|
||||
FileLockManager.getWriteLock(zoomFile_day);
|
||||
if(updated_dfname || (!zoomFile_day.exists())) {
|
||||
saveZoomedTile(zmtile, zoomFile_day, zimg_day, ox, oy);
|
||||
MapManager.mapman.pushUpdate(zmtile.getWorld(),
|
||||
|
|
@ -294,6 +304,7 @@ public class DefaultTileRenderer implements MapTileRenderer {
|
|||
ztile_updated = true;
|
||||
}
|
||||
KzedMap.freeBufferedImage(zimg_day);
|
||||
FileLockManager.releaseWriteLock(zoomFile_day);
|
||||
MapManager.mapman.updateStatistics(zmtile, "day", true, ztile_updated, !rendered);
|
||||
}
|
||||
}
|
||||
|
|
@ -431,10 +442,10 @@ public class DefaultTileRenderer implements MapTileRenderer {
|
|||
if (colors != null) {
|
||||
Color c = colors[seq];
|
||||
if (c.getAlpha() > 0) {
|
||||
/* we found something that isn't transparent! */
|
||||
if (c.getAlpha() == 255) {
|
||||
/* we found something that isn't transparent, or not doing transparency */
|
||||
if ((!transparency) || (c.getAlpha() == 255)) {
|
||||
/* it's opaque - the ray ends here */
|
||||
result.setColor(c);
|
||||
result.setARGB(c.getARGB() | 0xFF000000);
|
||||
if(lightlevel < 15) { /* Not full light? */
|
||||
shadowColor(result, lightlevel);
|
||||
}
|
||||
|
|
|
|||
105
src/main/java/org/dynmap/utils/FileLockManager.java
Normal file
105
src/main/java/org/dynmap/utils/FileLockManager.java
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
package org.dynmap.utils;
|
||||
import java.io.File;
|
||||
import java.util.HashMap;
|
||||
|
||||
import org.dynmap.Log;
|
||||
/**
|
||||
* Implements soft-locks for prevent concurrency issues with file updates
|
||||
*/
|
||||
public class FileLockManager {
|
||||
private static Object lock = new Object();
|
||||
private static HashMap<String, Integer> filelocks = new HashMap<String, Integer>();
|
||||
private static final Integer WRITELOCK = new Integer(-1);
|
||||
/**
|
||||
* Get write lock on file - exclusive lock, no other writers or readers
|
||||
* @throws InterruptedException
|
||||
*/
|
||||
public static void getWriteLock(File f) {
|
||||
String fn = f.getPath();
|
||||
synchronized(lock) {
|
||||
boolean got_lock = false;
|
||||
while(!got_lock) {
|
||||
Integer lockcnt = filelocks.get(fn); /* Get lock count */
|
||||
if(lockcnt != null) { /* If any locks, can't get write lock */
|
||||
try {
|
||||
lock.wait();
|
||||
} catch (InterruptedException ix) {
|
||||
Log.severe("getWriteLock(" + fn + ") interrupted");
|
||||
}
|
||||
}
|
||||
else {
|
||||
filelocks.put(fn, WRITELOCK);
|
||||
got_lock = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
//Log.info("getWriteLock(" + f + ")");
|
||||
}
|
||||
/**
|
||||
* Release write lock
|
||||
*/
|
||||
public static void releaseWriteLock(File f) {
|
||||
String fn = f.getPath();
|
||||
synchronized(lock) {
|
||||
Integer lockcnt = filelocks.get(fn); /* Get lock count */
|
||||
if(lockcnt == null)
|
||||
Log.severe("releaseWriteLock(" + fn + ") on unlocked file");
|
||||
else if(lockcnt.equals(WRITELOCK)) {
|
||||
filelocks.remove(fn); /* Remove lock */
|
||||
lock.notifyAll(); /* Wake up folks waiting for locks */
|
||||
}
|
||||
else
|
||||
Log.severe("releaseWriteLock(" + fn + ") on read-locked file");
|
||||
}
|
||||
//Log.info("releaseWriteLock(" + f + ")");
|
||||
}
|
||||
/**
|
||||
* Get read lock on file - multiple readers allowed, blocks writers
|
||||
*/
|
||||
public static void getReadLock(File f) {
|
||||
String fn = f.getPath();
|
||||
synchronized(lock) {
|
||||
boolean got_lock = false;
|
||||
while(!got_lock) {
|
||||
Integer lockcnt = filelocks.get(fn); /* Get lock count */
|
||||
if(lockcnt == null) {
|
||||
filelocks.put(fn, Integer.valueOf(1)); /* First lock */
|
||||
got_lock = true;
|
||||
}
|
||||
else if(!lockcnt.equals(WRITELOCK)) { /* Other read locks */
|
||||
filelocks.put(fn, Integer.valueOf(lockcnt+1));
|
||||
got_lock = true;
|
||||
}
|
||||
else { /* Write lock in place */
|
||||
try {
|
||||
lock.wait();
|
||||
} catch (InterruptedException ix) {
|
||||
Log.severe("getReadLock(" + fn + ") interrupted");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//Log.info("getReadLock(" + f + ")");
|
||||
}
|
||||
/**
|
||||
* Release read lock
|
||||
*/
|
||||
public static void releaseReadLock(File f) {
|
||||
String fn = f.getPath();
|
||||
synchronized(lock) {
|
||||
Integer lockcnt = filelocks.get(fn); /* Get lock count */
|
||||
if(lockcnt == null)
|
||||
Log.severe("releaseReadLock(" + fn + ") on unlocked file");
|
||||
else if(lockcnt.equals(WRITELOCK))
|
||||
Log.severe("releaseReadLock(" + fn + ") on write-locked file");
|
||||
else if(lockcnt > 1) {
|
||||
filelocks.put(fn, Integer.valueOf(lockcnt-1));
|
||||
}
|
||||
else {
|
||||
filelocks.remove(fn); /* Remove lock */
|
||||
lock.notifyAll(); /* Wake up folks waiting for locks */
|
||||
}
|
||||
}
|
||||
//Log.info("releaseReadLock(" + f + ")");
|
||||
}
|
||||
}
|
||||
|
|
@ -13,6 +13,7 @@ import org.dynmap.web.HttpHandler;
|
|||
import org.dynmap.web.HttpRequest;
|
||||
import org.dynmap.web.HttpResponse;
|
||||
import org.dynmap.web.HttpStatus;
|
||||
import org.dynmap.utils.FileLockManager;
|
||||
|
||||
public abstract class FileHandler implements HttpHandler {
|
||||
protected static final Logger log = Logger.getLogger("Minecraft");
|
||||
|
|
@ -42,6 +43,10 @@ public abstract class FileHandler implements HttpHandler {
|
|||
}
|
||||
|
||||
protected abstract InputStream getFileInput(String path, HttpRequest request, HttpResponse response);
|
||||
|
||||
protected void closeFileInput(String path, InputStream in) throws IOException {
|
||||
in.close();
|
||||
}
|
||||
|
||||
protected String getExtension(String path) {
|
||||
int dotindex = path.lastIndexOf('.');
|
||||
|
|
@ -108,15 +113,17 @@ public abstract class FileHandler implements HttpHandler {
|
|||
out.write(readBuffer, 0, readBytes);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
fileInput.close();
|
||||
throw e;
|
||||
} finally {
|
||||
freeReadBuffer(readBuffer);
|
||||
if(fileInput != null) {
|
||||
closeFileInput(path, fileInput);
|
||||
fileInput = null;
|
||||
}
|
||||
}
|
||||
fileInput.close();
|
||||
} catch (Exception e) {
|
||||
if (fileInput != null) {
|
||||
try { fileInput.close(); } catch (IOException ex) { }
|
||||
try { closeFileInput(path, fileInput); fileInput = null; } catch (IOException ex) { }
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,8 +3,10 @@ package org.dynmap.web.handlers;
|
|||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import org.dynmap.utils.FileLockManager;
|
||||
import org.dynmap.web.HttpField;
|
||||
import org.dynmap.web.HttpRequest;
|
||||
import org.dynmap.web.HttpResponse;
|
||||
|
|
@ -20,16 +22,25 @@ public class FilesystemHandler extends FileHandler {
|
|||
@Override
|
||||
protected InputStream getFileInput(String path, HttpRequest request, HttpResponse response) {
|
||||
File file = new File(root, path);
|
||||
FileLockManager.getReadLock(file);
|
||||
if (file.getAbsolutePath().startsWith(root.getAbsolutePath()) && file.isFile()) {
|
||||
FileInputStream result;
|
||||
try {
|
||||
result = new FileInputStream(file);
|
||||
} catch (FileNotFoundException e) {
|
||||
FileLockManager.releaseReadLock(file);
|
||||
return null;
|
||||
}
|
||||
response.fields.put(HttpField.ContentLength, Long.toString(file.length()));
|
||||
return result;
|
||||
}
|
||||
FileLockManager.releaseReadLock(file);
|
||||
return null;
|
||||
}
|
||||
protected void closeFileInput(String path, InputStream in) throws IOException {
|
||||
super.closeFileInput(path, in);
|
||||
File file = new File(root, path);
|
||||
FileLockManager.releaseReadLock(file);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue