Java 从jar文件写入$HOME
我正在尝试写入$HOME目录中的文件。写入该文件的代码已打包到jar文件中。当我运行单元测试来打包jar文件时,一切都按照预期工作——即填充文件并可以再次读取 当我尝试从另一个应用程序运行此代码时,jar文件包含在Java 从jar文件写入$HOME,java,scala,bitcoin,Java,Scala,Bitcoin,我正在尝试写入$HOME目录中的文件。写入该文件的代码已打包到jar文件中。当我运行单元测试来打包jar文件时,一切都按照预期工作——即填充文件并可以再次读取 当我尝试从另一个应用程序运行此代码时,jar文件包含在lib目录中,它失败了。文件已创建,但从未写入。当应用程序读取文件时,由于文件为空,因此无法解析该文件 以下是写入文件的代码: logger.warn("TestNet wallet does not exist creating one now in the directory:
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();
}
}
以及钱包文件的定义
公共钱包文件(最终钱包、文件文件、长延迟、时间单位延迟时间单位){
//在需要时启动线程并在以后关闭它们的执行器。
this.executor=new ScheduledThreadPoolExecutor(1,new contextpropagationthreadfactory(“Wallet autosave thread”,thread.MIN_PRIORITY));
this.executor.setKeepAliveTime(5,TimeUnit.SECONDS);
this.executor.allowCoreThreadTimeOut(true);
this.executor.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);
this.wallet=checkNotNull(wallet);
//从现在起,只能从自动保存执行器访问文件,以避免同时访问。
this.file=checkNotNull(文件);
this.savePending=new AtomicBoolean();
延迟=延迟;
this.delayTimeUnit=checkNotNull(delayTimeUnit);
this.saver=newcallable(){
@Override public Void call()引发异常{
//在自动保存线程中运行。
如果(!savePending.getAndSet(false)){
//其他一些预定的请求已经超过了我们。
返回null;
}
log.info(“后台保存钱包,最后看到的块是{}/{}”,wallet.getLastBlockSeenHeight(),wallet.getlastblockseenhah());
saveNowInternal();
返回null;
}
};
}
我猜这是某种权限问题,但我似乎无法理解
编辑:这一切都是在完全相同的Ubuntu 14.04机器上运行的-没有增加不同操作系统的复杂性 通常不能依赖$HOME
的存在或可写性。实际上,只有两种可移植的方法可以识别(即提供指向)外部文件
使用调用命令行上的属性集或环境中提供的属性提供显式路径,或
在配置属性文件中提供路径,其位置本身在命令行或环境中作为属性提供
使用$HOME
的问题在于,您无法知道应用程序运行时使用的用户ID。用户可能有主目录,也可能没有主目录,即使用户有主目录,该目录也可能不可写。在您的特定情况下,您的进程可能能够创建文件(对目录本身的写访问),但对文件的写访问可能受到umask和/或acl(在Windows上)或selinux(在Linux上)的限制
换句话说,库的安装程序/用户必须显式提供一个已知的可写路径供应用程序使用
另一种思考方式是,您正在编写可能在完全未知的环境中使用的库代码。除了您和用户之间的明确合同中的内容外,您不能对外部环境进行任何假设。您可以在接口规范中声明,$HOME
必须是可写的,但对于某些环境中没有$HOME
可写的用户来说,这可能会非常不方便
一个更好的、可移植的解决方案是
在命令行上指定-Dcom.xyz.workdir=[path]
,以指示要使用的工作路径
或
xyz库将在xyz_work
环境变量指定的路径中查找其工作目录
理想情况下,您可以同时执行这两项操作,以给用户一些灵活性。通常不能依赖$HOME
的存在或可写性。实际上,只有两种可移植的方法可以识别(即提供指向)外部文件
使用调用命令行上的属性集或环境中提供的属性提供显式路径,或
在配置属性文件中提供路径,其位置本身在命令行或环境中作为属性提供
使用$HOME
的问题在于,您无法知道应用程序运行时使用的用户ID。用户可能有主目录,也可能没有主目录,即使用户有主目录,该目录也可能不可写。在您的特定情况下,您的进程可能能够创建文件(对目录本身的写访问),但对文件的写访问可能受到umask和/或acl(在Windows上)或selinux(在Linux上)的限制
换句话说,库的安装程序/用户必须显式提供一个已知的可写路径供应用程序使用
另一种思考方式是,您正在编写可能在完全未知的环境中使用的库代码。除了您和用户之间的明确合同中的内容外,您不能对外部环境进行任何假设。您可以在接口规范中声明,$HOME
必须是可写的,但对于某些环境中没有$HOME
可写的用户来说,这可能会非常不方便
一个更好的、可移植的解决方案是
在命令行上指定-Dcom.xyz.workdir=[path]
,以指示要使用的工作路径
或
xyz库将查找其w
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;
}
};
}