/* ==================================================================
* WSAddressingFromHandler.java - 13/06/2015 12:57:24 pm
*
* Copyright 2007-2015 SolarNetwork.net Dev Team
*
* This program 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 2 of
* the License, or (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
* ==================================================================
*/
package ocpp.v15.support;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.Set;
import javax.xml.namespace.QName;
import javax.xml.soap.SOAPElement;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPHeader;
import javax.xml.ws.handler.MessageContext;
import javax.xml.ws.handler.soap.SOAPHandler;
import javax.xml.ws.handler.soap.SOAPMessageContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* {@link SOAPHandler} for inserting {@code wsa:From} headers into outbound
* messages.
*
* @author matt
* @version 1.0
*/
public class WSAddressingFromHandler implements SOAPHandler<SOAPMessageContext> {
public static final String WSA_NS = "http://www.w3.org/2005/08/addressing";
public static final QName WSA_FROM = new QName(WSA_NS, "From");
public static final QName WSA_ADDRESS = new QName(WSA_NS, "Address");
private String dynamicFromPath = "/ocpp/v15";
private String networkInterfaceName;
private String fromURL;
private boolean preferIPv4Address = Boolean.valueOf(System.getProperty("java.net.preferIPv4Stack",
"true"));
private final Logger log = LoggerFactory.getLogger(getClass());
// <wsa:From><wsa:Address>fromURL</wsa:Address></wsa:From>
@Override
public boolean handleMessage(SOAPMessageContext context) {
Boolean outboundProperty = (Boolean) context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
if ( outboundProperty != null && outboundProperty.booleanValue() ) {
final String fromAddressValue = resolveFromURL();
if ( fromAddressValue != null ) {
try {
SOAPHeader header = context.getMessage().getSOAPHeader();
SOAPElement from = null;
SOAPElement fromAddr = null;
for ( @SuppressWarnings("unchecked")
Iterator<SOAPElement> itr = header.getChildElements(WSA_FROM); itr.hasNext(); ) {
from = itr.next();
break;
}
if ( from == null ) {
from = header.addHeaderElement(WSA_FROM);
}
for ( @SuppressWarnings("unchecked")
Iterator<SOAPElement> itr = from.getChildElements(WSA_ADDRESS); itr.hasNext(); ) {
fromAddr = itr.next();
break;
}
if ( fromAddr == null ) {
fromAddr = from.addChildElement(WSA_ADDRESS);
}
fromAddr.setTextContent(fromAddressValue);
} catch ( SOAPException e ) {
log.error("Exception generating WS-Addressing From header to {}", fromAddressValue,
e);
}
}
}
return true;
}
private String resolveFromURL() {
String fromPath = dynamicFromPath;
if ( fromPath != null ) {
InetAddress addr = getHostInetAddress();
if ( addr != null ) {
return "http://" + addr.getHostAddress() + fromPath;
}
return null;
}
return fromURL;
}
private InetAddress getHostInetAddress() {
String nicName = networkInterfaceName;
try {
Enumeration<NetworkInterface> nics = NetworkInterface.getNetworkInterfaces();
while ( nics.hasMoreElements() ) {
NetworkInterface nic = nics.nextElement();
if ( nicName != null && !nicName.equalsIgnoreCase(nic.getName()) ) {
continue;
}
Enumeration<InetAddress> addrs = nic.getInetAddresses();
while ( addrs.hasMoreElements() ) {
InetAddress inet = addrs.nextElement();
if ( preferIPv4Address && (inet instanceof Inet6Address) ) {
continue;
}
if ( !(inet.isAnyLocalAddress() || inet.isLinkLocalAddress()
|| inet.isLoopbackAddress() || inet.isMulticastAddress()) ) {
return inet;
}
}
}
} catch ( SocketException e ) {
// ignore
}
return null;
}
@Override
public boolean handleFault(SOAPMessageContext context) {
return true;
}
@Override
public void close(MessageContext context) {
}
@Override
public Set<QName> getHeaders() {
return Collections.singleton(new QName(WSA_NS, "From"));
}
public String getFromURL() {
return fromURL;
}
/**
* Set a specific URL to use for the {@code From} address. If
* {@code dynamicFromPath} is configured this property is ignored.
*
* @param url
* The specific URL to use.
*/
public void setFromURL(String url) {
this.fromURL = url;
}
public String getDynamicFromPath() {
return dynamicFromPath;
}
/**
* Set a URL path to use for the {@code From} address. The host is resolved
* dynamically at runtime based on the host's IP address. If configured the
* {@code fromURL} property is ignored.
*
* @param dynamicFromPath
* A URL path to use with a dynamically resolved host.
*/
public void setDynamicFromPath(String dynamicFromPath) {
this.dynamicFromPath = dynamicFromPath;
}
public String getNetworkInterfaceName() {
return networkInterfaceName;
}
/**
* If {@code dynamicFromPath} is configured, this property can be configured
* to restrict which network interface to look at for the resolved IP
* address. If not configured then all network interfaces will be inspected,
* and the first available non-local IP address will be used at runtime.
*
* @param networkInterfaceName
* A network interface name to limit resolving the host IP address
* to.
*/
public void setNetworkInterfaceName(String networkInterfaceName) {
this.networkInterfaceName = networkInterfaceName;
}
public boolean isPreferIPv4Address() {
return preferIPv4Address;
}
/**
* Set the preference to resolve IPv4 address values when
* {@code dynamicFromPath} is configured.
*
* @param preferIPv4Address
* Boolean
*/
public void setPreferIPv4Address(boolean preferIPv4Address) {
this.preferIPv4Address = preferIPv4Address;
}
}