Netcache.java 6.8 KB

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