السلام عليكم ،
في بعض الأحيان قد نجبر المستخدم على عدم فتح أكثر من نسخه من البرنامج ، وخاصه في البرامج التي تتصل مع قاعده البيانات والتي لا تقوم بعمل Commit الا مثلا عند الخروج من البرنامج ...... أو أي برنامج أخر مثلا مثل برامج المنبهات والتي تعمل كل مده معينه .. أو لأي سبب أخر .
ولأن أي برنامج نقوم بتشغليه فسوف يعمل على VM خاصه به ، لذلك لا توجد طريقه مباشره لهذا الأمر .. .
وسوف نستعرض اشهر طريقتين ، أحدهما عن طريق socket (لا تستغرب :) ) ، والأخرى عن طريق FileLocking .
الطريقه الأولى The socket technique :
هذه الطريقه فكرتها كالأتي ، هو عندما نشغل البرنامج لأول مره نقوم بتشغيل Server برقم بورت معين مثلا 2000 ، ويكون هذا السيرفر دليل على أن هناك نسخه تعمل . في حال شغلت نسخه أخرى من البرنامج نقوم بالاتصال بالسيرفر فاذا أتصل فهذا معناه أن هناك نسخه تعمل وهنا سوف أغلق البرنامج الجديد .
أي الخوارزميه كالأتي ،
- قم بالأتصال بالسيرفر بالمنفذ 2000 ، في حال أتصل فمعناه أنه هناك نسخه تعمل ، فقم الأن باغلاق النسخه الجديده .
- في حال لم يتصل البرنامج بالسيرفر (معناه أنه لا يوجد سيرفر يعمل ) سوف ينفذ جزء Exception وهنا سوف نقوم بتشغيل السيرفر من داخل الException .
كود بسيط للسيرفر :
// This MonitorServer will start when you open myApp
import java.net.ServerSocket;
import java.net.Socket;
import java.io.IOException;
public class MonitorServer extends Thread
{
private ServerSocket serverSocket;
private Socket socket;
private static final int PORT = 5000;
public void run ()
{
try
{
serverSocket = new ServerSocket(PORT);
while ( true )
{
socket = serverSocket.accept(); // get client
socket.close();
}
}
catch ( IOException e)
{
e.printStackTrace();
}
}
}وهذا كود البرنامج :
// romansy
import java.net.Socket;
import java.io.IOException;
import javax.swing.JOptionPane;
public class MyApp
{
public static void main (String args[])
{
try
{
Socket socket = new Socket("localhost",5000);
// if get connection in mean that server is open , it mean that is client is opening
JOptionPane.showMessageDialog(null,"Their is Opening Vesrion of this Program",
"Cannot Open More Than One Version",JOptionPane.ERROR_MESSAGE);
System.exit(1);
}
catch (IOException e)
{
// here the server is not opening , so we open it now
MonitorServer server = new MonitorServer();
server.start();
}
// here application code
// good Luck
}
}نأخذ مثال لتطبيق الفكره ، وهو لبرنامج مفكره بسيط يقوم بحفظ وقرائه ملفات txt وبه خاصيه نسخ ولصق ... المهم أنظر الصوره التاليه وستجد أننا نستطيع تشغيل أكثر من نسخه .. نريد الأن اضافه الطريقه التي تعلمناه قبل قليل عليه ...

ننظر الى الداله الرئيسيه :
public class JNotePad
{
public static void main (String args[])
{
JNotePadFrame app = new JNotePadFrame();
app.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
app.setVisible(true);
}
}نقوم الأن باضافه كود الأتصال في البدايه -أول سطر في البرنامج- :
public static void main (String args[])
{
try
{
Socket socket = new Socket("localhost",5000);
// if get connection in mean that server is open , it mean that is client is opening
JOptionPane.showMessageDialog(null,"Their is Opening Vesrion of this Program",
"Cannot Open More Than One Version",JOptionPane.ERROR_MESSAGE);
System.exit(1);
}
catch (IOException e)
{
// here the server is not opening , so we open it now
MonitorServer server = new MonitorServer();
server.start();
}
JNotePadFrame app = new JNotePadFrame();
app.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
app.setVisible(true);
}وها هو برنامجنا الأن يصيح عندما تقوم بتشغيل أكثر من نسخه :

على العموم ، الطريقه ليست سريعه كما في الطريقه التاليه ، ولكنها تفي بالغرض !!
الطريقه الثانيه The file lock technique :
تعتمد هذه الطريقه على java.nio.channels وطريقتها هي انشاء ملف في مجلد user.home وعمل lock عليه channel.tryLock() ، وفي حاله لم يستطع البرنامج عمل lock وذلك بسبب وجود نسخه تعمل سوف يحصل Exception من نوع OverlappingFileLockException . وعند اغلاق البرنامج يجب أن نزيح هذا المقبض على الملف وذلك باستخدام shutdown hook .
على ما أظن الكلام غير واضح :) ، والسبب يرجع لكوني لم أتعامل مع java.nio.channels من قبل . على العموم هذا الكود ، قم بتشغيله وستجد أنه أسرع من الطريقه الأولى ..
الملف الأول JustOneLock وهو الخاص بوضع الLock . ونستطيع استخدامه مع أي برنامج أخر بلا تغيير .
import java.io.*;
import java.nio.channels.*;
public class JustOneLock
{
private String appName;
private File file;
private FileChannel channel;
private FileLock lock;
public JustOneLock(String appName)
{
this.appName = appName;
}
public boolean isAppActive()
{
try
{
file = new File(System.getProperty("user.home"), appName + ".tmp");
channel = new RandomAccessFile(file, "rw").getChannel();
try
{
lock = channel.tryLock();
}
catch (OverlappingFileLockException e)
{
// already locked
closeLock();
return true;
}
if (lock == null)
{
closeLock();
return true;
}
Runtime.getRuntime().addShutdownHook
(
new Thread()
{
// destroy the lock when the JVM is closing
public void run()
{
closeLock();
deleteFile();
}
}
);
return false;
}
catch (Exception e)
{
closeLock();
return true;
}
}
private void closeLock()
{
try
{
lock.release();
}
catch (Exception e) { }
try
{
channel.close();
}
catch (Exception e) { }
}
private void deleteFile()
{
try
{
file.delete();
}
catch (Exception e) { }
}
}برنامجنا الذي نريده أن يعمل مره واحده :
public class JustOneTest
{
public static void main(String[] args)
{
new JustOneTest().test();
}
void test()
{
JustOneLock ua = new JustOneLock("JustOneId");
if (ua.isAppActive())
{
System.out.println("Already active.");
System.exit(1);
}
else
{
System.out.println("NOT already active.");
try
{
while(true)
{
try
{
System.out.print(".");
Thread.sleep(5 * 60);
}
catch(Exception e)
{ e.printStackTrace(); }
}
}
catch (Exception e) { }
}
}
}نقوم بالتعديل الأن في برنامج المفكره لكي نستخدم هذ الطريقه ، وسوف تكون الداله الرئيسيه بهذا الشكل :
public static void main (String args[])
{
JustOneLock ua = new JustOneLock("JustOneId");
if (ua.isAppActive())
{
JOptionPane.showMessageDialog(null,"Their is Opening Vesrion of this Program","Cannot Open More Than One Version",
JOptionPane.ERROR_MESSAGE);
System.exit(1);
}
JNotePadFrame app = new JNotePadFrame();
app.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
app.setVisible(true);
}أتمنى أن لا يكون الموضوع مكرر ، وفي حال كان كذلك يمكن للمشرف حذف الموضوع .....
البرنامج المفكره في المرفقات .
الى اللقاء .
