[英]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
的定義
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上)的限制。
換句話說,庫的安裝程序/用戶必須為應用程序顯式提供一個已知的可寫路徑。
還有一種思考的方式是,您正在編寫可能在完全未知的環境中使用的庫代碼。 除了您和用戶之間的明確合同中的內容外,您不能假設任何有關外部環境的信息。 您可以在接口規范中聲明$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.