java从jar文件写入$HOME
我正试图写入$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;
}
};
}
我猜这是某种权限问题,但我似乎无法理解
编辑:这一切都是在完全相同的Ubuntu14.04机器上运行的——不同操作系统没有增加复杂性
# 1 楼答案
通常不能依赖
$HOME
的存在或可写性。实际上,只有两种可移植的方法可以识别(即提供指向)外部文件使用
$HOME
的问题是,您无法知道应用程序运行时使用的用户ID。用户可能有或可能没有主目录,即使用户有,该目录也可能不可写。在您的特定情况下,您的进程可能能够创建文件(对目录本身的写访问),但对文件的写访问可能受到umask和/或acl(在Windows上)或selinux(在Linux上)的限制换句话说,库的安装程序/用户必须显式地为应用程序提供一个已知的可写路径
另一种思考方式是,您正在编写可能在完全未知的环境中使用的库代码。除了你和用户之间的明确合同之外,你不能对外部环境做任何假设。您可以在接口规范中声明
$HOME
必须是可写的,但对于某些环境不具有$HOME
可写性的用户来说,这可能非常不方便一个更好、更便携的解决方案是
或者
理想情况下,您可以同时执行这两项操作,以便为用户提供一定的灵活性
# 2 楼答案
savePending
总是错误的。在call
的开头,检查它是否为false
,然后返回null
。实际的保存代码永远不会执行。我猜你是想检查它是否在true
那里,并将其设置为true
,而不是false
。然后,最后还需要将其重置为false
现在,这在单元测试中起作用的原因就不同了。测试必须执行不同的代码