/*
* ProActive Parallel Suite(TM):
* The Open Source library for parallel and distributed
* Workflows & Scheduling, Orchestration, Cloud Automation
* and Big Data Analysis on Enterprise Grids & Clouds.
*
* Copyright (c) 2007 - 2017 ActiveEon
* Contact: contact@activeeon.com
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Affero General Public License
* as published by the Free Software Foundation: version 3 of
* the License.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* If needed, contact us to obtain a release under GPL Version 2 or 3
* or a different license than the AGPL.
*/
package org.ow2.proactive.authentication;
import java.io.IOException;
import java.io.Serializable;
import java.net.NetworkInterface;
import java.net.Socket;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.UnknownHostException;
import org.apache.log4j.Logger;
import org.objectweb.proactive.api.PAActiveObject;
import org.objectweb.proactive.core.config.CentralPAPropertyRepository;
import org.objectweb.proactive.core.exceptions.NotBoundException;
import org.objectweb.proactive.core.remoteobject.AbstractRemoteObjectFactory;
import org.objectweb.proactive.core.remoteobject.RemoteObjectFactory;
import org.objectweb.proactive.core.util.ProActiveInet;
/**
* Class implements connection to the service with authentication. It attempts lookup authentication
* active object and checks that system is up and running (in another words authentication object is activated).
* Provides an ability to connect in blocking and non blocking manner.
*
* @param <T> the real type of authentication
* @author The ProActive Team
* @since ProActive Scheduling 0.9.1
*/
public abstract class Connection<T extends Authentication> implements Loggable, Serializable {
/** Error msg */
private static final String ERROR_CANNOT_LOOKUP_AUTH = "Cannot lookup authentication active object.";
/** Error msg */
private static final String ERROR_NOT_ACTIVATED = "System is initializing. Try to connect later.";
/** Error msg */
private static final String ERROR_CONNECTION_INTERRUPTED = "Connection is interrupted.";
/** Time to wait inside connecting loop retries */
private static final int PERIOD_IN_MS = 100;
/** loggers */
private Logger logger = getLogger();
/** The real class type of the authentication */
private Class<? extends Authentication> clazz = null;
/**
* Create a new instance of Connection
*
* @param clazz the real type of the authentication
*/
public Connection(Class<? extends Authentication> clazz) {
this.clazz = clazz;
}
/**
* Lookup of authentication active object
*
* @param url the URL of the service to join.
* @throws Exception if something wrong append
*/
@SuppressWarnings("unchecked")
private T lookupAuthentication(String url) throws Exception {
logger.trace("Looking up authentication interface '" + url + "'");
return (T) (PAActiveObject.lookupActive(clazz.getName(), url));
}
/**
* Connect to the service with given url. If it is not available or initializing throws an exception.
*
* @param url the URL of the service to join.
* @return the service authentication interface at the specified URL.
* @throws Exception if something wrong append
*/
public T connect(String url) throws Exception {
T authentication = lookupAuthentication(url);
if (authentication.isActivated()) {
return authentication;
} else {
throw new Exception(ERROR_NOT_ACTIVATED);
}
}
/**
* Connects to the service using given URL. The current thread will be block until
* connection established or an error occurs.
*
* @param url the url on which to connect
* @return the authentication if connection succeed
* @throws Exception if something wrong append
*/
public T waitAndConnect(String url) throws Exception {
return waitAndConnect(url, 0);
}
/**
* Connects to the service with a specified timeout value. A timeout of
* zero is interpreted as an infinite timeout. The connection will then
* block until established or an error occurs.
*
* @param url the url on which to connect
* @param timeout the maximum amount of time to wait if it cannot connect
* @return the authentication if connection succeed
* @throws Exception if something wrong append
*/
public T waitAndConnect(String url, long timeout) throws Exception {
T authentication = null;
long leftTime = timeout == 0 ? Long.MAX_VALUE : timeout;
try {
// obtaining authentication active object
while (leftTime > 0) {
long startTime = System.currentTimeMillis();
try {
authentication = lookupAuthentication(url);
if (authentication == null) {
// strange situation : should not be here
// simulating an exception during lookup
throw new Exception(ERROR_CANNOT_LOOKUP_AUTH);
}
// success
break;
} catch (NotBoundException e) {
// expected NotBoundException is not printed in the log
leftTime = sleepOrThrow(startTime, leftTime, e);
} catch (IOException e) {
leftTime = sleepOrThrow(startTime, leftTime, e);
} catch (Exception e) {
// unexpected Exception
logger.error("", e);
leftTime = sleepOrThrow(startTime, leftTime, e);
}
}
// waiting until scheduling is initialized
while (leftTime > 0) {
long startTime = System.currentTimeMillis();
if (authentication.isActivated()) {
// success
break;
} else {
leftTime = sleepOrThrow(startTime, leftTime, new Exception(ERROR_NOT_ACTIVATED));
}
}
} catch (InterruptedException e) {
throw new Exception(ERROR_CONNECTION_INTERRUPTED);
}
// TODO two cycles has the same pattern => the code can be unified
return authentication;
}
/**
* Waits for the configured period or throw an exception if not time is left
* @param startTime original starting time
* @param leftTime time still available
* @param toThrow exception to throw if time is consumed
* @return new available time
* @throws Exception
*/
private long sleepOrThrow(final long startTime, long leftTime, final Exception toThrow) throws Exception {
Thread.sleep(Math.min(PERIOD_IN_MS, leftTime));
leftTime -= (System.currentTimeMillis() - startTime);
if (leftTime <= 0) {
logger.error(toThrow);
throw toThrow;
}
return leftTime;
}
/**
* Normalize the URL <code>url</code>.<br>
*
* @param url, the URL to normalize.
* @return the default base URI if the given url is null. @see {@link RemoteObjectFactory}<br>
* the given URL with // prepended if it does not contain any scheme identifier
* the given URL with / appended if URL does not end with / and does not
* contain any path element,<br>
* or the given URL, unchanged.
*/
public static URI normalize(String url) {
if (url == null || url.trim().equals("")) {
try {
RemoteObjectFactory rof = AbstractRemoteObjectFactory.getDefaultRemoteObjectFactory();
// NO_LOOP_BACK property is handled by getBaseURI()
url = rof.getBaseURI().toString();
} catch (Exception e) {
url = "//" + ProActiveInet.getInstance().getHostname() + "/";
}
} else {
url = url.trim();
}
if (!url.contains("//")) {
url = "//" + url;
}
return URI.create(url);
}
/**
* Selects a network interface for proactive runtime based on provided url.
*
* @throws IOException if an I/O error occurs when creating the socket.
* @throws UnknownHostException if the IP address of the host could not be determined.
* @throws URISyntaxException if the given string violates RFC 2396,
*/
public static String getNetworkInterfaceFor(String url) throws IOException, URISyntaxException {
if (url != null && CentralPAPropertyRepository.PA_NET_INTERFACE.getValue() == null &&
CentralPAPropertyRepository.PA_NET_NETMASK.getValue() == null) {
URI parsedUrl = new URI(url);
int port = parsedUrl.getPort();
if (port == -1) {
// use default port for rmi
port = 1099;
}
Socket socket = null;
try {
socket = new Socket(parsedUrl.getHost(), port);
NetworkInterface ni = NetworkInterface.getByInetAddress(socket.getLocalAddress());
return ni.getName();
} finally {
if (socket != null) {
socket.close();
}
}
}
throw new IllegalStateException("The network interface is already selected");
}
}