All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.sun.grizzly.jruby.RubyObjectPool Maven / Gradle / Ivy

/*
 * 
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 * 
 * Copyright 2007-2008 Sun Microsystems, Inc. All rights reserved.
 * 
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License. You can obtain
 * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
 * or glassfish/bootstrap/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 * 
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
 * Sun designates this particular file as subject to the "Classpath" exception
 * as provided by Sun in the GPL Version 2 section of the License file that
 * accompanied this code.  If applicable, add the following below the License
 * Header, with the fields enclosed by brackets [] replaced by your own
 * identifying information: "Portions Copyrighted [year]
 * [name of copyright owner]"
 * 
 * Contributor(s):
 * 
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 *
 */
package com.sun.grizzly.jruby;

import com.sun.grizzly.http.SelectorThread;
import org.jruby.Ruby;
import org.jruby.RubyInstanceConfig;
import org.jruby.javasupport.JavaEmbedUtils;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.runtime.load.LoadService;
import org.jruby.util.ClassCache;

import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;

/**
 * An object pool for ruby runtimes.
 *
 * TODO: Check thread-safeness for a) actual thread-safeness and b) efficency.
 * It is likely that a competent application of volatile and such would make this faster
 *
 * @author TAKAI Naoto
 * @author Pramod Gopinath
 * @author Vivek Pandey
 * @author Jacob Kessler
 */
public class RubyObjectPool {

    /**
     * How long to wait before giving up.
     */
    public static final long DEFAULT_TIMEOUT = 360L;
    /**
     * JRUBY_LIB directory
     */
    private final String jrubyLib;
    /**
     * The number of runtimes.
     */
    private final int defaultNumberOfRuntime = 5;
    /**
     * Runtime queue
     */
    private final BlockingQueue queue = new LinkedBlockingQueue();
    /**
     * Is Grizzly ARP enabled.
     */
    private final boolean asyncEnabled;
    private final int numberOfRuntime;
    private final String railsRoot;
    // We use this so much, we might as well cache it
    private final int procs = Runtime.getRuntime().availableProcessors();
    // CachedThreadPool size limited by currentlyGeneratingRuntimes: We want to avoid overloading the system
    // during times of high load by trying to generate lots of new runtimes. However, we may want to
    // generate multiple runtimes at the same time on a large machine
    private ExecutorService runtimeGenerator = Executors.newCachedThreadPool();

    private final int maxGeneratingRuntimes;
    private final int maxGeneratingRuntimes_def = 1;

    /**
     * The maximum runtimes that we want sitting in the queue (idle) at a time)
     */
    private AtomicInteger maximumIdleRuntimes = new AtomicInteger(1);

    /*
     * currentlyActiveRuntimes keeps track of how many runtimes the pool "knows" about, either lent out
     * or sitting in the queue. It uses that knowledge, along with the limits on the number of runtimes,
     * to prevent itself from making excessive runtimes, even under heavy load, since at some point making
     * a new runtime won't actualy help the load conditions
     *
     * currentlyGeneratingRuntimes keeps track of how many runtimes are scheduled for creation but have not
     * yet been added to the queue. Because runtime creation takes a long time relative to the request
     * service time, requests on a capacity pool have a tendency to impatiently request 3 or 4 runtimes
     * while waiting for a new runtime to initialize, which would otherwise cause 3 or 4 too many runtimes
     * to be created, costing both memory while the runtime lives (bad) and CPU time to create and remove
     * the runtime (really bad, since runtime creation should be while under load). CurrentlyGeneratingRuntimes
     * allows the runtime creator to refuse both runtime creation requests and runtime creation ticks while
     * it already has runtimes queued to be created.
     *
     */
    private volatile int currentlyActiveRuntimes = 0;

    private volatile int currentlyGeneratingRuntimes = 0;

    // Soft limit on the number of runtimes we will create
    // Will adjust based on load
    private AtomicInteger maximumActiveRuntimes = new AtomicInteger (procs);
    // Hard minimum and Maximum values for number of runtimes. The dynamic resizer will not exceed these limits
    //private final int hardMinActiveRuntimes_def = procs;
    private final int hardMinActiveRuntimes_def = 1;

    private final int hardMaxActiveRuntimes;
    private final int hardMinActiveRuntimes;


    /*
     * The "Tick" variables are the variables that keep track of how close to the various thresholds
     * the pool is. Despite being read and modified all over the place by different threads, they have been
     * maintained as normal variables, since they have wide margins of error and tend to be self-correcting
     * Furthermore, the consequences of them being off by a read or two aren't very severe: To trigger the
     * threashold, they would need to have been reasonably close anyway. As such, it seems like labling them
     * as volatile would be an unneeded performance hit. I'm happy to accept criticism from someone more experianced
     * than me, though.
     */
    private AtomicInteger downTicks = new AtomicInteger(0); // Manages increasing the number of runtimes
    private AtomicInteger upTicks = new AtomicInteger(0); // Manages decreasing the number of runtimes
    private AtomicInteger queueTicks = new AtomicInteger(0); // Manages the queue length
    private AtomicInteger newTicks = new AtomicInteger(0); // Manages new Runtime creation

    /*
     * Threasholds control how many ticks are required to change their associated property. Decreasing
     * these values will make the pool change size more rapidly in response to changing traffice, but
     * also makes it more likely to create or delete runtimes in response to brief spikes or dips in
     * incoming requests. A constructor has been provided to allow users to set their own values.
     */

    private final int upThreashold;
    private final int downThreashold;
    private final int queueThreashold;
    private final int newThreashold;

    private final long baseTime;

    private final boolean dynamic;



    /**
     * Create a pool of Ruby runtimes. This can be created for the whole container and each of the runtime can be used
     * from the pool to setup Rails instance that serves the request.
     *
     * @param railsRoot    the root directory of the rails project
     * @param jrubyLib     non-null, location to the directory where jruby libraries can be found
     * @param numRunTime   number of runtimes to be created initially.
     * @param asyncEnabled whether grizzly ARP needs to be enabled
     * @param minRuntime    the minimum number of runtimes to have active at one time
     * @param maxRuntime    the maximum number of runtimes to have active at one time
     */
    public RubyObjectPool(String railsRoot, String jrubyLib, int numRunTime, int minRuntime, int maxRuntime, boolean asyncEnabled) {
        this(railsRoot, jrubyLib, numRunTime, asyncEnabled, 10, minRuntime, maxRuntime, -1); // -1 provides default
    }

    /**
     * Create a pool of Ruby runtimes. This can be created for the whole container and each of the runtime can be used
     * from the pool to setup Rails instance that serves the request. This constructor allows finer control over some of the
     * dynamic bounds and runtime pool resizing logic.
     * Entering values of 0 or less for minRuntimes, maxRuntimes, and maxInProg will result in default values.
     * minRuntimes will make sure that it is less than or equal to maxRuntimes and adjust itself if needed. 
     *
     * @param railsRoot    the root directory of the rails project
     * @param jrubyLib     non-null, location to the directory where jruby libraries can be found
     * @param numRunTime   number of runtime to be created initially.
     * @param asyncEnabled whether grizzly ARP needs to be enabled
     * @param responsiveness How quickly the pool adjusts its bounds. Higher values will react more quickly to changes in load. Suggested values are between 5 and 15, 10 is default. Values of more than 19 are set to 19
     * @param minRuntimes   Minimal number of runtimes to keep active at all times, regardless of load. If minRuntimes is larger than numRunTime, runtimes will increase as needed until minRuntimes is reached. Default is the number of processors on the machine
     * @param maxRuntimes   Maximal number of runtimes to keep active at all times, regardless of load. Default is twice the number of processors on the machine
     * @param maxInProg     Maximum number of runtimes to schedule for creation at once. Defaults to one, but performance may be increased in situations where there are lots of processors and highly variable load by increasing it.
     */
    public RubyObjectPool (String railsRoot, String jrubyLib, int numRunTime, boolean asyncEnabled,
                           int responsiveness, int minRuntimes, int maxRuntimes, int maxInProg) {
        this.railsRoot = railsRoot;

        this.jrubyLib = jrubyLib;
        this.asyncEnabled = asyncEnabled;
        if (numRunTime > 0) {
            this.numberOfRuntime = numRunTime;
        } else {
            this.numberOfRuntime = defaultNumberOfRuntime;
        }

        maximumIdleRuntimes.set(numberOfRuntime);
        baseTime = System.currentTimeMillis();

        if (responsiveness > 19) {
            responsiveness = 19;
        }

        upThreashold = 20-responsiveness;
        downThreashold = 20-responsiveness;
        queueThreashold = 20-responsiveness;
        newThreashold = 20-responsiveness;

        if (maxRuntimes > 0) {
            hardMaxActiveRuntimes = maxRuntimes;
        } else {
            hardMaxActiveRuntimes = maximumActiveRuntimes.intValue() * 2;
        }
        if (minRuntimes > 0 && minRuntimes <= hardMaxActiveRuntimes) {
            hardMinActiveRuntimes = minRuntimes;
        } else {
            hardMinActiveRuntimes = Math.min(hardMinActiveRuntimes_def,hardMaxActiveRuntimes);
        }


        String dynamicEnable = System.getProperty("jruby.runtime.dynamic");
        dynamic = dynamicEnable == null || Boolean.valueOf(dynamicEnable);

        if (maxInProg > 0) {
            maxGeneratingRuntimes = maxInProg;
        } else {
            maxGeneratingRuntimes = maxGeneratingRuntimes_def;
        }
        logDynamicStatus();
    }

    public long getBaseTime() {
        return baseTime;
    }
    /**
     * Retrives ruby runtime from the object pool.
     *
     * @return JRuby runtime.
     */
    public Ruby borrowRuntime() {
        if (isAsyncEnabled()) {
            // check to see if we can get one right now
            try {
                Ruby gotten = queue.poll(50, TimeUnit.MILLISECONDS); // Wait, but only briefly
                if (gotten != null) {
                        if (dynamic) { // Only keep track of statistics if dynamic is enabled
                            if (queue.size() == 0) { // If we took the last runtime
                                int localQueue = queueTicks.incrementAndGet();
                                if (localQueue > queueThreashold) { // Deal with increasing the maximum queue size.
                                    queueTicks.set(0);
                                    int localIdle = maximumIdleRuntimes.incrementAndGet();
                                    if (localIdle > currentlyActiveRuntimes) {
                                        maximumIdleRuntimes.set(currentlyActiveRuntimes); // Volatile is enough for non-assignment operations (right?)
                                    }
                                }
                            } else { // Otherwise, there were at least two idle runtimes, so we have plenty lying around
                                int localNew = newTicks.decrementAndGet();
                                if (localNew < 0) {
                                    newTicks.set(0); // may drop an update, oh well
                                }
                                int localQueue = queueTicks.decrementAndGet(); // start thinking about reducing the queue size
                                if (localQueue < -queueThreashold) {
                                    queueTicks.set(0);
                                    // Reduce size of queue;
                                    int localIdle = maximumIdleRuntimes.decrementAndGet();
                                    if (localIdle < procs) { // Keep a minimum queue size of # of processors
                                        maximumIdleRuntimes.set(procs);
                                    }
                                }
                            }
                        }
                    SelectorThread.logger().log(Level.FINE,
                            "Recieved new runtime from the queue. " + currentlyActiveRuntimes + "/" + maximumActiveRuntimes +
                                    " active runtimes (" +  queue.size() + " idle, " + (currentlyActiveRuntimes - queue.size()) + " active");
                    return gotten;
                } else { // This is the branch for "We waited 50ms for a runtime, but did not recieve one."
                    // request that the number of runtimes be increased
                    if (dynamic) { // think about increasing the maximum number of active runtimes
                            int localUp = upTicks.incrementAndGet();
                            int localQueue = queueTicks.incrementAndGet();
                            if (localUp > upThreashold) {
                                upTicks.set(0);
                                queueTicks.set(0);
                                maximumIdleRuntimes.incrementAndGet();
                                int localMax = maximumActiveRuntimes.incrementAndGet();
                                if (localMax > hardMaxActiveRuntimes) {
                                    maximumActiveRuntimes.set(hardMaxActiveRuntimes);
                                }
                            }
                            if (localQueue > queueThreashold) {
                                queueTicks.set(0);
                                int localIdle = maximumIdleRuntimes.incrementAndGet();
                                if (localIdle > currentlyActiveRuntimes) {
                                    maximumIdleRuntimes.set(currentlyActiveRuntimes);
                                }
                            }
                        makeNewRuntime(); // Vote for more runtimes to be created
                    }

                    // Block until a runtime becomes available
                    gotten = queue.poll(DEFAULT_TIMEOUT, TimeUnit.SECONDS);
                    //System.out.println("Wait time (pool miss):							" + (System.currentTimeMillis() - time));
                    SelectorThread.logger().log(Level.FINE,
                            "Recieved new runtime from the queue. " + currentlyActiveRuntimes + "/" + maximumActiveRuntimes +
                                    " active runtimes (" +  queue.size() + " idle, " + (currentlyActiveRuntimes - queue.size()) + " active");
                    return gotten;
                }
            } catch (InterruptedException e) {
                // Following old InterruptedException behavior
                throw new RuntimeException(e);
            }
        } else {
            // old non-async behavior
            return queue.poll();
        }
    }

    /**
     * Returns runtime to the object pool.
     *
     * @param runtime - JRuby Rails runtime
     */
    public void returnRuntime(Ruby runtime) {
        //System.out.println((System.currentTimeMillis() - baseTime) + "\tDownticks\t\t" + downTicks + "\t" + queue.size());
        // check to see if we should bother returning this runtime to the queue
        if (queue.size() < maximumIdleRuntimes.intValue()) {
            queue.offer(runtime);
            if (dynamic) {
                int localDown = downTicks.decrementAndGet();
                if (localDown < 0) {
                    downTicks.set(0); // may drop an update, oh well.
                }
                SelectorThread.logger().log(Level.FINE,
                        "Returned runtime to the queue. " + currentlyActiveRuntimes + "/" + maximumActiveRuntimes +
                                " active runtimes (" +  queue.size() + " idle, " + (currentlyActiveRuntimes - queue.size()) + " active)");
            }
        } else {
            SelectorThread.logger().log(Level.FINE,
                    "Excessive idle Ruby runtimes: returned runtime not re-added to the queue. " + currentlyActiveRuntimes + "/" + maximumActiveRuntimes +
                            " active runtimes (" +  queue.size() + " idle, " + (currentlyActiveRuntimes - queue.size()) + " active)");
            if (dynamic) {
                    int localDown = downTicks.incrementAndGet();
                    int localUp = upTicks.decrementAndGet();
                    if (localDown > downThreashold) {
                        downTicks.set(0);
                        if (currentlyActiveRuntimes > hardMinActiveRuntimes) {
                            currentlyActiveRuntimes--;
                            // Otherwise, we just allow it to fall on the floor and be cleaned up by the GC
                            runtime.tearDown();
                        } else { // We think we should discard it, but doing so would violate hardMinActiveRuntimes
                            queue.offer(runtime);
                        }
                    } else {
                        // If we haven't exceeded the queue several times in a row, we keep the runtime anyway. Runtimes are expensive to make
                        // This means that maximumIdleRuntimes is a very soft limit, but it should be coming down fairly quickly if we are actually
                        // mostly idle.
                        queue.offer(runtime);
                    }
                    if (localUp < -upThreashold && maximumIdleRuntimes.intValue() > numberOfRuntime) {
                        // Reduce the number of runtimes we are willing to hold around
                        upTicks.set(0);
                        int localIdle = maximumIdleRuntimes.decrementAndGet();
                        int localActive = maximumActiveRuntimes.decrementAndGet();
                        if (localIdle < numberOfRuntime) {
                            maximumIdleRuntimes.set(numberOfRuntime);
                        }
                        if (localActive < hardMinActiveRuntimes) {
                            maximumActiveRuntimes.set(hardMinActiveRuntimes);
                        }
                    }
            } else {
                // If we aren't dynamic, this code is remarkably boring: We obey maximumIdleRuntimes and don't think too much about it
                currentlyActiveRuntimes--;
                runtime.tearDown();
            }
        }


    }

    /**
     * Starts the object pool.
     */
    public void start() {
        try {
            if (jrubyLib == null) {
                throw new IllegalStateException("jrubyLib or railsRoot can not be null.");
            }
            int pnum = procs;
            ExecutorService exec = Executors.newFixedThreadPool(pnum + 1);

            for (int i = 0; i < numberOfRuntime; i++) {
                currentlyActiveRuntimes++;
                exec.execute(new Runnable() {

                    public void run() {
                        long startTime = System.currentTimeMillis();
                        Ruby runtime = initializeRubyRuntime();
                        /*                       SelectorThread.logger().log(Level.INFO,
                                                        "JRuby and Rails instance instantiation took: " +
                                                                (System.currentTimeMillis() - startTime) + " ms");
                        */                      SelectorThread.logger().log(Level.INFO,
                            " 	" + (System.currentTimeMillis() - baseTime) + " 	New Ruby instance created	 	 " + (System.currentTimeMillis() - startTime)/1000);
                        queue.offer(runtime);
                    }
                });
            }
            exec.shutdown();
            exec.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }

    /**
     * Creates a new Ruby Runtime, to be called when the last runtime is removed from the queue
     */
    private void makeNewRuntime() {
            if ((currentlyActiveRuntimes < maximumActiveRuntimes.intValue()) && (currentlyGeneratingRuntimes < maxGeneratingRuntimes)) {
                int localNew = newTicks.addAndGet(2);
                //System.out.println("New Ticks: " + newTicks);
                if (localNew > newThreashold) {
                    currentlyActiveRuntimes++;
                    currentlyGeneratingRuntimes++;
                    newTicks.set(0);
                    runtimeGenerator.submit(new Runnable() {

                        public void run() {
                            long startTime = System.currentTimeMillis();
                            Ruby runtime = initializeRubyRuntime();
/*                    SelectorThread.logger().log(Level.INFO,
                                "JRuby and Rails instance instantiation took: " +
                                        (System.currentTimeMillis() - startTime) + " ms");
*/                  SelectorThread.logger().log(Level.INFO,
                                " 	" + (System.currentTimeMillis() - baseTime) + " 	New Ruby instance created	 	 " + (System.currentTimeMillis() - startTime)/1000);

                            queue.offer(runtime);
                            currentlyGeneratingRuntimes--;
                        }
                    });
                }
            }
    }

    /**
     * Shutdowns the object pool.
     */
    public void stop() {
        for (Ruby ruby : queue) {
            ruby.tearDown();
        }
        queue.clear();
        // Stop our runtime-creation thread
        try {
            runtimeGenerator.shutdown();
            runtimeGenerator.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            SelectorThread.logger().log(Level.WARNING,
                    "Object Pool interrupted on shutdown!");
        }

    }

    private void logDynamicStatus() {
        // logs the min, max, etc. values of the dynamic pool on startup
        if (dynamic) {
        SelectorThread.logger().log(Level.INFO,
                "JRuby dynamic pool created. Initial runtimes will be " + numberOfRuntime + ", hard minimum is " + hardMinActiveRuntimes +
                        ", hard maximum is " + hardMaxActiveRuntimes +
                        ". If you experiance out of memory errors, consider increasing the heap size or " +
                        "setting the jruby.runtime.min or jruby.runtime.max Java system properties. If " +
                        "starting GlassFish using java CLI then provide it as system property, such as -Djruby.runtime.min=1 -Djruby.runtime.max=2" +
                        ", otherwise make an entry into $GLASSFISH_INSTALL/domains/domain1/config/domain.xml, such as " +
                        "-Djruby.runtime.min=1-Djruby.runtime.max=2.");
        } else {
            SelectorThread.logger().log(Level.INFO,
                    "Pool started without dynamic resizing enabled. Pool will not attempt to determine the upper and lower bounds that it should be using");
        }
    }

    private Ruby setupRails(Ruby runtime, File rdebugScript) {
//        long startTime = System.currentTimeMillis();
        LoadService loadService = runtime.getLoadService();

        if (rdebugScript != null) {
            // synchronize io is optional
            String iosynch = System.getProperty("glassfish.rdebug.iosynch");
            if (iosynch != null && iosynch.length() > 0 && new File(iosynch).exists()) {
                SelectorThread.logger().log(Level.FINEST,
                        "rdebug io synchronized by " + new File(iosynch).getAbsolutePath());
                loadService.require(iosynch);
            }

            try {
                SelectorThread.logger().log(Level.FINEST, "Initializing rdebug...");
                runtime.runFromMain(
                        new java.io.BufferedInputStream(new FileInputStream(rdebugScript)),
                        rdebugScript.getAbsolutePath());
                SelectorThread.logger().log(Level.FINE, "rdebug started for " + railsRoot);
            } catch (Exception ex) {
                SelectorThread.logger().log(Level.SEVERE, ex.getLocalizedMessage(), ex);
            }
        } else {
            // load rails
            loadService.require(railsRoot + "/config/environment");
            loadService.require("cgi/force_nph");
            loadService.require("dispatcher");
        }

//        SelectorThread.logger().log(Level.INFO,
//                "loadRailsLibraries took: " +
//                        (System.currentTimeMillis() - startTime) + "ms");
        IRubyObject responder = (JavaEmbedUtils.newRuntimeAdapter()).eval(runtime, getDispatcherString());
        runtime.defineReadonlyVariable("$responder", responder);
//        SelectorThread.logger().log(Level.INFO,
//                "Rails Application: " + railsRoot + " instance instantiation took : " +
//                        (System.currentTimeMillis() - startTime) + "ms");
        return runtime;
    }

    private Ruby initializeRubyRuntime() {
        ArrayList libs = new ArrayList();
        libs.add("META-INF/jruby.home/lib/ruby/site_ruby/1.8");
        ClassCache cache = new ClassCache(RubyObjectPool.class.getClassLoader());
        RubyInstanceConfig config = new RubyInstanceConfig();
        File rdebugScript = configureRDebug(config);
        config.setLoader(RubyObjectPool.class.getClassLoader());
        config.setClassCache(cache);
        Ruby runtime = JavaEmbedUtils.initialize(libs, config);
        return setupRails(runtime, rdebugScript);
    }

    /**
     * Rails debugging for the ruby runtime via rdebug is enabled by setting
     * the following runtime properties.
     *
     *  -Dglassfish.rdebug=
     *  -Dglassfish.rdebug.port= (mandatory)
     *  -Dglassfish.rdebug.version= (optional)
     *  -Dglassfish.rdebug.iosynch= (optional)
     *  -Dglassfish.rdebug.verbose=true enables rdebug diagnostics
     *
     * @param config Ruby runtime configuration object (manages argv for rdebug)
     * @return File reference to rdebug script for execution.
     */
    private File configureRDebug(RubyInstanceConfig config) {
        String rdebug = System.getProperty("glassfish.rdebug");
        File rdebugScript = null;
        if(rdebug != null && rdebug.length() > 0) {
            SelectorThread.logger().log(Level.FINER, "Enabling rdebug-ide for Grizzly/Rails.");
            rdebugScript = new File(rdebug);
            if(rdebugScript.exists()) {
                List args = new ArrayList();

                // version is optional
                String rdebugVersion = System.getProperty("glassfish.rdebug.version");
                if(rdebugVersion != null && rdebugVersion.length() > 0) {
                    args.add(rdebugVersion);
                }

                // port spec is required
                String debugPort = System.getProperty("glassfish.rdebug.port");
                if(debugPort != null && debugPort.length() > 0) {
                    args.add("-p");
                    args.add(debugPort);
                    if("true".equals(System.getProperty("glassfish.rdebug.verbose"))) {
                        args.add("-d");
                    }
                    args.add("--");

                    File script = null;
                    Writer writer = null;
                    try {
                        script = File.createTempFile("grizzly_jruby_debug_", ".rb");
                        script.deleteOnExit();
                        writer = new FileWriter(script);
                        writer.write(
                                "#!/usr/bin/env jruby\n" +
                                        "#\n" +
                                        "# Launch script for Grizzly/JRuby connector when debugging Rails apps\n" +
                                        "# on GlassFish V3.\n" +
                                        "#\n" +
                                        "require \"" + railsRoot.replace("\\", "/") + "/config/environment\"\n" +
                                        "require \"cgi/force_nph\"\n" +
                                        "require \"dispatcher\"\n"
                        );
                        writer.flush();
                        args.add(script.getAbsolutePath());
                        config.setArgv(args.toArray(new String [args.size()]));
                    } catch(IOException ex) {
                        SelectorThread.logger().log(Level.SEVERE,
                                "Error writing Rails launch script for rdebug.", ex);
                        rdebugScript = null;
                    } finally {
                        if(writer != null) {
                            try {
                                writer.close();
                            } catch(IOException ex) {
                                SelectorThread.logger().log(Level.SEVERE,
                                        "Exception closing " + script.getAbsolutePath(), ex);
                            }
                        }
                    }
                } else {
                    SelectorThread.logger().log(Level.SEVERE,
                            "glassfish.rdebug.port undefined.  Disabling rdebug.");
                    rdebugScript = null;
                }
            } else {
                SelectorThread.logger().log(Level.SEVERE,
                        "rdebug-ide script " + rdebugScript.getAbsolutePath() +
                                " does not exist.  Disabling rdebug");
                rdebugScript = null;
            }
        }

        return rdebugScript;
    }


    /**
     * Gets JRUBY_LIB directory.
     *
     * @return JRUBY_LIB directory.
     */
    public String getJrubyLib() {
        return jrubyLib;
    }

    /**
     * Gets the number of directory.
     *
     * @return the number of directory;
     */
    public int getNumberOfRuntime() {
        return numberOfRuntime;
    }

    protected BlockingQueue getRubyRuntimeQueue() {
        return queue;
    }

    public boolean isAsyncEnabled() {
        return asyncEnabled;
    }

    private String getDispatcherString() {
        String str;
        StringBuffer completeText = new StringBuffer();
        try {
            InputStream is = getClass().getResourceAsStream("/dispatch.rb");
            BufferedReader br = new BufferedReader(new InputStreamReader(is));
            str = br.readLine();
            while (str != null) {
                completeText.append(str);
                completeText.append("\n");
                str = br.readLine();
            }
        } catch (Exception e) {
            SelectorThread.logger().log(Level.WARNING, "Exception when trying to read the dispatch.rb script", e);
        }

        return completeText.toString();
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy