vrijdag 17 januari 2014

Detecting USB-devices with Java 7.0 under Linux

For my backup-program I wanted to know which USB-disks are available on my system, which runs Linux. USB support in Java is limited to third party libraries, for example, JUSB.

But I found another solution.

The first step is to use the Linux command 'mount'. It shows which partitions are attached to your system.

arjen@linux-ta51:~> mount

      Partial output:
  • /dev/sda7 on /develop type ext3
  • /dev/sda13 on /home type ext4
  • /dev/sda11 on /data type ext4
  • /dev/sdc5 on /bigdata type ext4                    = Usb hard-disk 1
  • /dev/sdb1 on /media/3TB type ext4              = Usb hard-disk 2
  • /dev/sdd1 on /media/USB DISK type vfat    = Usb stick

My system has only 1 hard-disk, so I ignore the lines which start with  '/dev/sda'.
If your system has 2 hard-disks, you should ignore '/dev/sdb' too.

The next step is using the API WatchService, introduced with Java 7.0.
You register the directories with this service, and get notifications, when plugging or unplugging an USB-device.

I've tested this approach in Eclipse with my Usb-stick at /dev/sdd1, and it works!
The usb-stick is mounted on /media/USB DISK. 


You should watch the directory /media, and not /media/USB DISK, to get the notifications.

The Demo-program starts the WatchService in a separate thread. There's no GUI to end this thread. The Demo-program doesn't use the first step, 'mount'.
See the source of MaxDrive for an example of using 'mount'. There is a main procedure for testing this.



package nl.yokoz.maxwatchdir;

import java.nio.file.Path;
import java.nio.file.Paths;

import nl.yokoz.io.file.util.dir.MaxWatchDir;

public class MaxWatchDirDemo extends MaxWatchDir {

    public MaxWatchDirDemo(Path dir) {
        super(dir);
    }

    public static void main(String[] args) {
        Path dir = Paths.get("/media/USB DISK");
        new MaxWatchDirDemo(dir).start();
    }

    @Override
    protected boolean onModify(Path child) {
        return false;
    }

    @Override
    protected boolean onCreate(Path child) {
        sysOut(child, true);
        return false;
    }

    @Override
    protected boolean onDelete(Path child) {
        sysOut(child, false);
        return false;
    }

    private void sysOut(Path child, boolean bPlugged) {
        String un;
        if (!bPlugged) {
            un = "un";
        } else {
            un = "";
        }
        System.out.format("Usb-stick %s: %splugged\n", child, un);
    }

}



----------------------------------------------------------------------



package nl.yokoz.io.file.util.dir;

/*
 * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *   - Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *
 *   - Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *
 *   - Neither the name of Oracle nor the names of its
 *     contributors may be used to endorse or promote products derived
 *     from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * http://docs.oracle.com/javase/tutorial/displayCode.html?code=http://docs.oracle.com/javase/tutorial/essential/io/examples/WatchDir.java
 *
 */

import static java.nio.file.LinkOption.NOFOLLOW_LINKS;
import static java.nio.file.StandardWatchEventKinds.ENTRY_CREATE;
import static java.nio.file.StandardWatchEventKinds.ENTRY_DELETE;
import static java.nio.file.StandardWatchEventKinds.ENTRY_MODIFY;
import static java.nio.file.StandardWatchEventKinds.OVERFLOW;

import java.io.File;
import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.WatchEvent;
import java.nio.file.WatchEvent.Kind;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.HashMap;
import java.util.Map;

class MaxFileName {

    private final String fileName;

    public MaxFileName(String aFileName) {
        fileName = aFileName;
    }

    public boolean isFile() {
        return newFile().isFile();
    }

    public String getParent() {
        return newFile().getParent();
    }

    public boolean hasParent() {
        final String parent = getParent();
        return !parent.equalsIgnoreCase("/");
    }

    private File newFile() {
        return new File(fileName);
    }

}

/**
 * Watch a directory (or tree) for changes to files.
 */
public abstract class MaxWatchDir extends Thread {

    public enum MaxWatchEventKinds {
        _CREATE(ENTRY_CREATE.toString()), _MODIFY(ENTRY_MODIFY.toString()), _DELETE(ENTRY_DELETE.toString()), _OVERFLOW(OVERFLOW.toString());

        private final String name;

        private MaxWatchEventKinds(String name) {
            this.name = name;
        }

        @Override
        public String toString() {
            return name.toString();
        }

        public static MaxWatchEventKinds parse(Kind kind) {
            for (MaxWatchEventKinds aKind : values()) {
                if (aKind.toString().equalsIgnoreCase(kind.name())) {
                    return aKind;
                }
            }
            return null;
        }
    }

    abstract protected boolean onModify(Path child);

    abstract protected boolean onDelete(Path child);

    abstract protected boolean onCreate(Path child);

    private WatchService watcher;
    private Map keys;
    private boolean recursive;
    private boolean trace = false;

    public MaxWatchDir() {
        super("MaxWatchDir");
        try {
            watcher = FileSystems.getDefault().newWatchService();
            keys = new HashMap();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    public MaxWatchDir(Path dir) {
        this(dir, false);
    }

    public MaxWatchDir(Path dir, boolean recursive) {
        this();
        this.recursive = recursive;
        if (recursive) {
            System.out.format("Scanning %s ...\n", dir);
            registerRecursive(dir);
            System.out.println("Done.");
        } else {
            add(dir);
        }
    }

    private void add(Path dir) {
        add(dir.toString());
    }

    /**
     * Register the given directory with the WatchService
     */
    protected boolean register(Path dir) {
        WatchKey key;
        try {
            key = dir.register(watcher, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY);
        } catch (IOException e) {
            return false;
        }
        if (trace) {
            Path prev = keys.get(key);
            if (prev == null) {
                System.out.format("register: %s\n", dir);
            } else {
                if (!dir.equals(prev)) {
                    System.out.format("update: %s -> %s\n", prev, dir);
                }
            }
        }
        keys.put(key, dir);
        return true;
    }

    /**
     * Register the given directory, and all its sub-directories, with the
     * WatchService.
     */
    protected void registerRecursive(final Path start) {
        // register directory and sub-directories
        try {
            Files.walkFileTree(start, new SimpleFileVisitor() {
                @Override
                public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) {
                    register(dir);
                    return FileVisitResult.CONTINUE;
                }
            });
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * Process all events for keys queued to the watcher
     */
    private void processEvents() {
        // enable trace after initial registration
        trace = true;
        for (;;) {
            // wait for key to be signalled
            WatchKey key;
            try {
                key = watcher.take();
            } catch (InterruptedException x) {
                return;
            }
            if (onWatchEvent(key)) {
                // reset key and remove from set if directory no longer
                // accessible
                boolean valid = key.reset();
                if (!valid) {
                    keys.remove(key);
                    // all directories are inaccessible
                    if (keys.isEmpty()) {
                        break;
                    }
                }
            }
        }
    }

    private boolean onWatchEvent(WatchKey key) {
        Path dir = keys.get(key);
        if (dir == null) {
            System.err.println("WatchKey not recognized!!");
            return false;
        }
        for (WatchEvent event : key.pollEvents()) {
            // Context for directory entry event is the file name of entry
            WatchEvent ev = cast(event);
            Path name = ev.context();
            Path child = dir.resolve(name);
            MaxWatchEventKinds kind = MaxWatchEventKinds.parse(event.kind());
            if (kind == null) {
                continue; // this should be unreachable
            }
            showEvent(event, child);
            switch (kind) {
            case _MODIFY:
                if (onModify(child)) {
                }
                break;
            case _OVERFLOW:
                continue;
            case _DELETE:
                if (onDelete(child)) {
                }
                break;
            case _CREATE:
                if (onCreate(child)) {
                }
                // if directory is created, and watching recursively, then
                // register it and its sub-directories
                if (recursive) {
                    if (Files.isDirectory(child, NOFOLLOW_LINKS)) {
                        registerRecursive(child);
                    }
                }
                break;
            }
        }
        return true;
    }

    private void showEvent(WatchEvent event, Path child) {
        System.out.format("%s: %s\n", event.kind().name(), child);
    }

    @SuppressWarnings("unchecked")
    static WatchEvent cast(WatchEvent event) {
        return (WatchEvent) event;
    }

    @Override
    public void run() {
        processEvents();
    }

    public boolean add(String entry2watch) {
        MaxFileName maxFileName = new MaxFileName(entry2watch);
        Path path2register;
        if (maxFileName.isFile()) {
            path2register = Paths.get(maxFileName.getParent());
        } else {
            if (maxFileName.hasParent()) {
                path2register = Paths.get(maxFileName.getParent());
            } else {
                path2register = Paths.get(maxFileName.toString());
            }
        }
        return register(path2register);
    }

    public void stopIt() {
        interrupt();
    }

}

------------------------------------------------------------------------


package nl.yokoz.io.file.util.drive;

import java.io.BufferedReader;
import java.io.IOException;
import java.util.StringTokenizer;

import nl.yokoz.exec.MaxExecPgm;
import nl.yokoz.string.MaxString;
import nl.yokoz.util.collections.MaxArrayList;

public class MaxDrive {

    private static final String DEV_SD = "/dev/sd";
    private String partition;
    private String path;

    public static void main(String[] args) {
        boolean bExcludeRoot = true;
        MaxArrayList drives = MaxDrive.getDrives(bExcludeRoot);
        MaxArrayList portableDrives = MaxDrive.getPortableDrives(drives);
        for (MaxDrive maxDrive : portableDrives) {
            System.out.println(maxDrive.toString());
        }
    }

    private static MaxArrayList getPortableDrives(MaxArrayList drives) {
        MaxArrayList portableDrives = new MaxArrayList(drives.size());
        for (MaxDrive maxDrive : drives) {
            if (maxDrive.isPortable()) {
                portableDrives.add(maxDrive);
            }
        }
        return portableDrives;
    }

    public static MaxArrayList getPortableDrives() {
        return getPortableDrives(MaxDrive.getDrives(true));
    }

    private boolean isPortable() {
        return !partition.startsWith("a");
    }

    public static MaxArrayList getDrives(boolean bExcludeRoot) {
        MaxArrayList drives = new MaxArrayList();
        try {
            BufferedReader reader = MaxExecPgm.execGetBufferedReader("mount");
            String line = null;
            while ((line = reader.readLine()) != null) {
                MaxDrive drive = MaxDrive.parse(line);
                if (drive != null) {
                    if (bExcludeRoot) {
                        if (drive.isRootDevice()) {
                            continue;
                        }
                    }
                    drives.add(drive);
                }
            }
            reader.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return drives;
    }

    private boolean isRootDevice() {
        return path.compareTo("/") == 0;
    }

    /**
     *
     * @param line
     *

     *

     *            /dev/sda12 on / type ext4 (rw,relatime,data=ordered)

     *            /dev/sdb5 on /bigdata type ext4 (rw,relatime,data=ordered)

     *            /dev/sda11 on /data type ext4 (rw,relatime,data=ordered)

     *            /dev/sda13 on /home type ext4 (rw,relatime,data=ordered)

     *            /dev/sdc1 on /media/3TB type ext4 (rw,relatime,data=ordered)

     *            /dev/sdd1 on /media/USB DISK type vfat

     *
     * @return
     */
    private static MaxDrive parse(String line) {
        if (!line.startsWith(DEV_SD)) {
            return null;
        }
        MaxDrive drive = new MaxDrive();
        StringTokenizer st = new StringTokenizer(line);
        int tokensCount = st.countTokens() - 6;
        int fieldNr = 1;
        while (st.hasMoreTokens()) {
            MaxString token = MaxString._new(st.nextToken());
            switch (fieldNr) {
            case 1:
                drive.partition = token.substringRight(DEV_SD.length() + 1);
                break;
            case 3:
                if (tokensCount > 0) {
                    for (int i = 0; i < tokensCount; i++) {
                        token.appendString(" " + st.nextToken());
                    }
                }
                drive.path = token.toString();
                return drive;
            }
            fieldNr++;
        }
        return null;
    }

    @Override
    public String toString() {
        return getPartition() + "   " + path;
    }

    public final String getPartition() {
        return DEV_SD + partition;
    }

    public final String getPath() {
        return path;
    }

}


-------------------------------------------------------------------------------

package nl.yokoz.exec;

import java.io.*;

public class MaxExecPgm {

     public static BufferedReader execGetBufferedReader(String cmd) {
        BufferedReader reader = null;
        try {
            reader = new BufferedReader(new InputStreamReader(Runtime.getRuntime().exec(cmd).getInputStream()));
        } catch (IOException e) {
            e.printStackTrace();
        }
        return reader;
    }


}

-------------------------------------------------------------------------------

 package nl.yokoz.util.collections;

import java.util.ArrayList;

public class MaxArrayList extends ArrayList {

    private static final long serialVersionUID = 1L;

    public MaxArrayList() {
    }

    public MaxArrayList(int size) {
        super(size);
    }

    public E firstElement() {
        return get(0);
    }

    public E lastElement() {
        return get(size() - 1);
    }

}


-------------------------------------------------------------------------------

package nl.yokoz.string;

import java.io.File;
import java.io.PrintWriter;
import java.math.BigDecimal;
import java.util.Calendar;
import java.util.StringTokenizer;
import java.util.TimeZone;

final public class MaxString {

    private static final String _ZERO = "0";
    public static char DOT = '.';
    public static char COMMA = ',';
    private String s;
    public final static String _EMPTY_STRING = "";

    public MaxString() {
        set2empty();
    }

    private void set2empty() {
        s = _EMPTY_STRING;
    }

    public MaxString(String aString) {
        if (aString != null) {
            if (aString.length() != 0) {
                s = aString;
                return;
            }
        }
        set2empty();
    }

    public MaxString(int aInt) {
        this(String.valueOf(aInt));
    }

    public MaxString(MaxString aMaxString) {
        this(aMaxString.s);
    }

    public MaxString(Object o) {
        if (o == null) {
            set2empty();
            return;
        }
        s = o.toString();
        if (Boolean.class.isInstance(o)) {
            Boolean b = new Boolean(s);
            if (!b) {
                s = _ZERO;
            } else {
                s = "1";
            }
        }
    }

    public static MaxString _new(String aString) {
        return new MaxString(aString);
    }

    public static MaxString _new(long aLong) {
        return new MaxString(String.valueOf(aLong));
    }

    public static MaxString _new(int anInt) {
        return new MaxString(String.valueOf(anInt));
    }

    public static MaxString _new(Object o) {
        if (o != null) {
            return new MaxString(o);
        }
        return new MaxString();
    }

    public boolean isEmpty() {
        return s == _EMPTY_STRING;
    }

    public boolean isNotEmpty() {
        return !isEmpty();
    }

    public boolean hasData() {
        return isNotEmpty();
    }

    public boolean isInteger() {
        try {
            Integer.parseInt(s);
        } catch (NumberFormatException e) {
            return false;
        }
        return true;
    }

    public int toInt() {
        int i;
        try {
            i = Integer.parseInt(s);
        } catch (NumberFormatException e) {
            i = 0;
        }
        return i;
    }

    public float toFloat() {
        return toInt();
    }

    public BigDecimal toBigDecimal() {
        return new BigDecimal(toInt());
    }

    public long toLong() {
        long l = -1;
        try {
            l = Long.parseLong(s);
        } catch (NumberFormatException e) {
         //   l = 0;
        }
        return l;
    }

    public static String getDateString() {
        Calendar cal = Calendar.getInstance(TimeZone.getDefault());
        String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";
        java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat(DATE_FORMAT);
        sdf.setTimeZone(TimeZone.getDefault());
        return sdf.format(cal.getTime()).replace(":", "").replace("-", "");
    }

    public static String appendFileSeparatorChar(String s) {
        String separatorChar = String.valueOf(File.separatorChar);
        if (!s.endsWith(separatorChar)) {
            s += separatorChar;
        }
        return s;
    }

    public void appendSpace() {
        appendString(" ");
    }

    public void appendString(String aString) {
        s = s + aString;
    }

    public void appendChar(char aChar) {
        s = s + String.valueOf(aChar);
    }

    public void prependString(String aString) {
        s = aString + s;
    }

    public String capitalize() {
        return toUpperCase(firstElement().s) + toLowerCase(substringRight(2));
    }

    public static String toUpperCase(String string) {
        return string.toUpperCase();
    }

    public static String toLowerCase(String string) {
        return string.toLowerCase();
    }

    public int getLength() {
        return s.length();
    }

    public char toChar() {
        char c;
        try {
            c = s.charAt(0);
        } catch (NumberFormatException e) {
            c = ' ';
        }
        return c;
    }

    public String appendFileSeparatorChar() {
        s = appendFileSeparatorChar(s);
        return s;
    }

    public static MaxString appendFileSeparatorChar(MaxString s) {
        return new MaxString(appendFileSeparatorChar(s.s));
    }

    public boolean toBool() {
        return toInt() != 0;
    }

    public static long getDateAsLong() {
        Calendar cal = Calendar.getInstance(TimeZone.getDefault());
        return cal.getTimeInMillis();
    }

    public int nextInt() {
        return toInt() + 1;
    }

    public MaxString eatSpaces() {
        return eatChar(' ');
    }

    public StringTokenizer getStringTokenizer() {
        return new StringTokenizer(s, " ");
    }

    public boolean isZero() {
        return s.compareTo(_ZERO) == 0;
    }

    public boolean compareTo(MaxString value) {
        return s.compareTo(value.s) == 0;
    }

    public StringTokenizer getStringTokenizer(String delimiter) {
        return new StringTokenizer(s, delimiter);
    }

    public MaxString eatChar(char c) {
        StringTokenizer st = getStringTokenizer(String.valueOf(c));
        String newValue = "";
        while (st.hasMoreTokens()) {
            newValue += st.nextToken();
        }
        return new MaxString(newValue);
    }

    public static int toInt(String value) {
        return new MaxString(value).toInt();
    }

    @Override
    public String toString() {
        return s;
    }

    public final void setText(String string) {
        s = string;
    }

    public boolean startsWith(String string) {
        return s.startsWith(string);
    }

    public boolean endsWith(String string) {
        return s.endsWith(string);
    }

    public int length() {
        return s.length();
    }

    private String substring(int beginIndex, int eindIndex) {
        return s.substring(beginIndex, eindIndex);
    }

    public String substringLeft(int lengte) {
        return substring(0, lengte);
    }

    public String substringRight(int beginIndex) {
        return substring(beginIndex - 1, length());
    }

    public int indexOf(char c) {
        return s.indexOf(c);
    }

    public int indexOf(String string) {
        return s.indexOf(string);
    }

    public int lastIndexOf(String string) {
        return s.lastIndexOf(string);
    }

    public void appendString(MaxString string) {
        appendString(string.toString());
    }

    public void sysOut() {
        System.out.println(s);
    }

    public void writerOut(PrintWriter writer) {
        writer.println(s);
    }

    public void empty() {
        set2empty();
    }

    public String trim() {
        return s.trim();
    }

    public static int toChar(String key) {
        return new MaxString(key).toChar();
    }

    public void appendInt(int anInt) {
        appendString(String.valueOf(anInt));
    }

    public void appendIntComma(int anInt) {
        appendInt(anInt);
        appendComma();
    }

    public void appendComma() {
        appendChar(COMMA);
    }

    public MaxString firstElement() {
        return new MaxString(substringLeft(1));
    }

    public static MaxString _new() {
        return new MaxString();
    }

    public String increase() {
        int i = toInt();
        i++;
        if (i < 10) {
            set2Zero();
        } else {
            set2empty();
        }
        appendInt(i);
        return s;
    }

    public MaxString set2Zero() {
        s = _ZERO;
        return this;
    }

    public boolean isInt() {
        int i = -1;
        try {
            i = Integer.parseInt(s);
        } catch (NumberFormatException e) {
            return false;
        }
        return i != -1;
    }

    public boolean isBool() {
        int i = -1;
        try {
            i = Integer.parseInt(s);
        } catch (NumberFormatException e) {
            return false;
        }
        switch (i) {
        case 0:
        case 1:
            return true;
        }
        return false;
    }

    public void eatLastChar() {
        s = substringLeft(length() - 1);
    }

    public char lastChar() {
        return s.charAt(length() - 1);
    }

    public boolean isLong() {
        if (toLong() == -1) {
            return false;
        }
        return true;
    }

}


Geen opmerkingen:

Een reactie posten