/* This file is part of JFLICKS. JFLICKS is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. JFLICKS is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with JFLICKS. If not, see <http://www.gnu.org/licenses/>. */ package org.jflicks.discovery; import java.io.IOException; import java.net.DatagramPacket; import java.net.InetAddress; import java.net.MulticastSocket; import java.net.SocketTimeoutException; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.StringTokenizer; import java.util.Timer; import java.util.TimerTask; import org.jflicks.util.RandomGUID; /** * A client side job to discover server side services. * * @author Doug Barnum * @version 1.0 */ public class ServiceBrowserJob extends BaseDiscoveryJob implements Discoverable { private ArrayList<DiscoverListener> discoverList = new ArrayList<DiscoverListener>(); private Timer myTimer; private String id; /** * Constructor with our one required argument. * * @param name A given service name. */ public ServiceBrowserJob(String name) { super(name); setId(RandomGUID.createGUID()); } private String getId() { return (id); } private void setId(String s) { id = s; } /** * {@inheritDoc} */ public void addDiscoverListener(DiscoverListener l) { discoverList.add(l); } /** * {@inheritDoc} */ public void removeDiscoverListener(DiscoverListener l) { discoverList.remove(l); } /** * Start the timer that repeats so new services can be found. */ public void startLookup() { if (myTimer == null) { myTimer = new Timer("QueryTimer"); myTimer.scheduleAtFixedRate(new QueryTimerTask(), 0L, ServiceConstants.BROWSER_QUERY_INTERVAL); } } /** * Start a single lookup. */ public void startSingleLookup() { if (myTimer == null) { myTimer = new Timer("QueryTimer"); myTimer.schedule(new QueryTimerTask(), 0L); myTimer = null; } } /** * Stop the timer that repeats so new services can be found. */ public void stopLookup() { if (myTimer != null) { myTimer.cancel(); myTimer = null; } } private boolean isReplyPacket() { boolean result = false; DatagramPacket packet = getReceivedDatagramPacket(); if (packet != null) { String s = new String(packet.getData()); int pos = s.indexOf((char) 0); if (pos > -1) { s = s.substring(0, pos); } /* REQUIRED TOKEN TO START */ result = s.startsWith("SERVICE REPLY " + getEncodedServiceName()); } return (result); } private ServiceDescription getServiceDescription() { ServiceDescription result = null; DatagramPacket packet = getReceivedDatagramPacket(); if (packet != null) { String s = new String(packet.getData()); int pos = s.indexOf((char) 0); if (pos > -1) { s = s.substring(0, pos); } StringTokenizer tokens = new StringTokenizer(s.substring(15 + getEncodedServiceName().length())); if (tokens.countTokens() == 3) { result = ServiceDescription.parse(tokens.nextToken(), tokens.nextToken(), tokens.nextToken()); } } return (result); } private DatagramPacket getQueryPacket() { DatagramPacket result = null; StringBuilder buf = new StringBuilder(); buf.append("SERVICE QUERY " + getEncodedServiceName()); buf.append(" " + getId()); byte[] bytes = buf.toString().getBytes(); result = new DatagramPacket(bytes, bytes.length); result.setAddress(getMulticastInetAddress()); result.setPort(getMulticastPort()); return (result); } /** * {@inheritDoc} */ public void start() { setTerminate(false); try { setMulticastInetAddress(InetAddress.getByName( ServiceConstants.MULTICAST_INET_ADDRESS)); setMulticastPort(ServiceConstants.MULTICAST_PORT); MulticastSocket socket = new MulticastSocket(getMulticastPort()); socket.joinGroup(getMulticastInetAddress()); socket.setSoTimeout(ServiceConstants.BROWSER_SOCKET_TIMEOUT); setMulticastSocket(socket); startLookup(); } catch (UnknownHostException uhe) { System.err.println("Unexpected exception: " + uhe); uhe.printStackTrace(); setTerminate(true); } catch (IOException ex) { System.err.println("Unexpected exception: " + ex); ex.printStackTrace(); setTerminate(true); } } /** * {@inheritDoc} */ public void run() { while (!isTerminate()) { byte[] buf = new byte[ServiceConstants.DATAGRAM_LENGTH]; DatagramPacket packet = new DatagramPacket(buf, buf.length); setReceivedDatagramPacket(packet); try { MulticastSocket socket = getMulticastSocket(); if (socket != null) { socket.receive(packet); if (isReplyPacket()) { ServiceDescription sd = getServiceDescription(); if (sd != null) { fireDiscoverEvent(sd); } } } } catch (SocketTimeoutException ex) { } catch (IOException ex) { System.err.println("Unexpected exception: " + ex); } sendQueuedPacket(); } } /** * {@inheritDoc} */ public void stop() { setTerminate(true); stopLookup(); } private void fireDiscoverEvent(ServiceDescription sd) { processDiscoverEvent(new DiscoverEvent(this, sd)); } private synchronized void processDiscoverEvent(DiscoverEvent event) { for (int i = 0; i < discoverList.size(); i++) { DiscoverListener l = discoverList.get(i); l.serviceReply(event); } } class QueryTimerTask extends TimerTask { public void run() { DatagramPacket packet = getQueryPacket(); if (packet != null) { setQueuedDatagramPacket(packet); } } } }