/* * Eoulsan development code * * This code may be freely distributed and modified under the * terms of the GNU Lesser General Public License version 2.1 or * later and CeCILL-C. This should be distributed with the code. * If you do not have a copy, see: * * http://www.gnu.org/licenses/lgpl-2.1.txt * http://www.cecill.info/licences/Licence_CeCILL-C_V1-en.txt * * Copyright for this code is held jointly by the Genomic platform * of the Institut de Biologie de l'École normale supérieure and * the individual authors. These should be listed in @author doc * comments. * * For more information on the Eoulsan project and its aims, * or to join the Eoulsan Google group, visit the home page * at: * * http://outils.genomique.biologie.ens.fr/eoulsan * */ package fr.ens.biologie.genomique.eoulsan.util.locker; import java.io.IOException; import java.net.MalformedURLException; import java.rmi.NotBoundException; import java.rmi.RemoteException; import java.rmi.registry.LocateRegistry; import java.rmi.registry.Registry; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Set; /** * This class define a global locker using RMI messaging. TODO tune waiting * times * @since 1.1 * @author Laurent Jourdren */ public class TicketLocker implements Locker { static final String RMI_SERVICE_PREFIX = "locker-"; private LockerThread thread; private final String lockerName; private final int port; private final String description; /** * Define the locker thread that wait the end of the lock. * @author Laurent Jourdren */ private final class LockerThread implements Runnable { private final Ticket ticket; private Set<Ticket> tickets; @Override public void run() { while (true) { try { final TicketScheduler stub = getStub(); if (stub != null) { this.tickets = stub.getTickets(this.ticket); // Safety: if ticket scheduler return one or zero ticket, the locker // ticket is allowed to work if (this.tickets == null || this.tickets.isEmpty() || this.tickets.size() == 1) { this.ticket.setWorking(true); return; } // Test if locker ticket is allowed to work for (Ticket t : this.tickets) { if (t.equals(this.ticket)) { if (t.isWorking()) { return; } } } } else { startRMIServer(this.tickets); } } catch (RemoteException e) { // e.printStackTrace(); } try { Thread.sleep(5000); } catch (InterruptedException e) { } } } /** * Stop the thread when release the lock. */ public void end() { final TicketScheduler stub = getStub(); if (stub != null) { try { stub.endWork(this.ticket); } catch (RemoteException e) { startRMIServer(this.tickets); } } } /** * Private constructor. * @param ticket the ticket to wait */ private LockerThread(final Ticket ticket) { this.ticket = ticket; } } /** * Get the stub of TicketScheduler. * @return a TicketScheduler object */ private TicketScheduler getStub() { try { Registry registry = LocateRegistry.getRegistry(this.port); return (TicketScheduler) registry .lookup(RMI_SERVICE_PREFIX + this.lockerName); } catch (IOException e) { return null; } catch (NotBoundException e) { return null; } } // // Locker methods // @Override public void lock() throws IOException { if (this.thread == null) { this.thread = new LockerThread(new Ticket(this.description)); } final Thread t = new Thread(this.thread); t.start(); while (t.isAlive()) { try { Thread.sleep(1000); } catch (InterruptedException e) { } } } @Override public void unlock() throws IOException { this.thread.end(); } // // Other methods // /** * Start a new RMI server. * @param tickets a set with tickets to populate the TicketScheduler at * startup */ private void startRMIServer(final Set<Ticket> tickets) { new Thread(new Runnable() { @Override public void run() { try { LocateRegistry.createRegistry(TicketLocker.this.port); TicketSchedulerServer.newServer(tickets, TicketLocker.this.lockerName, TicketLocker.this.port); // TODO the server must be halted if no more tickets to process since // several minutes while (true) { Thread.sleep(10000); } } catch (InterruptedException | RemoteException e) { } } }).start(); } // // Constructor // /** * Public constructor. * @param lockerName The name of the locker * @param port port to use */ public TicketLocker(final String lockerName, final int port) { this(lockerName, port, null); } /** * Public constructor. * @param lockerName The name of the locker * @param port port to use * @param description locker description */ public TicketLocker(final String lockerName, final int port, final String description) { this.lockerName = lockerName; this.port = port; this.description = description; } // // Main method : list current tickets // /** * Main method. * @param args command line arguments * @throws RemoteException if an error occurs while inspecting tickets * @throws MalformedURLException */ public static final void main(final String[] args) throws RemoteException, MalformedURLException { if (args.length < 2) { System.err.println("List current lock tickets\nSyntax: java " + TicketLocker.class.getName() + " locker_name server_port"); return; } // System.out.println("Available servers:"); // for (String name : Naming.list("//localhost:" + args[1] + "/")) // System.out.println("\t" + name); TicketLocker locker = new TicketLocker(args[0], Integer.parseInt(args[1]), null); List<Ticket> tickets = new ArrayList<>(locker.getStub().getTickets(null)); Collections.sort(tickets); for (Ticket t : tickets) { System.out.println(t); } } }