FileExtendedAttributes的进化过程

原创
2011/04/23 20:09
阅读数 304

正忙于的系统需要用到linux文件系统的扩展属性的特性, 由于jdk6还未能提供API上的支持, 只好选用JNA来调用Native方法实现了.

参考JNA的官方文档, 核心部分的代码很简单, 如下:

interface NativeLib extends Library {
    NativeLib LIB = (NativeLib) synchronizedLibrary((NativeLib) loadLibrary("libc.so.6", NativeLib.class));
    int setxattr(String pathname, String name, Buffer value, int size, int flags);
    int getxattr(String pathname, String name, Buffer value, int size);
  }
在系统中调用的代码, 看上去会是这样的:

Buffer id = IntBuffer.wrap(new int[]{998});
NativeLib.LIB.setxattr("/path/to/file", "user.id", id, 4, 0);
上面的写法很累赘, 使用起来不够简洁, 若我是编写调用的程序员, 我可能会更喜欢这样:

FileExtendedAttributes xattr = new FileExtendedAttributes("/path/to/file");
xattr.setId(998);
于是, 最初版本的FileExtendedAttributes实现会是:

public final class FileExtendedAttributes {
  public static final String ID = "user.id";
  private final String path;
  private FileExtendedAttributes(String path) {this.path = path;}

  public void setId(int id) throws IOException {
    final Buffer buffer = IntBuffer.wrap(new int[]{id});
    final int returnCode = LIB.setxattr(path, ID, buffer, 4, 0);
    if (returnCode == ERROR) throw new IOException("Operation failed, and error no is " + returnCode);
  }

  public int getId() throws IOException {
    final IntBuffer buffer = IntBuffer.allocate(1);
    final int returnCode = LIB.getxattr(path, ID, buffer, 4);
    if (returnCode == ERROR) throw new IOException("Operation failed, and error no is " + returnCode);
    return buffer.get(0);
  }
}
看上去不错. 当然, 这儿并非只有id这样一个属性, 还有replication, blockSize,length... 

如此一个个加上实现的代码, 很快就发现, 我在不断的重复编写:

public void setXxx(T value) throws IOException {
    final Buffer buffer = TBuffer.wrap(new T[]{value});
    final int returnCode = LIB.setxattr(path, Xxx, buffer, SIZE, 0);
    if (returnCode == ERROR) throw new IOException("Operation failed, and error no is " + returnCode);
  }

  public T getXxx() throws IOException {
    final TBuffer buffer = TBuffer.allocate(1);
    final T returnCode = LIB.getxattr(path, Xxx, buffer, SIZE);
    if (returnCode == ERROR) throw new IOException("Operation failed, and error no is " + returnCode);
    return buffer.get(0);
  }

天知道以后还有有多少属性会变化, 事情决不能像这样糟糕下去, 毕竟重复事情应该由电脑帮我们完成, 不然程序员的价值何在?

仔细观察代码规律, 可以抽取共同的方法:

public static final String USER_PREFIX = "user.";
  public static final int FLAGS = 0;

  public void set(String name, Buffer buffer, int size) throws IOException {
    throwIoExceptionIfFailed(LIB.setxattr(path, USER_PREFIX + name, buffer, size, FLAGS));
  }

  public void get(String name, Buffer buffer, int size) throws IOException {
    throwIoExceptionIfFailed(LIB.getxattr(path, USER_PREFIX + name, buffer, size));
  }

  private static void throwIoExceptionIfFailed(int returnCode) throws IOException {
    if (returnCode < 0)  
        throw new IOException("Operation failed, and error no is " + returnCode);
  }
接下来, 只需要根据可预见的属性值类型提供若干 人本接口即可了:

public void set(String name, short value) throws IOException { set(name, buffer(value), 2); }
  public void set(String name, int value) throws IOException { set(name, buffer(value), 4); }
  public void set(String name, long value) throws IOException { set(name, buffer(value), 8); }
  public void set(String name, String value) throws IOException { set(name, buffer(value), value.getBytes().length); }

  public short getShort(String name) throws IOException {
    final ShortBuffer buffer = ShortBuffer.allocate(1);
    get(name, buffer, 2);
    return buffer.get(0);
  }

  public int getInt(String name) throws IOException {
    final IntBuffer buffer = IntBuffer.allocate(1);
    get(name, buffer, 4);
    return buffer.get(0);
  }

  public long getLong(String name) throws IOException {
    final LongBuffer buffer = LongBuffer.allocate(1);
    get(name, buffer, 8);
    return buffer.get(0);
  }

  public String getString(String name, int size) throws IOException {
    final ByteBuffer buffer = ByteBuffer.allocate(size);
    get(name, buffer, size);
    return new String(buffer.array());
  }

  private static Buffer buffer(short value) {return ShortBuffer.wrap(new short[]{value});}

  private static Buffer buffer(int value) {return IntBuffer.wrap(new int[]{value});}

  private static Buffer buffer(long value) {return LongBuffer.wrap(new long[]{value});}

  private static Buffer buffer(String value) {return ByteBuffer.wrap(value.getBytes());}

至于像setXxx之类的, 由于属性变化可能太频繁, 不如set(name, value)来的灵活, 而且这样也足够简单, 所以就省了吧.

嗯, 差不多了. 若是属性值类型不可预见, 也许还可考虑使用对象的多态性进行进一步重构, 但就目前的场景应该够用了吧.

收工!

 

展开阅读全文
加载中
点击引领话题📣 发布并加入讨论🔥
0 评论
1 收藏
0
分享
返回顶部
顶部