Netcache.java (6951B)
1 // Thomas Nagy, 2011 2 3 // TODO security 4 // TODO handle all exceptions properly 5 6 import java.util.HashMap; 7 import java.util.Map; 8 import java.util.List; 9 import java.util.Date; 10 import java.util.ArrayList; 11 import java.util.Comparator; 12 import java.util.Collections; 13 import java.lang.Math; 14 import java.lang.StringBuilder; 15 import java.io.*; 16 import java.net.*; 17 import java.security.*; 18 19 public class Netcache implements Runnable, Comparator<Object[]> { 20 private static int PORT_UPLOAD = 11001; 21 private static int PORT_DOWNLOAD = 12001; 22 private static String CACHEDIR = "/tmp/wafcache/"; 23 private static long MAX = 10l * 1024l * 1024l * 1024l; 24 private static double CLEANRATIO = 0.8; 25 private static int BUF = 16 * 8192; 26 27 private final static HashMap<String, Object[]> flist = new HashMap<String, Object[]>(); 28 private Socket sock = null; 29 private int port = 0; 30 31 public Netcache(Socket sock, int port) { 32 this.sock = sock; 33 this.port = port; 34 } 35 36 public void run () { 37 try { 38 if (sock != null) 39 { 40 while (true) { 41 InputStream in = sock.getInputStream(); 42 OutputStream out = sock.getOutputStream(); 43 44 byte b[] = new byte[128]; 45 int off = 0; 46 while (off < b.length) { 47 off += in.read(b, off, b.length - off); 48 } 49 50 //System.out.println(new String(b)); 51 String[] args = new String(b).split(","); 52 if (args[0].equals("LST")) { 53 lst(args, in, out); 54 } 55 else if (args[0].equals("PUT") && port == PORT_UPLOAD) { 56 put(args, in, out); 57 } 58 else if (args[0].equals("GET") && port == PORT_DOWNLOAD) { 59 get(args, in, out); 60 } 61 else if (args[0].equals("CLEAN") && port == PORT_UPLOAD) { 62 clean(args, in, out); 63 } 64 else if (args[0].equals("BYE")) { 65 sock.close(); 66 break; 67 } 68 else { 69 System.out.println("Invalid command " + new String(b) + " on port " + this.port); 70 sock.close(); 71 break; 72 } 73 } 74 } else { 75 // magic trick to avoid creating a new inner class 76 ServerSocket server = new ServerSocket(port); 77 server.setReuseAddress(true); 78 while(true) { 79 Netcache tmp = new Netcache(server.accept(), port); 80 Thread t = new Thread(tmp); 81 t.start(); 82 } 83 } 84 } catch (IOException e) { 85 e.printStackTrace(); 86 } 87 } 88 89 public void lst(String[] args, InputStream in, OutputStream out) throws IOException { 90 StringBuilder b = new StringBuilder(); 91 int k = 0; 92 synchronized(flist) { 93 for (String name : flist.keySet()) { 94 b.append(name); 95 if (k <= flist.size()) { 96 k++; 97 b.append("\n"); 98 } 99 } 100 } 101 102 byte[] ret = b.toString().getBytes(); 103 String header = String.format("%-128s", String.format("%d,", ret.length)); 104 105 out.write(header.getBytes()); 106 out.write(ret); 107 } 108 109 public void put(String[] args, InputStream in, OutputStream out) throws IOException { 110 File cachedir = new File(CACHEDIR); 111 File temp = File.createTempFile("foo", ".suffix", cachedir); 112 113 long size = new Long(args[3].trim()); 114 115 //System.out.println("" + args[1] + " " + args[2] + " " + args[3] + " " + args.length); 116 117 byte[] buf = new byte[BUF]; 118 long cnt = 0; 119 OutputStream w = new FileOutputStream(temp); 120 try { 121 while (cnt < size) { 122 int c = in.read(buf, 0, (int) Math.min(BUF, size-cnt)); 123 if (c == 0) { 124 throw new RuntimeException("Connection closed too early"); 125 } 126 w.write(buf, 0, c); 127 cnt += c; 128 } 129 } finally { 130 w.close(); 131 } 132 133 /*if (cnt != size) { 134 System.out.println("error!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); 135 }*/ 136 137 File parent = new File(new File(new File(CACHEDIR), args[1].substring(0, 2)), args[1]); 138 File dest = new File(parent, args[2]); 139 try { 140 dest.getParentFile().mkdirs(); 141 } catch (Exception e) { 142 } 143 144 if (!temp.renameTo(dest)) { 145 throw new RuntimeException("Could not rename the file"); 146 } 147 148 long total = 0; 149 for (File f : parent.listFiles()) { 150 total += f.length(); 151 } 152 153 synchronized(flist) { 154 if (flist.containsKey(parent.getName())) { 155 flist.get(parent.getName())[0] = parent.lastModified(); 156 } 157 else 158 { 159 flist.put(parent.getName(), new Object[] {parent.lastModified(), total, parent.getName()}); 160 } 161 } 162 } 163 164 public void get(String[] args, InputStream in, OutputStream out) throws IOException { 165 File f = new File(new File(new File(new File(CACHEDIR), args[1].substring(0, 2)), args[1]), args[2].trim()); 166 long fsize = -1; 167 try { 168 fsize = f.length(); 169 } catch (Exception e) { 170 // return -1 to the client 171 } 172 173 String ret = String.format("%-128s", String.format("%d,", fsize)); 174 out.write(ret.getBytes()); 175 176 byte[] buf = new byte[BUF]; 177 178 long cnt = 0; 179 InputStream s = new FileInputStream(f); 180 try { 181 while (cnt < fsize) { 182 long c = s.read(buf); 183 cnt += c; 184 out.write(buf, 0, (int) c); 185 } 186 } finally { 187 s.close(); 188 } 189 190 File parent = f.getParentFile(); 191 Date d = new Date(); 192 parent.setLastModified(d.getTime()); 193 synchronized(flist) { 194 flist.get(parent.getName())[0] = parent.lastModified(); 195 } 196 } 197 198 public void clean(String[] args, InputStream in, OutputStream out) throws IOException { 199 synchronized(flist) { 200 long total = 0; 201 for (Map.Entry<String, Object[]> entry : flist.entrySet()) { 202 total += (Long) entry.getValue()[1]; 203 } 204 205 List<Object[]> k = new ArrayList<Object[]>(flist.values()); 206 Collections.sort(k, this); 207 208 int cur = 0; 209 while (total > MAX * CLEANRATIO) { 210 Object[] kk = k.get(cur); 211 212 String name = (String) kk[2]; 213 File f = new File(new File(new File(CACHEDIR), name.substring(0, 2)), name); 214 //System.out.println("removing " + cur + " " + kk[0] + " " + kk[1] + " " + f.getAbsolutePath()); 215 rm(f); 216 217 total -= (Long) kk[1]; 218 219 flist.remove(name); 220 cur++; 221 } 222 } 223 } 224 225 public static void init_flist() { 226 synchronized(flist) { 227 flist.clear(); 228 File dir = new File(CACHEDIR); 229 try { 230 dir.mkdirs(); 231 } catch (Exception e) { 232 233 } 234 235 for (File d : dir.listFiles()) { 236 if (!d.isDirectory()) continue; 237 for (File sd : d.listFiles()) { 238 if (!sd.isDirectory()) continue; 239 long total = 0; 240 for (File f : sd.listFiles()) { 241 total += f.length(); 242 } 243 //System.out.println(sd.getName()); 244 flist.put(sd.getName(), new Object[] {sd.lastModified(), total, sd.getName()}); 245 } 246 } 247 } 248 } 249 250 public int compare(Object[] a, Object[] b) { 251 return ((Long) a[0]).compareTo((Long) b[0]); 252 } 253 254 public static void rm(File dir) { 255 if (dir.isDirectory()) { 256 for (File f: dir.listFiles()) 257 { 258 rm(f); 259 } 260 } 261 dir.delete(); 262 } 263 264 public static void main(String[] args) { 265 init_flist(); 266 System.out.println("ready (" + flist.keySet().size() + " dirs)"); 267 268 // different ports for upload and download, another port could be added for the clean command 269 Thread upload = null; 270 if (PORT_UPLOAD != PORT_DOWNLOAD) { 271 Netcache tmp = new Netcache(null, PORT_UPLOAD); 272 upload = new Thread(tmp); 273 upload.start(); 274 } 275 276 Netcache tmp = new Netcache(null, PORT_DOWNLOAD); 277 tmp.run(); 278 } 279 } 280