/******************************************************************************* * Copyright (c) 2011 GigaSpaces Technologies Ltd. All rights reserved * * 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 org.cloudifysource.dsl.utils; import java.io.IOException; import java.net.HttpURLConnection; import java.net.InetSocketAddress; import java.net.MalformedURLException; import java.net.ProtocolException; import java.net.Socket; import java.net.URL; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.List; import org.cloudifysource.dsl.internal.CloudifyConstants; import org.cloudifysource.dsl.internal.tools.download.ResourceDownloadFacade; import org.cloudifysource.dsl.internal.tools.download.ResourceDownloadFacadeImpl; import org.cloudifysource.dsl.internal.tools.download.ResourceDownloader; import org.hyperic.sigar.Sigar; import org.hyperic.sigar.SigarException; import org.hyperic.sigar.ptql.ProcessFinder; // import com.gigaspaces.internal.sigar.SigarHolder; /****************** * ServiceUtils exposes a range of methods that recipes can use in closures, including TCP port checks, HTTP requests * and OS tests. The ServiceUtils is always imported into a recipe, so there is no need to add an import statement to a * service recipe for it. * * @author barakme, adaml * @since 1.0 * */ public final class ServiceUtils { private static final int DEFAULT_HTTP_READ_TIMEOUT = 1000; private static final int DEFAULT_HTTP_CONNECTION_TIMEOUT = 1000; private static Sigar sigar; private ServiceUtils() { // private constructor to prevent initialization. } private static java.util.logging.Logger logger = java.util.logging.Logger.getLogger(ServiceUtils.class.getName()); /*********** * Tests if a port of the localhost interface is in use. * * @param port * the port number. * @return true if the port is not in use, false otherwise. */ public static boolean isPortFree(final int port) { return !isPortOccupied(port); } /** * Checks that the specified ports are free. * * @param portList * - list of ports to check. * @return - true if all ports are free */ public static boolean arePortsFree(final List<Integer> portList) { int portCounter = 0; for (final int port : portList) { if (!isPortOccupied(port)) { logger.fine("port: " + port + " is open."); portCounter++; } if (portCounter == portList.size()) { // All ports are free. return true; } } return false; } /** * Checks whether the specified port is in use on the the localhost ("127.0.0.1") interface. * * @param port * the port to check. * @return true if in use, false otherwise. */ public static boolean isPortOccupied(final int port) { return isPortOccupied("127.0.0.1", port); } /** * Checks whether a specified port is occupied. * * @param host * - the interface/host to test. * @param port * - port to check. * @return - true if port is occupied. */ public static boolean isPortOccupied(final String host, final int port) { final Socket sock = new Socket(); logger.fine("Checking port " + port); try { sock.connect(new InetSocketAddress(host, port)); logger.fine("Connected to port " + port); sock.close(); return true; } catch (final IOException e1) { logger.fine("Port " + port + " is free."); return false; } finally { try { sock.close(); } catch (final IOException e) { // ignore } } } /** * arePortsOccupied will repeatedly test the connection to the ports defined in the groovy configuration file to see * whether the ports are open. Having all the tested ports opened means that the process has completed loading * successfully and is up and running. * * @param portList * list of port to check. * @return true if all ports are in use, false otherwise. * * */ public static boolean arePortsOccupied(final List<Integer> portList) { int portCounter = 0; for (final int port : portList) { if (isPortOccupied(port)) { portCounter++; } if (portCounter == portList.size()) { // connection succeeded - the port is not free return true; } } return false; } /********* * Executes an HTTP GET Request to the given URL, using a one second connect timeout and read timeout. * * @param url * the HTTP URL. * @return the HTTP return code. If an error occured while sending the request, for instance if a connection could * not be made, returns 500 */ public static boolean isHttpURLAvailable(final String url) { return getHttpReturnCode(url) == HttpURLConnection.HTTP_OK; } /********* * Executes an HTTP GET Request to the given URL, using a one second connect timeout and read timeout. * * @param url * the HTTP URL. * @return the HTTP return code. If an error occured while sending the request, for instance if a connection could * not be made, returns 500 */ public static int getHttpReturnCode(final String url) { return getHttpReturnCode(url, DEFAULT_HTTP_CONNECTION_TIMEOUT, DEFAULT_HTTP_READ_TIMEOUT); } /********* * Executes an HTTP GET Request to the given URL. * * @param url * the HTTP URL. * @param connectTimeout * the connection timeout. * @param readTimeout * the read timeout. * @return the HTTP return code. If an error occured while sending the request, for instance if a connection could * not be made, returns 500 */ public static int getHttpReturnCode(final String url, final int connectTimeout, final int readTimeout) { HttpURLConnection connection = null; try { try { connection = (HttpURLConnection) new URL(url).openConnection(); } catch (final MalformedURLException e) { throw new IllegalArgumentException("Failed to parse url: " + url, e); } try { connection.setRequestMethod("GET"); } catch (final ProtocolException e) { throw new IllegalArgumentException(e); } connection.setConnectTimeout(connectTimeout); connection.setReadTimeout(readTimeout); final int responseCode = connection.getResponseCode(); return responseCode; } catch (final IOException ioe) { return HttpURLConnection.HTTP_INTERNAL_ERROR; } finally { if (connection != null) { connection.disconnect(); } } } /** * returns an implementation of a {@link org.cloudifysource.dsl.internal.tools.download.ResourceDownloadFacade} this * utility exposed different get implementation and verification options. * * @return a new ResourceDownloaderFacade implementation. */ public static ResourceDownloadFacade getDownloadUtil() { return new ResourceDownloadFacadeImpl(new ResourceDownloader()); } /************ * Returns the PU name for a service. * * Important: when changing this method you must also change the getApplicationServiceName method that extracts the * service name from the absolute processing unit's name. * * @param applicationName * the service's application name. * @param serviceName * the service name. * @return the PU name. */ // . public static String getAbsolutePUName(final String applicationName, final String serviceName) { return applicationName + "." + serviceName; } /********** * Full name details of a service. * * @author adaml, barakme * */ public static class FullServiceName { private final String serviceName; private final String applicationName; /************ * Constructor. * * @param applicationName * . * @param serviceName * . */ public FullServiceName(final String applicationName, final String serviceName) { super(); this.serviceName = serviceName; this.applicationName = applicationName; } public String getServiceName() { return serviceName; } public String getApplicationName() { return applicationName; } @Override public String toString() { return "FullServiceName [applicationName=" + applicationName + ", serviceName=" + serviceName + "]"; } } /*************** * Return the service name of a PU. * * @param absolutePuName * the PU name. * @param applicationName * the application name. * @return the service name. */ public static String getApplicationServiceName(final String absolutePuName, final String applicationName) { // Management services do no have the application prefix in their processing unit's name. if (applicationName.equalsIgnoreCase(CloudifyConstants.MANAGEMENT_APPLICATION_NAME)) { return absolutePuName; } final boolean legitPuNamePrefix = absolutePuName.startsWith(applicationName + "."); if (legitPuNamePrefix) { return absolutePuName.substring(applicationName.length() + 1); } logger.severe("Application name " + applicationName + " is not contained in the absolute processing unit's name " + absolutePuName + ". returning absolute pu name"); return absolutePuName; } /*********** * Returns the application name and service name of a PU. * * @param puName * the pu name. * @return the application and service names. */ public static FullServiceName getFullServiceName(final String puName) { final int index = puName.lastIndexOf('.'); if (index < 0) { throw new IllegalArgumentException("Could not parse PU name: " + puName + " to read service and application names."); } final String applicationName = puName.substring(0, index); final String serviceName = puName.substring(index + 1); return new FullServiceName(applicationName, serviceName); } public static String getFullServiceInstanceName(final String applicationName, final String serviceName, final int serviceId) { return getApplicationServiceName(applicationName, serviceName) + "_" + serviceId; } /*********** * Returns true if the current operating system is some variant of Windows. * * @return true if running on Windows. */ public static boolean isWindows() { final String os = System.getProperty("os.name").toLowerCase(); return os.contains("win"); } /*********** * Returns true if the current operating system is NOT some variant of Windows. * * * @return true if not running on Windows. */ public static boolean isLinuxOrUnix() { return !isWindows(); } /****** * Returns the primary local-host address. If the local-host interface could not be resolved, "localhost" is * returned. * * @return the primary local-host address. */ public static String getPrimaryInetAddress() { try { return java.net.InetAddress.getLocalHost().toString(); } catch (UnknownHostException e) { logger.severe(e.getMessage()); return "localhost"; } } /*********** * * Tests if a port of some host is in use. * * @param host * the host to check. * * @param port * the port number. * * @return true if the port is not in use, false otherwise. */ public static boolean isPortFree(final String host, final int port) { return !isPortOccupied(host, port); } /********** * Utility method related to operating system processes.. * * @author barakme * @since 2.1.1 * */ public static final class ProcessUtils { private ProcessUtils() { // } /************* * Executes a SIGAR PTQL query, returning the PIDs of the processes that match the query. For more info on * SIGAR's PTQL - Process Table Query Language, see: http://support.hyperic.com/display/SIGAR/PTQL * * @param query * the PTQL query. * @return the pids. * @throws SigarException * in case of an error. */ public static List<Long> getPidsWithQuery(final String query) throws SigarException { final Sigar sigar = getSigar(); final ProcessFinder finder = new ProcessFinder(sigar); final long[] results = finder.find(query); final List<Long> list = new ArrayList<Long>(results.length); for (final long result : results) { list.add(result); } return list; } /********* * Returns the pids of Java processes where the name specified in in the process arguments. This will usually be * the java process' main class, though that may not always be the case. * * PTQL Query: "State.Name.eq=java,Args.*.eq=" + name * * @param name * the java main class or jar file name. * @return the pids that match the query, may be zero, one or more. * @throws SigarException * in case of an error. */ public static List<Long> getPidsWithMainClass(final String name) throws SigarException { return getPidsWithQuery("State.Name.eq=java,Args.*.eq=" + name); } /************* * Returns the pids of processes where the base name of the process executable is as specified. PTQL Query: * "State.Name.eq=" + name * * @param name * the process name. * @return the matching PIDs. * @throws SigarException * in case of an error. */ public static List<Long> getPidsWithName(final String name) throws SigarException { return getPidsWithQuery("State.Name.eq=" + name); } /************* * Returns the pids of processes where the full name of the process executable is as specified. PTQL Query: * "Exe.Name.eq=" + name. * * @param name * the process name. * @return the matching PIDs. * @throws SigarException * in case of an error. */ public static List<Long> getPidsWithFullName(final String name) throws SigarException { return getPidsWithQuery("Exe.Name.eq=" + name); } /************* * Returns PID of process that has the specified port open. * * @param port * the port number. * @return pid of the process. * @throws SigarException * in case of an error. */ // TODO - this does not work - SIGAR has not implemented getProPort on Win 7, Win 2008 and Solaris, no we can't // really use this. // public static List<Long> getPidWithPort(final int port) // throws SigarException { // final Sigar sigar = getSigar(); // final long pid = sigar.getProcPort(NetFlags.CONN_TCP, port); // if (pid > 0) { // return Arrays.asList(pid); // } else { // return Arrays.asList(); // } // // // NetConnection[] list = sigar.getNetConnectionList(NetFlags.CONN_SERVER | NetFlags.CONN_PROTOCOLS); // // for (NetConnection netConnection : list) { // // int localPort = (int) netConnection.getLocalPort(); // // int state = netConnection.getState(); // // // // if (state == NetFlags.TCP_LISTEN) { // // if (localPort == port) { // // netConnection.get // // return true; // // } // // } // // } // } /*********** * Retrieves an instance of SIGAR, which offers access to Operating System level information not typically * available in the JDK. * * Important note: Not all SIGAR functions are implemented on all operating systems and architecture platforms. * If you use SIGAR directly, make sure to test first on your target platform. * * For more information on SIGAR, please see: http://support.hyperic.com/display/SIGAR/Home * * @return the sigar instance. */ public static synchronized Sigar getSigar() { if (sigar == null) { sigar = new Sigar(); } return sigar; } } }