DÜZENLEME : Bu WatchService yaklaşımını kullanmak yerine, 1 saniyelik basit bir zamanlayıcı iş parçacığı, göstergeFile.exists () olup olmadığını kontrol etmek için kullanılabilir. Silin, ardından uygulamayı Front () konumuna getirin.
DÜZENLEME : Bunun neden reddedildiğini bilmek istiyorum. Şimdiye kadar gördüğüm en iyi çözüm bu. Örneğin, başka bir uygulama bağlantı noktasını dinliyorsa, sunucu soket yaklaşımı başarısız olur.
Sadece Microsoft Windows Sysinternals TCPView'ı indirin (veya netstat kullanın), başlatın, " Duruma " göre sıralayın, "DİNLEME" yazan satır bloğunu arayın, uzak adresi bilgisayarınızın adını yazan birini seçin ve bu bağlantı noktasını yeni Soketinize yerleştirin ()-çözüm. Bunu uygulamamda her seferinde başarısızlık üretebilirim. Ve mantıklı çünkü yaklaşımın temelini oluşturuyor. Ya da bunun nasıl uygulanacağına dair neyi anlamıyorum?
Lütfen bana bu konuda yanıldığımı ve nasıl yanıldığımı bildirin!
Benim görüşüm - mümkünse çürütmenizi istediğim - geliştiricilere üretim kodunda yaklaşık 60000 vakanın en az 1'inde başarısız olacak bir yaklaşım kullanmaları tavsiye ediliyor. Ve eğer bu görüş doğruysa, o zaman bu soruna sahip olmayan bir çözümün kod miktarı nedeniyle reddedilmesi ve eleştirilmesi kesinlikle olamaz .
Soket yaklaşımının karşılaştırmalı dezavantajları:
- Yanlış piyango bileti (port numarası) seçilirse başarısız olur.
- Çok kullanıcılı ortamda başarısız olur: Uygulamayı aynı anda yalnızca bir kullanıcı çalıştırabilir. (Kullanıcı ağacında dosya (lar) oluşturmak için yaklaşımımın biraz değiştirilmesi gerekecekti, ama bu önemsiz.)
- Güvenlik duvarı kuralları çok katı ise başarısız olur.
- Şüpheli kullanıcıları (vahşi doğada tanıştım) metin düzenleyiciniz bir sunucu soketi talep ettiğinde ne tür maskaralıklarla uğraştığınızı merak ediyor.
Yeni örnek-var olan Java iletişim problemini her sistemde çalışması gereken bir şekilde nasıl çözeceğime dair güzel bir fikrim var. Yani, bu dersi yaklaşık iki saat içinde hazırladım. Cazibe gibi çalışır: D
Robert'in o zamandan beri kullandığım dosya kilitleme yaklaşımına (bu sayfada da) dayanmaktadır . Zaten çalışan örneğe başka bir örneğin başlamayı denediğini (ancak başlamadığını) söylemek için ... bir dosya oluşturulur ve hemen silinir ve ilk örnek, bu klasör içeriği değişikliğini algılamak için WatchService'i kullanır. Sorunun ne kadar temel olduğu düşünüldüğünde, görünüşe göre bunun yeni bir fikir olduğuna inanamıyorum.
Bu , dosyayı yalnızca oluşturmak ve silmemek için kolayca değiştirilebilir ve daha sonra, uygun örneğin değerlendirebileceği bilgiler, örneğin komut satırı argümanları - ve uygun örnek daha sonra silme işlemini gerçekleştirebilir. Şahsen, sadece uygulamamın penceresini ne zaman geri yükleyeceğimi ve onu öne göndermem gerektiğini bilmem gerekiyordu.
Örnek kullanım:
public static void main(final String[] args) {
if (!SingleInstanceChecker.INSTANCE.isOnlyInstance(Main::otherInstanceTriedToLaunch, false)) {
System.exit(0);
}
System.out.println("Application starts properly because it's the only instance.");
}
private static void otherInstanceTriedToLaunch() {
System.err.println("Deiconified because other instance tried to start.");
}
İşte sınıf:
package yourpackagehere;
import javax.swing.*;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.channels.FileLock;
import java.nio.file.*;
public enum SingleInstanceChecker {
INSTANCE;
final public static int POLLINTERVAL = 1000;
final public static File LOCKFILE = new File("SINGLE_INSTANCE_LOCKFILE");
final public static File DETECTFILE = new File("EXTRA_INSTANCE_DETECTFILE");
private boolean hasBeenUsedAlready = false;
private WatchService watchService = null;
private RandomAccessFile randomAccessFileForLock = null;
private FileLock fileLock = null;
public boolean isOnlyInstance(final Runnable codeToRunIfOtherInstanceTriesToStart, final boolean executeOnAWTEventDispatchThread) {
if (hasBeenUsedAlready) {
throw new IllegalStateException("This class/method can only be used once, which kinda makes sense if you think about it.");
}
hasBeenUsedAlready = true;
final boolean ret = canLockFileBeCreatedAndLocked();
if (codeToRunIfOtherInstanceTriesToStart != null) {
if (ret) {
installOtherInstanceLaunchAttemptWatcher(codeToRunIfOtherInstanceTriesToStart, executeOnAWTEventDispatchThread);
} else {
createAndDeleteOtherInstanceWatcherTriggerFile();
}
}
optionallyInstallShutdownHookThatCleansEverythingUp();
return ret;
}
private void createAndDeleteOtherInstanceWatcherTriggerFile() {
try {
final RandomAccessFile randomAccessFileForDetection = new RandomAccessFile(DETECTFILE, "rw");
randomAccessFileForDetection.close();
Files.deleteIfExists(DETECTFILE.toPath());
} catch (Exception e) {
e.printStackTrace();
}
}
private boolean canLockFileBeCreatedAndLocked() {
try {
randomAccessFileForLock = new RandomAccessFile(LOCKFILE, "rw");
fileLock = randomAccessFileForLock.getChannel().tryLock();
return fileLock != null;
} catch (Exception e) {
return false;
}
}
private void installOtherInstanceLaunchAttemptWatcher(final Runnable codeToRunIfOtherInstanceTriesToStart, final boolean executeOnAWTEventDispatchThread) {
try {
watchService = FileSystems.getDefault().newWatchService();
} catch (IOException e) {
e.printStackTrace();
return;
}
final File appFolder = new File("").getAbsoluteFile();
final Path appFolderWatchable = appFolder.toPath();
try {
appFolderWatchable.register(watchService, StandardWatchEventKinds.ENTRY_DELETE);
} catch (IOException e) {
e.printStackTrace();
return;
}
final Thread t = new Thread(() -> watchForDirectoryChangesOnExtraThread(codeToRunIfOtherInstanceTriesToStart, executeOnAWTEventDispatchThread));
t.setDaemon(true);
t.setName("directory content change watcher");
t.start();
}
private void optionallyInstallShutdownHookThatCleansEverythingUp() {
if (fileLock == null && randomAccessFileForLock == null && watchService == null) {
return;
}
final Thread shutdownHookThread = new Thread(() -> {
try {
if (fileLock != null) {
fileLock.release();
}
if (randomAccessFileForLock != null) {
randomAccessFileForLock.close();
}
Files.deleteIfExists(LOCKFILE.toPath());
} catch (Exception ignore) {
}
if (watchService != null) {
try {
watchService.close();
} catch (IOException e) {
e.printStackTrace();
}
}
});
Runtime.getRuntime().addShutdownHook(shutdownHookThread);
}
private void watchForDirectoryChangesOnExtraThread(final Runnable codeToRunIfOtherInstanceTriesToStart, final boolean executeOnAWTEventDispatchThread) {
while (true) {
try {
Thread.sleep(POLLINTERVAL);
} catch (InterruptedException e) {
e.printStackTrace();
}
final WatchKey wk;
try {
wk = watchService.poll();
} catch (ClosedWatchServiceException e) {
e.printStackTrace();
return;
}
if (wk == null || !wk.isValid()) {
continue;
}
for (WatchEvent<?> we : wk.pollEvents()) {
final WatchEvent.Kind<?> kind = we.kind();
if (kind == StandardWatchEventKinds.OVERFLOW) {
System.err.println("OVERFLOW of directory change events!");
continue;
}
final WatchEvent<Path> watchEvent = (WatchEvent<Path>) we;
final File file = watchEvent.context().toFile();
if (file.equals(DETECTFILE)) {
if (!executeOnAWTEventDispatchThread || SwingUtilities.isEventDispatchThread()) {
codeToRunIfOtherInstanceTriesToStart.run();
} else {
SwingUtilities.invokeLater(codeToRunIfOtherInstanceTriesToStart);
}
break;
} else {
System.err.println("THIS IS THE FILE THAT WAS DELETED: " + file);
}
}
wk.reset();
}
}
}