/**
* Copyright 2011-2012 Universite Joseph Fourier, LIG, ADELE team
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package fr.imag.adele.apam.distriman.discovery;
import java.io.IOException;
import java.net.InetAddress;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import javax.jmdns.JmDNS;
import javax.jmdns.NetworkTopologyDiscovery;
import javax.jmdns.ServiceEvent;
import javax.jmdns.ServiceInfo;
import org.apache.felix.ipojo.annotations.Component;
import org.apache.felix.ipojo.annotations.Instantiate;
import org.apache.felix.ipojo.annotations.Invalidate;
import org.apache.felix.ipojo.annotations.Property;
import org.apache.felix.ipojo.annotations.Provides;
import org.apache.felix.ipojo.annotations.Requires;
import org.apache.felix.ipojo.annotations.Validate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import fr.imag.adele.apam.distriman.provider.LocalMachine;
/**
* <p>
* The MachineDiscovery component allows for the discovery of other machines
* (Apam/Distriman) over the network, thanks to the mdns protocol.
* </p>
*
* A RemoteMachine instance is created for each machine discovered.
*
*
* User: barjo / jander Date: 04/12/12 Time: 14:48
*/
@Component(name = "Apam::Distriman::Discovery")
@Instantiate
@Provides
public class ApamDiscoveryImpl implements ApamDiscovery,
NetworkTopologyDiscovery.Factory.ClassDelegate {
private static Logger logger = LoggerFactory
.getLogger(ApamDiscoveryImpl.class);
@Property(name = "inet.host", value = "127.0.0.1", mandatory = true)
private String HOST;
private LocalMachine local;
/**
* JmDNS, Java Multicast DNS, use to announce and discovered Apam/Distriman
* machine over the network.
*/
private Map<JmDNS, String> jmDNSMachines;// ArrayList<JmDNS>();
/**
* Compute a default name for that machine, TODO compute a more relevant
* name.
*/
private String name = UUID.randomUUID().toString();
@Requires
private ApamMachineFactory machineFactory;
public ApamDiscoveryImpl() {
super();
NetworkTopologyDiscovery.Factory.setClassDelegate(this);
}
private boolean isItLocal(ServiceEvent serviceEvent) {
for (Map.Entry<JmDNS, String> entry : jmDNSMachines.entrySet()) {
JmDNS jmDNS = entry.getKey();
try {
if (jmDNS
.getInterface()
.getHostAddress()
.equals(serviceEvent.getDNS().getInterface()
.getHostAddress())
&& serviceEvent.getInfo().getPort() == this.local
.getPort()) {
return true;
}
} catch (IOException e) {
// consider as not local in case of problem (conservative
// approach
}
}
return false;
}
/**
* Factory that determines the euristics to choose/filter the network cards
* to be considered
*/
@Override
public NetworkTopologyDiscovery newNetworkTopologyDiscovery() {
return new NetworkTopology();
}
@Override
public void publishLocalMachine(LocalMachine local) throws IOException {
this.local = local;
for (Map.Entry<JmDNS, String> entry : jmDNSMachines.entrySet()) {
JmDNS jmDNS = entry.getKey();
String urlaux = entry.getValue();
String url = String.format("http://%s:%d", urlaux, local.getPort());
// Register a local machine
logger.info("publishing machine {} on the mdns bus", url);
jmDNS.registerService(ServiceInfo.create(local.getType(),
local.getName(), local.getPort(), url));
}
}
@Override
public void serviceAdded(ServiceEvent serviceEvent) {
// Ignore, only handle resolved
logger.info("service added {}", serviceEvent.getInfo()
.getNiceTextString());
}
/**
* @param serviceEvent
* The mdns event triggered by a remote machine that is no longer
* available.
*/
@Override
public void serviceRemoved(ServiceEvent serviceEvent) {
String id = String.format("%s.%s", serviceEvent.getName(),
serviceEvent.getType());
logger.info("service removing {} with id {}", serviceEvent.getInfo()
.getNiceTextString(), id);
if (serviceEvent.getName().equalsIgnoreCase(name)) {
return; // ignore my message
}
ServiceInfo info = serviceEvent.getInfo();
String url = info.getNiceTextString();
machineFactory.destroyRemoteMachine(url, id);
}
@Override
public void serviceResolved(ServiceEvent serviceEvent) {
String id = String.format("%s.%s", serviceEvent.getName(),
serviceEvent.getType());
logger.info("service resolved {} subtype {}", serviceEvent.getInfo()
.getNiceTextString(), id);
boolean isLocalhost = isItLocal(serviceEvent);
if (serviceEvent.getName().equalsIgnoreCase(name)) {
return; // ignore this machine message
}
ServiceInfo info = serviceEvent.getDNS().getServiceInfo(MDNS_TYPE,
serviceEvent.getName());
String url = info.getNiceTextString();
machineFactory.newRemoteMachine(url, id, isLocalhost);
}
@Validate
public void start() {
logger.info("Starting mdns...");
if (jmDNSMachines != null) {
throw new RuntimeException(
"Trying to start machine discovery twice.");
}
jmDNSMachines = new HashMap<JmDNS, String>();// new
// CopyOnWriteArrayList<JmDNS>();
logger.info("Iteratings interfaces..");
// Create the jmdns server
for (InetAddress address : NetworkTopologyDiscovery.Factory
.getInstance().getInetAddresses()) {
try {
JmDNS current = JmDNS.create(address);
current.registerServiceType(MDNS_TYPE);
jmDNSMachines.put(current, address.getHostAddress());
for (ServiceInfo sinfo : current.list(MDNS_TYPE)) {
if (sinfo.getName().equalsIgnoreCase(name)) {
continue; // ignore my services..
}
// Create and Add the machine
String url = sinfo.getNiceTextString();
logger.info("mDNS detected the url {} subtype {}", url,
sinfo.getTypeWithSubtype());
String id = String.format("%s.%s", sinfo.getName(),
sinfo.getType());
machineFactory.newRemoteMachine(url, id, false);
}
current.addServiceListener(MDNS_TYPE, this);
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
logger.info("/Iteratings interfaces..");
logger.info("mdns started.");
}
@Override
@Invalidate
public void stop() {
for (Map.Entry<JmDNS, String> entry : jmDNSMachines.entrySet()) {
JmDNS jmDNS = entry.getKey();
jmDNS.unregisterAllServices();
// unregister the listener
jmDNS.removeServiceListener(MDNS_TYPE, this);
try {
jmDNS.close();
} catch (IOException e) {
}
}
}
}