1
20 package org.crosswire.common.util;
21
22 import java.io.BufferedReader;
23 import java.io.File;
24 import java.io.FileInputStream;
25 import java.io.FileNotFoundException;
26 import java.io.FileOutputStream;
27 import java.io.FilenameFilter;
28 import java.io.IOException;
29 import java.io.InputStream;
30 import java.io.InputStreamReader;
31 import java.io.OutputStream;
32 import java.net.JarURLConnection;
33 import java.net.MalformedURLException;
34 import java.net.URI;
35 import java.net.URISyntaxException;
36 import java.net.URL;
37 import java.net.URLConnection;
38 import java.util.ArrayList;
39 import java.util.Arrays;
40 import java.util.Date;
41 import java.util.List;
42 import java.util.jar.JarEntry;
43
44 import org.crosswire.jsword.JSMsg;
45 import org.crosswire.jsword.JSOtherMsg;
46 import org.slf4j.Logger;
47 import org.slf4j.LoggerFactory;
48
49
58 public final class NetUtil {
59
62 private NetUtil() {
63 }
64
65
68 public static final String PROTOCOL_FILE = "file";
69
70
73 public static final String PROTOCOL_HTTP = "http";
74
75
78 public static final String PROTOCOL_FTP = "ftp";
79
80
83 public static final String PROTOCOL_JAR = "jar";
84
85
88 public static final String INDEX_FILE = "index.txt";
89
90
93 public static final String SEPARATOR = "/";
94
95
98 public static final String AUTH_SEPERATOR_USERNAME = "@";
99
100
103 public static final String AUTH_SEPERATOR_PASSWORD = ":";
104
105
109 private static final String TEMP_SUFFIX = "tmp";
110
111 public static URI copy(URI uri) {
112 try {
113 return new URI(uri.toString());
114 } catch (URISyntaxException e) {
115 assert false : e;
116 return null;
117 }
118 }
119
120
129 public static void makeDirectory(URI orig) throws MalformedURLException {
130 checkFileURI(orig);
131
132 File file = new File(orig.getPath());
133
134 if (file.isFile()) {
136 throw new MalformedURLException(JSMsg.gettext("The URL {0} is a file.", orig));
138 }
139
140 if (!file.isDirectory()) {
142 if (!file.mkdirs()) {
144 throw new MalformedURLException(JSMsg.gettext("The URL {0} could not be created as a directory.", orig));
146 }
147 }
148 }
149
150
160 public static void makeFile(URI orig) throws MalformedURLException, IOException {
161 checkFileURI(orig);
162
163 File file = new File(orig.getPath());
164
165 if (file.isDirectory()) {
167 throw new MalformedURLException(JSMsg.gettext("The URL {0} is a directory.", orig));
169 }
170
171 if (!file.isFile()) {
173 FileOutputStream fout = new FileOutputStream(file);
174 fout.close();
175
176 if (!file.isFile()) {
178 throw new MalformedURLException(JSMsg.gettext("The URL {0} could not be created as a file.", orig));
180 }
181 }
182 }
183
184
191 public static boolean isFile(URI uri) {
192 if (uri.getScheme().equals(PROTOCOL_FILE)) {
193 return new File(uri.getPath()).isFile();
194 }
195
196 try {
197 uri.toURL().openStream().close();
199 return true;
200 } catch (IOException ex) {
201 return false;
203 }
204 }
205
206
214 public static boolean isDirectory(URI orig) {
215 if (!orig.getScheme().equals(PROTOCOL_FILE)) {
216 return false;
217 }
218
219 return new File(orig.getPath()).isDirectory();
220 }
221
222
230 public static boolean canWrite(URI orig) {
231 if (!orig.getScheme().equals(PROTOCOL_FILE)) {
232 return false;
233 }
234
235 return new File(orig.getPath()).canWrite();
236 }
237
238
246 public static boolean canRead(URI orig) {
247 if (!orig.getScheme().equals(PROTOCOL_FILE)) {
248 return false;
249 }
250
251 return new File(orig.getPath()).canRead();
252 }
253
254
266 public static boolean move(URI oldUri, URI newUri) throws IOException {
267 checkFileURI(oldUri);
268 checkFileURI(newUri);
269
270 File oldFile = new File(oldUri.getPath());
271 File newFile = new File(newUri.getPath());
272 return oldFile.renameTo(newFile);
273 }
274
275
284 public static boolean delete(URI orig) throws IOException {
285 checkFileURI(orig);
286
287 return new File(orig.getPath()).delete();
288 }
289
290
299 public static File getAsFile(URI uri) throws IOException {
300 if (uri.getScheme().equals(PROTOCOL_FILE)) {
302 return new File(uri.getPath());
303 }
304 String hashString = (uri.toString().hashCode() + "").replace('-', 'm');
305
306 File workingDir = getURICacheDir();
308 File workingFile = null;
309
310 if (workingDir != null && workingDir.isDirectory()) {
311 workingFile = new File(workingDir, hashString);
312 } else {
313 workingFile = File.createTempFile(hashString, TEMP_SUFFIX);
315 }
316 workingFile.deleteOnExit();
317
318 OutputStream output = null;
320 InputStream input = null;
321 try {
322 output = new FileOutputStream(workingFile);
323 input = uri.toURL().openStream();
324 byte[] data = new byte[512];
325 for (int read = 0; read != -1; read = input.read(data)) {
326 output.write(data, 0, read);
327 }
328 } finally {
329 try {
330 if (input != null) {
331 input.close();
332 }
333 } finally {
334 if (output != null) {
335 output.close();
336 }
337 }
338 }
339
340 return workingFile;
342 }
343
344
355 public static URI shortenURI(URI orig, String strip) throws MalformedURLException {
356 String file = orig.getPath();
357 char lastChar = file.charAt(file.length() - 1);
358 if (isSeparator(lastChar)) {
359 file = file.substring(0, file.length() - 1);
360 }
361
362 String test = file.substring(file.length() - strip.length());
363 if (!test.equals(strip)) {
364 throw new MalformedURLException(JSOtherMsg.lookupText("The URL {0} does not end in {1}.", orig, strip));
365 }
366
367 String newFile = file.substring(0, file.length() - strip.length());
368
369 try {
370 return new URI(orig.getScheme(), orig.getUserInfo(), orig.getHost(), orig.getPort(), newFile, "",
371 "");
372 } catch (URISyntaxException e) {
373 throw new MalformedURLException(JSOtherMsg.lookupText("The URL {0} does not end in {1}.", orig, strip));
374 }
375 }
376
377
386 public static URI lengthenURI(URI orig, String anExtra) {
387 String extra = anExtra;
388 try {
389 StringBuilder path = new StringBuilder(orig.getPath());
390 char lastChar = path.charAt(path.length() - 1);
391 char firstChar = extra.charAt(0);
392 if (isSeparator(firstChar)) {
393 if (isSeparator(lastChar)) {
394 path.append(extra.substring(1));
395 } else {
396 path.append(extra);
397 }
398 } else {
399 if (!isSeparator(lastChar)) {
400 path.append(SEPARATOR);
401 }
402 path.append(extra);
403 }
404
405 return new URI(orig.getScheme(), orig.getUserInfo(), orig.getHost(), orig.getPort(), path.toString(), orig.getQuery(), orig.getFragment());
406 } catch (URISyntaxException ex) {
407 return null;
408 }
409 }
410
411 private static boolean isSeparator(char c) {
412 return c == '/' || c == '\\';
413 }
414
415
424 public static InputStream getInputStream(URI uri) throws IOException {
425 if (uri.getScheme().equals(PROTOCOL_FILE)) {
427 return new FileInputStream(uri.getPath());
428 }
429 return uri.toURL().openStream();
430 }
431
432
443 public static OutputStream getOutputStream(URI uri) throws IOException {
444 return getOutputStream(uri, false);
445 }
446
447
460 public static OutputStream getOutputStream(URI uri, boolean append) throws IOException {
461 if (uri.getScheme().equals(PROTOCOL_FILE)) {
464 return new FileOutputStream(uri.getPath(), append);
465 }
466 URLConnection cnx = uri.toURL().openConnection();
467 cnx.setDoOutput(true);
468 return cnx.getOutputStream();
469 }
470
471
489 public static String[] list(URI uri, URIFilter filter) throws MalformedURLException, IOException {
490 String[] reply = {};
493 try {
494 URI search = NetUtil.lengthenURI(uri, INDEX_FILE);
495 reply = listByIndexFile(search, filter);
496 } catch (FileNotFoundException ex) {
497 LOGGER.warn("index file for " + uri.toString() + " was not found.");
502 if (uri.getScheme().equals(PROTOCOL_FILE)) {
503 return listByFile(uri, filter);
504 }
505 }
506
507 if (uri.getScheme().equals(PROTOCOL_FILE)) {
509 String[] files = listByFile(uri, filter);
510
511 if (files.length != reply.length) {
513 LOGGER.warn("index file for {} has incorrect number of entries.", uri.toString());
514 } else {
515 List<String> list = Arrays.asList(files);
516 for (int i = 0; i < files.length; i++) {
517 if (!list.contains(files[i])) {
518 LOGGER.warn("file: based index found {} but this was not found using index file.", files[i]);
519 }
520 }
521 }
522 }
523
524 return reply;
525 }
526
527
536 public static String[] listByFile(URI uri, URIFilter filter) throws MalformedURLException {
537 File fdir = new File(uri.getPath());
538 if (!fdir.isDirectory()) {
539 throw new MalformedURLException(JSMsg.gettext("The URL {0} is not a directory", uri.toString()));
541 }
542
543 return fdir.list(new URIFilterFilenameFilter(filter));
544 }
545
546
555 public static String[] listByIndexFile(URI index) throws IOException {
556 return listByIndexFile(index, new DefaultURIFilter());
557 }
558
559
579 public static String[] listByIndexFile(URI index, URIFilter filter) throws IOException {
580 InputStream in = null;
581 BufferedReader din = null;
582 try {
583 in = NetUtil.getInputStream(index);
584 din = new BufferedReader(new InputStreamReader(in), 8192);
587
588 List<String> list = new ArrayList<String>();
590
591 while (true) {
592 String line = din.readLine();
593
594 if (line == null) {
595 break;
596 }
597
598 String name = line;
599
600 int len = name.length();
602 int commentPos;
603 for (commentPos = 0; commentPos < len && name.charAt(commentPos) != '#'; ++commentPos) {
604 continue; }
606
607 if (commentPos < len) {
608 name = name.substring(0, commentPos);
609 }
610
611 name = name.trim();
613
614 if (name.length() > 0 && !name.equals(INDEX_FILE) && filter.accept(name)) {
616 list.add(name);
617 }
618 }
619
620 return list.toArray(new String[list.size()]);
621 } finally {
622 IOUtil.close(din);
623 IOUtil.close(in);
624 }
625 }
626
627
635 public static PropertyMap loadProperties(URI uri) throws IOException {
636 InputStream is = null;
637 try {
638 is = NetUtil.getInputStream(uri);
639 PropertyMap prop = new PropertyMap();
640 prop.load(is);
641 is.close();
642 return prop;
643 } finally {
644 IOUtil.close(is);
645 }
646 }
647
648
660 public static void storeProperties(PropertyMap properties, URI uri, String title) throws IOException {
661 OutputStream out = null;
662
663 try {
664 out = NetUtil.getOutputStream(uri);
665 PropertyMap temp = new PropertyMap();
666 temp.putAll(properties);
667 temp.store(out, title);
668 } finally {
669 IOUtil.close(out);
670 }
671 }
672
673
678 public static int getSize(URI uri) {
679 return getSize(uri, null, null);
680 }
681
682 public static int getSize(URI uri, String proxyHost) {
683 return getSize(uri, proxyHost, null);
684 }
685
686 public static int getSize(URI uri, String proxyHost, Integer proxyPort) {
687 try {
688 if (uri.getScheme().equals(PROTOCOL_HTTP)) {
689 WebResource resource = new WebResource(uri, proxyHost, proxyPort);
690 int size = resource.getSize();
691 resource.shutdown();
692 return size;
693 }
694
695 return uri.toURL().openConnection().getContentLength();
696 } catch (IOException e) {
697 return 0;
698 }
699 }
700
701
708 public static long getLastModified(URI uri) {
709 return getLastModified(uri, null, null);
710 }
711
712
720 public static long getLastModified(URI uri, String proxyHost) {
721 return getLastModified(uri, proxyHost, null);
722 }
723
724
733 public static long getLastModified(URI uri, String proxyHost, Integer proxyPort) {
734 try {
735 if (uri.getScheme().equals(PROTOCOL_HTTP)) {
736 WebResource resource = new WebResource(uri, proxyHost, proxyPort);
737 long time = resource.getLastModified();
738 resource.shutdown();
739 return time;
740 }
741
742 URLConnection urlConnection = uri.toURL().openConnection();
743 long time = urlConnection.getLastModified();
744
745 if (urlConnection instanceof JarURLConnection) {
747 JarURLConnection jarConnection = (JarURLConnection) urlConnection;
749 JarEntry jarEntry = jarConnection.getJarEntry();
750 time = jarEntry.getTime();
751 }
752
753 return time;
754 } catch (IOException ex) {
755 LOGGER.warn("Failed to get modified time", ex);
756 return new Date().getTime();
757 }
758 }
759
760
768 public static boolean isNewer(URI left, URI right) {
769 return isNewer(left, right, null, null);
770 }
771
772
781 public static boolean isNewer(URI left, URI right, String proxyHost) {
782 return isNewer(left, right, proxyHost, null);
783 }
784
785
795 public static boolean isNewer(URI left, URI right, String proxyHost, Integer proxyPort) {
796 return NetUtil.getLastModified(left, proxyHost, proxyPort) > NetUtil.getLastModified(right, proxyHost, proxyPort);
797 }
798
799
802 public static class URIFilterFilenameFilter implements FilenameFilter {
803
808 public URIFilterFilenameFilter(URIFilter filter) {
809 this.filter = filter;
810 }
811
812
815 public boolean accept(File arg0, String name) {
816 return filter.accept(name);
817 }
818
819 private URIFilter filter;
820 }
821
822
830 private static void checkFileURI(URI uri) throws MalformedURLException {
831 if (!uri.getScheme().equals(PROTOCOL_FILE)) {
832 throw new MalformedURLException(JSMsg.gettext("The URL {0} is not a file.", uri));
834 }
835 }
836
837
842 public static File getURICacheDir() {
843 return cachedir;
844 }
845
846
852 public static void setURICacheDir(File cachedir) {
853 NetUtil.cachedir = cachedir;
854 }
855
856
863 public static URI getURI(File file) {
864 return file.toURI();
865 }
866
867
876 public static URI getTemporaryURI(String prefix, String suffix) throws IOException {
877 File tempFile = File.createTempFile(prefix, suffix);
878 return getURI(tempFile);
879 }
880
881
888 public static URI toURI(URL url) {
889 try {
890 return new URI(url.toExternalForm());
891 } catch (URISyntaxException e) {
892 return null;
893 }
894 }
895
896
903 public static URL toURL(URI uri) {
904 try {
905 return uri.toURL();
906 } catch (MalformedURLException e) {
907 return null;
908 }
909 }
910
911
915 public static class IsDirectoryURIFilter implements URIFilter {
916
921 public IsDirectoryURIFilter(URI parent) {
922 this.parent = parent;
923 }
924
925
928 public boolean accept(String name) {
929 return NetUtil.isDirectory(NetUtil.lengthenURI(parent, name));
930 }
931
932 private URI parent;
933 }
934
935
938 private static File cachedir;
939
940
943 private static final Logger LOGGER = LoggerFactory.getLogger(NetUtil.class);
944 }
945