簡體   English   中英

從jar文件寫入$ HOME

[英]Writing to $HOME from a jar file

我正在嘗試寫入位於$ 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上)的限制。

換句話說,庫的安裝程序/用戶必須為應用程序顯式提供一個已知的可寫路徑。

還有一種思考的方式是,您正在編寫可能在完全未知的環境中使用的庫代碼。 除了您和用戶之間的明確合同中的內容外,您不能假設任何有關外部環境的信息。 您可以在接口規范中聲明$HOME必須是可寫的,但對於某些環境中沒有$HOME可寫的用戶而言,這可能非常不便。

更好,更便攜的解決方案是

在命令行上指定-Dcom.xyz.workdir=[path]以指示要使用的工作路徑

要么

xyz庫將在XYZ_WORK環境變量指定的路徑中查找其工作目錄。

理想情況下,您需要同時執行這兩項操作,才能為用戶提供一定的靈活性。

savePending始終為false。 call開始時, call檢查它是否為false ,然后返回null 實際的保存代碼永遠不會執行。 我猜您打算檢查那里是否為true ,並將其設置為true ,而不是false 然后,您還需要最終將其重置為false

現在,為什么這在您的單元測試中起作用卻是另一回事。 測試必須執行不同的代碼。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM