Implemented RUN mode for ant jshell_support jshell_support_201604_trunk_premerge
authorSvata Dedic <sdedic@netbeans.org>
Thu, 07 Apr 2016 14:00:03 +0200
branchjshell_support
changeset 31221621bb04a00594
parent 312214 f11df1bdb337
child 312237 b125e87a9ea7
Implemented RUN mode for ant
java.preprocessorbridge/nbproject/project.xml
jshell.support/nbproject/project.xml
jshell.support/src/org/netbeans/modules/jshell/editor/CompletionFilter.java
jshell.support/src/org/netbeans/modules/jshell/editor/ConsoleEditor.java
jshell.support/src/org/netbeans/modules/jshell/j2se/JShellStartupExtender.java
jshell.support/src/org/netbeans/modules/jshell/j2se/LaunchAntListener.java
jshell.support/src/org/netbeans/modules/jshell/launch/DebugExecutionEnvironment.java
jshell.support/src/org/netbeans/modules/jshell/launch/JShellConnection.java
jshell.support/src/org/netbeans/modules/jshell/launch/JShellStartupExtender.java
jshell.support/src/org/netbeans/modules/jshell/launch/NIOStreams.java
jshell.support/src/org/netbeans/modules/jshell/launch/RemoteJShellAccessor.java
jshell.support/src/org/netbeans/modules/jshell/launch/RunExecutionEnvironment.java
jshell.support/src/org/netbeans/modules/jshell/launch/ShellAgent.java
jshell.support/src/org/netbeans/modules/jshell/launch/ShellDebuggerUtils.java
jshell.support/src/org/netbeans/modules/jshell/project/LaunchedProjectOpener.java
jshell.support/src/org/netbeans/modules/jshell/project/ProjectShellEnv.java
jshell.support/src/org/netbeans/modules/jshell/support/ShellSession.java
lib.jshell.agent/agentsrc/jdk/internal/jshell/remote/AgentWorker.java
lib.jshell.agent/agentsrc/jdk/internal/jshell/remote/RemoteAgent.java
lib.nbjshell/src/jdk/jshell/NbExecutionControl.java
lib.nbjshell/src/jdk/jshell/RemoteJShellService.java
libs.jshell/nbproject/project.xml
     1.1 --- a/java.preprocessorbridge/nbproject/project.xml	Wed Apr 06 13:12:27 2016 +0200
     1.2 +++ b/java.preprocessorbridge/nbproject/project.xml	Thu Apr 07 14:00:03 2016 +0200
     1.3 @@ -81,6 +81,7 @@
     1.4                  <friend>org.netbeans.modules.scala.editor</friend>
     1.5                  <friend>org.netbeans.modules.web.core.syntax</friend>
     1.6                  <friend>org.netbeans.modules.whitelist</friend>
     1.7 +                <friend>org.netbeans.modules.jshell.support</friend>
     1.8                  <package>org.netbeans.modules.java.preprocessorbridge.api</package>
     1.9                  <package>org.netbeans.modules.java.preprocessorbridge.spi</package>
    1.10              </friend-packages>
     2.1 --- a/jshell.support/nbproject/project.xml	Wed Apr 06 13:12:27 2016 +0200
     2.2 +++ b/jshell.support/nbproject/project.xml	Thu Apr 07 14:00:03 2016 +0200
     2.3 @@ -279,6 +279,14 @@
     2.4                      </run-dependency>
     2.5                  </dependency>
     2.6                  <dependency>
     2.7 +                    <code-name-base>org.netbeans.modules.java.preprocessorbridge</code-name-base>
     2.8 +                    <build-prerequisite/>
     2.9 +                    <compile-dependency/>
    2.10 +                    <run-dependency>
    2.11 +                        <specification-version>1.39</specification-version>
    2.12 +                    </run-dependency>
    2.13 +                </dependency>
    2.14 +                <dependency>
    2.15                      <code-name-base>org.netbeans.modules.java.project</code-name-base>
    2.16                      <build-prerequisite/>
    2.17                      <compile-dependency/>
     3.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     3.2 +++ b/jshell.support/src/org/netbeans/modules/jshell/editor/CompletionFilter.java	Thu Apr 07 14:00:03 2016 +0200
     3.3 @@ -0,0 +1,224 @@
     3.4 +/*
     3.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     3.6 + *
     3.7 + * Copyright 2016 Oracle and/or its affiliates. All rights reserved.
     3.8 + *
     3.9 + * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
    3.10 + * Other names may be trademarks of their respective owners.
    3.11 + *
    3.12 + * The contents of this file are subject to the terms of either the GNU
    3.13 + * General Public License Version 2 only ("GPL") or the Common
    3.14 + * Development and Distribution License("CDDL") (collectively, the
    3.15 + * "License"). You may not use this file except in compliance with the
    3.16 + * License. You can obtain a copy of the License at
    3.17 + * http://www.netbeans.org/cddl-gplv2.html
    3.18 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
    3.19 + * specific language governing permissions and limitations under the
    3.20 + * License.  When distributing the software, include this License Header
    3.21 + * Notice in each file and include the License file at
    3.22 + * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
    3.23 + * particular file as subject to the "Classpath" exception as provided
    3.24 + * by Oracle in the GPL Version 2 section of the License file that
    3.25 + * accompanied this code. If applicable, add the following below the
    3.26 + * License Header, with the fields enclosed by brackets [] replaced by
    3.27 + * your own identifying information:
    3.28 + * "Portions Copyrighted [year] [name of copyright owner]"
    3.29 + *
    3.30 + * If you wish your version of this file to be governed by only the CDDL
    3.31 + * or only the GPL Version 2, indicate your decision by adding
    3.32 + * "[Contributor] elects to include this software in this distribution
    3.33 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
    3.34 + * single choice of license, a recipient has the option to distribute
    3.35 + * your version of this file under either the CDDL, the GPL Version 2 or
    3.36 + * to extend the choice of license to its licensees as provided above.
    3.37 + * However, if you add GPL Version 2 code and therefore, elected the GPL
    3.38 + * Version 2 license, then the option applies only if the new code is
    3.39 + * made subject to such option by the copyright holder.
    3.40 + *
    3.41 + * Contributor(s):
    3.42 + *
    3.43 + * Portions Copyrighted 2016 Sun Microsystems, Inc.
    3.44 + */
    3.45 +package org.netbeans.modules.jshell.editor;
    3.46 +
    3.47 +import com.sun.source.doctree.DocCommentTree;
    3.48 +import com.sun.source.doctree.DocTree;
    3.49 +import com.sun.source.tree.CatchTree;
    3.50 +import com.sun.source.tree.ClassTree;
    3.51 +import com.sun.source.tree.CompilationUnitTree;
    3.52 +import com.sun.source.tree.MethodTree;
    3.53 +import com.sun.source.tree.Scope;
    3.54 +import com.sun.source.tree.Tree;
    3.55 +import com.sun.source.util.DocSourcePositions;
    3.56 +import com.sun.source.util.DocTreePath;
    3.57 +import com.sun.source.util.DocTrees;
    3.58 +import com.sun.source.util.TreePath;
    3.59 +import com.sun.source.util.Trees;
    3.60 +import java.io.IOException;
    3.61 +import java.text.BreakIterator;
    3.62 +import java.util.List;
    3.63 +import javax.lang.model.element.AnnotationMirror;
    3.64 +import javax.lang.model.element.AnnotationValue;
    3.65 +import javax.lang.model.element.Element;
    3.66 +import javax.lang.model.element.ExecutableElement;
    3.67 +import javax.lang.model.element.TypeElement;
    3.68 +import javax.lang.model.type.DeclaredType;
    3.69 +import javax.lang.model.type.ErrorType;
    3.70 +import javax.lang.model.type.TypeMirror;
    3.71 +import javax.tools.Diagnostic;
    3.72 +import javax.tools.FileObject;
    3.73 +
    3.74 +/**
    3.75 + * Filters out REPL generated classes from code completion.
    3.76 + * @author sdedic
    3.77 + */
    3.78 +final class CompletionFilter extends DocTrees {
    3.79 +
    3.80 +    public CompletionFilter(Trees delegate) {
    3.81 +        this.delegate = (DocTrees)delegate;
    3.82 +    }
    3.83 +
    3.84 +    @Override
    3.85 +    public BreakIterator getBreakIterator() {
    3.86 +        return delegate.getBreakIterator();
    3.87 +    }
    3.88 +
    3.89 +    @Override
    3.90 +    public DocCommentTree getDocCommentTree(TreePath tp) {
    3.91 +        return delegate.getDocCommentTree(tp);
    3.92 +    }
    3.93 +
    3.94 +    @Override
    3.95 +    public DocCommentTree getDocCommentTree(Element elmnt) {
    3.96 +        return delegate.getDocCommentTree(elmnt);
    3.97 +    }
    3.98 +
    3.99 +    @Override
   3.100 +    public DocCommentTree getDocCommentTree(FileObject fo) {
   3.101 +        return delegate.getDocCommentTree(fo);
   3.102 +    }
   3.103 +
   3.104 +    @Override
   3.105 +    public DocCommentTree getDocCommentTree(Element elmnt, String string) throws IOException {
   3.106 +        return delegate.getDocCommentTree(elmnt, string);
   3.107 +    }
   3.108 +
   3.109 +    @Override
   3.110 +    public Element getElement(DocTreePath dtp) {
   3.111 +        return delegate.getElement(dtp);
   3.112 +    }
   3.113 +
   3.114 +    @Override
   3.115 +    public List<DocTree> getFirstSentence(List<? extends DocTree> list) {
   3.116 +        return delegate.getFirstSentence(list);
   3.117 +    }
   3.118 +
   3.119 +    @Override
   3.120 +    public void printMessage(Diagnostic.Kind kind, CharSequence cs, DocTree dt, DocCommentTree dct, CompilationUnitTree cut) {
   3.121 +        delegate.printMessage(kind, cs, dt, dct, cut);
   3.122 +    }
   3.123 +
   3.124 +    @Override
   3.125 +    public void setBreakIterator(BreakIterator bi) {
   3.126 +        delegate.setBreakIterator(bi);
   3.127 +    }
   3.128 +
   3.129 +    @Override
   3.130 +    public DocSourcePositions getSourcePositions() {
   3.131 +        return delegate.getSourcePositions();
   3.132 +    }
   3.133 +
   3.134 +    @Override
   3.135 +    public Tree getTree(Element elmnt) {
   3.136 +        return delegate.getTree(elmnt);
   3.137 +    }
   3.138 +
   3.139 +    @Override
   3.140 +    public ClassTree getTree(TypeElement te) {
   3.141 +        return delegate.getTree(te);
   3.142 +    }
   3.143 +
   3.144 +    @Override
   3.145 +    public MethodTree getTree(ExecutableElement ee) {
   3.146 +        return delegate.getTree(ee);
   3.147 +    }
   3.148 +
   3.149 +    @Override
   3.150 +    public Tree getTree(Element elmnt, AnnotationMirror am) {
   3.151 +        return delegate.getTree(elmnt, am);
   3.152 +    }
   3.153 +
   3.154 +    @Override
   3.155 +    public Tree getTree(Element elmnt, AnnotationMirror am, AnnotationValue av) {
   3.156 +        return delegate.getTree(elmnt, am, av);
   3.157 +    }
   3.158 +
   3.159 +    @Override
   3.160 +    public TreePath getPath(CompilationUnitTree cut, Tree tree) {
   3.161 +        return delegate.getPath(cut, tree);
   3.162 +    }
   3.163 +
   3.164 +    @Override
   3.165 +    public TreePath getPath(Element elmnt) {
   3.166 +        return delegate.getPath(elmnt);
   3.167 +    }
   3.168 +
   3.169 +    @Override
   3.170 +    public TreePath getPath(Element elmnt, AnnotationMirror am) {
   3.171 +        return delegate.getPath(elmnt, am);
   3.172 +    }
   3.173 +
   3.174 +    @Override
   3.175 +    public TreePath getPath(Element elmnt, AnnotationMirror am, AnnotationValue av) {
   3.176 +        return delegate.getPath(elmnt, am, av);
   3.177 +    }
   3.178 +
   3.179 +    @Override
   3.180 +    public Element getElement(TreePath tp) {
   3.181 +        return delegate.getElement(tp);
   3.182 +    }
   3.183 +
   3.184 +    @Override
   3.185 +    public TypeMirror getTypeMirror(TreePath tp) {
   3.186 +        return delegate.getTypeMirror(tp);
   3.187 +    }
   3.188 +
   3.189 +    @Override
   3.190 +    public Scope getScope(TreePath tp) {
   3.191 +        return delegate.getScope(tp);
   3.192 +    }
   3.193 +
   3.194 +    @Override
   3.195 +    public String getDocComment(TreePath tp) {
   3.196 +        return delegate.getDocComment(tp);
   3.197 +    }
   3.198 +
   3.199 +    @Override
   3.200 +    public boolean isAccessible(Scope scope, TypeElement te) {
   3.201 +        if (te.getQualifiedName().toString().startsWith("REPL.")) {
   3.202 +            return false;
   3.203 +        }
   3.204 +        return delegate.isAccessible(scope, te);
   3.205 +    }
   3.206 +
   3.207 +    @Override
   3.208 +    public boolean isAccessible(Scope scope, Element elmnt, DeclaredType dt) {
   3.209 +        return delegate.isAccessible(scope, elmnt, dt);
   3.210 +    }
   3.211 +
   3.212 +    @Override
   3.213 +    public TypeMirror getOriginalType(ErrorType et) {
   3.214 +        return delegate.getOriginalType(et);
   3.215 +    }
   3.216 +
   3.217 +    @Override
   3.218 +    public void printMessage(Diagnostic.Kind kind, CharSequence cs, Tree tree, CompilationUnitTree cut) {
   3.219 +        delegate.printMessage(kind, cs, tree, cut);
   3.220 +    }
   3.221 +
   3.222 +    @Override
   3.223 +    public TypeMirror getLub(CatchTree ct) {
   3.224 +        return delegate.getLub(ct);
   3.225 +    }
   3.226 +    private DocTrees   delegate;
   3.227 +}
     4.1 --- a/jshell.support/src/org/netbeans/modules/jshell/editor/ConsoleEditor.java	Wed Apr 06 13:12:27 2016 +0200
     4.2 +++ b/jshell.support/src/org/netbeans/modules/jshell/editor/ConsoleEditor.java	Thu Apr 07 14:00:03 2016 +0200
     4.3 @@ -41,6 +41,7 @@
     4.4   */
     4.5  package org.netbeans.modules.jshell.editor;
     4.6  
     4.7 +import com.sun.source.util.Trees;
     4.8  import java.awt.Point;
     4.9  import java.awt.Rectangle;
    4.10  import java.awt.event.ActionEvent;
    4.11 @@ -61,6 +62,7 @@
    4.12  import javax.swing.text.Position;
    4.13  import org.netbeans.api.editor.document.LineDocument;
    4.14  import org.netbeans.api.editor.document.LineDocumentUtils;
    4.15 +import org.netbeans.modules.java.preprocessorbridge.spi.WrapperFactory;
    4.16  import org.netbeans.modules.jshell.env.JShellEnvironment;
    4.17  import org.netbeans.modules.jshell.env.ShellEvent;
    4.18  import org.netbeans.modules.jshell.env.ShellListener;
    4.19 @@ -210,6 +212,14 @@
    4.20          });
    4.21          
    4.22          (pane = getEditorPane()).setNavigationFilter(new NavFilter());
    4.23 +        
    4.24 +        d.putProperty(WrapperFactory.class, new WrapperFactory() {
    4.25 +            @Override
    4.26 +            public Trees wrapTrees(Trees trees) {
    4.27 +                return new CompletionFilter(trees);
    4.28 +            }
    4.29 +            
    4.30 +        });
    4.31          SwingUtilities.invokeLater(this::updateHourglass);
    4.32      }
    4.33      
     5.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     5.2 +++ b/jshell.support/src/org/netbeans/modules/jshell/j2se/JShellStartupExtender.java	Thu Apr 07 14:00:03 2016 +0200
     5.3 @@ -0,0 +1,106 @@
     5.4 +/*
     5.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     5.6 + *
     5.7 + * Copyright 2016 Oracle and/or its affiliates. All rights reserved.
     5.8 + *
     5.9 + * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
    5.10 + * Other names may be trademarks of their respective owners.
    5.11 + *
    5.12 + * The contents of this file are subject to the terms of either the GNU
    5.13 + * General Public License Version 2 only ("GPL") or the Common
    5.14 + * Development and Distribution License("CDDL") (collectively, the
    5.15 + * "License"). You may not use this file except in compliance with the
    5.16 + * License. You can obtain a copy of the License at
    5.17 + * http://www.netbeans.org/cddl-gplv2.html
    5.18 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
    5.19 + * specific language governing permissions and limitations under the
    5.20 + * License.  When distributing the software, include this License Header
    5.21 + * Notice in each file and include the License file at
    5.22 + * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
    5.23 + * particular file as subject to the "Classpath" exception as provided
    5.24 + * by Oracle in the GPL Version 2 section of the License file that
    5.25 + * accompanied this code. If applicable, add the following below the
    5.26 + * License Header, with the fields enclosed by brackets [] replaced by
    5.27 + * your own identifying information:
    5.28 + * "Portions Copyrighted [year] [name of copyright owner]"
    5.29 + *
    5.30 + * If you wish your version of this file to be governed by only the CDDL
    5.31 + * or only the GPL Version 2, indicate your decision by adding
    5.32 + * "[Contributor] elects to include this software in this distribution
    5.33 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
    5.34 + * single choice of license, a recipient has the option to distribute
    5.35 + * your version of this file under either the CDDL, the GPL Version 2 or
    5.36 + * to extend the choice of license to its licensees as provided above.
    5.37 + * However, if you add GPL Version 2 code and therefore, elected the GPL
    5.38 + * Version 2 license, then the option applies only if the new code is
    5.39 + * made subject to such option by the copyright holder.
    5.40 + *
    5.41 + * Contributor(s):
    5.42 + *
    5.43 + * Portions Copyrighted 2016 Sun Microsystems, Inc.
    5.44 + */
    5.45 +package org.netbeans.modules.jshell.j2se;
    5.46 +
    5.47 +import java.io.IOException;
    5.48 +import java.net.InetSocketAddress;
    5.49 +import java.util.Collections;
    5.50 +import java.util.EnumSet;
    5.51 +import java.util.List;
    5.52 +import java.util.logging.Level;
    5.53 +import java.util.logging.Logger;
    5.54 +import org.netbeans.api.extexecution.startup.StartupExtender.StartMode;
    5.55 +import org.netbeans.api.project.Project;
    5.56 +import org.netbeans.modules.java.j2seproject.api.J2SEPropertyEvaluator;
    5.57 +import org.netbeans.modules.jshell.launch.ShellAgent;
    5.58 +import org.netbeans.modules.jshell.launch.ShellLaunchManager;
    5.59 +import org.netbeans.modules.jshell.project.LaunchedProjectOpener;
    5.60 +import org.netbeans.modules.jshell.project.ProjectUtils;
    5.61 +import org.netbeans.spi.extexecution.startup.StartupExtenderImplementation;
    5.62 +import org.openide.util.Lookup;
    5.63 +
    5.64 +/**
    5.65 + * Hooks onto the J2SE project startup, and injects JShell agent as java agent.
    5.66 + *
    5.67 + * @author sdedic
    5.68 + */
    5.69 +@StartupExtenderImplementation.Registration(displayName = "Java Shell", startMode = {
    5.70 +    StartMode.DEBUG,
    5.71 +    StartMode.NORMAL,
    5.72 +})
    5.73 +public class JShellStartupExtender implements StartupExtenderImplementation {
    5.74 +    private static final Logger LOG = Logger.getLogger(JShellStartupExtender.class.getName());
    5.75 +    
    5.76 +    @Override
    5.77 +    public List<String> getArguments(Lookup context, StartMode mode) {
    5.78 +        LaunchedProjectOpener.init();
    5.79 +        
    5.80 +        Project p = context.lookup(Project.class);
    5.81 +        if (p == null) {
    5.82 +            return Collections.emptyList();
    5.83 +        }
    5.84 +        
    5.85 +        LOG.log(Level.FINE, "Augmenting {0} of project {1}", new Object[] { mode, p });
    5.86 +        
    5.87 +        InetSocketAddress isa;
    5.88 +        ShellAgent agent;
    5.89 +        // first check that the project has JShell enabled:
    5.90 +        if (!ProjectUtils.isJShellRunEnabled(p)) {
    5.91 +            LOG.log(Level.FINE, "Request for agent: Project {0} does not enable Java Shell.", p);
    5.92 +            return Collections.emptyList();
    5.93 +        }
    5.94 +        try {
    5.95 +            agent = ShellLaunchManager.getInstance().openForProject(p, 
    5.96 +                    mode == StartMode.DEBUG || mode == StartMode.TEST_DEBUG);
    5.97 +            isa = agent.getHandshakeAddress();
    5.98 +        } catch (IOException ex) {
    5.99 +            LOG.log(Level.INFO, "Could not obtain handshake address and key: ", ex);
   5.100 +            return Collections.emptyList();
   5.101 +        }
   5.102 +        LOG.log(Level.FINE, "Connect address is: {0}:{1}", new Object[] { isa.getHostString(), isa.getPort() });
   5.103 +        
   5.104 +        J2SEPropertyEvaluator  prjEval = p.getLookup().lookup(J2SEPropertyEvaluator.class);
   5.105 +        List<String> args = ShellLaunchManager.buildLocalJVMAgentArgs(agent, prjEval.evaluator()::getProperty);
   5.106 +        LOG.log(Level.FINE, "Final args: {0}", args);
   5.107 +        return args;
   5.108 +    }
   5.109 +}
     6.1 --- a/jshell.support/src/org/netbeans/modules/jshell/j2se/LaunchAntListener.java	Wed Apr 06 13:12:27 2016 +0200
     6.2 +++ b/jshell.support/src/org/netbeans/modules/jshell/j2se/LaunchAntListener.java	Thu Apr 07 14:00:03 2016 +0200
     6.3 @@ -75,7 +75,8 @@
     6.4          "debug-test-main-nb", // NOI18N
     6.5          "debug-test-with-main", // NOI18N
     6.6          "debug-single", // NOI18N
     6.7 -        "debug" // NOI18N
     6.8 +        "debug", // NOI18N
     6.9 +        "run",
    6.10      };
    6.11      
    6.12      private ShellLaunchManager mgr;
     7.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     7.2 +++ b/jshell.support/src/org/netbeans/modules/jshell/launch/DebugExecutionEnvironment.java	Thu Apr 07 14:00:03 2016 +0200
     7.3 @@ -0,0 +1,146 @@
     7.4 +/*
     7.5 + * To change this license header, choose License Headers in Project Properties.
     7.6 + * To change this template file, choose Tools | Templates
     7.7 + * and open the template in the editor.
     7.8 + */
     7.9 +package org.netbeans.modules.jshell.launch;
    7.10 +
    7.11 +import com.sun.jdi.ObjectReference;
    7.12 +import com.sun.jdi.VirtualMachine;
    7.13 +import java.io.IOException;
    7.14 +import java.io.InputStream;
    7.15 +import java.io.OutputStream;
    7.16 +import java.util.function.UnaryOperator;
    7.17 +import jdk.jshell.JDIRemoteAgent;
    7.18 +import org.openide.awt.StatusDisplayer;
    7.19 +import org.openide.util.NbBundle;
    7.20 +
    7.21 +/**
    7.22 + *
    7.23 + * @author sdedic
    7.24 + */
    7.25 +public class DebugExecutionEnvironment extends JDIRemoteAgent implements RemoteJShellAccessor, ShellLaunchListener {
    7.26 +    private boolean added;
    7.27 +    private volatile JShellConnection shellConnection;
    7.28 +    private boolean closed;
    7.29 +    final ShellAgent agent;
    7.30 +
    7.31 +    public DebugExecutionEnvironment(ShellAgent agent) {
    7.32 +        super(UnaryOperator.identity());
    7.33 +        this.agent = agent;
    7.34 +    }
    7.35 +    
    7.36 +    public JShellConnection getOpenedConnection() {
    7.37 +        synchronized (this) {
    7.38 +            return shellConnection;
    7.39 +        }
    7.40 +    }
    7.41 +    
    7.42 +    public ShellAgent getAgent() {
    7.43 +        return agent;
    7.44 +    }
    7.45 +
    7.46 +    @NbBundle.Messages({
    7.47 +        "MSG_AgentConnectionBroken2=Connection to Java Shell agent broken. Please re-run the project.", 
    7.48 +        "# {0} - error message", 
    7.49 +        "MSG_ErrorConnectingToAgent=Error connecting to Java Shell agent: {0}"
    7.50 +    })
    7.51 +    private JShellConnection getConnection(boolean dontConnect) throws IOException {
    7.52 +        synchronized (this) {
    7.53 +            if (closed || (dontConnect && shellConnection == null)) {
    7.54 +                throw new IOException(Bundle.MSG_AgentConnectionBroken2());
    7.55 +            }
    7.56 +            if (shellConnection != null) {
    7.57 +                return shellConnection;
    7.58 +            }
    7.59 +        }
    7.60 +        try {
    7.61 +            JShellConnection x = agent.createConnection();
    7.62 +            synchronized (this) {
    7.63 +                if (!added) {
    7.64 +                    ShellLaunchManager.getInstance().addLaunchListener(this);
    7.65 +                    added = true;
    7.66 +                }
    7.67 +                return this.shellConnection = x;
    7.68 +            }
    7.69 +        } catch (IOException ex) {
    7.70 +            StatusDisplayer.getDefault().setStatusText(Bundle.MSG_ErrorConnectingToAgent(ex.getLocalizedMessage()), 100);
    7.71 +            throw ex;
    7.72 +        }
    7.73 +    }
    7.74 +
    7.75 +    @Override
    7.76 +    public OutputStream getCommandStream() throws IOException {
    7.77 +        return getConnection(true).getAgentInput();
    7.78 +    }
    7.79 +
    7.80 +    @Override
    7.81 +    public InputStream getResponseStream() throws IOException {
    7.82 +        return getConnection(true).getAgentOutput();
    7.83 +    }
    7.84 +
    7.85 +    @Override
    7.86 +    public synchronized void closeStreams() {
    7.87 +        if (shellConnection == null) {
    7.88 +            return;
    7.89 +        }
    7.90 +        try {
    7.91 +            OutputStream os = shellConnection.getAgentInput();
    7.92 +            os.close();
    7.93 +        } catch (IOException ex) {
    7.94 +        }
    7.95 +        try {
    7.96 +            InputStream is = shellConnection.getAgentOutput();
    7.97 +            is.close();
    7.98 +        } catch (IOException ex) {
    7.99 +        }
   7.100 +        requestShutdown();
   7.101 +    }
   7.102 +
   7.103 +    @Override
   7.104 +    protected ObjectReference getAgentObjectReference() {
   7.105 +        return shellConnection.getAgentHandle();
   7.106 +    }
   7.107 +
   7.108 +    @Override
   7.109 +    protected VirtualMachine acquireVirtualMachine() throws IOException {
   7.110 +        return getConnection(false).getVirtualMachine();
   7.111 +    }
   7.112 +
   7.113 +    @Override
   7.114 +    public boolean requestShutdown() {
   7.115 +        agent.closeConnection(shellConnection);
   7.116 +        return false;
   7.117 +    }
   7.118 +
   7.119 +    @Override
   7.120 +    public void connectionInitiated(ShellLaunchEvent ev) {
   7.121 +    }
   7.122 +
   7.123 +    @Override
   7.124 +    public void handshakeCompleted(ShellLaunchEvent ev) {
   7.125 +    }
   7.126 +
   7.127 +    @Override
   7.128 +    public void connectionClosed(ShellLaunchEvent ev) {
   7.129 +        synchronized (this) {
   7.130 +            if (ev.getConnection() != this.shellConnection || closed) {
   7.131 +                return;
   7.132 +            }
   7.133 +            closed = true;
   7.134 +        }
   7.135 +//        shellEnv.reportClosedBridge(reportSession, true);
   7.136 +    }
   7.137 +
   7.138 +    @Override
   7.139 +    public void agentDestroyed(ShellLaunchEvent ev) {
   7.140 +        synchronized (this) {
   7.141 +            if (ev.getAgent() != agent || closed) {
   7.142 +                return;
   7.143 +            }
   7.144 +            closed = true;
   7.145 +        }
   7.146 +//        shellEnv.reportClosedBridge(reportSession, false);
   7.147 +    }
   7.148 +    
   7.149 +}
     8.1 --- a/jshell.support/src/org/netbeans/modules/jshell/launch/JShellConnection.java	Wed Apr 06 13:12:27 2016 +0200
     8.2 +++ b/jshell.support/src/org/netbeans/modules/jshell/launch/JShellConnection.java	Thu Apr 07 14:00:03 2016 +0200
     8.3 @@ -81,7 +81,7 @@
     8.4   * The connection is only valid during its operational time.
     8.5   * @author sdedic
     8.6   */
     8.7 -public final class JShellConnection {
     8.8 +public final class JShellConnection implements AutoCloseable {
     8.9      private static final Logger LOG = Logger.getLogger(JShellConnection.class.getName());
    8.10      
    8.11      private static final int WRITE_TIMEOUT = 10000;
    8.12 @@ -163,6 +163,14 @@
    8.13          return agentHandle;
    8.14      }
    8.15      
    8.16 +    public int getRemoteAgentId() {
    8.17 +        try {
    8.18 +            return ((InetSocketAddress)controlSocket.getRemoteAddress()).getPort();
    8.19 +        } catch (IOException ex) {
    8.20 +            return -1;
    8.21 +        }
    8.22 +    }
    8.23 +    
    8.24      /**
    8.25       * Simple wrapper, which interprets close() call as local close and will
    8.26       * fire appropriate disconnect events
    8.27 @@ -205,6 +213,10 @@
    8.28          return theAgent;
    8.29      }
    8.30      
    8.31 +    public void close() {
    8.32 +        shutDown();
    8.33 +    }
    8.34 +    
    8.35      /**
    8.36       * Shuts down the connection, may be called for local or remote close.
    8.37       */
     9.1 --- a/jshell.support/src/org/netbeans/modules/jshell/launch/JShellStartupExtender.java	Wed Apr 06 13:12:27 2016 +0200
     9.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     9.3 @@ -1,106 +0,0 @@
     9.4 -/*
     9.5 - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     9.6 - *
     9.7 - * Copyright 2016 Oracle and/or its affiliates. All rights reserved.
     9.8 - *
     9.9 - * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
    9.10 - * Other names may be trademarks of their respective owners.
    9.11 - *
    9.12 - * The contents of this file are subject to the terms of either the GNU
    9.13 - * General Public License Version 2 only ("GPL") or the Common
    9.14 - * Development and Distribution License("CDDL") (collectively, the
    9.15 - * "License"). You may not use this file except in compliance with the
    9.16 - * License. You can obtain a copy of the License at
    9.17 - * http://www.netbeans.org/cddl-gplv2.html
    9.18 - * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
    9.19 - * specific language governing permissions and limitations under the
    9.20 - * License.  When distributing the software, include this License Header
    9.21 - * Notice in each file and include the License file at
    9.22 - * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
    9.23 - * particular file as subject to the "Classpath" exception as provided
    9.24 - * by Oracle in the GPL Version 2 section of the License file that
    9.25 - * accompanied this code. If applicable, add the following below the
    9.26 - * License Header, with the fields enclosed by brackets [] replaced by
    9.27 - * your own identifying information:
    9.28 - * "Portions Copyrighted [year] [name of copyright owner]"
    9.29 - *
    9.30 - * If you wish your version of this file to be governed by only the CDDL
    9.31 - * or only the GPL Version 2, indicate your decision by adding
    9.32 - * "[Contributor] elects to include this software in this distribution
    9.33 - * under the [CDDL or GPL Version 2] license." If you do not indicate a
    9.34 - * single choice of license, a recipient has the option to distribute
    9.35 - * your version of this file under either the CDDL, the GPL Version 2 or
    9.36 - * to extend the choice of license to its licensees as provided above.
    9.37 - * However, if you add GPL Version 2 code and therefore, elected the GPL
    9.38 - * Version 2 license, then the option applies only if the new code is
    9.39 - * made subject to such option by the copyright holder.
    9.40 - *
    9.41 - * Contributor(s):
    9.42 - *
    9.43 - * Portions Copyrighted 2016 Sun Microsystems, Inc.
    9.44 - */
    9.45 -package org.netbeans.modules.jshell.launch;
    9.46 -
    9.47 -import java.io.File;
    9.48 -import java.io.IOException;
    9.49 -import java.net.InetSocketAddress;
    9.50 -import java.util.ArrayList;
    9.51 -import java.util.Collections;
    9.52 -import java.util.List;
    9.53 -import java.util.logging.Level;
    9.54 -import java.util.logging.Logger;
    9.55 -import org.netbeans.api.extexecution.startup.StartupExtender.StartMode;
    9.56 -import org.netbeans.api.project.Project;
    9.57 -import org.netbeans.modules.java.j2seproject.api.J2SEPropertyEvaluator;
    9.58 -import org.netbeans.modules.jshell.project.LaunchedProjectOpener;
    9.59 -import org.netbeans.modules.jshell.project.ProjectUtils;
    9.60 -import org.netbeans.spi.extexecution.startup.StartupExtenderImplementation;
    9.61 -import org.openide.modules.InstalledFileLocator;
    9.62 -import org.openide.util.Lookup;
    9.63 -
    9.64 -/**
    9.65 - * Hooks onto the J2SE project startup, and injects JShell agent as java agent.
    9.66 - *
    9.67 - * @author sdedic
    9.68 - */
    9.69 -@StartupExtenderImplementation.Registration(displayName = "Java Shell", startMode = StartMode.DEBUG)
    9.70 -public class JShellStartupExtender implements StartupExtenderImplementation {
    9.71 -    private static final Logger LOG = Logger.getLogger(JShellStartupExtender.class.getName());
    9.72 -    
    9.73 -    @Override
    9.74 -    public List<String> getArguments(Lookup context, StartMode mode) {
    9.75 -        LaunchedProjectOpener.init();
    9.76 -        
    9.77 -        if (mode != StartMode.DEBUG &&
    9.78 -            mode != StartMode.TEST_DEBUG) {
    9.79 -            return Collections.emptyList();
    9.80 -        }
    9.81 -        Project p = context.lookup(Project.class);
    9.82 -        if (p == null) {
    9.83 -            return Collections.emptyList();
    9.84 -        }
    9.85 -        
    9.86 -        LOG.log(Level.FINE, "Augmenting {0} of project {1}", new Object[] { mode, p });
    9.87 -        
    9.88 -        InetSocketAddress isa;
    9.89 -        ShellAgent agent;
    9.90 -        // first check that the project has JShell enabled:
    9.91 -        if (!ProjectUtils.isJShellRunEnabled(p)) {
    9.92 -            LOG.log(Level.FINE, "Request for agent: Project {0} does not enable Java Shell.", p);
    9.93 -            return Collections.emptyList();
    9.94 -        }
    9.95 -        try {
    9.96 -            agent = ShellLaunchManager.getInstance().openForProject(p, true);
    9.97 -            isa = agent.getHandshakeAddress();
    9.98 -        } catch (IOException ex) {
    9.99 -            LOG.log(Level.INFO, "Could not obtain handshake address and key: ", ex);
   9.100 -            return Collections.emptyList();
   9.101 -        }
   9.102 -        LOG.log(Level.FINE, "Connect address is: {0}:{1}", new Object[] { isa.getHostString(), isa.getPort() });
   9.103 -        
   9.104 -        J2SEPropertyEvaluator  prjEval = p.getLookup().lookup(J2SEPropertyEvaluator.class);
   9.105 -        List<String> args = ShellLaunchManager.buildLocalJVMAgentArgs(agent, prjEval.evaluator()::getProperty);
   9.106 -        LOG.log(Level.FINE, "Final args: {0}", args);
   9.107 -        return args;
   9.108 -    }
   9.109 -}
    10.1 --- a/jshell.support/src/org/netbeans/modules/jshell/launch/NIOStreams.java	Wed Apr 06 13:12:27 2016 +0200
    10.2 +++ b/jshell.support/src/org/netbeans/modules/jshell/launch/NIOStreams.java	Thu Apr 07 14:00:03 2016 +0200
    10.3 @@ -310,7 +310,7 @@
    10.4              byte out = (byte)(b & 0xff);
    10.5              singleBuffer.clear();
    10.6              singleBuffer.put(out);
    10.7 -          
    10.8 +            singleBuffer.flip();
    10.9              int sel;
   10.10              
   10.11              do {
    11.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    11.2 +++ b/jshell.support/src/org/netbeans/modules/jshell/launch/RemoteJShellAccessor.java	Thu Apr 07 14:00:03 2016 +0200
    11.3 @@ -0,0 +1,49 @@
    11.4 +/*
    11.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
    11.6 + *
    11.7 + * Copyright 2016 Oracle and/or its affiliates. All rights reserved.
    11.8 + *
    11.9 + * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
   11.10 + * Other names may be trademarks of their respective owners.
   11.11 + *
   11.12 + * The contents of this file are subject to the terms of either the GNU
   11.13 + * General Public License Version 2 only ("GPL") or the Common
   11.14 + * Development and Distribution License("CDDL") (collectively, the
   11.15 + * "License"). You may not use this file except in compliance with the
   11.16 + * License. You can obtain a copy of the License at
   11.17 + * http://www.netbeans.org/cddl-gplv2.html
   11.18 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
   11.19 + * specific language governing permissions and limitations under the
   11.20 + * License.  When distributing the software, include this License Header
   11.21 + * Notice in each file and include the License file at
   11.22 + * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
   11.23 + * particular file as subject to the "Classpath" exception as provided
   11.24 + * by Oracle in the GPL Version 2 section of the License file that
   11.25 + * accompanied this code. If applicable, add the following below the
   11.26 + * License Header, with the fields enclosed by brackets [] replaced by
   11.27 + * your own identifying information:
   11.28 + * "Portions Copyrighted [year] [name of copyright owner]"
   11.29 + *
   11.30 + * If you wish your version of this file to be governed by only the CDDL
   11.31 + * or only the GPL Version 2, indicate your decision by adding
   11.32 + * "[Contributor] elects to include this software in this distribution
   11.33 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
   11.34 + * single choice of license, a recipient has the option to distribute
   11.35 + * your version of this file under either the CDDL, the GPL Version 2 or
   11.36 + * to extend the choice of license to its licensees as provided above.
   11.37 + * However, if you add GPL Version 2 code and therefore, elected the GPL
   11.38 + * Version 2 license, then the option applies only if the new code is
   11.39 + * made subject to such option by the copyright holder.
   11.40 + *
   11.41 + * Contributor(s):
   11.42 + *
   11.43 + * Portions Copyrighted 2016 Sun Microsystems, Inc.
   11.44 + */
   11.45 +package org.netbeans.modules.jshell.launch;
   11.46 +
   11.47 +import jdk.jshell.RemoteJShellService;
   11.48 +import org.netbeans.modules.jshell.launch.JShellConnection;
   11.49 +
   11.50 +public interface RemoteJShellAccessor extends RemoteJShellService {
   11.51 +    public JShellConnection getOpenedConnection();
   11.52 +}
    12.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    12.2 +++ b/jshell.support/src/org/netbeans/modules/jshell/launch/RunExecutionEnvironment.java	Thu Apr 07 14:00:03 2016 +0200
    12.3 @@ -0,0 +1,264 @@
    12.4 +/*
    12.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
    12.6 + *
    12.7 + * Copyright 2016 Oracle and/or its affiliates. All rights reserved.
    12.8 + *
    12.9 + * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
   12.10 + * Other names may be trademarks of their respective owners.
   12.11 + *
   12.12 + * The contents of this file are subject to the terms of either the GNU
   12.13 + * General Public License Version 2 only ("GPL") or the Common
   12.14 + * Development and Distribution License("CDDL") (collectively, the
   12.15 + * "License"). You may not use this file except in compliance with the
   12.16 + * License. You can obtain a copy of the License at
   12.17 + * http://www.netbeans.org/cddl-gplv2.html
   12.18 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
   12.19 + * specific language governing permissions and limitations under the
   12.20 + * License.  When distributing the software, include this License Header
   12.21 + * Notice in each file and include the License file at
   12.22 + * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
   12.23 + * particular file as subject to the "Classpath" exception as provided
   12.24 + * by Oracle in the GPL Version 2 section of the License file that
   12.25 + * accompanied this code. If applicable, add the following below the
   12.26 + * License Header, with the fields enclosed by brackets [] replaced by
   12.27 + * your own identifying information:
   12.28 + * "Portions Copyrighted [year] [name of copyright owner]"
   12.29 + *
   12.30 + * If you wish your version of this file to be governed by only the CDDL
   12.31 + * or only the GPL Version 2, indicate your decision by adding
   12.32 + * "[Contributor] elects to include this software in this distribution
   12.33 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
   12.34 + * single choice of license, a recipient has the option to distribute
   12.35 + * your version of this file under either the CDDL, the GPL Version 2 or
   12.36 + * to extend the choice of license to its licensees as provided above.
   12.37 + * However, if you add GPL Version 2 code and therefore, elected the GPL
   12.38 + * Version 2 license, then the option applies only if the new code is
   12.39 + * made subject to such option by the copyright holder.
   12.40 + *
   12.41 + * Contributor(s):
   12.42 + *
   12.43 + * Portions Copyrighted 2016 Sun Microsystems, Inc.
   12.44 + */
   12.45 +package org.netbeans.modules.jshell.launch;
   12.46 +
   12.47 +import java.io.IOException;
   12.48 +import java.io.InputStream;
   12.49 +import java.io.ObjectInputStream;
   12.50 +import java.io.ObjectOutputStream;
   12.51 +import java.io.OutputStream;
   12.52 +import java.util.Map;
   12.53 +import java.util.Map.Entry;
   12.54 +import java.util.concurrent.Executor;
   12.55 +import jdk.internal.jshell.remote.RemoteCodes;
   12.56 +import jdk.jshell.NbExecutionControl;
   12.57 +import org.openide.awt.StatusDisplayer;
   12.58 +
   12.59 +/**
   12.60 + * Exec environment suitable for machines without active JDI connection.
   12.61 + *
   12.62 + * @author sdedic
   12.63 + */
   12.64 +public class RunExecutionEnvironment implements RemoteJShellAccessor, Executor, ShellLaunchListener {
   12.65 +    private final ShellAgent agent;
   12.66 +    
   12.67 +    private boolean added;
   12.68 +    private volatile boolean closed;
   12.69 +    private JShellConnection shellConnection;
   12.70 +    private ObjectInputStream dis;
   12.71 +    private ObjectOutputStream dos;
   12.72 +
   12.73 +    public RunExecutionEnvironment(ShellAgent agent) {
   12.74 +        this.agent = agent;
   12.75 +    }
   12.76 +    
   12.77 +    public JShellConnection getOpenedConnection() {
   12.78 +        synchronized (this) {
   12.79 +            return shellConnection;
   12.80 +        }
   12.81 +    }
   12.82 +
   12.83 +    @Override
   12.84 +    public void redefineClasses(Map<Object, byte[]> redefines) throws IOException, IllegalStateException {
   12.85 +        if (dis == null || dos == null) {
   12.86 +            throw new UnsupportedOperationException("streams not opened");
   12.87 +        }
   12.88 +        dos.writeInt(NbExecutionControl.CMD_REDEFINE);
   12.89 +        dos.writeInt(redefines.size());
   12.90 +        for (Entry<Object, byte[]> en : redefines.entrySet()) {
   12.91 +            Long l = (Long)en.getKey();
   12.92 +            
   12.93 +            dos.writeLong(l);
   12.94 +            dos.writeObject(en.getValue());
   12.95 +        }
   12.96 +        dos.flush();
   12.97 +        
   12.98 +        int code = dis.readInt();
   12.99 +        if (code == RemoteCodes.RESULT_SUCCESS) {
  12.100 +            return;
  12.101 +        }
  12.102 +        if (code == RemoteCodes.RESULT_FAIL) {
  12.103 +            String msg = dis.readUTF();
  12.104 +            throw new IllegalStateException(msg);
  12.105 +        } else {
  12.106 +            throw new IOException("Invalid response code");
  12.107 +        }
  12.108 +    }
  12.109 +
  12.110 +    @Override
  12.111 +    public Object getClassHandle(String className) {
  12.112 +        if (dis == null || dos == null) {
  12.113 +            // no class sent over -> no class ever could be defined.
  12.114 +            return null;
  12.115 +        }
  12.116 +        try {
  12.117 +            dos.writeInt(NbExecutionControl.CMD_CLASSID);
  12.118 +            dos.writeUTF(className);
  12.119 +            dos.flush();
  12.120 +            
  12.121 +            int code = dis.readInt();
  12.122 +            if (code != RemoteCodes.RESULT_SUCCESS) {
  12.123 +                return null;
  12.124 +            }
  12.125 +            long l = dis.readLong();
  12.126 +            return l == -1 ? null : l;
  12.127 +        } catch (IOException ex) {
  12.128 +            return null;
  12.129 +        }
  12.130 +    }
  12.131 +
  12.132 +    @Override
  12.133 +    public Executor getCodeExecutor() {
  12.134 +        return this;
  12.135 +    }
  12.136 +
  12.137 +    @Override
  12.138 +    public void waitConnected(long millis) throws IOException {
  12.139 +        getConnection(false);
  12.140 +    }
  12.141 +
  12.142 +    /**
  12.143 +     * Sends stop user code instruction. Must create a separate connection to the agent
  12.144 +     * @return
  12.145 +     * @throws IllegalStateException 
  12.146 +     */
  12.147 +    @Override
  12.148 +    public boolean sendStopUserCode() throws IOException {
  12.149 +        int id = this.shellConnection.getRemoteAgentId();
  12.150 +        try (JShellConnection stopConnection = agent.createConnection();
  12.151 +            ObjectInputStream in = new ObjectInputStream(stopConnection.getAgentOutput());
  12.152 +            ObjectOutputStream out = new ObjectOutputStream(stopConnection.getAgentInput())
  12.153 +        ) {
  12.154 +            
  12.155 +            out.writeInt(NbExecutionControl.CMD_STOP);
  12.156 +            out.writeInt(id);
  12.157 +            out.flush();
  12.158 +            
  12.159 +            int success = in.readInt();
  12.160 +            return success == RemoteCodes.RESULT_SUCCESS;
  12.161 +        }
  12.162 +    }
  12.163 +
  12.164 +    @Override
  12.165 +    public String decorateLaunchArgs(String baseArgs) {
  12.166 +        return baseArgs;
  12.167 +    }
  12.168 +
  12.169 +    @Override
  12.170 +    public boolean requestShutdown() {
  12.171 +        agent.closeConnection(shellConnection);
  12.172 +        return false;
  12.173 +    }
  12.174 +
  12.175 +    private JShellConnection getConnection(boolean dontConnect) throws IOException {
  12.176 +        synchronized (this) {
  12.177 +            if (closed || (dontConnect && shellConnection == null)) {
  12.178 +                throw new IOException(Bundle.MSG_AgentConnectionBroken());
  12.179 +            }
  12.180 +            if (shellConnection != null) {
  12.181 +                return shellConnection;
  12.182 +            }
  12.183 +        }
  12.184 +        try {
  12.185 +            JShellConnection x = agent.createConnection();
  12.186 +            synchronized (this) {
  12.187 +                if (!added) {
  12.188 +                    ShellLaunchManager.getInstance().addLaunchListener(this);
  12.189 +                    added = true;
  12.190 +                }
  12.191 +                return this.shellConnection = x;
  12.192 +            }
  12.193 +        } catch (IOException ex) {
  12.194 +            StatusDisplayer.getDefault().setStatusText(Bundle.MSG_ErrorConnectingToAgent(ex.getLocalizedMessage()), 100);
  12.195 +            throw ex;
  12.196 +        }
  12.197 +    }
  12.198 +
  12.199 +    @Override
  12.200 +    public OutputStream getCommandStream() throws IOException {
  12.201 +        if (dos != null) {
  12.202 +            return dos;
  12.203 +        }
  12.204 +        return dos = new ObjectOutputStream(getConnection(true).getAgentInput());
  12.205 +    }
  12.206 +
  12.207 +    @Override
  12.208 +    public InputStream getResponseStream() throws IOException {
  12.209 +        if (dis != null) {
  12.210 +            
  12.211 +        }
  12.212 +        return dis = new ObjectInputStream(getConnection(true).getAgentOutput());
  12.213 +    }
  12.214 +
  12.215 +    @Override
  12.216 +    public synchronized void closeStreams() {
  12.217 +        if (shellConnection == null) {
  12.218 +            return;
  12.219 +        }
  12.220 +        try {
  12.221 +            OutputStream os = shellConnection.getAgentInput();
  12.222 +            os.close();
  12.223 +        } catch (IOException ex) {
  12.224 +        }
  12.225 +        try {
  12.226 +            InputStream is = shellConnection.getAgentOutput();
  12.227 +            is.close();
  12.228 +        } catch (IOException ex) {
  12.229 +        }
  12.230 +
  12.231 +        requestShutdown();
  12.232 +    }
  12.233 +
  12.234 +    @Override
  12.235 +    public void connectionInitiated(ShellLaunchEvent ev) { }
  12.236 +
  12.237 +    @Override
  12.238 +    public void handshakeCompleted(ShellLaunchEvent ev) { }
  12.239 +
  12.240 +    @Override
  12.241 +    public void connectionClosed(ShellLaunchEvent ev) {
  12.242 +        synchronized (this) {
  12.243 +            if (ev.getConnection() != this.shellConnection || closed) {
  12.244 +                return;
  12.245 +            }
  12.246 +            closed = true;
  12.247 +        }
  12.248 +        ShellLaunchManager.getInstance().removeLaunchListener(this);
  12.249 +    }
  12.250 +
  12.251 +    @Override
  12.252 +    public void agentDestroyed(ShellLaunchEvent ev) {
  12.253 +        synchronized (this) {
  12.254 +            if (ev.getAgent() != agent || closed) {
  12.255 +                return;
  12.256 +            }
  12.257 +            this.shellConnection = null;
  12.258 +            closed = true;
  12.259 +        }
  12.260 +        ShellLaunchManager.getInstance().removeLaunchListener(this);
  12.261 +    }
  12.262 +
  12.263 +    @Override
  12.264 +    public void execute(Runnable command) {
  12.265 +        command.run();
  12.266 +    }
  12.267 +}
    13.1 --- a/jshell.support/src/org/netbeans/modules/jshell/launch/ShellAgent.java	Wed Apr 06 13:12:27 2016 +0200
    13.2 +++ b/jshell.support/src/org/netbeans/modules/jshell/launch/ShellAgent.java	Thu Apr 07 14:00:03 2016 +0200
    13.3 @@ -294,4 +294,18 @@
    13.4          }
    13.5          return con;
    13.6      }
    13.7 +    
    13.8 +    public RemoteJShellAccessor createRemoteService() throws IOException {
    13.9 +        if (closed) {
   13.10 +            throw new IOException("Closed");
   13.11 +        }
   13.12 +        if (expectDebugger) {
   13.13 +            if (debuggerSession == null) {
   13.14 +                throw new IOException("Debugger unavailable");
   13.15 +            }
   13.16 +            return new DebugExecutionEnvironment(this);
   13.17 +        } else {
   13.18 +            return new RunExecutionEnvironment(this);
   13.19 +        }
   13.20 +    }
   13.21  }
    14.1 --- a/jshell.support/src/org/netbeans/modules/jshell/launch/ShellDebuggerUtils.java	Wed Apr 06 13:12:27 2016 +0200
    14.2 +++ b/jshell.support/src/org/netbeans/modules/jshell/launch/ShellDebuggerUtils.java	Thu Apr 07 14:00:03 2016 +0200
    14.3 @@ -79,6 +79,9 @@
    14.4      }
    14.5      
    14.6      static ObjectReference getWorkerHandle(Session debuggerSession, int remotePortAddress) {
    14.7 +        if (debuggerSession == null) {
    14.8 +            return null;
    14.9 +        }
   14.10          JPDADebugger debugger = debuggerSession.lookupFirst(null, JPDADebugger.class);
   14.11          if (debugger == null) {
   14.12              return null;
    15.1 --- a/jshell.support/src/org/netbeans/modules/jshell/project/LaunchedProjectOpener.java	Wed Apr 06 13:12:27 2016 +0200
    15.2 +++ b/jshell.support/src/org/netbeans/modules/jshell/project/LaunchedProjectOpener.java	Thu Apr 07 14:00:03 2016 +0200
    15.3 @@ -102,7 +102,7 @@
    15.4              return;
    15.5          }
    15.6          
    15.7 -        final JShellEnvironment attachEnv = new DebugShellEnv(agent, p, 
    15.8 +        final JShellEnvironment attachEnv = new ProjectShellEnv(agent, p, 
    15.9                  Bundle.Title_JShellOnDebugger(ProjectUtils.getInformation(p).getDisplayName()));
   15.10  
   15.11          boolean ok = false;
   15.12 @@ -119,31 +119,6 @@
   15.13          }
   15.14      }
   15.15      
   15.16 -    private static class DebugShellEnv extends JShellEnvironment {
   15.17 -        private final ShellAgent agent;
   15.18 -
   15.19 -        public DebugShellEnv(ShellAgent agent, Project project, String displayName) {
   15.20 -            super(project, displayName);
   15.21 -            this.agent = agent;
   15.22 -        }
   15.23 -
   15.24 -        @Override
   15.25 -        protected InputOutput createInputOutput() {
   15.26 -            return agent.getIO();
   15.27 -        }
   15.28 -        
   15.29 -        public RemoteJShellService createExecutionEnv() {
   15.30 -            return new DebugExecutionEnvironment(getSession(), agent, this);
   15.31 -        }
   15.32 -        
   15.33 -        protected void reportClosedBridge(ShellSession s, boolean disconnectOrShutdown) {
   15.34 -            if (disconnectOrShutdown) {
   15.35 -                notifyDisconnected(s);
   15.36 -            } else {
   15.37 -                notifyShutdown();
   15.38 -            }
   15.39 -        }
   15.40 -    }
   15.41  
   15.42      @Override
   15.43      public void agentDestroyed(ShellLaunchEvent ev) { }
   15.44 @@ -151,125 +126,4 @@
   15.45      @Override
   15.46      public void connectionClosed(ShellLaunchEvent ev) { }
   15.47      
   15.48 -    static class DebugExecutionEnvironment extends JDIRemoteAgent implements ShellLaunchListener {
   15.49 -        private boolean             added;
   15.50 -        volatile JShellConnection    shellConnection;
   15.51 -        private boolean             closed;
   15.52 -        
   15.53 -        final ShellAgent          agent;
   15.54 -        final DebugShellEnv   shellEnv;
   15.55 -        final ShellSession    reportSession;
   15.56 -        
   15.57 -        public DebugExecutionEnvironment(ShellSession s, ShellAgent agent, DebugShellEnv env) {
   15.58 -            super(UnaryOperator.identity());
   15.59 -            this.shellEnv = env;
   15.60 -            this.agent = agent;
   15.61 -            this.reportSession = s;
   15.62 -        }
   15.63 -        
   15.64 -        @NbBundle.Messages({
   15.65 -            "MSG_AgentConnectionBroken=Connection to Java Shell agent broken. Please re-run the project.",
   15.66 -            "# {0} - error message",
   15.67 -            "MSG_ErrorConnectingToAgent=Error connecting to Java Shell agent: {0}"
   15.68 -        })
   15.69 -        private JShellConnection getConnection(boolean dontConnect) throws IOException {
   15.70 -            synchronized (this) {
   15.71 -                if (closed || (dontConnect && shellConnection == null)) {
   15.72 -                    throw new IOException(Bundle.MSG_AgentConnectionBroken());
   15.73 -                }
   15.74 -                if (shellConnection != null) {
   15.75 -                    return shellConnection;
   15.76 -                }
   15.77 -            }
   15.78 -            try {
   15.79 -                JShellConnection x = agent.createConnection();
   15.80 -                synchronized (this) {
   15.81 -                    if (!added) {
   15.82 -                        ShellLaunchManager.getInstance().addLaunchListener(this);
   15.83 -                        added = true;
   15.84 -                    }
   15.85 -                    return this.shellConnection = x;
   15.86 -                }
   15.87 -            } catch (IOException ex) {
   15.88 -                StatusDisplayer.getDefault().setStatusText(Bundle.MSG_ErrorConnectingToAgent(ex.getLocalizedMessage()), 100);
   15.89 -                throw ex;
   15.90 -            }
   15.91 -        }
   15.92 -        
   15.93 -        @Override
   15.94 -        public OutputStream getCommandStream() throws IOException {
   15.95 -            return getConnection(true).getAgentInput();
   15.96 -        }
   15.97 -
   15.98 -        @Override
   15.99 -        public InputStream getResponseStream() throws IOException {
  15.100 -            return getConnection(true).getAgentOutput();
  15.101 -        }
  15.102 -
  15.103 -        @Override
  15.104 -        public synchronized void closeStreams() {
  15.105 -            if (shellConnection == null) {
  15.106 -                return;
  15.107 -            }
  15.108 -            try {
  15.109 -                OutputStream os = shellConnection.getAgentInput();
  15.110 -                os.close();
  15.111 -            } catch (IOException ex) {
  15.112 -            }
  15.113 -            try {
  15.114 -                InputStream is = shellConnection.getAgentOutput();
  15.115 -                is.close();
  15.116 -            } catch (IOException ex) {
  15.117 -            }
  15.118 -            
  15.119 -            requestShutdown();
  15.120 -        }
  15.121 -
  15.122 -        @Override
  15.123 -        protected ObjectReference getAgentObjectReference() {
  15.124 -            return shellConnection.getAgentHandle();
  15.125 -        }
  15.126 -
  15.127 -        @Override
  15.128 -        protected VirtualMachine acquireVirtualMachine() throws IOException {
  15.129 -            return getConnection(false).getVirtualMachine();
  15.130 -        }
  15.131 -
  15.132 -        @Override
  15.133 -        public boolean requestShutdown() {
  15.134 -            agent.closeConnection(shellConnection);
  15.135 -            return false;
  15.136 -        }
  15.137 -
  15.138 -        @Override
  15.139 -        public void connectionInitiated(ShellLaunchEvent ev) {
  15.140 -        }
  15.141 -
  15.142 -        @Override
  15.143 -        public void handshakeCompleted(ShellLaunchEvent ev) {
  15.144 -        }
  15.145 -        
  15.146 -        @Override
  15.147 -        public  void connectionClosed(ShellLaunchEvent ev) {
  15.148 -            synchronized (this) {
  15.149 -                if (closed) {
  15.150 -                    return;
  15.151 -                }
  15.152 -                closed = true;
  15.153 -            }
  15.154 -            shellEnv.reportClosedBridge(reportSession, true);
  15.155 -        }
  15.156 -
  15.157 -        @Override
  15.158 -        public void agentDestroyed(ShellLaunchEvent ev) {
  15.159 -            synchronized (this) {
  15.160 -                if (ev.getAgent() != agent || closed) {
  15.161 -                    return;
  15.162 -                }
  15.163 -                this.shellConnection = null;
  15.164 -                closed = true;
  15.165 -            }
  15.166 -            shellEnv.reportClosedBridge(reportSession, false);
  15.167 -        }
  15.168 -    }
  15.169  }
    16.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    16.2 +++ b/jshell.support/src/org/netbeans/modules/jshell/project/ProjectShellEnv.java	Thu Apr 07 14:00:03 2016 +0200
    16.3 @@ -0,0 +1,100 @@
    16.4 +/*
    16.5 + * To change this license header, choose License Headers in Project Properties.
    16.6 + * To change this template file, choose Tools | Templates
    16.7 + * and open the template in the editor.
    16.8 + */
    16.9 +package org.netbeans.modules.jshell.project;
   16.10 +
   16.11 +import org.netbeans.modules.jshell.launch.RemoteJShellAccessor;
   16.12 +import java.io.IOException;
   16.13 +import jdk.jshell.RemoteJShellService;
   16.14 +import org.netbeans.api.project.Project;
   16.15 +import org.netbeans.modules.jshell.env.JShellEnvironment;
   16.16 +import org.netbeans.modules.jshell.launch.JShellConnection;
   16.17 +import org.netbeans.modules.jshell.launch.ShellAgent;
   16.18 +import org.netbeans.modules.jshell.launch.ShellLaunchEvent;
   16.19 +import org.netbeans.modules.jshell.launch.ShellLaunchListener;
   16.20 +import org.netbeans.modules.jshell.launch.ShellLaunchManager;
   16.21 +import org.netbeans.modules.jshell.support.ShellSession;
   16.22 +import org.openide.windows.InputOutput;
   16.23 +
   16.24 +/**
   16.25 + *
   16.26 + * @author sdedic
   16.27 + */
   16.28 +class ProjectShellEnv extends JShellEnvironment {
   16.29 +    private final ShellAgent agent;
   16.30 +
   16.31 +    public ProjectShellEnv(ShellAgent agent, Project project, String displayName) {
   16.32 +        super(project, displayName);
   16.33 +        this.agent = agent;
   16.34 +    }
   16.35 +
   16.36 +    @Override
   16.37 +    protected InputOutput createInputOutput() {
   16.38 +        return agent.getIO();
   16.39 +    }
   16.40 +
   16.41 +    public RemoteJShellService createExecutionEnv() {
   16.42 +        try {
   16.43 +            RemoteJShellAccessor accessor = agent.createRemoteService();
   16.44 +            CloseNotifier nf = new CloseNotifier(accessor, getSession());
   16.45 +            ShellLaunchManager.getInstance().addLaunchListener(nf);
   16.46 +            return accessor;
   16.47 +        } catch (IOException ex) {
   16.48 +            return null;
   16.49 +        }
   16.50 +    }
   16.51 +
   16.52 +    protected void reportClosedBridge(ShellSession s, boolean disconnectOrShutdown) {
   16.53 +        if (disconnectOrShutdown) {
   16.54 +            notifyDisconnected(s);
   16.55 +        } else {
   16.56 +            notifyShutdown();
   16.57 +        }
   16.58 +    }
   16.59 +    
   16.60 +    private class CloseNotifier implements ShellLaunchListener {
   16.61 +        private final RemoteJShellAccessor accessor;
   16.62 +        private final ShellSession         session;
   16.63 +        private boolean closed;
   16.64 +        
   16.65 +        public CloseNotifier(RemoteJShellAccessor accessor, ShellSession session) {
   16.66 +            this.accessor = accessor;
   16.67 +            this.session = session;
   16.68 +        }
   16.69 +        
   16.70 +        @Override
   16.71 +        public void connectionClosed(ShellLaunchEvent ev) {
   16.72 +            JShellConnection c = ev.getConnection();
   16.73 +            synchronized (this) {
   16.74 +                if (closed || c != accessor.getOpenedConnection()) {
   16.75 +                    return;
   16.76 +                }
   16.77 +                closed = true;
   16.78 +            }
   16.79 +
   16.80 +            notifyDisconnected(session);
   16.81 +            ShellLaunchManager.getInstance().removeLaunchListener(this);
   16.82 +        }
   16.83 +
   16.84 +        @Override
   16.85 +        public void agentDestroyed(ShellLaunchEvent ev) {
   16.86 +            synchronized (this) {
   16.87 +                if (closed || ev.getAgent() != agent) {
   16.88 +                    return;
   16.89 +                }
   16.90 +                closed = true;
   16.91 +            }
   16.92 +            notifyShutdown();
   16.93 +            ShellLaunchManager.getInstance().removeLaunchListener(this);
   16.94 +        }
   16.95 +
   16.96 +        @Override
   16.97 +        public void connectionInitiated(ShellLaunchEvent ev) {}
   16.98 +
   16.99 +        @Override
  16.100 +        public void handshakeCompleted(ShellLaunchEvent ev) {}
  16.101 +
  16.102 +    }
  16.103 +}
    17.1 --- a/jshell.support/src/org/netbeans/modules/jshell/support/ShellSession.java	Wed Apr 06 13:12:27 2016 +0200
    17.2 +++ b/jshell.support/src/org/netbeans/modules/jshell/support/ShellSession.java	Thu Apr 07 14:00:03 2016 +0200
    17.3 @@ -694,7 +694,7 @@
    17.4              this.shell = shell;
    17.5              // it's possible that the shell's startup will terminate the session
    17.6              if (isValid()) {
    17.7 -                shell.onShutdown(sh -> closed());
    17.8 +                shell.onShutdown(sh -> closedDelayed());
    17.9                  model.attach(shell);
   17.10                  // must first give chance to the model to map the snippet to console contents
   17.11                  model.forwardSnippetEvent(this::acceptSnippet);
   17.12 @@ -890,9 +890,22 @@
   17.13      }
   17.14  
   17.15      private void closed() {
   17.16 -        this.closed = true;
   17.17 +        synchronized (this) {
   17.18 +            if (closed) {
   17.19 +                return;
   17.20 +            }
   17.21 +            this.closed = true;
   17.22 +        }
   17.23          propSupport.firePropertyChange(PROP_ACTIVE, true, false);
   17.24      }
   17.25 +    
   17.26 +    private void closedDelayed() {
   17.27 +        FORCE_CLOSE_RP.post(new Runnable() {
   17.28 +            public void run() {
   17.29 +                closed();
   17.30 +            }
   17.31 +        }, 300);
   17.32 +    }
   17.33  
   17.34      private synchronized void init(ShellSession prev) {
   17.35          evaluator = new RequestProcessor("Evaluator for " + displayName);
    18.1 --- a/lib.jshell.agent/agentsrc/jdk/internal/jshell/remote/AgentWorker.java	Wed Apr 06 13:12:27 2016 +0200
    18.2 +++ b/lib.jshell.agent/agentsrc/jdk/internal/jshell/remote/AgentWorker.java	Thu Apr 07 14:00:03 2016 +0200
    18.3 @@ -62,6 +62,7 @@
    18.4  import java.util.logging.Level;
    18.5  import java.util.logging.Logger;
    18.6  import java.util.regex.Pattern;
    18.7 +import static jdk.internal.jshell.remote.RemoteCodes.RESULT_KILLED;
    18.8  import org.netbeans.lib.jshell.agent.NbJShellAgent;
    18.9  
   18.10  /**
   18.11 @@ -69,7 +70,9 @@
   18.12   * @author sdedic
   18.13   */
   18.14  public class AgentWorker extends RemoteAgent implements Executor, Runnable {
   18.15 -    public static final String PROPERTY_EXECUTOR = "jdk.internal.jshell.remote.AgentWorker.executor"; // NOI18N
   18.16 +    private static final Logger LOG = Logger.getLogger("org.netbeans.lib.jshell.agent.AgentWorker"); // NOI18N
   18.17 +    
   18.18 +    public static final String PROPERTY_EXECUTOR = "org.netbeans.lib.jshell.agent.AgentWorker.executor"; // NOI18N
   18.19      
   18.20      /**
   18.21       * Reference set by instrumented classes
   18.22 @@ -91,18 +94,46 @@
   18.23       * The Classloader last obtained from a field or method
   18.24       */
   18.25      private ClassLoader lastClassLoader;
   18.26 +    
   18.27 +    /**
   18.28 +     * Provider for the classloader. Contacted before every user invocation
   18.29 +     * and before every class load.
   18.30 +     */
   18.31      private Callable<ClassLoader>   loaderProvider;
   18.32 +    
   18.33 +
   18.34 +    /**
   18.35 +     * Executor for user's code. Can be provided from the environment using System.properties,
   18.36 +     * see {@link #PROPERTY_EXECUTOR}.
   18.37 +     */
   18.38      private Executor userExecutor;
   18.39 -    // perform classloader transformation
   18.40 -    private boolean doClassTransform;
   18.41 +    
   18.42 +    /**
   18.43 +     * Threads which execute user code, keyed by agent's socket local port.
   18.44 +     */
   18.45 +    // @GuardedBy(self)
   18.46 +    private final static Map<Integer, Thread>     userCodeExecutingThreads = new HashMap<>();
   18.47      
   18.48      private AgentWorker() {
   18.49          agent = null;
   18.50          socket = null;
   18.51          socketPort = -1;
   18.52 -        
   18.53 -        Executor exec = (Executor)System.getProperties().get(PROPERTY_EXECUTOR);
   18.54 -        this.userExecutor = exec != null ? exec : this;
   18.55 +        this.userExecutor = findExecutor();
   18.56 +    }
   18.57 +    
   18.58 +    private Executor findExecutor() {
   18.59 +        Object o = System.getProperties().get(PROPERTY_EXECUTOR);;
   18.60 +        if (o instanceof Executor) {
   18.61 +            this.userExecutor = (Executor)o;
   18.62 +        } else if (o instanceof String) {
   18.63 +            try {
   18.64 +                Class executorClazz = Class.forName((String)o);
   18.65 +                return (Executor)executorClazz.newInstance();
   18.66 +            } catch (ReflectiveOperationException ex) {
   18.67 +                LOG.log(Level.SEVERE, null, ex);
   18.68 +            }
   18.69 +        }
   18.70 +        return this;
   18.71      }
   18.72      
   18.73      private static class LoaderAccessor implements Callable<ClassLoader> {
   18.74 @@ -203,8 +234,7 @@
   18.75                  }
   18.76              };
   18.77          }
   18.78 -        Executor exec = (Executor)System.getProperties().get(PROPERTY_EXECUTOR);
   18.79 -        this.userExecutor = exec != null ? exec : this;
   18.80 +        this.userExecutor = findExecutor();
   18.81      }
   18.82      
   18.83      public static void main(String[] args) throws Exception {
   18.84 @@ -227,6 +257,8 @@
   18.85              // will read immediately
   18.86              ObjectInputStream ism = new ObjectInputStream(socket.getInputStream())) {
   18.87              commandLoop(ism, osm);
   18.88 +        } catch (EOFException ex) {
   18.89 +            // expected.
   18.90          } catch (Exception ex) {
   18.91              ex.printStackTrace();
   18.92          }
   18.93 @@ -263,6 +295,7 @@
   18.94              }
   18.95              result.put(s, props.getProperty(s));
   18.96          }
   18.97 +        LOG.log(Level.FINE, "Sending properties: " + props);
   18.98          
   18.99          prepareClassLoader();
  18.100          StringBuilder cp = new StringBuilder();
  18.101 @@ -293,7 +326,7 @@
  18.102              }
  18.103              cp.append(s);
  18.104          }
  18.105 -        
  18.106 +        LOG.log(Level.FINE, "Classloader path: " + cp);
  18.107          result.put("nb.class.path", cp.toString()); // NOI18N
  18.108          
  18.109          o.writeInt(result.size());
  18.110 @@ -304,6 +337,52 @@
  18.111          o.flush();
  18.112      }
  18.113      
  18.114 +    private ClassLoader contextLoader;
  18.115 +    
  18.116 +    void clientCodeEnter() {
  18.117 +        LOG.log(Level.FINER, "Entering client code");
  18.118 +        this.contextLoader = Thread.currentThread().getContextClassLoader();
  18.119 +        Thread.currentThread().setContextClassLoader(loader);
  18.120 +    }
  18.121 +    
  18.122 +    private boolean killed;
  18.123 +    
  18.124 +    /**
  18.125 +     * Removes our thread from the user-executing map.
  18.126 +     * It's important that clientCodeLeave is called BEFORE the agent starts to send
  18.127 +     * response code: either the thread is removed here, and the response code is sent by
  18.128 +     * the original Agent's code, or the entry is removed by the killer agent before/while
  18.129 +     * executing agent is waiting to lock the map - and in that case the `killed' will be set 
  18.130 +     * to true and response code is produced in performExecute. Since ThreadDeath is thrown, 
  18.131 +     * the executing agent will not complete the synchronized block normally
  18.132 +     */
  18.133 +    void clientCodeLeave() {
  18.134 +        LOG.log(Level.FINER, "Exiting client code");
  18.135 +        Thread.currentThread().setContextClassLoader(contextLoader);
  18.136 +        synchronized (userCodeExecutingThreads) {
  18.137 +            killed = !userCodeExecutingThreads.remove(socketPort, Thread.currentThread());
  18.138 +            LOG.log(Level.FINER, "User code killed: {0}", killed);
  18.139 +        }
  18.140 +    }
  18.141 +    
  18.142 +    private void performExecute(int cmd, ObjectInputStream in, ObjectOutputStream out) throws IOException {
  18.143 +        killed = false;
  18.144 +        try {
  18.145 +            synchronized (userCodeExecutingThreads) {
  18.146 +                userCodeExecutingThreads.put(socketPort, Thread.currentThread());
  18.147 +            }
  18.148 +            super.performCommand(cmd, in, out);
  18.149 +            return;
  18.150 +        } catch (ThreadDeath td) {
  18.151 +            LOG.log(Level.FINE, "Received ThreadDeath, killed: {0}", killed);
  18.152 +            if (!killed) {
  18.153 +                throw td;
  18.154 +            }
  18.155 +        }
  18.156 +        out.writeInt(RESULT_KILLED);
  18.157 +        out.flush();
  18.158 +    }
  18.159 +    
  18.160      @Override
  18.161      protected void performCommand(final int cmd, final ObjectInputStream in, final ObjectOutputStream out) throws IOException {
  18.162          try {
  18.163 @@ -314,7 +393,7 @@
  18.164                      userExecutor.execute(new Runnable() {
  18.165                          public void run() {
  18.166                              try {
  18.167 -                                AgentWorker.super.performCommand(cmd, in, out);
  18.168 +                                performExecute(cmd, in, out);
  18.169                              } catch (IOException ex) {
  18.170                                  err[0] = ex;
  18.171                              }
  18.172 @@ -329,12 +408,13 @@
  18.173                      returnVMInfo(out);
  18.174                      break;
  18.175                  case CMD_REDEFINE:
  18.176 -                    performRedefineClass(in, out);
  18.177 +                    performRedefineClasses(in, out);
  18.178                      break;
  18.179                  case CMD_CLASSID:
  18.180                      performClassId(in, out);
  18.181                      break;
  18.182                  case CMD_STOP:
  18.183 +                    performStop(in, out);
  18.184                      break;
  18.185                  default:
  18.186                      super.performCommand(cmd, in, out);
  18.187 @@ -345,19 +425,54 @@
  18.188          }
  18.189      }
  18.190      
  18.191 -    private void performRedefineClass(ObjectInputStream in, ObjectOutputStream out) throws IOException {
  18.192 -        long classId = in.readLong();
  18.193 +    /**
  18.194 +     * Executes STOP command. Note that STOP comes through <b>different</b> agent than
  18.195 +     * the one which should stop the user's code; the controlling process opens connection
  18.196 +     * and instructs the VM to stop running thread.
  18.197 +     * @param in
  18.198 +     * @param out
  18.199 +     * @throws IOException 
  18.200 +     */
  18.201 +    @SuppressWarnings({"deprecation", "CallToThreadStopSuspendOrResumeManager"})
  18.202 +    private void performStop(ObjectInputStream in, ObjectOutputStream out) throws IOException {
  18.203 +        int agentId = in.readInt();
  18.204 +        Thread targetThread;
  18.205 +        
  18.206 +        synchronized (userCodeExecutingThreads) {
  18.207 +            targetThread = userCodeExecutingThreads.remove(agentId);
  18.208 +            if (targetThread != null) {
  18.209 +                // throw ThreadDeath in the target thread
  18.210 +                targetThread.stop();
  18.211 +            }
  18.212 +        }
  18.213 +        if (targetThread != null) {
  18.214 +            out.writeInt(RemoteCodes.RESULT_SUCCESS);
  18.215 +        } else {
  18.216 +            out.writeInt(RemoteCodes.RESULT_FAIL);
  18.217 +        }
  18.218 +        out.flush();
  18.219 +    }
  18.220 +    
  18.221 +    private void performRedefineClasses(ObjectInputStream in, ObjectOutputStream out) throws IOException {
  18.222 +        int count = in.readInt();
  18.223          try {
  18.224 -            byte[] replaceBytecode = (byte[])in.readObject();
  18.225 -            Class defined = ((NbRemoteLoader)loader).getClassOfId(classId);
  18.226 -            
  18.227 -            agent.getInstrumentation().redefineClasses(
  18.228 -                new ClassDefinition(defined, replaceBytecode)
  18.229 -            );
  18.230 +            for (int i = 0; i < count; i++) {
  18.231 +                long classId = in.readLong();
  18.232 +                byte[] replaceBytecode = (byte[]) in.readObject();
  18.233 +                Class defined = ((NbRemoteLoader) loader).getClassOfId(classId);
  18.234 +
  18.235 +                agent.getInstrumentation().redefineClasses(
  18.236 +                        new ClassDefinition(defined, replaceBytecode)
  18.237 +                );
  18.238 +            }
  18.239              out.writeInt(RemoteCodes.RESULT_SUCCESS);
  18.240 -            out.writeLong(classId);
  18.241 -        } catch (ClassNotFoundException | UnmodifiableClassException ex) {
  18.242 -            Logger.getLogger(AgentWorker.class.getName()).log(Level.SEVERE, null, ex);
  18.243 +        } catch (UnsupportedOperationException ex) {
  18.244 +            LOG.log(Level.FINE, "Class redefinition failed");
  18.245 +            out.writeInt(RemoteCodes.RESULT_FAIL);
  18.246 +            out.writeInt(RemoteCodes.RESULT_SUCCESS);
  18.247 +        } catch (LinkageError | NullPointerException | ClassNotFoundException | UnmodifiableClassException ex) {
  18.248 +            LOG.log(Level.WARNING, "Could not redefine class: ", ex);
  18.249 +            out.writeInt(RemoteCodes.RESULT_FAIL);
  18.250              out.writeInt(RemoteCodes.RESULT_FAIL);
  18.251              out.writeUTF(ex.toString());
  18.252          }
    19.1 --- a/lib.jshell.agent/agentsrc/jdk/internal/jshell/remote/RemoteAgent.java	Wed Apr 06 13:12:27 2016 +0200
    19.2 +++ b/lib.jshell.agent/agentsrc/jdk/internal/jshell/remote/RemoteAgent.java	Thu Apr 07 14:00:03 2016 +0200
    19.3 @@ -108,6 +108,7 @@
    19.4                  // Invoke executable entry point in loaded code
    19.5                  String name = in.readUTF();
    19.6                  Class<?> klass = klasses.get(name);
    19.7 +                prepareClassLoader();
    19.8                  if (klass == null) {
    19.9                      debug("*** Invoke failure: no such class loaded %s\n", name);
   19.10                      out.writeInt(RESULT_FAIL);
   19.11 @@ -124,9 +125,9 @@
   19.12                          clientCodeEnter();
   19.13                          res = doitMethod.invoke(null, new Object[0]);
   19.14                      } catch (InvocationTargetException ex) {
   19.15 -                        if (ex.getCause() instanceof StopExecutionException) {
   19.16 +                        if (ex.getCause() instanceof ThreadDeath) {
   19.17                              expectingStop = false;
   19.18 -                            throw (StopExecutionException) ex.getCause();
   19.19 +                            throw (ThreadDeath) ex.getCause();
   19.20                          }
   19.21                          throw ex;
   19.22                      } catch (StopExecutionException ex) {
    20.1 --- a/lib.nbjshell/src/jdk/jshell/NbExecutionControl.java	Wed Apr 06 13:12:27 2016 +0200
    20.2 +++ b/lib.nbjshell/src/jdk/jshell/NbExecutionControl.java	Thu Apr 07 14:00:03 2016 +0200
    20.3 @@ -71,6 +71,10 @@
    20.4      private static final Logger LOG = Logger.getLogger(NbExecutionControl.class.getName());
    20.5      
    20.6      public static final int CMD_VERSION_INFO = 100;
    20.7 +
    20.8 +    public static final int CMD_REDEFINE   =    101;
    20.9 +    public static final int CMD_STOP        =   102;
   20.10 +    public static final int CMD_CLASSID     =   103;
   20.11      
   20.12      private final RemoteJShellService  execEnv;
   20.13      private final JDIEnv jdi;
   20.14 @@ -132,7 +136,8 @@
   20.15              try {
   20.16                  proc.debug(DBG_GEN, "Attempting to stop the client code...\n");
   20.17                  execEnv.sendStopUserCode();
   20.18 -            } catch (IllegalStateException ex) {
   20.19 +            } catch (IllegalStateException | IOException ex) {
   20.20 +                ex.printStackTrace();
   20.21                  proc.debug(DBG_GEN, "Exception on remote stop: %s\n", ex.getCause());
   20.22              }
   20.23          }
   20.24 @@ -143,9 +148,9 @@
   20.25          OutputStream os = execEnv.getCommandStream();
   20.26          InputStream is = execEnv.getResponseStream();
   20.27          out = os instanceof ObjectOutputStream ? (ObjectOutputStream)os : 
   20.28 -                new ObjectOutputStream(execEnv.getCommandStream());
   20.29 +                new ObjectOutputStream(os);
   20.30          in = is instanceof ObjectInputStream ? (ObjectInputStream)is : 
   20.31 -                new ObjectInputStream(execEnv.getResponseStream());
   20.32 +                new ObjectInputStream(is);
   20.33  
   20.34          /*
   20.35          try (ServerSocket listener = new ServerSocket(0)) {
    21.1 --- a/lib.nbjshell/src/jdk/jshell/RemoteJShellService.java	Wed Apr 06 13:12:27 2016 +0200
    21.2 +++ b/lib.nbjshell/src/jdk/jshell/RemoteJShellService.java	Thu Apr 07 14:00:03 2016 +0200
    21.3 @@ -24,7 +24,7 @@
    21.4       * 
    21.5       * @param redefines handle-to-classfile contents map.
    21.6       */
    21.7 -    public void         redefineClasses(Map<Object, byte[]> redefines);
    21.8 +    public void         redefineClasses(Map<Object, byte[]> redefines) throws IOException, IllegalStateException;
    21.9      
   21.10      /**
   21.11       * Returns handle that corresponds to a named class. Returns null, 
   21.12 @@ -71,7 +71,7 @@
   21.13       * @return true, if the implementation even attempted to stop
   21.14       * the machine.
   21.15       */
   21.16 -    public boolean sendStopUserCode() throws IllegalStateException;
   21.17 +    public boolean sendStopUserCode() throws IllegalStateException, IOException;
   21.18      
   21.19      /**
   21.20       * Decorates launcher arguments. Results undefined, if the environment
    22.1 --- a/libs.jshell/nbproject/project.xml	Wed Apr 06 13:12:27 2016 +0200
    22.2 +++ b/libs.jshell/nbproject/project.xml	Thu Apr 07 14:00:03 2016 +0200
    22.3 @@ -25,6 +25,7 @@
    22.4              </module-dependencies>
    22.5              <public-packages>
    22.6                  <package>jdk.internal.jshell.debug</package>
    22.7 +                <package>jdk.internal.jshell.remote</package>
    22.8                  <package>jdk.jshell</package>
    22.9              </public-packages>
   22.10              <class-path-extension>