Search code examples
javamacossmbjackcess

Using Jackcess with JCIFS to manipulate an Access database on an SMB share


I need to work with an MS Access file in Java using Jackcess. The file is located on an SMB share so I assume I would have to use JCIFS.

I tried this

    String testdirectory = "smb://" + "file location"; 

    SmbFile testsmbdir = null;

    try{
            testsmbdir = new SmbFile(testdirectory,auth);
    }catch(Exception e){
                e.printStackTrace();
    }


    SmbFileInputStream smbFilestream = new SmbFileInputStream(testsmbdir);
    db = DatabaseBuilder.open(testsmbdir);

However, it says SMBFile can not be converted to File for the

db = DatabaseBuilder.open(testsmbdir)" 

line. Also if I try using "smbFilestream" instead it says it cannot convert SmbFileInputStream to File either.

Do I have to copy the file to the local machine or something completely different? If how can I do so?

(I'm a windows user by the way. I am just converting my application to Mac so sorry if my lingo is off.)


Solution

  • In reply to a thread on the Jackcess forums here, James suggested that

    it should be relatively straightforward to implement a version of FileChannel which works with a SmbRandomAccessFile

    I just tried it in a Maven project named smb4jackcess in Eclipse, and I got it working without having to write too much code. The class I created is named SmbFileChannel:

    // FileChannel using jcifs.smb.SmbRandomAccessFile
    
    package smb4jackcess;
    
    import java.io.IOException;
    import java.net.MalformedURLException;
    import java.net.UnknownHostException;
    import java.nio.ByteBuffer;
    import java.nio.MappedByteBuffer;
    import java.nio.channels.FileChannel;
    import java.nio.channels.FileLock;
    import java.nio.channels.ReadableByteChannel;
    import java.nio.channels.WritableByteChannel;
    
    import jcifs.smb.SmbException;
    import jcifs.smb.SmbFile;
    import jcifs.smb.SmbRandomAccessFile;
    
    public class SmbFileChannel extends FileChannel {
        private final SmbRandomAccessFile _file;
        private long _length;
    
        public SmbFileChannel(String smbURL) throws SmbException, MalformedURLException, UnknownHostException {
            _file = new SmbRandomAccessFile(smbURL, "rw", SmbFile.FILE_NO_SHARE);
            _length = _file.length();
        }
    
        @Override
        public void force(boolean metaData) throws SmbException, MalformedURLException, UnknownHostException {
            // do nothing
        }
    
        @Override
        public FileLock lock(long position, long size, boolean shared) {
            throw new UnsupportedOperationException();
        }
    
        @Override
        public MappedByteBuffer map(MapMode mode, long position, long size) {
            throw new UnsupportedOperationException();
        }
    
        @Override
        public long position() throws SmbException {
            return _file.getFilePointer();
        }
    
        @Override
        public FileChannel position(long newPosition) throws SmbException {
            _file.seek(newPosition);
            return this;
        }
    
        @Override
        public int read(ByteBuffer dst) {
            throw new UnsupportedOperationException();
        }
    
        @Override
        public int read(ByteBuffer dst, long position) throws SmbException {
            byte[] b = new byte[dst.remaining()];
            _file.seek(position);
            int bytesRead =_file.read(b);
            dst.put(b);
            return bytesRead;
        }
    
        @Override
        public long read(ByteBuffer[] dsts, int offset, int length) {
            throw new UnsupportedOperationException();
        }
    
        @Override
        public long size() throws SmbException {
            return _length;
        }
    
        @Override
        public long transferFrom(ReadableByteChannel src, long position, long count) throws IOException {
            ByteBuffer bb = ByteBuffer.allocate((int)count);
            int bytesWritten = src.read(bb);
            bb.rewind();
            bb.limit(bytesWritten);
            this.write(bb, position);
            return bytesWritten;
        }
    
        @Override
        public long transferTo(long position, long count, WritableByteChannel target) {
            throw new UnsupportedOperationException();
        }
    
        @Override
        public FileChannel truncate(long newSize) throws SmbException {
            if (newSize < 0L) {
                throw new IllegalArgumentException("negative size");
            }
            _file.setLength(newSize);
            _length = newSize;
            return this;
        }
    
        @Override
        public FileLock tryLock(long position, long size, boolean shared) {
            throw new UnsupportedOperationException();
        }
    
        @Override
        public int write(ByteBuffer src) throws SmbException {
            throw new UnsupportedOperationException();
        }
    
        @Override
        public int write(ByteBuffer src, long position) throws SmbException {
            byte[] b = new byte[src.remaining()];
            src.get(b);
            _file.seek(position);
            _file.write(b);
    
            long endPos = position + b.length;
            if(endPos > _length) {
                _length = endPos;
            }
    
            return b.length;
        }
    
        @Override
        public long write(ByteBuffer[] srcs, int offset, int length) {
            throw new UnsupportedOperationException();
        }
    
        @Override
        protected void implCloseChannel() throws SmbException {
            _file.close();
        }
    
    }
    

    and the main class I used was

    package smb4jackcess;
    
    import java.nio.channels.FileChannel;
    
    import com.healthmarketscience.jackcess.Column;
    import com.healthmarketscience.jackcess.ColumnBuilder;
    import com.healthmarketscience.jackcess.DataType;
    import com.healthmarketscience.jackcess.Database;
    import com.healthmarketscience.jackcess.Database.FileFormat;
    import com.healthmarketscience.jackcess.DatabaseBuilder;
    import com.healthmarketscience.jackcess.IndexBuilder;
    import com.healthmarketscience.jackcess.Table;
    import com.healthmarketscience.jackcess.TableBuilder;
    
    public class Smb4jackcessMain {
    
        public static void main(String[] args) {
    
            String smbURL = "smb://gord:mypassword@SERVERNAME/sharename/etc/newdb.accdb";
            try (SmbFileChannel sfc = new SmbFileChannel(smbURL)) {
    
                // create a brand new database file                
                Database db = new DatabaseBuilder()
                        .setChannel(sfc)
                        .setFileFormat(FileFormat.V2010)
                        .create();
    
                // add a table to it
                Table newTable = new TableBuilder("NewTable")
                        .addColumn(new ColumnBuilder("ID", DataType.LONG)
                                .setAutoNumber(true))
                        .addColumn(new ColumnBuilder("TextField", DataType.TEXT))
                        .addIndex(new IndexBuilder(IndexBuilder.PRIMARY_KEY_NAME)
                                .addColumns("ID").setPrimaryKey())
                        .toTable(db);
    
                // insert a row into the table
                newTable.addRow(Column.AUTO_NUMBER, "This is a new row.");
    
                db.close();
            } catch (Exception e) {
                e.printStackTrace(System.err);
            }
    
        }
    
    }
    

    Updated 2016-02-04: Code improvements. Many thanks to James at Dell Boomi for his assistance!