我正在尝试写入位于 $HOME 目录中的文件。写入该文件的代码已打包到 jar 文件中。当我运行单元测试来打包 jar 文件时,一切都按预期工作 - 即文件已填充并且可以再次读取。
当我尝试从另一个应用程序运行此代码(其中 jar 文件包含在 lib
目录中)时,它会失败。该文件已创建 - 但该文件从未被写入。当应用程序读取文件时,它无法解析它,因为它是空的。
这是写入文件的代码:
logger.warn("TestNet wallet does not exist creating one now in the directory: " + walletPath)
testNetFileName.createNewFile()
logger.warn("Wallet file name: " + testNetFileName.getAbsolutePath)
logger.warn("Can write: "+ testNetFileName.canWrite())
logger.warn("Can read: " + testNetFileName.canRead)
val w = Wallet.fromWatchingKey(TestNet3Params.get(), testNetSeed)
w.autosaveToFile(testNetFileName, savingInterval, TimeUnit.MILLISECONDS, null)
w
}
这是上述相关方法的日志:
2015-12-30 15:11:46,416 - [WARN] - from class com.suredbits.core.wallet.ColdStorageWallet$ in play-akka.actor.default-dispatcher-9
TestNet wallet exists, reading in the one from disk
2015-12-30 15:11:46,416 - [WARN] - from class com.suredbits.core.wallet.ColdStorageWallet$ in play-akka.actor.default-dispatcher-9
Wallet file name: /home/chris/testnet-cold-storage.wallet
然后它就爆炸了。
这是 autoSaveToFile
的定义
public WalletFiles autosaveToFile(File f, long delayTime, TimeUnit timeUnit,
@Nullable WalletFiles.Listener eventListener) {
lock.lock();
try {
checkState(vFileManager == null, "Already auto saving this wallet.");
WalletFiles manager = new WalletFiles(this, f, delayTime, timeUnit);
if (eventListener != null)
manager.setListener(eventListener);
vFileManager = manager;
return manager;
} finally {
lock.unlock();
}
}
以及 WalletFiles
的定义
public WalletFiles(final Wallet wallet, File file, long delay, TimeUnit delayTimeUnit) {
// An executor that starts up threads when needed and shuts them down later.
this.executor = new ScheduledThreadPoolExecutor(1, new ContextPropagatingThreadFactory("Wallet autosave thread", Thread.MIN_PRIORITY));
this.executor.setKeepAliveTime(5, TimeUnit.SECONDS);
this.executor.allowCoreThreadTimeOut(true);
this.executor.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);
this.wallet = checkNotNull(wallet);
// File must only be accessed from the auto-save executor from now on, to avoid simultaneous access.
this.file = checkNotNull(file);
this.savePending = new AtomicBoolean();
this.delay = delay;
this.delayTimeUnit = checkNotNull(delayTimeUnit);
this.saver = new Callable<Void>() {
@Override public Void call() throws Exception {
// Runs in an auto save thread.
if (!savePending.getAndSet(false)) {
// Some other scheduled request already beat us to it.
return null;
}
log.info("Background saving wallet, last seen block is {}/{}", wallet.getLastBlockSeenHeight(), wallet.getLastBlockSeenHash());
saveNowInternal();
return null;
}
};
}
我猜这是某种权限问题,但我似乎无法弄清楚这一点。
编辑:这一切都在完全相同的 Ubuntu 14.04 机器上运行 - 不同操作系统没有增加复杂性。
最佳答案
通常不能依赖 $HOME
的存在或可写性。实际上只有两种可移植的方法来识别(即提供路径)外部文件。
- 使用在调用命令行上设置或在环境中提供的属性提供显式路径,或者
- 在配置属性文件中提供路径,该文件的位置本身作为命令行或环境中的属性提供。
使用$HOME
的问题是您无法知道应用程序正在哪个用户ID下运行。用户可能有也可能没有主目录,即使用户有,该目录也可能可写,也可能不可写。在您的特定情况下,您的进程可能能够创建文件(对目录本身的写访问权限),但对文件的写访问权限可能受到 umask 和/或 ACL(在 Windows 上)或 selinux(在 Linux 上)的限制。
换句话说,库的安装者/用户必须显式提供一个已知的可写路径供您的应用程序使用。
另一种思考方式是,您正在编写可能在完全未知的环境中使用的库代码。除了您和用户之间的明确契约(Contract)中的内容之外,您不能对外部环境做出任何假设。您可以在接口(interface)规范中声明 $HOME
必须可写,但这对于某些环境不具有 $HOME
可写的用户来说可能非常不方便。
一个更好且可移植的解决方案是说
specify
-Dcom.xyz.workdir=[path]
on the command line to indicate the work path to be used
或
The xyz library will look for its work directory in the path specified by the
XYZ_WORK
environment variable
理想情况下,您可以同时执行这两项操作,以便为用户提供一定的灵 active 。
关于java - 从 jar 文件写入 $HOME,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34537409/