package betsy.bpel.virtual.host.virtualbox; import java.util.List; import java.util.Set; import betsy.bpel.virtual.common.Constants; import betsy.bpel.virtual.host.VirtualBoxException; import betsy.bpel.virtual.host.exceptions.vm.PortRedirectException; import betsy.bpel.virtual.host.virtualbox.utils.port.PortUsageException; import betsy.bpel.virtual.host.virtualbox.utils.port.PortVerifier; import betsy.common.timeouts.timeout.TimeoutRepository; import org.apache.log4j.Logger; import org.virtualbox_4_2.IMachine; import org.virtualbox_4_2.INATEngine; import org.virtualbox_4_2.INetworkAdapter; import org.virtualbox_4_2.NATProtocol; public class PortForwardingConfigurator { private static final Logger log = Logger.getLogger(PortForwardingConfigurator.class); private final IMachine machine; public PortForwardingConfigurator(IMachine machine) { this.machine = machine; } /** * Setup port forwarding for all the given forwardingPorts. The port on the * host is always equal to the port on the guest system. All existing * redirections are deleted before applying the new redirections.<br> * <br> * This method is using the JAXWS methods which do have a severe issue until * version 4.2.11 if running VirtualBox on OS X. * (https://www.virtualbox.org/ticket/11635) * * @param forwardingPorts all ports to create a forwarding for. * @throws betsy.bpel.virtual.host.exceptions.vm.PortRedirectException thrown if redirection could not be created */ public void applyPortForwarding(final Set<Integer> forwardingPorts) throws VirtualBoxException { if (!isAlreadyRedirected(forwardingPorts)) { log.debug("Applying new port redirects..."); try { PortVerifier.failForUsedPorts(forwardingPorts); } catch (PortUsageException e) { throw new VirtualBoxException("ports could not be forwarded", e); } // remove old redirections first clearPortForwarding(); // add bVMS port if not yet contained forwardingPorts.add(Constants.SERVER_PORT); INATEngine natEngine = getNATEngine(); for (Integer port : forwardingPorts) { // assuming every service uses TCP, which is true for HTTP natEngine.addRedirect("", NATProtocol.TCP, "", port, "", port); } long timeout = TimeoutRepository.getTimeout("PortForwardingConfigurator.applyPortForwarding").getTimeoutInMs(); long start = -System.currentTimeMillis(); while (natEngine.getRedirects().size() != forwardingPorts.size()) { if (System.currentTimeMillis() + start > timeout) { throw new PortRedirectException("Could not set redirected " + "ports within " + TimeoutRepository.getTimeout("PortForwardingConfigurator.applyPortForwarding").getTimeoutInMs() + "s"); } try { Thread.sleep(TimeoutRepository.getTimeout("PortForwardingConfigurator.applyPortForwarding").getTimeToRepetitionInMs()); } catch (InterruptedException e) { // ignore } } } else { log.trace("All ports are already redirected, skip."); } } private void clearPortForwarding() throws PortRedirectException { log.debug("Deleting all existing port redirections"); INATEngine natEngine = getNATEngine(); for (String redirect : natEngine.getRedirects()) { String[] rds = redirect.split(","); String redirectName = rds[0]; natEngine.removeRedirect(redirectName); } long start = -System.currentTimeMillis(); while (natEngine.getRedirects().size() != 0) { if (System.currentTimeMillis() + start > TimeoutRepository.getTimeout("PortForwardingConfigurator.clearPortForwarding").getTimeoutInMs()) { throw new PortRedirectException("Could not delete all " + "redirected ports within " + TimeoutRepository.getTimeout("PortForwardingConfigurator.clearPortForwarding").getTimeoutInMs() + "s"); } try { Thread.sleep(TimeoutRepository.getTimeout("PortForwardingConfigurator.clearPortForwarding").getTimeToRepetitionInMs()); } catch (InterruptedException e) { // ignore } } } private boolean isAlreadyRedirected(final Set<Integer> forwardingPorts) { // get existing redirects INATEngine natEngine = getNATEngine(); List<String> redirects = natEngine.getRedirects(); int matchingForwards = 0; for (String redirect : redirects) { // resolve host and guest port String[] rds = redirect.split(","); int hostPort = Integer.parseInt(rds[3]); int guestPort = Integer.parseInt(rds[5]); // verify both are equal, ignoring any other manually created // redirection if (hostPort == guestPort && forwardingPorts.contains(hostPort)) { // is ok, increase count matchingForwards++; } } return matchingForwards == forwardingPorts.size(); } private INATEngine getNATEngine() { long idForFirstAdapater = 0l; // first adapter is always the NAT adapter INetworkAdapter networkAdapter = machine.getNetworkAdapter(idForFirstAdapater); return networkAdapter.getNATEngine(); } }