Package org.gradle.launcher

Source Code of org.gradle.launcher.DaemonConnector

/*
* Copyright 2010 the original author or authors.
*
* 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.gradle.launcher;

import org.gradle.api.Action;
import org.gradle.api.GradleException;
import org.gradle.api.internal.DefaultClassPathRegistry;
import org.gradle.api.logging.Logger;
import org.gradle.api.logging.Logging;
import org.gradle.initialization.DefaultCommandLineConverter;
import org.gradle.messaging.concurrent.CompositeStoppable;
import org.gradle.messaging.concurrent.DefaultExecutorFactory;
import org.gradle.messaging.concurrent.Stoppable;
import org.gradle.messaging.remote.ConnectEvent;
import org.gradle.messaging.remote.internal.ConnectException;
import org.gradle.messaging.remote.internal.Connection;
import org.gradle.messaging.remote.internal.TcpIncomingConnector;
import org.gradle.messaging.remote.internal.TcpOutgoingConnector;
import org.gradle.util.GUtil;
import org.gradle.util.GradleVersion;
import org.gradle.util.Jvm;
import org.gradle.util.UncheckedException;

import java.io.*;
import java.net.URI;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class DaemonConnector {
    private static final Logger LOGGER = Logging.getLogger(DaemonConnector.class);
    private final File userHomeDir;

    public DaemonConnector(File userHomeDir) {
        this.userHomeDir = userHomeDir;
    }

    /**
     * Attempts to connect to the daemon, if it is running.
     *
     * @return The connection, or null if not running.
     */
    Connection<Object> maybeConnect() {
        try {
            URI uri;
            try {
                FileInputStream inputStream = new FileInputStream(getRegistryFile());
                try {
                    // Acquire shared lock on file while reading it
                    inputStream.getChannel().lock(0, Long.MAX_VALUE, true);
                    DataInputStream dataInputStream = new DataInputStream(inputStream);
                    uri = new URI(dataInputStream.readUTF());
                } finally {
                    // Also releases the lock
                    inputStream.close();
                }
            } catch (FileNotFoundException e) {
                // Ignore
                return null;
            }
            try {
                return new TcpOutgoingConnector(getClass().getClassLoader()).connect(uri);
            } catch (ConnectException e) {
                // Ignore
                return null;
            }
        } catch (Exception e) {
            throw UncheckedException.asUncheckedException(e);
        }
    }

    /**
     * Connects to the daemon, starting it if required.
     *
     * @return The connection. Never returns null.
     */
    Connection<Object> connect() {
        Connection<Object> connection = maybeConnect();
        if (connection != null) {
            return connection;
        }

        LOGGER.info("Starting Gradle daemon");
        try {
            startDaemon();
            Date expiry = new Date(System.currentTimeMillis() + 30000L);
            do {
                connection = maybeConnect();
                if (connection != null) {
                    return connection;
                }
                Thread.sleep(200L);
            } while (System.currentTimeMillis() < expiry.getTime());
        } catch (Exception e) {
            throw UncheckedException.asUncheckedException(e);
        }

        throw new GradleException("Timeout waiting to connect to Gradle daemon.");
    }

    private void startDaemon() throws IOException {
        List<String> daemonArgs = new ArrayList<String>();
        daemonArgs.add(Jvm.current().getJavaExecutable().getAbsolutePath());
        daemonArgs.add("-Xmx1024m");
        daemonArgs.add("-XX:MaxPermSize=256m");
        daemonArgs.add("-cp");
        daemonArgs.add(GUtil.join(new DefaultClassPathRegistry().getClassPathFiles("GRADLE_RUNTIME"),
                File.pathSeparator));
        daemonArgs.add(GradleDaemon.class.getName());
        daemonArgs.add(String.format("-%s", DefaultCommandLineConverter.GRADLE_USER_HOME));
        daemonArgs.add(userHomeDir.getAbsolutePath());
        ProcessBuilder builder = new ProcessBuilder(daemonArgs);
        builder.directory(userHomeDir);
        userHomeDir.mkdirs();
        Process process = builder.start();
        process.getOutputStream().close();
        process.getErrorStream().close();
        process.getInputStream().close();
    }

    /**
     * Starts accepting connections.
     *
     * @param handler The handler for connections.
     */
    void accept(final IncomingConnectionHandler handler) {
        DefaultExecutorFactory executorFactory = new DefaultExecutorFactory();
        TcpIncomingConnector incomingConnector = new TcpIncomingConnector(executorFactory, getClass().getClassLoader());
        final CompletionHandler finished = new CompletionHandler();

        LOGGER.lifecycle("Awaiting requests.");
       
        URI uri = incomingConnector.accept(new Action<ConnectEvent<Connection<Object>>>() {
            public void execute(ConnectEvent<Connection<Object>> connectionConnectEvent) {
                try {
                    finished.onStartActivity();
                    handler.handle(connectionConnectEvent.getConnection(), finished);
                } finally {
                    finished.onActivityComplete();
                    connectionConnectEvent.getConnection().stop();
                }
            }
        });

        try {
            File registryFile = getRegistryFile();
            registryFile.getParentFile().mkdirs();
//            registryFile.createNewFile();
            FileOutputStream outputStream = new FileOutputStream(registryFile);
            try {
                // Lock file while writing to it
                outputStream.getChannel().lock();
                DataOutputStream dataOutputStream = new DataOutputStream(outputStream);
                dataOutputStream.writeUTF(uri.toString());
                dataOutputStream.flush();
            } finally {
                // Also releases the lock
                outputStream.close();
            }
        } catch (IOException e) {
            throw UncheckedException.asUncheckedException(e);
        }

        boolean stopped = finished.awaitStop();
        if (!stopped) {
            LOGGER.lifecycle("Time-out waiting for requests. Stopping.");
        }
        new CompositeStoppable(incomingConnector, executorFactory).stop();

        getRegistryFile().delete();
    }

    private File getRegistryFile() {
        return new File(userHomeDir, String.format("daemon/%s/registry.bin", new GradleVersion().getVersion()));
    }

    private static class CompletionHandler implements Stoppable {
        private static final int THREE_HOURS = 3 * 60 * 60 * 1000;
        private final Lock lock = new ReentrantLock();
        private final Condition condition = lock.newCondition();
        private boolean running;
        private boolean stopped;
        private long expiry;

        CompletionHandler() {
            onActivityComplete();
        }

        /**
         * Waits until stopped, or timeout.
         * @return true if stopped, false if timeout
         */
        public boolean awaitStop() {
            lock.lock();
            try {
                while (running || (!stopped && System.currentTimeMillis() < expiry)) {
                    try {
                        condition.awaitUntil(new Date(expiry));
                    } catch (InterruptedException e) {
                        throw UncheckedException.asUncheckedException(e);
                    }
                }
                assert !running;
                return stopped;
            } finally {
                lock.unlock();
            }
        }

        public void stop() {
            lock.lock();
            try {
                stopped = true;
                condition.signalAll();
            } finally {
                lock.unlock();
            }
        }

        public void onStartActivity() {
            lock.lock();
            try {
                assert !running;
                running = true;
                condition.signalAll();
            } finally {
                lock.unlock();
            }
        }

        public void onActivityComplete() {
            lock.lock();
            try {
                running = false;
                expiry = System.currentTimeMillis() + THREE_HOURS;
                condition.signalAll();
            } finally {
                lock.unlock();
            }
        }
    }
}
TOP

Related Classes of org.gradle.launcher.DaemonConnector

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.