正忙于的系统需要用到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)来的灵活, 而且这样也足够简单, 所以就省了吧.
嗯, 差不多了. 若是属性值类型不可预见, 也许还可考虑使用对象的多态性进行进一步重构, 但就目前的场景应该够用了吧.
收工!