cloudstack-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From r...@apache.org
Subject [09/45] git commit: updated refs/heads/master to 44ba14d
Date Tue, 25 Aug 2015 18:49:33 GMT
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/83fd8f60/utils/src/main/java/com/cloud/utils/nio/NioClient.java
----------------------------------------------------------------------
diff --git a/utils/src/main/java/com/cloud/utils/nio/NioClient.java b/utils/src/main/java/com/cloud/utils/nio/NioClient.java
new file mode 100644
index 0000000..2f742f9
--- /dev/null
+++ b/utils/src/main/java/com/cloud/utils/nio/NioClient.java
@@ -0,0 +1,125 @@
+//
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you 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 com.cloud.utils.nio;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.nio.channels.SelectionKey;
+import java.nio.channels.Selector;
+import java.nio.channels.SocketChannel;
+import java.security.GeneralSecurityException;
+
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLEngine;
+
+import org.apache.log4j.Logger;
+
+import org.apache.cloudstack.utils.security.SSLUtils;
+
+public class NioClient extends NioConnection {
+    private static final Logger s_logger = Logger.getLogger(NioClient.class);
+
+    protected String _host;
+    protected String _bindAddress;
+    protected SocketChannel _clientConnection;
+
+    public NioClient(String name, String host, int port, int workers, HandlerFactory factory) {
+        super(name, port, workers, factory);
+        _host = host;
+    }
+
+    public void setBindAddress(String ipAddress) {
+        _bindAddress = ipAddress;
+    }
+
+    @Override
+    protected void init() throws IOException {
+        _selector = Selector.open();
+        Task task = null;
+
+        try {
+            _clientConnection = SocketChannel.open();
+            _clientConnection.configureBlocking(true);
+            s_logger.info("Connecting to " + _host + ":" + _port);
+
+            if (_bindAddress != null) {
+                s_logger.info("Binding outbound interface at " + _bindAddress);
+
+                InetSocketAddress bindAddr = new InetSocketAddress(_bindAddress, 0);
+                _clientConnection.socket().bind(bindAddr);
+            }
+
+            InetSocketAddress peerAddr = new InetSocketAddress(_host, _port);
+            _clientConnection.connect(peerAddr);
+
+            SSLEngine sslEngine = null;
+            // Begin SSL handshake in BLOCKING mode
+            _clientConnection.configureBlocking(true);
+
+            SSLContext sslContext = Link.initSSLContext(true);
+            sslEngine = sslContext.createSSLEngine(_host, _port);
+            sslEngine.setUseClientMode(true);
+            sslEngine.setEnabledProtocols(SSLUtils.getSupportedProtocols(sslEngine.getEnabledProtocols()));
+
+            Link.doHandshake(_clientConnection, sslEngine, true);
+            s_logger.info("SSL: Handshake done");
+            s_logger.info("Connected to " + _host + ":" + _port);
+
+            _clientConnection.configureBlocking(false);
+            Link link = new Link(peerAddr, this);
+            link.setSSLEngine(sslEngine);
+            SelectionKey key = _clientConnection.register(_selector, SelectionKey.OP_READ);
+            link.setKey(key);
+            key.attach(link);
+            // Notice we've already connected due to the handshake, so let's get the
+            // remaining task done
+            task = _factory.create(Task.Type.CONNECT, link, null);
+        } catch (GeneralSecurityException e) {
+            _selector.close();
+            throw new IOException("Failed to initialise security", e);
+        } catch (IOException e) {
+            _selector.close();
+            throw e;
+        }
+
+        _executor.execute(task);
+    }
+
+    @Override
+    protected void registerLink(InetSocketAddress saddr, Link link) {
+        // don't do anything.
+    }
+
+    @Override
+    protected void unregisterLink(InetSocketAddress saddr) {
+        // don't do anything.
+    }
+
+    @Override
+    public void cleanUp() throws IOException {
+        super.cleanUp();
+        if (_clientConnection != null) {
+            _clientConnection.close();
+        }
+        s_logger.info("NioClient connection closed");
+
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/83fd8f60/utils/src/main/java/com/cloud/utils/nio/NioConnection.java
----------------------------------------------------------------------
diff --git a/utils/src/main/java/com/cloud/utils/nio/NioConnection.java b/utils/src/main/java/com/cloud/utils/nio/NioConnection.java
new file mode 100644
index 0000000..4c66360
--- /dev/null
+++ b/utils/src/main/java/com/cloud/utils/nio/NioConnection.java
@@ -0,0 +1,476 @@
+//
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you 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 com.cloud.utils.nio;
+
+import static com.cloud.utils.AutoCloseableUtil.closeAutoCloseable;
+
+import java.io.IOException;
+import java.net.ConnectException;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.nio.channels.CancelledKeyException;
+import java.nio.channels.ClosedChannelException;
+import java.nio.channels.SelectionKey;
+import java.nio.channels.Selector;
+import java.nio.channels.ServerSocketChannel;
+import java.nio.channels.SocketChannel;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLEngine;
+
+import org.apache.log4j.Logger;
+
+import org.apache.cloudstack.utils.security.SSLUtils;
+
+import com.cloud.utils.concurrency.NamedThreadFactory;
+
+/**
+ * NioConnection abstracts the NIO socket operations.  The Java implementation
+ * provides that.
+ */
+public abstract class NioConnection implements Runnable {
+    private static final Logger s_logger = Logger.getLogger(NioConnection.class);;
+
+    protected Selector _selector;
+    protected Thread _thread;
+    protected boolean _isRunning;
+    protected boolean _isStartup;
+    protected int _port;
+    protected List<ChangeRequest> _todos;
+    protected HandlerFactory _factory;
+    protected String _name;
+    protected ExecutorService _executor;
+
+    public NioConnection(String name, int port, int workers, HandlerFactory factory) {
+        _name = name;
+        _isRunning = false;
+        _thread = null;
+        _selector = null;
+        _port = port;
+        _factory = factory;
+        _executor = new ThreadPoolExecutor(workers, 5 * workers, 1, TimeUnit.DAYS, new LinkedBlockingQueue<Runnable>(), new NamedThreadFactory(name + "-Handler"));
+    }
+
+    public void start() {
+        _todos = new ArrayList<ChangeRequest>();
+
+        _thread = new Thread(this, _name + "-Selector");
+        _isRunning = true;
+        _thread.start();
+        // Wait until we got init() done
+        synchronized (_thread) {
+            try {
+                _thread.wait();
+            } catch (InterruptedException e) {
+                s_logger.warn("Interrupted start thread ", e);
+            }
+        }
+    }
+
+    public void stop() {
+        _executor.shutdown();
+        _isRunning = false;
+        if (_thread != null) {
+            _thread.interrupt();
+        }
+    }
+
+    public boolean isRunning() {
+        return _thread.isAlive();
+    }
+
+    public boolean isStartup() {
+        return _isStartup;
+    }
+
+    @Override
+    public void run() {
+        synchronized (_thread) {
+            try {
+                init();
+            } catch (ConnectException e) {
+                s_logger.warn("Unable to connect to remote: is there a server running on port " + _port);
+                return;
+            } catch (IOException e) {
+                s_logger.error("Unable to initialize the threads.", e);
+                return;
+            } catch (Exception e) {
+                s_logger.error("Unable to initialize the threads due to unknown exception.", e);
+                return;
+            }
+            _isStartup = true;
+            _thread.notifyAll();
+        }
+
+        while (_isRunning) {
+            try {
+                _selector.select();
+
+                // Someone is ready for I/O, get the ready keys
+                Set<SelectionKey> readyKeys = _selector.selectedKeys();
+                Iterator<SelectionKey> i = readyKeys.iterator();
+
+                if (s_logger.isTraceEnabled()) {
+                    s_logger.trace("Keys Processing: " + readyKeys.size());
+                }
+                // Walk through the ready keys collection.
+                while (i.hasNext()) {
+                    SelectionKey sk = i.next();
+                    i.remove();
+
+                    if (!sk.isValid()) {
+                        if (s_logger.isTraceEnabled()) {
+                            s_logger.trace("Selection Key is invalid: " + sk.toString());
+                        }
+                        Link link = (Link)sk.attachment();
+                        if (link != null) {
+                            link.terminated();
+                        } else {
+                            closeConnection(sk);
+                        }
+                    } else if (sk.isReadable()) {
+                        read(sk);
+                    } else if (sk.isWritable()) {
+                        write(sk);
+                    } else if (sk.isAcceptable()) {
+                        accept(sk);
+                    } else if (sk.isConnectable()) {
+                        connect(sk);
+                    }
+                }
+
+                s_logger.trace("Keys Done Processing.");
+
+                processTodos();
+            } catch (Throwable e) {
+                s_logger.warn("Caught an exception but continuing on.", e);
+            }
+        }
+        synchronized (_thread) {
+            _isStartup = false;
+        }
+    }
+
+    abstract void init() throws IOException;
+
+    abstract void registerLink(InetSocketAddress saddr, Link link);
+
+    abstract void unregisterLink(InetSocketAddress saddr);
+
+    protected void accept(SelectionKey key) throws IOException {
+        ServerSocketChannel serverSocketChannel = (ServerSocketChannel)key.channel();
+
+        SocketChannel socketChannel = serverSocketChannel.accept();
+        Socket socket = socketChannel.socket();
+        socket.setKeepAlive(true);
+
+        if (s_logger.isTraceEnabled()) {
+            s_logger.trace("Connection accepted for " + socket);
+        }
+
+        // Begin SSL handshake in BLOCKING mode
+        socketChannel.configureBlocking(true);
+
+        SSLEngine sslEngine = null;
+        try {
+            SSLContext sslContext = Link.initSSLContext(false);
+            sslEngine = sslContext.createSSLEngine();
+            sslEngine.setUseClientMode(false);
+            sslEngine.setNeedClientAuth(false);
+            sslEngine.setEnabledProtocols(SSLUtils.getSupportedProtocols(sslEngine.getEnabledProtocols()));
+
+            Link.doHandshake(socketChannel, sslEngine, false);
+
+        } catch (Exception e) {
+            if (s_logger.isTraceEnabled()) {
+                s_logger.trace("Socket " + socket + " closed on read.  Probably -1 returned: " + e.getMessage());
+            }
+            closeAutoCloseable(socketChannel, "accepting socketChannel");
+            closeAutoCloseable(socket, "opened socket");
+            return;
+        }
+
+        if (s_logger.isTraceEnabled()) {
+            s_logger.trace("SSL: Handshake done");
+        }
+        socketChannel.configureBlocking(false);
+        InetSocketAddress saddr = (InetSocketAddress)socket.getRemoteSocketAddress();
+        Link link = new Link(saddr, this);
+        link.setSSLEngine(sslEngine);
+        link.setKey(socketChannel.register(key.selector(), SelectionKey.OP_READ, link));
+        Task task = _factory.create(Task.Type.CONNECT, link, null);
+        registerLink(saddr, link);
+        _executor.execute(task);
+    }
+
+    protected void terminate(SelectionKey key) {
+        Link link = (Link)key.attachment();
+        closeConnection(key);
+        if (link != null) {
+            link.terminated();
+            Task task = _factory.create(Task.Type.DISCONNECT, link, null);
+            unregisterLink(link.getSocketAddress());
+            _executor.execute(task);
+        }
+    }
+
+    protected void read(SelectionKey key) throws IOException {
+        Link link = (Link)key.attachment();
+        try {
+            SocketChannel socketChannel = (SocketChannel)key.channel();
+            if (s_logger.isTraceEnabled()) {
+                s_logger.trace("Reading from: " + socketChannel.socket().toString());
+            }
+            byte[] data = link.read(socketChannel);
+            if (data == null) {
+                if (s_logger.isTraceEnabled()) {
+                    s_logger.trace("Packet is incomplete.  Waiting for more.");
+                }
+                return;
+            }
+            Task task = _factory.create(Task.Type.DATA, link, data);
+            _executor.execute(task);
+        } catch (Exception e) {
+            logDebug(e, key, 1);
+            terminate(key);
+        }
+    }
+
+    protected void logTrace(Exception e, SelectionKey key, int loc) {
+        if (s_logger.isTraceEnabled()) {
+            Socket socket = null;
+            if (key != null) {
+                SocketChannel ch = (SocketChannel)key.channel();
+                if (ch != null) {
+                    socket = ch.socket();
+                }
+            }
+
+            s_logger.trace("Location " + loc + ": Socket " + socket + " closed on read.  Probably -1 returned.");
+        }
+    }
+
+    protected void logDebug(Exception e, SelectionKey key, int loc) {
+        if (s_logger.isDebugEnabled()) {
+            Socket socket = null;
+            if (key != null) {
+                SocketChannel ch = (SocketChannel)key.channel();
+                if (ch != null) {
+                    socket = ch.socket();
+                }
+            }
+
+            s_logger.debug("Location " + loc + ": Socket " + socket + " closed on read.  Probably -1 returned: " + e.getMessage());
+        }
+    }
+
+    protected void processTodos() {
+        List<ChangeRequest> todos;
+        if (_todos.size() == 0) {
+            return;             // Nothing to do.
+        }
+
+        synchronized (this) {
+            todos = _todos;
+            _todos = new ArrayList<ChangeRequest>();
+        }
+
+        if (s_logger.isTraceEnabled()) {
+            s_logger.trace("Todos Processing: " + todos.size());
+        }
+        SelectionKey key;
+        for (ChangeRequest todo : todos) {
+            switch (todo.type) {
+                case ChangeRequest.CHANGEOPS:
+                    try {
+                        key = (SelectionKey)todo.key;
+                        if (key != null && key.isValid()) {
+                            if (todo.att != null) {
+                                key.attach(todo.att);
+                                Link link = (Link)todo.att;
+                                link.setKey(key);
+                            }
+                            key.interestOps(todo.ops);
+                        }
+                    } catch (CancelledKeyException e) {
+                        s_logger.debug("key has been cancelled");
+                    }
+                    break;
+                case ChangeRequest.REGISTER:
+                    try {
+                        key = ((SocketChannel)(todo.key)).register(_selector, todo.ops, todo.att);
+                        if (todo.att != null) {
+                            Link link = (Link)todo.att;
+                            link.setKey(key);
+                        }
+                    } catch (ClosedChannelException e) {
+                        s_logger.warn("Couldn't register socket: " + todo.key);
+                        try {
+                            ((SocketChannel)(todo.key)).close();
+                        } catch (IOException ignore) {
+                            s_logger.info("[ignored] socket channel");
+                        } finally {
+                            Link link = (Link)todo.att;
+                            link.terminated();
+                        }
+                    }
+                    break;
+                case ChangeRequest.CLOSE:
+                    if (s_logger.isTraceEnabled()) {
+                        s_logger.trace("Trying to close " + todo.key);
+                    }
+                    key = (SelectionKey)todo.key;
+                    closeConnection(key);
+                    if (key != null) {
+                        Link link = (Link)key.attachment();
+                        if (link != null) {
+                            link.terminated();
+                        }
+                    }
+                    break;
+                default:
+                    s_logger.warn("Shouldn't be here");
+                    throw new RuntimeException("Shouldn't be here");
+            }
+        }
+        s_logger.trace("Todos Done processing");
+    }
+
+    protected void connect(SelectionKey key) throws IOException {
+        SocketChannel socketChannel = (SocketChannel)key.channel();
+
+        try {
+            socketChannel.finishConnect();
+            key.interestOps(SelectionKey.OP_READ);
+            Socket socket = socketChannel.socket();
+            if (!socket.getKeepAlive()) {
+                socket.setKeepAlive(true);
+            }
+            if (s_logger.isDebugEnabled()) {
+                s_logger.debug("Connected to " + socket);
+            }
+            Link link = new Link((InetSocketAddress)socket.getRemoteSocketAddress(), this);
+            link.setKey(key);
+            key.attach(link);
+            Task task = _factory.create(Task.Type.CONNECT, link, null);
+            _executor.execute(task);
+        } catch (IOException e) {
+            logTrace(e, key, 2);
+            terminate(key);
+        }
+    }
+
+    protected void scheduleTask(Task task) {
+        _executor.execute(task);
+    }
+
+    protected void write(SelectionKey key) throws IOException {
+        Link link = (Link)key.attachment();
+        try {
+            if (s_logger.isTraceEnabled()) {
+                s_logger.trace("Writing to " + link.getSocketAddress().toString());
+            }
+            boolean close = link.write((SocketChannel)key.channel());
+            if (close) {
+                closeConnection(key);
+                link.terminated();
+            } else {
+                key.interestOps(SelectionKey.OP_READ);
+            }
+        } catch (Exception e) {
+            logDebug(e, key, 3);
+            terminate(key);
+        }
+    }
+
+    protected void closeConnection(SelectionKey key) {
+        if (key != null) {
+            SocketChannel channel = (SocketChannel)key.channel();
+            key.cancel();
+            try {
+                if (channel != null) {
+                    if (s_logger.isDebugEnabled()) {
+                        s_logger.debug("Closing socket " + channel.socket());
+                    }
+                    channel.close();
+                }
+            } catch (IOException ignore) {
+                s_logger.info("[ignored] channel");
+            }
+        }
+    }
+
+    public void register(int ops, SocketChannel key, Object att) {
+        ChangeRequest todo = new ChangeRequest(key, ChangeRequest.REGISTER, ops, att);
+        synchronized (this) {
+            _todos.add(todo);
+        }
+        _selector.wakeup();
+    }
+
+    public void change(int ops, SelectionKey key, Object att) {
+        ChangeRequest todo = new ChangeRequest(key, ChangeRequest.CHANGEOPS, ops, att);
+        synchronized (this) {
+            _todos.add(todo);
+        }
+        _selector.wakeup();
+    }
+
+    public void close(SelectionKey key) {
+        ChangeRequest todo = new ChangeRequest(key, ChangeRequest.CLOSE, 0, null);
+        synchronized (this) {
+            _todos.add(todo);
+        }
+        _selector.wakeup();
+    }
+
+    /* Release the resource used by the instance */
+    public void cleanUp() throws IOException {
+        if (_selector != null) {
+            _selector.close();
+        }
+    }
+
+    public class ChangeRequest {
+        public static final int REGISTER = 1;
+        public static final int CHANGEOPS = 2;
+        public static final int CLOSE = 3;
+
+        public Object key;
+        public int type;
+        public int ops;
+        public Object att;
+
+        public ChangeRequest(Object key, int type, int ops, Object att) {
+            this.key = key;
+            this.type = type;
+            this.ops = ops;
+            this.att = att;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/83fd8f60/utils/src/main/java/com/cloud/utils/nio/NioServer.java
----------------------------------------------------------------------
diff --git a/utils/src/main/java/com/cloud/utils/nio/NioServer.java b/utils/src/main/java/com/cloud/utils/nio/NioServer.java
new file mode 100644
index 0000000..98a4a51
--- /dev/null
+++ b/utils/src/main/java/com/cloud/utils/nio/NioServer.java
@@ -0,0 +1,97 @@
+//
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you 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 com.cloud.utils.nio;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.nio.channels.ClosedChannelException;
+import java.nio.channels.SelectionKey;
+import java.nio.channels.ServerSocketChannel;
+import java.nio.channels.spi.SelectorProvider;
+import java.util.WeakHashMap;
+
+import org.apache.log4j.Logger;
+
+public class NioServer extends NioConnection {
+    private final static Logger s_logger = Logger.getLogger(NioServer.class);
+
+    protected InetSocketAddress _localAddr;
+    private ServerSocketChannel _serverSocket;
+
+    protected WeakHashMap<InetSocketAddress, Link> _links;
+
+    public NioServer(String name, int port, int workers, HandlerFactory factory) {
+        super(name, port, workers, factory);
+        _localAddr = null;
+        _links = new WeakHashMap<InetSocketAddress, Link>(1024);
+    }
+
+    @Override
+    protected void init() throws IOException {
+        _selector = SelectorProvider.provider().openSelector();
+
+        _serverSocket = ServerSocketChannel.open();
+        _serverSocket.configureBlocking(false);
+
+        _localAddr = new InetSocketAddress(_port);
+        _serverSocket.socket().bind(_localAddr);
+
+        _serverSocket.register(_selector, SelectionKey.OP_ACCEPT, null);
+
+        s_logger.info("NioConnection started and listening on " + _localAddr.toString());
+    }
+
+    @Override
+    public void cleanUp() throws IOException {
+        super.cleanUp();
+        if (_serverSocket != null) {
+            _serverSocket.close();
+        }
+        s_logger.info("NioConnection stopped on " + _localAddr.toString());
+    }
+
+    @Override
+    protected void registerLink(InetSocketAddress addr, Link link) {
+        _links.put(addr, link);
+    }
+
+    @Override
+    protected void unregisterLink(InetSocketAddress saddr) {
+        _links.remove(saddr);
+    }
+
+    /**
+     * Sends the data to the address specified.  If address is not already
+     * connected, this does nothing and returns null.  If address is already
+     * connected, then it returns the attached object so the caller can
+     * prepare for any responses.
+     * @param saddr
+     * @param data
+     * @return null if not sent.  attach object in link if sent.
+     */
+    public Object send(InetSocketAddress saddr, byte[] data) throws ClosedChannelException {
+        Link link = _links.get(saddr);
+        if (link == null) {
+            return null;
+        }
+        link.send(data);
+        return link.attachment();
+    }
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/83fd8f60/utils/src/main/java/com/cloud/utils/nio/Task.java
----------------------------------------------------------------------
diff --git a/utils/src/main/java/com/cloud/utils/nio/Task.java b/utils/src/main/java/com/cloud/utils/nio/Task.java
new file mode 100644
index 0000000..c77c703
--- /dev/null
+++ b/utils/src/main/java/com/cloud/utils/nio/Task.java
@@ -0,0 +1,89 @@
+//
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you 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 com.cloud.utils.nio;
+
+import org.apache.log4j.Logger;
+
+/**
+ * Task represents one todo item for the AgentManager or the AgentManager
+ *
+ */
+public abstract class Task implements Runnable {
+    private static final Logger s_logger = Logger.getLogger(Task.class);
+
+    public enum Type {
+        CONNECT,     // Process a new connection.
+        DISCONNECT,  // Process an existing connection disconnecting.
+        DATA,        // data incoming.
+        CONNECT_FAILED, // Connection failed.
+        OTHER        // Allows other tasks to be defined by the caller.
+    };
+
+    Object _data;
+    Type _type;
+    Link _link;
+
+    public Task(Type type, Link link, byte[] data) {
+        _data = data;
+        _type = type;
+        _link = link;
+    }
+
+    public Task(Type type, Link link, Object data) {
+        _data = data;
+        _type = type;
+        _link = link;
+    }
+
+    protected Task() {
+    }
+
+    public Type getType() {
+        return _type;
+    }
+
+    public Link getLink() {
+        return _link;
+    }
+
+    public byte[] getData() {
+        return (byte[])_data;
+    }
+
+    public Object get() {
+        return _data;
+    }
+
+    @Override
+    public String toString() {
+        return _type.toString();
+    }
+
+    abstract protected void doTask(Task task) throws Exception;
+
+    @Override
+    public final void run() {
+        try {
+            doTask(this);
+        } catch (Throwable e) {
+            s_logger.warn("Caught the following exception but pushing on", e);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/83fd8f60/utils/src/main/java/com/cloud/utils/nio/TrustAllManager.java
----------------------------------------------------------------------
diff --git a/utils/src/main/java/com/cloud/utils/nio/TrustAllManager.java b/utils/src/main/java/com/cloud/utils/nio/TrustAllManager.java
new file mode 100644
index 0000000..5dffd62
--- /dev/null
+++ b/utils/src/main/java/com/cloud/utils/nio/TrustAllManager.java
@@ -0,0 +1,45 @@
+//
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you 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 com.cloud.utils.nio;
+
+public class TrustAllManager implements javax.net.ssl.TrustManager, javax.net.ssl.X509TrustManager {
+    @Override
+    public java.security.cert.X509Certificate[] getAcceptedIssuers() {
+        return null;
+    }
+
+    public boolean isServerTrusted(java.security.cert.X509Certificate[] certs) {
+        return true;
+    }
+
+    public boolean isClientTrusted(java.security.cert.X509Certificate[] certs) {
+        return true;
+    }
+
+    @Override
+    public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType) throws java.security.cert.CertificateException {
+        return;
+    }
+
+    @Override
+    public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType) throws java.security.cert.CertificateException {
+        return;
+    }
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/83fd8f60/utils/src/main/java/com/cloud/utils/rest/BasicEncodedRESTValidationStrategy.java
----------------------------------------------------------------------
diff --git a/utils/src/main/java/com/cloud/utils/rest/BasicEncodedRESTValidationStrategy.java b/utils/src/main/java/com/cloud/utils/rest/BasicEncodedRESTValidationStrategy.java
new file mode 100644
index 0000000..bf001cd
--- /dev/null
+++ b/utils/src/main/java/com/cloud/utils/rest/BasicEncodedRESTValidationStrategy.java
@@ -0,0 +1,66 @@
+//
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you 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 com.cloud.utils.rest;
+
+import java.io.IOException;
+
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.httpclient.HttpClient;
+import org.apache.commons.httpclient.HttpException;
+import org.apache.commons.httpclient.HttpMethodBase;
+
+/**
+ * Base 64 encoded authorization strategy. This implementation as opposed to
+ * {@link RESTValidationStrategy} doesn't do a login after auth error, but instead
+ * includes the encoded credentials in each request, instead of a cookie.
+ */
+public class BasicEncodedRESTValidationStrategy extends RESTValidationStrategy {
+
+    public BasicEncodedRESTValidationStrategy(final String host, final String adminuser, final String adminpass) {
+        super();
+        this.host = host;
+        user = adminuser;
+        password = adminpass;
+    }
+
+    public BasicEncodedRESTValidationStrategy() {
+    }
+
+    @Override
+    public void executeMethod(final HttpMethodBase method, final HttpClient client,
+            final String protocol)
+                    throws CloudstackRESTException, HttpException, IOException {
+        if (host == null || host.isEmpty() || user == null || user.isEmpty() || password == null || password.isEmpty()) {
+            throw new CloudstackRESTException("Hostname/credentials are null or empty");
+        }
+
+        final String encodedCredentials = encodeCredentials();
+        method.setRequestHeader("Authorization", "Basic " + encodedCredentials);
+        client.executeMethod(method);
+    }
+
+    private String encodeCredentials() {
+        final String authString = user + ":" + password;
+        final byte[] authEncBytes = Base64.encodeBase64(authString.getBytes());
+        final String authStringEnc = new String(authEncBytes);
+        return authStringEnc;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/83fd8f60/utils/src/main/java/com/cloud/utils/rest/CloudstackRESTException.java
----------------------------------------------------------------------
diff --git a/utils/src/main/java/com/cloud/utils/rest/CloudstackRESTException.java b/utils/src/main/java/com/cloud/utils/rest/CloudstackRESTException.java
new file mode 100644
index 0000000..5985fa0
--- /dev/null
+++ b/utils/src/main/java/com/cloud/utils/rest/CloudstackRESTException.java
@@ -0,0 +1,39 @@
+//
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you 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 com.cloud.utils.rest;
+
+public class CloudstackRESTException extends Exception {
+
+    public CloudstackRESTException() {
+    }
+
+    public CloudstackRESTException(final String message) {
+        super(message);
+    }
+
+    public CloudstackRESTException(final Throwable cause) {
+        super(cause);
+    }
+
+    public CloudstackRESTException(final String message, final Throwable cause) {
+        super(message, cause);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/83fd8f60/utils/src/main/java/com/cloud/utils/rest/RESTServiceConnector.java
----------------------------------------------------------------------
diff --git a/utils/src/main/java/com/cloud/utils/rest/RESTServiceConnector.java b/utils/src/main/java/com/cloud/utils/rest/RESTServiceConnector.java
new file mode 100644
index 0000000..6ededcb
--- /dev/null
+++ b/utils/src/main/java/com/cloud/utils/rest/RESTServiceConnector.java
@@ -0,0 +1,395 @@
+//
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you 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 com.cloud.utils.rest;
+
+import com.google.gson.FieldNamingPolicy;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.JsonDeserializer;
+import com.google.gson.reflect.TypeToken;
+import org.apache.cloudstack.utils.security.SSLUtils;
+import org.apache.cloudstack.utils.security.SecureSSLSocketFactory;
+import org.apache.commons.httpclient.ConnectTimeoutException;
+import org.apache.commons.httpclient.HttpClient;
+import org.apache.commons.httpclient.HttpException;
+import org.apache.commons.httpclient.HttpMethod;
+import org.apache.commons.httpclient.HttpMethodBase;
+import org.apache.commons.httpclient.HttpStatus;
+import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager;
+import org.apache.commons.httpclient.NameValuePair;
+import org.apache.commons.httpclient.cookie.CookiePolicy;
+import org.apache.commons.httpclient.methods.DeleteMethod;
+import org.apache.commons.httpclient.methods.GetMethod;
+import org.apache.commons.httpclient.methods.PostMethod;
+import org.apache.commons.httpclient.methods.PutMethod;
+import org.apache.commons.httpclient.methods.StringRequestEntity;
+import org.apache.commons.httpclient.params.HttpConnectionParams;
+import org.apache.commons.httpclient.protocol.Protocol;
+import org.apache.commons.httpclient.protocol.ProtocolSocketFactory;
+import org.apache.commons.httpclient.protocol.SecureProtocolSocketFactory;
+import org.apache.log4j.Logger;
+
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.SSLSocketFactory;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509TrustManager;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.lang.reflect.Type;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.MalformedURLException;
+import java.net.Socket;
+import java.net.URL;
+import java.net.UnknownHostException;
+import java.security.KeyManagementException;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+/**
+ * This abstraction encapsulates client side code for REST service communication. It encapsulates
+ * access in a delegate validation strategy. There may different implementations extending
+ * {@link RESTValidationStrategy}, and any of them should mention the needed data to work.
+ *
+ * This connector allows the use of {@link JsonDeserializer} for specific classes. You can provide
+ * in the constructor a list of classes and a list of deserializers for these classes. These should
+ * be a correlated so that Nth deserializer is correctly mapped to Nth class.
+ */
+public class RESTServiceConnector {
+    private static final String HTTPS = "https";
+    protected static final String GET_METHOD_TYPE = "get";
+    protected static final String DELETE_METHOD_TYPE = "delete";
+    protected static final String PUT_METHOD_TYPE = "put";
+    protected static final String POST_METHOD_TYPE = "post";
+    private static final String TEXT_HTML_CONTENT_TYPE = "text/html";
+    private static final String JSON_CONTENT_TYPE = "application/json";
+    private static final String CONTENT_TYPE = "Content-Type";
+    private static final int BODY_RESP_MAX_LEN = 1024;
+    private static final int HTTPS_PORT = 443;
+
+    private static final Logger s_logger = Logger.getLogger(RESTServiceConnector.class);
+
+    protected final static String protocol = HTTPS;
+
+    private final static MultiThreadedHttpConnectionManager s_httpClientManager = new MultiThreadedHttpConnectionManager();
+
+    protected RESTValidationStrategy validation;
+
+    private final HttpClient client;
+
+    private final Gson gson;
+
+
+    /**
+     * Getter that may be needed only for test purpose
+     *
+     * @return
+     */
+    public Gson getGson() {
+        return gson;
+    }
+
+    public RESTServiceConnector(final RESTValidationStrategy validationStrategy) {
+        this(validationStrategy, null, null);
+    }
+
+    public RESTServiceConnector(final RESTValidationStrategy validationStrategy, final List<Class<?>> classList, final List<JsonDeserializer<?>> deserializerList) {
+        validation = validationStrategy;
+        client = createHttpClient();
+        client.getParams().setCookiePolicy(CookiePolicy.BROWSER_COMPATIBILITY);
+
+        try {
+            // Cast to ProtocolSocketFactory to avoid the deprecated constructor with the SecureProtocolSocketFactory parameter
+            Protocol.registerProtocol(HTTPS, new Protocol(HTTPS, (ProtocolSocketFactory)new TrustingProtocolSocketFactory(), HTTPS_PORT));
+        } catch (final IOException e) {
+            s_logger.warn("Failed to register the TrustingProtocolSocketFactory, falling back to default SSLSocketFactory", e);
+        }
+
+        final GsonBuilder gsonBuilder = new GsonBuilder();
+        if(classList != null && deserializerList != null) {
+            for(int i = 0; i < classList.size() && i < deserializerList.size(); i++) {
+                gsonBuilder.registerTypeAdapter(classList.get(i), deserializerList.get(i));
+            }
+        }
+        gson = gsonBuilder.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create();
+    }
+
+    public HttpClient createHttpClient() {
+        return new HttpClient(s_httpClientManager);
+    }
+
+    public HttpMethod createMethod(final String type, final String uri) throws CloudstackRESTException {
+        String url;
+        try {
+            url = new URL(protocol, validation.getHost(), uri).toString();
+        } catch (final MalformedURLException e) {
+            s_logger.error("Unable to build REST Service URL", e);
+            throw new CloudstackRESTException("Unable to build Nicira API URL", e);
+        }
+
+        if (POST_METHOD_TYPE.equalsIgnoreCase(type)) {
+            return new PostMethod(url);
+        } else if (GET_METHOD_TYPE.equalsIgnoreCase(type)) {
+            return new GetMethod(url);
+        } else if (DELETE_METHOD_TYPE.equalsIgnoreCase(type)) {
+            return new DeleteMethod(url);
+        } else if (PUT_METHOD_TYPE.equalsIgnoreCase(type)) {
+            return new PutMethod(url);
+        } else {
+            throw new CloudstackRESTException("Requesting unknown method type");
+        }
+    }
+
+    public void setControllerAddress(final String address) {
+        validation.setHost(address);
+    }
+
+    public void setAdminCredentials(final String username, final String password) {
+        validation.setUser(username);
+        validation.setPassword(password);
+    }
+
+    public <T> void executeUpdateObject(final T newObject, final String uri, final Map<String, String> parameters) throws CloudstackRESTException {
+
+        final PutMethod pm = (PutMethod)createMethod(PUT_METHOD_TYPE, uri);
+        pm.setRequestHeader(CONTENT_TYPE, JSON_CONTENT_TYPE);
+        try {
+            pm.setRequestEntity(new StringRequestEntity(gson.toJson(newObject), JSON_CONTENT_TYPE, null));
+        } catch (final UnsupportedEncodingException e) {
+            throw new CloudstackRESTException("Failed to encode json request body", e);
+        }
+
+        executeMethod(pm);
+
+        if (pm.getStatusCode() != HttpStatus.SC_OK) {
+            final String errorMessage = responseToErrorMessage(pm);
+            pm.releaseConnection();
+            s_logger.error("Failed to update object : " + errorMessage);
+            throw new CloudstackRESTException("Failed to update object : " + errorMessage);
+        }
+        pm.releaseConnection();
+    }
+
+    @SuppressWarnings("unchecked")
+    public <T> T executeCreateObject(final T newObject, final Type returnObjectType, final String uri, final Map<String, String> parameters)
+            throws CloudstackRESTException {
+
+        final PostMethod pm = (PostMethod)createMethod(POST_METHOD_TYPE, uri);
+        pm.setRequestHeader(CONTENT_TYPE, JSON_CONTENT_TYPE);
+        try {
+            pm.setRequestEntity(new StringRequestEntity(gson.toJson(newObject), JSON_CONTENT_TYPE, null));
+        } catch (final UnsupportedEncodingException e) {
+            throw new CloudstackRESTException("Failed to encode json request body", e);
+        }
+
+        executeMethod(pm);
+
+        if (pm.getStatusCode() != HttpStatus.SC_CREATED) {
+            final String errorMessage = responseToErrorMessage(pm);
+            pm.releaseConnection();
+            s_logger.error("Failed to create object : " + errorMessage);
+            throw new CloudstackRESTException("Failed to create object : " + errorMessage);
+        }
+
+        T result;
+        try {
+            result = (T)gson.fromJson(pm.getResponseBodyAsString(), TypeToken.get(newObject.getClass()).getType());
+        } catch (final IOException e) {
+            throw new CloudstackRESTException("Failed to decode json response body", e);
+        } finally {
+            pm.releaseConnection();
+        }
+
+        return result;
+    }
+
+    public void executeDeleteObject(final String uri) throws CloudstackRESTException {
+        final DeleteMethod dm = (DeleteMethod)createMethod(DELETE_METHOD_TYPE, uri);
+        dm.setRequestHeader(CONTENT_TYPE, JSON_CONTENT_TYPE);
+
+        executeMethod(dm);
+
+        if (dm.getStatusCode() != HttpStatus.SC_NO_CONTENT) {
+            final String errorMessage = responseToErrorMessage(dm);
+            dm.releaseConnection();
+            s_logger.error("Failed to delete object : " + errorMessage);
+            throw new CloudstackRESTException("Failed to delete object : " + errorMessage);
+        }
+        dm.releaseConnection();
+    }
+
+    @SuppressWarnings("unchecked")
+    public <T> T executeRetrieveObject(final Type returnObjectType, final String uri, final Map<String, String> parameters) throws CloudstackRESTException {
+        final GetMethod gm = (GetMethod)createMethod(GET_METHOD_TYPE, uri);
+        gm.setRequestHeader(CONTENT_TYPE, JSON_CONTENT_TYPE);
+        if (parameters != null && !parameters.isEmpty()) {
+            final List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(parameters.size());
+            for (final Entry<String, String> e : parameters.entrySet()) {
+                nameValuePairs.add(new NameValuePair(e.getKey(), e.getValue()));
+            }
+            gm.setQueryString(nameValuePairs.toArray(new NameValuePair[0]));
+        }
+
+        executeMethod(gm);
+
+        if (gm.getStatusCode() != HttpStatus.SC_OK) {
+            final String errorMessage = responseToErrorMessage(gm);
+            gm.releaseConnection();
+            s_logger.error("Failed to retrieve object : " + errorMessage);
+            throw new CloudstackRESTException("Failed to retrieve object : " + errorMessage);
+        }
+
+        T returnValue;
+        try {
+            returnValue = (T)gson.fromJson(gm.getResponseBodyAsString(), returnObjectType);
+        } catch (final IOException e) {
+            s_logger.error("IOException while retrieving response body", e);
+            throw new CloudstackRESTException(e);
+        } finally {
+            gm.releaseConnection();
+        }
+        return returnValue;
+    }
+
+    public void executeMethod(final HttpMethodBase method) throws CloudstackRESTException {
+        try {
+            validation.executeMethod(method, client, protocol);
+        } catch (final HttpException e) {
+            s_logger.error("HttpException caught while trying to connect to the REST Service", e);
+            method.releaseConnection();
+            throw new CloudstackRESTException("API call to REST Service Failed", e);
+        } catch (final IOException e) {
+            s_logger.error("IOException caught while trying to connect to the REST Service", e);
+            method.releaseConnection();
+            throw new CloudstackRESTException("API call to Nicira REST Service Failed", e);
+        }
+    }
+
+    private String responseToErrorMessage(final HttpMethodBase method) {
+        assert method.isRequestSent() : "no use getting an error message unless the request is sent";
+
+        if (TEXT_HTML_CONTENT_TYPE.equals(method.getResponseHeader(CONTENT_TYPE).getValue())) {
+            // The error message is the response content
+            // Safety margin of 1024 characters, anything longer is probably useless
+            // and will clutter the logs
+            try {
+                return method.getResponseBodyAsString(BODY_RESP_MAX_LEN);
+            } catch (final IOException e) {
+                s_logger.debug("Error while loading response body", e);
+            }
+        }
+
+        // The default
+        return method.getStatusText();
+    }
+
+    /* Some controllers use a self-signed certificate. The
+     * TrustingProtocolSocketFactory will accept any provided
+     * certificate when making an SSL connection to the SDN
+     * Manager
+     */
+    private class TrustingProtocolSocketFactory implements SecureProtocolSocketFactory {
+
+        private SSLSocketFactory ssf;
+
+        public TrustingProtocolSocketFactory() throws IOException {
+            // Create a trust manager that does not validate certificate chains
+            final TrustManager[] trustAllCerts = new TrustManager[] {new X509TrustManager() {
+                @Override
+                public X509Certificate[] getAcceptedIssuers() {
+                    return null;
+                }
+
+                @Override
+                public void checkClientTrusted(final X509Certificate[] certs, final String authType) {
+                    // Trust always
+                }
+
+                @Override
+                public void checkServerTrusted(final X509Certificate[] certs, final String authType) {
+                    // Trust always
+                }
+            }};
+
+            try {
+                // Install the all-trusting trust manager
+                final SSLContext sc = SSLUtils.getSSLContext();
+                sc.init(null, trustAllCerts, new java.security.SecureRandom());
+                ssf = new SecureSSLSocketFactory(sc);
+            } catch (final KeyManagementException e) {
+                throw new IOException(e);
+            } catch (final NoSuchAlgorithmException e) {
+                throw new IOException(e);
+            }
+        }
+
+        @Override
+        public Socket createSocket(final String host, final int port) throws IOException {
+            SSLSocket socket = (SSLSocket) ssf.createSocket(host, port);
+            socket.setEnabledProtocols(SSLUtils.getSupportedProtocols(socket.getEnabledProtocols()));
+            return socket;
+        }
+
+        @Override
+        public Socket createSocket(final String address, final int port, final InetAddress localAddress, final int localPort) throws IOException, UnknownHostException {
+            Socket socket = ssf.createSocket(address, port, localAddress, localPort);
+            if (socket instanceof SSLSocket) {
+                ((SSLSocket)socket).setEnabledProtocols(SSLUtils.getSupportedProtocols(((SSLSocket)socket).getEnabledProtocols()));
+            }
+            return socket;
+        }
+
+        @Override
+        public Socket createSocket(final Socket socket, final String host, final int port, final boolean autoClose) throws IOException, UnknownHostException {
+            Socket s = ssf.createSocket(socket, host, port, autoClose);
+            if (s instanceof SSLSocket) {
+                ((SSLSocket)s).setEnabledProtocols(SSLUtils.getSupportedProtocols(((SSLSocket)s).getEnabledProtocols()));
+            }
+            return s;
+        }
+
+        @Override
+        public Socket createSocket(final String host, final int port, final InetAddress localAddress, final int localPort, final HttpConnectionParams params)
+                throws IOException, UnknownHostException, ConnectTimeoutException {
+            final int timeout = params.getConnectionTimeout();
+            if (timeout == 0) {
+                Socket socket = createSocket(host, port, localAddress, localPort);
+                if (socket instanceof SSLSocket) {
+                    ((SSLSocket)socket).setEnabledProtocols(SSLUtils.getSupportedProtocols(((SSLSocket)socket).getEnabledProtocols()));
+                }
+                return socket;
+            } else {
+                final Socket s = ssf.createSocket();
+                if (s instanceof SSLSocket) {
+                    ((SSLSocket)s).setEnabledProtocols(SSLUtils.getSupportedProtocols(((SSLSocket)s).getEnabledProtocols()));
+                }
+                s.bind(new InetSocketAddress(localAddress, localPort));
+                s.connect(new InetSocketAddress(host, port), timeout);
+                return s;
+            }
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/83fd8f60/utils/src/main/java/com/cloud/utils/rest/RESTValidationStrategy.java
----------------------------------------------------------------------
diff --git a/utils/src/main/java/com/cloud/utils/rest/RESTValidationStrategy.java b/utils/src/main/java/com/cloud/utils/rest/RESTValidationStrategy.java
new file mode 100644
index 0000000..77ac8d0
--- /dev/null
+++ b/utils/src/main/java/com/cloud/utils/rest/RESTValidationStrategy.java
@@ -0,0 +1,165 @@
+//
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you 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 com.cloud.utils.rest;
+
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import org.apache.commons.httpclient.HttpClient;
+import org.apache.commons.httpclient.HttpException;
+import org.apache.commons.httpclient.HttpMethodBase;
+import org.apache.commons.httpclient.HttpStatus;
+import org.apache.commons.httpclient.methods.PostMethod;
+import org.apache.log4j.Logger;
+
+/**
+ * Basic authentication strategy. This strategy needs user and password for authentication.
+ *
+ * A login URL is needed which will be used for login and getting the cookie to be
+ * used in next requests. If an executeMethod request fails due to authorization it will try
+ * to login, get the cookie and repeat the attempt to execute the method.
+ */
+public class RESTValidationStrategy {
+
+    private static final Logger s_logger = Logger.getLogger(RESTValidationStrategy.class);
+
+    protected String host;
+    protected String user;
+    protected String password;
+    protected String serverVersion;
+    protected String loginUrl;
+
+    public RESTValidationStrategy(final String host, final String user, final String password,
+            final String serverVersion, final String loginUrl) {
+        super();
+        this.host = host;
+        this.user = user;
+        this.password = password;
+        this.serverVersion = serverVersion;
+        this.loginUrl = loginUrl;
+    }
+
+    public RESTValidationStrategy(final String loginUrl) {
+        this.loginUrl = loginUrl;
+    }
+
+    public RESTValidationStrategy() {
+    }
+
+    public String getUser() {
+        return user;
+    }
+
+    public void setUser(final String user) {
+        this.user = user;
+    }
+
+    public String getPassword() {
+        return password;
+    }
+
+    public void setPassword(final String password) {
+        this.password = password;
+    }
+
+    public String getLoginUrl() {
+        return loginUrl;
+    }
+
+    public void setLoginUrl(final String loginUrl) {
+        this.loginUrl = loginUrl;
+    }
+
+    public String getHost() {
+        return host;
+    }
+
+    public void setHost(final String host) {
+        this.host = host;
+    }
+
+    public void executeMethod(final HttpMethodBase method, final HttpClient client,
+            final String protocol)
+                    throws CloudstackRESTException, HttpException, IOException {
+        if (host == null || host.isEmpty() || user == null || user.isEmpty() || password == null || password.isEmpty()) {
+            throw new CloudstackRESTException("Hostname/credentials are null or empty");
+        }
+
+        client.executeMethod(method);
+        if (method.getStatusCode() == HttpStatus.SC_UNAUTHORIZED) {
+            method.releaseConnection();
+            // login and try again
+            login(protocol, client);
+            client.executeMethod(method);
+        }
+    }
+
+    /**
+     * Logs against the REST server. The cookie is stored in the <code>_authcookie<code> variable.
+     * <p>
+     * The method returns false if the login failed or the connection could not be made.
+     *
+     */
+    protected void login(final String protocol,
+            final HttpClient client)
+                    throws CloudstackRESTException {
+        String url;
+
+        if (host == null || host.isEmpty() || user == null || user.isEmpty() || password == null || password.isEmpty()) {
+            throw new CloudstackRESTException("Hostname/credentials are null or empty");
+        }
+
+        try {
+            url = new URL(protocol, host, loginUrl).toString();
+        } catch (final MalformedURLException e) {
+            s_logger.error("Unable to build Nicira API URL", e);
+            throw new CloudstackRESTException("Unable to build Nicira API URL", e);
+        }
+
+        final PostMethod pm = new PostMethod(url);
+        pm.addParameter("username", user);
+        pm.addParameter("password", password);
+
+        try {
+            client.executeMethod(pm);
+        } catch (final HttpException e) {
+            throw new CloudstackRESTException("REST Service API login failed ", e);
+        } catch (final IOException e) {
+            throw new CloudstackRESTException("REST Service API login failed ", e);
+        } finally {
+            pm.releaseConnection();
+        }
+
+        if (pm.getStatusCode() != HttpStatus.SC_OK) {
+            s_logger.error("REST Service API login failed : " + pm.getStatusText());
+            throw new CloudstackRESTException("REST Service API login failed " + pm.getStatusText());
+        }
+
+        // Extract the version for later use
+        if (pm.getResponseHeader("Server") != null) {
+            serverVersion = pm.getResponseHeader("Server").getValue();
+            s_logger.debug("Server reports version " + serverVersion);
+        }
+
+        // Success; the cookie required for login is kept in _client
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/83fd8f60/utils/src/main/java/com/cloud/utils/script/OutputInterpreter.java
----------------------------------------------------------------------
diff --git a/utils/src/main/java/com/cloud/utils/script/OutputInterpreter.java b/utils/src/main/java/com/cloud/utils/script/OutputInterpreter.java
new file mode 100644
index 0000000..654f87e
--- /dev/null
+++ b/utils/src/main/java/com/cloud/utils/script/OutputInterpreter.java
@@ -0,0 +1,141 @@
+//
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you 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 com.cloud.utils.script;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+
+import org.apache.log4j.Logger;
+
+/**
+ */
+public abstract class OutputInterpreter {
+    public boolean drain() {
+        return false;
+    }
+
+    public String processError(BufferedReader reader) throws IOException {
+        StringBuilder buff = new StringBuilder();
+        String line = null;
+        while ((line = reader.readLine()) != null) {
+            buff.append(line);
+        }
+        return buff.toString();
+    }
+
+    public abstract String interpret(BufferedReader reader) throws IOException;
+
+    public static final OutputInterpreter NoOutputParser = new OutputInterpreter() {
+        @Override
+        public String interpret(BufferedReader reader) throws IOException {
+            return null;
+        }
+    };
+
+    public static class TimedOutLogger extends OutputInterpreter {
+        private static final Logger s_logger = Logger.getLogger(TimedOutLogger.class);
+        Process _process;
+
+        public TimedOutLogger(Process process) {
+            _process = process;
+        }
+
+        @Override
+        public boolean drain() {
+            return true;
+        }
+
+        @Override
+        public String interpret(BufferedReader reader) throws IOException {
+            StringBuilder buff = new StringBuilder();
+
+            while (reader.ready()) {
+                buff.append(reader.readLine());
+            }
+
+            _process.destroy();
+
+            try {
+                while (reader.ready()) {
+                    buff.append(reader.readLine());
+                }
+            } catch (IOException e) {
+                s_logger.info("[ignored] can not append line to buffer",e);
+            }
+
+            return buff.toString();
+        }
+    }
+
+    public static class OutputLogger extends OutputInterpreter {
+        Logger _logger;
+
+        public OutputLogger(Logger logger) {
+            _logger = logger;
+        }
+
+        @Override
+        public String interpret(BufferedReader reader) throws IOException {
+            StringBuilder builder = new StringBuilder();
+            String line = null;
+            while ((line = reader.readLine()) != null) {
+                builder.append(line).append("\n");
+            }
+            if (builder.length() > 0) {
+                _logger.debug(builder.toString());
+            }
+            return null;
+        }
+    }
+
+    public static class OneLineParser extends OutputInterpreter {
+        String line = null;
+
+        @Override
+        public String interpret(BufferedReader reader) throws IOException {
+            line = reader.readLine();
+            return null;
+        }
+
+        public String getLine() {
+            return line;
+        }
+    };
+
+    public static class AllLinesParser extends OutputInterpreter {
+        String allLines = null;
+
+        @Override
+        public String interpret(BufferedReader reader) throws IOException {
+            StringBuilder builder = new StringBuilder();
+            String line = null;
+            while ((line = reader.readLine()) != null) {
+                builder.append(line).append("\n");
+            }
+            allLines = builder.toString();
+            return null;
+        }
+
+        public String getLines() {
+            return allLines;
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/83fd8f60/utils/src/main/java/com/cloud/utils/script/Script.java
----------------------------------------------------------------------
diff --git a/utils/src/main/java/com/cloud/utils/script/Script.java b/utils/src/main/java/com/cloud/utils/script/Script.java
new file mode 100644
index 0000000..487c62c
--- /dev/null
+++ b/utils/src/main/java/com/cloud/utils/script/Script.java
@@ -0,0 +1,502 @@
+//
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you 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 com.cloud.utils.script;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+import java.util.concurrent.Callable;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.log4j.Logger;
+
+import com.cloud.utils.PropertiesUtil;
+import com.cloud.utils.concurrency.NamedThreadFactory;
+import com.cloud.utils.script.OutputInterpreter.TimedOutLogger;
+
+public class Script implements Callable<String> {
+    private static final Logger s_logger = Logger.getLogger(Script.class);
+
+    private final Logger _logger;
+
+    public static final String ERR_EXECUTE = "execute.error";
+    public static final String ERR_TIMEOUT = "timeout";
+    private int _defaultTimeout = 3600 * 1000; /* 1 hour */
+    private volatile boolean _isTimeOut = false;
+
+    private boolean _passwordCommand = false;
+
+    private static final ScheduledExecutorService s_executors = Executors.newScheduledThreadPool(10, new NamedThreadFactory("Script"));
+
+    String _workDir;
+    ArrayList<String> _command;
+    long _timeout;
+    Process _process;
+    Thread _thread;
+
+    public int getExitValue()  {
+        return _process.exitValue();
+    }
+
+    public Script(String command, long timeout, Logger logger) {
+        _command = new ArrayList<String>();
+        _command.add(command);
+        _timeout = timeout;
+        if (_timeout == 0) {
+            /* always using default timeout 1 hour to avoid thread hang */
+            _timeout = _defaultTimeout;
+        }
+        _process = null;
+        _logger = logger != null ? logger : s_logger;
+    }
+
+    public Script(boolean runWithSudo, String command, long timeout, Logger logger) {
+        this(command, timeout, logger);
+        if (runWithSudo) {
+            _command.add(0, "sudo");
+        }
+    }
+
+    public Script(String command, Logger logger) {
+        this(command, 0, logger);
+    }
+
+    public Script(String command) {
+        this(command, 0, s_logger);
+    }
+
+    public Script(String command, long timeout) {
+        this(command, timeout, s_logger);
+    }
+
+    public void add(String... params) {
+        for (String param : params) {
+            _command.add(param);
+        }
+    }
+
+    public void add(String param) {
+        _command.add(param);
+    }
+
+    public Script set(String name, String value) {
+        _command.add(name);
+        _command.add(value);
+        return this;
+    }
+
+    public void setWorkDir(String workDir) {
+        _workDir = workDir;
+    }
+
+    protected String buildCommandLine(String[] command) {
+        StringBuilder builder = new StringBuilder();
+        boolean obscureParam = false;
+        for (int i = 0; i < command.length; i++) {
+            String cmd = command[i];
+            if (obscureParam) {
+                builder.append("******").append(" ");
+                obscureParam = false;
+            } else {
+                builder.append(command[i]).append(" ");
+            }
+
+            if ("-y".equals(cmd) || "-z".equals(cmd)) {
+                obscureParam = true;
+                _passwordCommand = true;
+            }
+        }
+        return builder.toString();
+    }
+
+    protected String buildCommandLine(List<String> command) {
+        StringBuilder builder = new StringBuilder();
+        boolean obscureParam = false;
+        for (String cmd : command) {
+            if (obscureParam) {
+                builder.append("******").append(" ");
+                obscureParam = false;
+            } else {
+                builder.append(cmd).append(" ");
+            }
+
+            if ("-y".equals(cmd) || "-z".equals(cmd)) {
+                obscureParam = true;
+                _passwordCommand = true;
+            }
+        }
+        return builder.toString();
+    }
+
+    public String execute() {
+        return execute(new OutputInterpreter.OutputLogger(_logger));
+    }
+
+    @Override
+    public String toString() {
+        String[] command = _command.toArray(new String[_command.size()]);
+        return buildCommandLine(command);
+    }
+
+    static String stackTraceAsString(Throwable throwable) {
+        //TODO: a StringWriter is bit to heavy weight
+        try(StringWriter out = new StringWriter(); PrintWriter writer = new PrintWriter(out);) {
+            throwable.printStackTrace(writer);
+            return out.toString();
+        } catch (IOException e) {
+            return "";
+        }
+    }
+
+    public String execute(OutputInterpreter interpreter) {
+        String[] command = _command.toArray(new String[_command.size()]);
+
+        if (_logger.isDebugEnabled()) {
+            _logger.debug("Executing: " + buildCommandLine(command));
+        }
+
+        try {
+            ProcessBuilder pb = new ProcessBuilder(command);
+            pb.redirectErrorStream(true);
+            if (_workDir != null)
+                pb.directory(new File(_workDir));
+
+            _process = pb.start();
+            if (_process == null) {
+                _logger.warn("Unable to execute: " + buildCommandLine(command));
+                return "Unable to execute the command: " + command[0];
+            }
+
+            BufferedReader ir = new BufferedReader(new InputStreamReader(_process.getInputStream()));
+
+            _thread = Thread.currentThread();
+            ScheduledFuture<String> future = null;
+            if (_timeout > 0) {
+                future = s_executors.schedule(this, _timeout, TimeUnit.MILLISECONDS);
+            }
+
+            Task task = null;
+            if (interpreter != null && interpreter.drain()) {
+                task = new Task(interpreter, ir);
+                s_executors.execute(task);
+            }
+
+            while (true) {
+                try {
+                    if (_process.waitFor() == 0) {
+                        _logger.debug("Execution is successful.");
+                        if (interpreter != null) {
+                            return interpreter.drain() ? task.getResult() : interpreter.interpret(ir);
+                        } else {
+                            // null return exitValue apparently
+                            return String.valueOf(_process.exitValue());
+                        }
+                    } else {
+                        break;
+                    }
+                } catch (InterruptedException e) {
+                    if (!_isTimeOut) {
+                        /*
+                         * This is not timeout, we are interrupted by others,
+                         * continue
+                         */
+                        _logger.debug("We are interrupted but it's not a timeout, just continue");
+                        continue;
+                    }
+
+                    TimedOutLogger log = new TimedOutLogger(_process);
+                    Task timedoutTask = new Task(log, ir);
+
+                    timedoutTask.run();
+                    if (!_passwordCommand) {
+                        _logger.warn("Timed out: " + buildCommandLine(command) + ".  Output is: " + timedoutTask.getResult());
+                    } else {
+                        _logger.warn("Timed out: " + buildCommandLine(command));
+                    }
+
+                    return ERR_TIMEOUT;
+                } finally {
+                    if (future != null) {
+                        future.cancel(false);
+                    }
+                    Thread.interrupted();
+                }
+            }
+
+            _logger.debug("Exit value is " + _process.exitValue());
+
+            BufferedReader reader = new BufferedReader(new InputStreamReader(_process.getInputStream()), 128);
+
+            String error;
+            if (interpreter != null) {
+                error = interpreter.processError(reader);
+            } else {
+                error = String.valueOf(_process.exitValue());
+            }
+
+            if (_logger.isDebugEnabled()) {
+                _logger.debug(error);
+            }
+            return error;
+        } catch (SecurityException ex) {
+            _logger.warn("Security Exception....not running as root?", ex);
+            return stackTraceAsString(ex);
+        } catch (Exception ex) {
+            _logger.warn("Exception: " + buildCommandLine(command), ex);
+            return stackTraceAsString(ex);
+        } finally {
+            if (_process != null) {
+                IOUtils.closeQuietly(_process.getErrorStream());
+                IOUtils.closeQuietly(_process.getOutputStream());
+                IOUtils.closeQuietly(_process.getInputStream());
+                _process.destroy();
+            }
+        }
+    }
+
+    @Override
+    public String call() {
+        try {
+            _logger.trace("Checking exit value of process");
+            _process.exitValue();
+            _logger.trace("Script ran within the alloted time");
+        } catch (IllegalThreadStateException e) {
+            _logger.warn("Interrupting script.");
+            _isTimeOut = true;
+            _thread.interrupt();
+        }
+        return null;
+    }
+
+    public static class Task implements Runnable {
+        OutputInterpreter interpreter;
+        BufferedReader reader;
+        String result;
+        boolean done;
+
+        public Task(OutputInterpreter interpreter, BufferedReader reader) {
+            this.interpreter = interpreter;
+            this.reader = reader;
+            result = null;
+        }
+
+        @Override
+        public void run() {
+            synchronized(this) {
+                done = false;
+                try {
+                    result = interpreter.interpret(reader);
+                } catch (IOException ex) {
+                    result = stackTraceAsString(ex);
+                } catch (Exception ex) {
+                    result = stackTraceAsString(ex);
+                } finally {
+                        done = true;
+                        notifyAll();
+                        IOUtils.closeQuietly(reader);
+                }
+            }
+        }
+
+        public synchronized String getResult() throws InterruptedException {
+            if (!done) {
+                wait();
+            }
+            return result;
+        }
+    }
+
+    public static String findScript(String path, String script) {
+        s_logger.debug("Looking for " + script + " in the classpath");
+
+        URL url = ClassLoader.getSystemResource(script);
+        s_logger.debug("System resource: " + url);
+        File file = null;
+        if (url != null) {
+            file = new File(url.getFile());
+            s_logger.debug("Absolute path =  " + file.getAbsolutePath());
+            return file.getAbsolutePath();
+        }
+
+        if (path == null) {
+            s_logger.warn("No search path specified, unable to look for " + script);
+            return null;
+        }
+        path = path.replace("/", File.separator);
+
+        /**
+         * Look in WEB-INF/classes of the webapp
+         * URI workaround the URL encoding of url.getFile
+         */
+        if (path.endsWith(File.separator)) {
+            url = Script.class.getClassLoader().getResource(path + script);
+        } else {
+            url = Script.class.getClassLoader().getResource(path + File.separator + script);
+        }
+        s_logger.debug("Classpath resource: " + url);
+        if (url != null) {
+            try {
+                file = new File(new URI(url.toString()).getPath());
+                s_logger.debug("Absolute path =  " + file.getAbsolutePath());
+                return file.getAbsolutePath();
+            } catch (URISyntaxException e) {
+                s_logger.warn("Unable to convert " + url.toString() + " to a URI");
+            }
+        }
+
+        if (path.endsWith(File.separator)) {
+            path = path.substring(0, path.lastIndexOf(File.separator));
+        }
+
+        if (path.startsWith(File.separator)) {
+            // Path given was absolute so we assume the caller knows what they want.
+            file = new File(path + File.separator + script);
+            return file.exists() ? file.getAbsolutePath() : null;
+        }
+
+        s_logger.debug("Looking for " + script);
+        String search = null;
+        for (int i = 0; i < 3; i++) {
+            if (i == 0) {
+                String cp = Script.class.getResource(Script.class.getSimpleName() + ".class").toExternalForm();
+                int begin = cp.indexOf(File.separator);
+
+                // work around with the inconsistency of java classpath and file separator on Windows 7
+                if (begin < 0)
+                    begin = cp.indexOf('/');
+
+                int endBang = cp.lastIndexOf("!");
+                int end = cp.lastIndexOf(File.separator, endBang);
+                if (end < 0)
+                    end = cp.lastIndexOf('/', endBang);
+                if (end < 0)
+                    cp = cp.substring(begin);
+                else
+                    cp = cp.substring(begin, end);
+
+                s_logger.debug("Current binaries reside at " + cp);
+                search = cp;
+            } else if (i == 1) {
+                s_logger.debug("Searching in environment.properties");
+                try {
+                    final File propsFile = PropertiesUtil.findConfigFile("environment.properties");
+                    if (propsFile == null) {
+                        s_logger.debug("environment.properties could not be opened");
+                    } else {
+                        final Properties props = PropertiesUtil.loadFromFile(propsFile);
+                        search = props.getProperty("paths.script");
+                    }
+                } catch (IOException e) {
+                    s_logger.debug("environment.properties could not be opened");
+                    continue;
+                }
+                s_logger.debug("environment.properties says scripts should be in " + search);
+            } else {
+                s_logger.debug("Searching in the current directory");
+                search = ".";
+            }
+
+            search += File.separatorChar + path + File.separator;
+            do {
+                search = search.substring(0, search.lastIndexOf(File.separator));
+                file = new File(search + File.separator + script);
+                s_logger.debug("Looking for " + script + " in " + file.getAbsolutePath());
+            } while (!file.exists() && search.lastIndexOf(File.separator) != -1);
+
+            if (file.exists()) {
+                return file.getAbsolutePath();
+            }
+
+        }
+
+        search = System.getProperty("paths.script");
+
+        search += File.separatorChar + path + File.separator;
+        do {
+            search = search.substring(0, search.lastIndexOf(File.separator));
+            file = new File(search + File.separator + script);
+            s_logger.debug("Looking for " + script + " in " + file.getAbsolutePath());
+        } while (!file.exists() && search.lastIndexOf(File.separator) != -1);
+
+        if (file.exists()) {
+            return file.getAbsolutePath();
+        }
+
+        s_logger.warn("Unable to find script " + script);
+        return null;
+    }
+
+    public static String runSimpleBashScript(String command) {
+        return Script.runSimpleBashScript(command, 0);
+    }
+
+    public static String runSimpleBashScript(String command, int timeout) {
+
+        Script s = new Script("/bin/bash", timeout);
+        s.add("-c");
+        s.add(command);
+
+        OutputInterpreter.OneLineParser parser = new OutputInterpreter.OneLineParser();
+        if (s.execute(parser) != null)
+            return null;
+
+        String result = parser.getLine();
+        if (result == null || result.trim().isEmpty())
+            return null;
+        else
+            return result.trim();
+    }
+
+    public static int runSimpleBashScriptForExitValue(String command) {
+        return runSimpleBashScriptForExitValue(command, 0);
+    }
+
+    public static int runSimpleBashScriptForExitValue(String command, int timeout) {
+
+        Script s = new Script("/bin/bash", timeout);
+        s.add("-c");
+        s.add(command);
+
+        String result = s.execute(null);
+        if (result == null || result.trim().isEmpty())
+            return -1;
+        else {
+            try {
+                return Integer.parseInt(result.trim());
+            } catch (NumberFormatException e) {
+                return -1;
+            }
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/83fd8f60/utils/src/main/java/com/cloud/utils/script/Script2.java
----------------------------------------------------------------------
diff --git a/utils/src/main/java/com/cloud/utils/script/Script2.java b/utils/src/main/java/com/cloud/utils/script/Script2.java
new file mode 100644
index 0000000..03c0e0d
--- /dev/null
+++ b/utils/src/main/java/com/cloud/utils/script/Script2.java
@@ -0,0 +1,70 @@
+//
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you 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 com.cloud.utils.script;
+
+import java.util.HashMap;
+
+import org.apache.log4j.Logger;
+
+public class Script2 extends Script {
+    HashMap<String, ParamType> _params = new HashMap<String, ParamType>();
+
+    public static enum ParamType {
+        NORMAL, PASSWORD,
+    }
+
+    public Script2(String command, Logger logger) {
+        this(command, 0, logger);
+    }
+
+    public Script2(String command, long timeout, Logger logger) {
+        super(command, timeout, logger);
+    }
+
+    public void add(String param, ParamType type) {
+        _params.put(param, type);
+        super.add(param);
+    }
+
+    @Override
+    public void add(String param) {
+        add(param, ParamType.NORMAL);
+    }
+
+    private ParamType getType(String cmd) {
+        return _params.get(cmd);
+    }
+
+    @Override
+    protected String buildCommandLine(String[] command) {
+        StringBuilder builder = new StringBuilder();
+        for (int i = 0; i < command.length; i++) {
+            String cmd = command[i];
+            ParamType type = getType(cmd);
+            if (type == ParamType.PASSWORD) {
+                builder.append("******").append(" ");
+            } else {
+                builder.append(command[i]).append(" ");
+            }
+        }
+
+        return builder.toString();
+    }
+}


Mime
View raw message