package com.swingsane.business.discovery;
import java.io.IOException;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.InterfaceAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import javax.jmdns.JmDNS;
import javax.jmdns.ServiceInfo;
import javax.swing.SwingWorker;
import javax.swing.event.EventListenerList;
import org.apache.log4j.Logger;
import au.com.southsky.jfreesane.SaneDevice;
import au.com.southsky.jfreesane.SaneException;
import au.com.southsky.jfreesane.SanePasswordProvider;
import au.com.southsky.jfreesane.SaneSession;
import com.swingsane.business.notification.ConsoleNotificationImpl;
import com.swingsane.business.notification.INotification;
import com.swingsane.business.scanning.IScanService;
import com.swingsane.business.scanning.ScanServiceImpl;
import com.swingsane.i18n.Localizer;
import com.swingsane.preferences.model.SaneServiceIdentity;
import com.swingsane.preferences.model.Scanner;
/**
* @author Roland Quast (roland@formreturn.com)
*
*/
public class DiscoveryJob {
/**
* Event Listener List for Discovery Events.
*/
private EventListenerList listenerList = new EventListenerList();
/**
* Discovery Event.
*/
private DiscoveryEvent discoveryEvent;
/**
* Log4J logger.
*/
private static final Logger LOG = Logger.getLogger(DiscoveryJob.class);
/**
* SANE service name.
*/
public static final String SANE_SERVICE_NAME = "_sane-port._tcp.local.";
/**
* Status notification with console output as default implementation.
*/
private INotification notification = new ConsoleNotificationImpl();
private SwingWorker<ArrayList<Scanner>, Void> worker;
private IScanService scanService;
/**
* Constructor for SaneDiscovery.
*
* @param scanService
*/
public DiscoveryJob(IScanService scanService) {
this.scanService = scanService;
}
/**
* @param listener
* a discovery listener
*/
public final void addDiscoveryListener(final DiscoveryListener listener) {
listenerList.add(DiscoveryListener.class, listener);
}
public final void cancel() {
worker.cancel(true);
}
private void detectScanners(JmDNS jmdns, InetAddress address,
ArrayList<Scanner> discoveredScanners, ServiceInfo serviceInfo) throws IOException,
SaneException {
getNotificaiton().message(
String.format(Localizer.localize("QueryingServerMessage"), serviceInfo.getName() + " ("
+ address.getHostAddress() + ":" + serviceInfo.getPort())
+ ")");
if (worker.isCancelled()) {
return;
}
SaneSession session = null;
try {
session = SaneSession.withRemoteSane(address, serviceInfo.getPort());
session.setPasswordProvider(getPasswordProvider());
List<SaneDevice> devices = session.listDevices();
if ((devices != null) && (devices.size() > 0)) {
getNotificaiton().message(
String.format(devices.size() > 1 ? Localizer.localize("FoundDevicesMessage")
: Localizer.localize("FoundDeviceMessage"), devices.size()));
for (SaneDevice device : devices) {
if (worker.isCancelled()) {
return;
}
try {
Scanner scanner = scanService.create(device, serviceInfo, address.getHostAddress());
discoveredScanners.add(scanner);
} catch (Exception ex) {
getNotificaiton().message(
address.getHostAddress() + ":" + serviceInfo.getPort() + " - "
+ ex.getLocalizedMessage());
}
}
}
} catch (SocketException ex) {
getNotificaiton()
.message(
address.getHostAddress() + ":" + serviceInfo.getPort() + " - "
+ ex.getLocalizedMessage());
} finally {
if (session != null) {
session.close();
}
try {
jmdns.unregisterAllServices();
} catch (Exception e) {
getNotificaiton().message(e.getLocalizedMessage());
LOG.error(e, e);
}
try {
jmdns.close();
} catch (IOException e) {
getNotificaiton().message(e.getLocalizedMessage());
LOG.error(e, e);
}
}
}
/**
* Perform discovery of SANE scanners.
*
* @param scanService
* an instance of {@link ScanServiceImpl}
*/
public final synchronized void discover() {
worker = new SwingWorker<ArrayList<Scanner>, Void>() {
@Override
protected ArrayList<Scanner> doInBackground() throws Exception {
getNotificaiton().message(Localizer.localize("DiscoverSanedServersMessage"));
JmDNS jmdns = JmDNS.create(getLocalAddress());
ServiceInfo[] serviceInfoArr = jmdns.list(getSaneServiceIdentity().getServiceName());
ArrayList<Scanner> discoveredScanners = new ArrayList<Scanner>();
for (ServiceInfo serviceInfo : serviceInfoArr) {
if (isCancelled()) {
return discoveredScanners;
}
// try IPV4 addresses before IPV6 ones.
for (Inet4Address address : serviceInfo.getInet4Addresses()) {
detectScanners(jmdns, address, discoveredScanners, serviceInfo);
}
if (discoveredScanners.size() <= 0) {
for (Inet6Address address : serviceInfo.getInet6Addresses()) {
detectScanners(jmdns, address, discoveredScanners, serviceInfo);
}
}
}
return discoveredScanners;
}
@Override
protected void done() {
ArrayList<Scanner> discoveredScanners = null;
try {
discoveredScanners = get();
getNotificaiton().message(Localizer.localize("DiscoveryCompleteMessage"));
} catch (InterruptedException e) {
getNotificaiton().message(e.getLocalizedMessage());
LOG.error(e, e);
} catch (ExecutionException e) {
getNotificaiton().message(e.getLocalizedMessage());
LOG.error(e, e);
} catch (CancellationException e) {
getNotificaiton().message(Localizer.localize("DiscoveryCancelledMessage"));
} finally {
fireDiscoveryEvent(discoveredScanners);
}
}
};
worker.execute();
}
public final synchronized void discover(final InetAddress address, final int portNumber,
final String description) throws IOException, SaneException {
worker = new SwingWorker<ArrayList<Scanner>, Void>() {
@Override
protected ArrayList<Scanner> doInBackground() throws Exception {
getNotificaiton().message(
String.format(Localizer.localize("QueryingServerMessage"), address.getHostName() + " ("
+ address.getHostAddress() + ":" + portNumber)
+ ")");
ArrayList<Scanner> discoveredScanners = new ArrayList<Scanner>();
SaneSession session = null;
try {
session = SaneSession.withRemoteSane(address, portNumber);
session.setPasswordProvider(getPasswordProvider());
List<SaneDevice> devices = session.listDevices();
if ((devices != null) && (devices.size() > 0)) {
getNotificaiton().message(
String.format(devices.size() > 1 ? Localizer.localize("FoundDevicesMessage")
: Localizer.localize("FoundDeviceMessage"), devices.size()));
for (SaneDevice device : devices) {
try {
Scanner scanner = scanService.create(device, address.getHostAddress(), portNumber,
description);
discoveredScanners.add(scanner);
} catch (Exception ex) {
getNotificaiton().message(
address.getHostAddress() + ":" + portNumber + " - " + ex.getLocalizedMessage());
}
}
}
} catch (SocketException ex) {
getNotificaiton().message(
address.getHostAddress() + ":" + portNumber + " - " + ex.getLocalizedMessage());
} finally {
if (session != null) {
session.close();
}
}
return discoveredScanners;
}
@Override
protected void done() {
ArrayList<Scanner> discoveredScanners = null;
try {
discoveredScanners = get();
getNotificaiton().message(Localizer.localize("DiscoveryCompleteMessage"));
} catch (InterruptedException e) {
getNotificaiton().message(e.getLocalizedMessage());
LOG.error(e, e);
} catch (ExecutionException e) {
getNotificaiton().message(e.getLocalizedMessage());
LOG.error(e, e);
} catch (CancellationException e) {
getNotificaiton().message(Localizer.localize("DiscoveryCancelledMessage"));
} finally {
fireDiscoveryEvent(discoveredScanners);
}
}
};
worker.execute();
}
/**
* @param discoveredScanners
* an {@link ArrayList} of discovered {@link Scanner}(s)
*/
private void fireDiscoveryEvent(final ArrayList<Scanner> discoveredScanners) {
Object[] listeners = listenerList.getListenerList();
for (int i = listeners.length - 2; i >= 0; i -= 2) {
if (listeners[i] == DiscoveryListener.class) {
if (discoveryEvent == null) {
discoveryEvent = new DiscoveryEvent(this);
discoveryEvent.setDiscoveredScanners(discoveredScanners);
}
((DiscoveryListener) listeners[i + 1]).discoveryEventOccurred(discoveryEvent);
}
}
}
public final InetAddress getLocalAddress() throws SocketException {
for (final Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces(); interfaces
.hasMoreElements();) {
final NetworkInterface networkInterface = interfaces.nextElement();
if (networkInterface.isLoopback()) {
continue;
}
for (final InterfaceAddress interfaceAddr : networkInterface.getInterfaceAddresses()) {
final InetAddress inetAddr = interfaceAddr.getAddress();
if (!(inetAddr instanceof Inet4Address)) {
continue;
}
return inetAddr;
}
}
return null;
}
/**
* Returns an instance of INotification if set.
*
* @return an instance of INotification if set.
*/
public final INotification getNotificaiton() {
return notification;
}
private SanePasswordProvider getPasswordProvider() {
return scanService.getPasswordProvider();
}
private SaneServiceIdentity getSaneServiceIdentity() {
return scanService.getSaneServiceIdentity();
}
public final boolean isActive() {
return !(worker.isDone());
}
/**
* @param listener
* a discovery listener
*/
public final void removeDiscoveryListener(final DiscoveryListener listener) {
listenerList.remove(DiscoveryListener.class, listener);
}
/**
* Sets an instance of INotification.
*
* @param notificationImpl
* an instance of INotification
*/
public final void setNotificaiton(final INotification notificationImpl) {
notification = notificationImpl;
}
}