java - 从 jar 文件写入 $HOME

标签 java scala bitcoin

我正在尝试写入位于 $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 的定义

https://github.com/bitcoinj/bitcoinj/blob/master/core/src/main/java/org/bitcoinj/wallet/WalletFiles.java#L68

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 的存在或可写性。实际上只有两种可移植的方法来识别(即提供路径)外部文件。

  1. 使用在调用命令行上设置或在环境中提供的属性提供显式路径,或者
  2. 在配置属性文件中提供路径,该文件的位置本身作为命令行或环境中的属性提供。

使用$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/

相关文章:

scala - 如何合并/组合不同大小的元组列表

scala - akka http配置中的空闲超时和请求超时有什么区别?

scala - Spark - 提交应用程序时出现错误 "A master URL must be set in your configuration"

arrays - Angular 4 [对象对象]

c# - 将应用程序中的 KeyPath 与 Bip44 钱包的 Wallet32 Keypath 相匹配

java - Java的Crystal report sdk在Ubuntu中不起作用

java - Akka官方教程: Parent actor doesn't receive termination message when child is stopped with PoisonPill

macos - "llvm-g++: command not found"构建比特币时

Java - MySQL --> SQLite - 无法使用 java 插入 SQLite 数据库

java - Java 序列化、JSON、JAXB 之间的区别?