Java tutorial
/* * Copyright (C) 2012 Helsinki Institute of Physics, University of Helsinki * All rights reserved. See the copyright.txt in the distribution for a full * listing of individual contributors. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package fi.hip.sicx.webdav; import static org.junit.Assert.assertTrue; import fi.hip.sicx.store.StorageClient; import fi.hip.sicx.store.StorageIOException; import fi.hip.sicx.store.StorageClientObserver; import io.milton.http.exceptions.BadRequestException; import io.milton.http.exceptions.ConflictException; import io.milton.http.exceptions.NotAuthorizedException; import io.milton.http.exceptions.NotFoundException; import io.milton.httpclient.Folder; import io.milton.httpclient.Host; import io.milton.httpclient.HttpException; import io.milton.httpclient.Resource; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.DataOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.PipedInputStream; import java.io.PipedOutputStream; import java.util.List; import java.util.StringTokenizer; import java.util.Vector; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import org.apache.commons.io.input.CountingInputStream; /** * WebdavClient class. * * @author Seppo Heikkila <seppo.heikkila@cern.ch> */ public class WebdavClient implements StorageClient { public Host host = null; private Future<?> future = null; private String address = null; private int port = 80; private String username = null; private String password = null; private String path = null; private Resource pr = null; private int datasize = 0; private CountingInputStream cis = null; // These enable non-blocking uploading private ExecutorService myExecutor = null; private io.milton.httpclient.File uploadedFile = null; public WebdavClient(String webdav_address, int webdav_port, String webdav_username, String webdav_password, String webdav_path) { this.address = webdav_address; this.port = webdav_port; this.username = webdav_username; this.password = webdav_password; this.path = webdav_path; this.pr = null; } /** * Generates valid filename for uploading. Constraints are * 1) Size max 29 characters (google drive) * 2) There has to be valid ending, otherwise it is assigned (google drive) * 3) Remove "-" chars, so that they do not cause trouble (optional) * 4) The filename should be unique, e.g. every second letter (optional) * 5) Two last letters have to stay same * @param inpath Original name. * @return Modified name. */ public String generateValidStorageFilename(String inname) { String retme = null; if (inname == null) { return null; } // 3) Remove special chars, at least '-' retme = inname; retme = retme.replace('-', 'S'); // 4) Take every 2nd letter if (retme.length() >= 25) { String newretme = retme.substring(0, 4); for (int i = 4; retme.length() > i; i += 2) { newretme += retme.substring(i, i + 1); } retme = newretme; } // 5 retme += inname.substring(inname.length() - 2, inname.length()); // 1) Take only 25 chars, 5) last ones if (retme.length() > 25) { retme = retme.substring(retme.length() - 25); } // 2) retme += ".bin"; // This is needed, or sometimes files might have autogenerated ending .html etc. return retme; } /** * Checks if we have valid path. Path is valid if path * and pr are not null. * * @param inpath Path to be checked. Any slashes mean that there is path also. * @return The filename without path. */ private String refreshUploadPath(String inpath) { String retme = null; // Check if we have a path update if (inpath != null && inpath.lastIndexOf('/') != -1) { retme = inpath.substring(inpath.lastIndexOf('/') + 1); path = inpath.substring(0, inpath.lastIndexOf('/')); System.out.println("Inpath: '" + inpath + "'."); System.out.println("Path: '" + path + "'."); System.out.println("File: '" + retme + "'."); pr = null; } else { retme = inpath; } // Check if given path is valid if (path != null && pr == null) { try { pr = host.find(path); } catch (NotAuthorizedException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (BadRequestException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (HttpException e) { // TODO Auto-generated catch block e.printStackTrace(); } if (pr == null || !(pr instanceof Folder)) { path = null; pr = null; } } return retme; } @SuppressWarnings("unused") private boolean tryConnect() { // Connect this.host = new Host(this.address, null, this.port, this.username, this.password, null, 3000000, null, null); try { List<? extends Resource> lr = this.host.children(); if (lr != null) { return true; } } catch (IOException e) { System.out.println("We would need longer timeout: " + e); } catch (NotAuthorizedException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (BadRequestException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (HttpException e) { // TODO Auto-generated catch block e.printStackTrace(); } return false; } @Override public boolean connect() { // Check parameters if (this.address == null) { return false; } if (this.port <= 0) { return false; } myExecutor = Executors.newSingleThreadExecutor(); // Lets set timeout to 5 minutes this.host = new Host(this.address, null, this.port, this.username, this.password, null, 300000, null, null); // Lets check if the connection works try { List<? extends Resource> lr = this.host.children(); if (lr == null) { return false; } } catch (Exception e) { return false; } refreshUploadPath(null); // Check success if (this.host != null) { return true; } else { return false; } } @Override public boolean storeFile(String localInputFilename, String fileInTheCloud, StorageClientObserver sco) { try { java.io.File f = new java.io.File(localInputFilename); FileInputStream fis = new FileInputStream(f); OutputStream out = writeData(fileInTheCloud, (int) f.length(), null); do { int d8b = fis.read(); if (d8b != -1) { out.write(d8b); } else { break; } } while (true); fis.close(); out.close(); // must - otherwise file will not be uploaded // We have to wait for the upload to be completed while (writeDataIsCompleted() == false) { Thread.sleep(10); } } catch (StorageIOException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } if (uploadedFile == null) { return false; } return true; } @Override public boolean getFile(String cloudFile, String localOutFile, StorageClientObserver sco) { // TODO Auto-generated method stub return false; } /** * Waits for the file upload to finish. If the time period is * exceeded, the upload is terminated. * * @param time_in_seconds Time in seconds to wait for the upload to finish * @return Resource to the uploaded file * @deprecated replaced by {@link #writeDataWaitToComplete(int)} */ @SuppressWarnings("unused") @Deprecated private Resource waitWriteDataToFinish(int time_in_seconds) { try { myExecutor.awaitTermination(time_in_seconds, TimeUnit.SECONDS); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } if (uploadedFile == null) { return null; } return uploadedFile; } DataOutputStream dos = null; // This is needed to know the number of bytes written @Override public OutputStream writeData(String fileInTheCloud, int indatasize, StorageClientObserver sco) throws StorageIOException { this.datasize = indatasize; // For calculating transfer progress //PipedOutputStream pos = null; // Parameters for the Thread call (final needed for parameters of the thread) final String paramfileInTheCloud = fileInTheCloud; final int paramindatasize = indatasize; final Host paramhost = host; final PipedInputStream ins = new PipedInputStream(); try { this.dos = new DataOutputStream(new PipedOutputStream(ins)); } catch (IOException e1) { throw new StorageIOException("Could not create pipeoutputstream."); } // Uploading is blocking so we need to execute it on separate thread this.future = myExecutor.submit(new Runnable() { public void run() { String justFileName = "INVALID-NAME.txt"; try { justFileName = refreshUploadPath(paramfileInTheCloud); justFileName = generateValidStorageFilename(justFileName); if (path != null && pr != null) { uploadedFile = ((Folder) pr).upload(justFileName, ins, (long) paramindatasize, null); } else { uploadedFile = paramhost.upload(justFileName, ins, (long) paramindatasize, null); } } catch (NotAuthorizedException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (ConflictException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (BadRequestException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (NotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (HttpException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("DONE: " + justFileName); } }); return this.dos; } @Override public InputStream readData(String fileInTheCloud, int indatasize, StorageClientObserver sco) { FileInputStream fis = null; try { // FXIME: There should be more elegant solutions than downloading to temp file String pathlocal = "/tmp/"; String body = "SICX_WebdavClient_readData"; String ending = ".tmp"; File dir = new File(pathlocal); File f = File.createTempFile(body, ending, dir); fileInTheCloud = refreshUploadPath(fileInTheCloud); fileInTheCloud = generateValidStorageFilename(fileInTheCloud); Resource cloudfile = null; if (path != null && pr != null) { cloudfile = host.find(path + File.separator + fileInTheCloud); } else { cloudfile = host.find(fileInTheCloud); } if (cloudfile == null) { return null; } cloudfile.downloadTo(f, null); fis = new FileInputStream(f); } catch (NotAuthorizedException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (BadRequestException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (HttpException e) { // TODO Auto-generated catch block e.printStackTrace(); } return fis; } @Override public boolean checkFile(String cloudFile, StorageClientObserver sco) { System.out.println("Checking file: " + cloudFile); try { cloudFile = refreshUploadPath(cloudFile); cloudFile = generateValidStorageFilename(cloudFile); Resource cloudfile = null; if (path != null && pr != null) { System.out.println("Checking file1: " + path + File.separator + cloudFile); cloudfile = host.find(path + File.separator + cloudFile); if (cloudfile == null) { // Lets see if the non-finding was due to invalid cache cloudfile = host.find(path + File.separator + cloudFile, true); } } else { System.out.println("Checking file2: " + cloudFile); cloudfile = host.find(cloudFile); if (cloudfile == null) { // Lets see if the non-finding was due to invalid cache cloudfile = host.find(cloudFile, true); } } if (cloudfile != null) { return true; } } catch (NotAuthorizedException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (BadRequestException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (HttpException e) { // TODO Auto-generated catch block e.printStackTrace(); } return false; } @Override public boolean deleteFile(String cloudFile, StorageClientObserver sco) { System.out.println("Deleting file: " + cloudFile); try { cloudFile = refreshUploadPath(cloudFile); cloudFile = generateValidStorageFilename(cloudFile); Resource cloudfile = null; if (path != null && pr != null) { cloudfile = host.find(path + File.separator + cloudFile); } else { cloudfile = host.find(cloudFile); } if (cloudfile != null) { cloudfile.delete(); return true; } } catch (NotAuthorizedException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (BadRequestException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (HttpException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (ConflictException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (NotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } return true; // Delete always works! :) //return false; } @Override public boolean logout() { return true; } @Override public int getTransferProgress() { double retme = 0; if (this.dos != null && this.datasize > 0) { retme = 100.0 * dos.size() * 1.0 / (1.0 * this.datasize); } else if (this.cis != null && this.datasize > 0) { retme = 100.0 * cis.getCount() * 1.0 / (1.0 * this.datasize); } return (int) retme; } @Override public boolean writeDataWaitToComplete(int timeout_ms) { try { int sleeptime = 10; while (this.future.get() != null || timeout_ms <= 0) { //System.out.println("Waiting thread to finish."); Thread.sleep(sleeptime); timeout_ms -= sleeptime; } if (timeout_ms <= 0) { return false; } return true; } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (ExecutionException e) { // TODO Auto-generated catch block e.printStackTrace(); } return false; } @Override public boolean writeDataIsCompleted() { try { if (this.future.get() == null) { return true; } } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (ExecutionException e) { // TODO Auto-generated catch block e.printStackTrace(); } return false; //return myExecutor.isTerminated(); } String uriPath = "webdav:///"; @Override public String getURI(String path) { return uriPath + path; } @Override public String setURI(String newURIStart) { uriPath += newURIStart; return uriPath; } @Override public String getType() { return "webdav"; } @Override public String getVersion() { return "1.0"; } @Override public boolean isReusable() { return true; //return false; } /** * Update the path which we are accessing/manipulating. * * @param webdav_path Path to the directory that is being referred. * @return true if all ok, else false */ public boolean setPath(String webdav_path) { this.path = webdav_path; this.pr = null; refreshUploadPath(null); return true; } /** * Get names of all the children in the current directory. * @return Vector containing all the names */ public Vector<String> getChildrenDirectories() { Vector<String> retme = new Vector<String>(); try { refreshUploadPath(null); if (path != null && pr != null) { if (pr instanceof Folder) { for (Resource r : ((Folder) pr).children()) { if (r instanceof Folder) { retme.add(r.name); } } } } else { for (Resource r : host.children()) { if (r instanceof Folder) { retme.add(r.name); } } } } catch (NotAuthorizedException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (BadRequestException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (HttpException e) { // TODO Auto-generated catch block e.printStackTrace(); } return retme; } ////////////////////////////////////////////////////////// ///////// Code that is not useful at this point ////////// ////////////////////////////////////////////////////////// //@Test @SuppressWarnings("unused") private void testCreateDir() throws Exception { boolean exception = false; printHost(); Resource fold = findResource("Files/Elaimet2/kissa"); if (fold == null) { System.out.println("Target not found."); } fold = findResource("Files/Elaimet/kissa"); System.out.println("Found: " + fold.displayName); // Test that null directory name fails exception = false; try { Folder test_folder = createDirectory(null, null); } catch (BadRequestException e) { exception = true; } assertTrue(exception); // Test that creating an existing directory fails exception = false; try { Folder test_folder = createDirectory("siiamilais4", "Files/Elaimet"); test_folder = createDirectory("siiamilais4", "Files/Elaimet"); } catch (IOException e) { exception = true; } assertTrue(exception); exception = false; try { Folder test_folder = createDirectory("siiamilais4", null); test_folder = createDirectory("siiamilais4", null); } catch (IOException e) { exception = true; } assertTrue(exception); // Test that invalid path fails exception = false; try { Folder test_folder = createDirectory("siiamilais4", "I_Hope_There_is_Not_this_Directory/Here"); } catch (IOException e) { exception = true; } assertTrue(exception); printHost(); // Upload a file to the new folder //java.io.File presents = new File("presents.xls"); //xmasShopping.upload(presents); //Folder myDocs = (Folder)host.child("koe"); // look up the My Documents folder } private void printDirectory(Folder f, int depth) throws NotAuthorizedException, BadRequestException, IOException, HttpException { for (Resource r : f.children()) { // Resource is base class for File and Folder String s = ""; if (depth > 1) { s = String.format("%1$" + (depth - 1) * 3 + "s", ""); } s += "\\--"; if (r instanceof Folder) { System.out.println(s + r.name + "/ (" + r.getClass() + ")"); printDirectory((Folder) r, depth + 1); } else { System.out.println(s + r.name + " (" + r.getClass() + ")"); } } } private void printHost() { try { for (Resource r : host.children()) { // Resource is base class for File and Folder if (r instanceof Folder) { System.out.println(r.name + "/ (" + r.getClass() + ")"); printDirectory((Folder) r, 1); } else { System.out.println(r.name + " (" + r.getClass() + ")"); } } } catch (NotAuthorizedException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (BadRequestException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (HttpException e) { // TODO Auto-generated catch block e.printStackTrace(); } } public Resource findResource(String path) throws NotAuthorizedException, BadRequestException, IOException, HttpException { Resource retme = null; StringTokenizer st = new StringTokenizer(path, "/"); // Find root first if (st.hasMoreTokens()) { String token = st.nextToken(); for (Resource r : host.children()) { if (r.name.equals(token)) { retme = r; } } } // Then rest of the path while (st.hasMoreTokens()) { if (retme == null || !(retme instanceof Folder)) { return null; } String token = st.nextToken(); boolean nothing_found = true; for (Resource r : ((Folder) retme).children()) { if (r.name.equals(token)) { retme = r; nothing_found = false; } } if (nothing_found) return null; } return retme; } private Folder createDirectory(String dirname, String path) throws NotAuthorizedException, ConflictException, BadRequestException, NotFoundException, IOException, HttpException { Folder retme = null; if (dirname == null || dirname.length() == 0) { throw new BadRequestException("mkdir: cannot create directory without name."); } // If no path is given, lets create the directory at root if (path == null) { if (host.child(dirname) != null) { throw new IOException("mkdir: cannot create directory '" + dirname + "': Folder exists."); } return host.createFolder(dirname); } // Otherwise, find target folder and create directory there Resource fold = findResource(path); if (fold != null && fold instanceof Folder) { // Check if it exist already if (((Folder) fold).child(dirname) != null) { throw new IOException("mkdir: cannot create directory '" + dirname + "': Folder exists."); } retme = ((Folder) fold).createFolder(dirname); } else { throw new IOException("mkdir: cannot create directory '" + path + "': No such file or directory."); } return retme; } //@Test @SuppressWarnings("unused") private void testUploadFile() throws Exception { boolean exception = false; // Lets create example input String str = "This is a String ~ GoGoGo"; InputStream is = new ByteArrayInputStream(str.getBytes()); BufferedReader br = new BufferedReader(new InputStreamReader(is)); //this.connect("localhost/oc451/remote.php/webdav", 80, "sicx", "sicx"); printHost(); Resource fold = findResource("Files/Elaimet2/kissa"); if (fold == null) { System.out.println("Target not found."); } fold = findResource("Files/Elaimet/kissa"); System.out.println("Found: " + fold.displayName); // Test that null directory name fails exception = false; try { Folder test_folder = createDirectory(null, null); } catch (BadRequestException e) { exception = true; } assertTrue(exception); // Test that creating an existing directory fails exception = false; try { Folder test_folder = createDirectory("siiamilais4", "Files/Elaimet"); test_folder = createDirectory("siiamilais4", "Files/Elaimet"); } catch (IOException e) { exception = true; } assertTrue(exception); exception = false; try { Folder test_folder = createDirectory("siiamilais4", null); test_folder = createDirectory("siiamilais4", null); } catch (IOException e) { exception = true; } assertTrue(exception); // Test that invalid path fails exception = false; try { Folder test_folder = createDirectory("siiamilais4", "I_Hope_There_is_Not_this_Directory/Here"); } catch (IOException e) { exception = true; } assertTrue(exception); printHost(); } }