View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.maven.shared.scriptinterpreter;
20  
21  import java.io.File;
22  import java.io.IOException;
23  import java.io.PrintStream;
24  import java.io.UncheckedIOException;
25  import java.net.MalformedURLException;
26  import java.net.URL;
27  import java.net.URLClassLoader;
28  import java.util.List;
29  import java.util.Map;
30  
31  import bsh.Capabilities;
32  import bsh.EvalError;
33  import bsh.Interpreter;
34  import bsh.TargetError;
35  
36  /**
37   * Provides a facade to evaluate BeanShell scripts.
38   *
39   * @author Benjamin Bentmann
40   */
41  class BeanShellScriptInterpreter implements ScriptInterpreter {
42  
43      private static class ChildFirstURLClassLoader extends URLClassLoader {
44          ChildFirstURLClassLoader() {
45              super(new URL[] {}, Thread.currentThread().getContextClassLoader());
46          }
47  
48          @Override
49          public void addURL(URL url) {
50              super.addURL(url);
51          }
52  
53          @Override
54          protected synchronized Class<?> loadClass(final String name, final boolean resolve)
55                  throws ClassNotFoundException {
56  
57              Class<?> c = findLoadedClass(name);
58              if (c != null) {
59                  return c;
60              }
61  
62              try {
63                  c = super.findClass(name);
64              } catch (ClassNotFoundException e) {
65                  // ignore
66              }
67  
68              if (c == null) {
69                  c = super.loadClass(name, resolve);
70              }
71  
72              if (resolve) {
73                  resolveClass(c);
74              }
75  
76              return c;
77          }
78  
79          @Override
80          public URL getResource(final String name) {
81              URL url = findResource(name);
82              return url != null ? url : super.getResource(name);
83          }
84      }
85  
86      private final ChildFirstURLClassLoader classLoader = new ChildFirstURLClassLoader();
87  
88      @Override
89      public void setClassPath(List<String> classPath) {
90          if (classPath == null || classPath.isEmpty()) {
91              return;
92          }
93  
94          classPath.stream().map(this::toUrl).forEach(classLoader::addURL);
95      }
96  
97      private URL toUrl(String path) {
98          try {
99              return new File(path).toURI().toURL();
100         } catch (MalformedURLException e) {
101             throw new UncheckedIOException(e);
102         }
103     }
104 
105     @Override
106     public Object evaluateScript(String script, Map<String, ?> globalVariables, PrintStream scriptOutput)
107             throws ScriptEvaluationException {
108         PrintStream origOut = System.out;
109         PrintStream origErr = System.err;
110 
111         try {
112             Interpreter engine = new Interpreter();
113 
114             if (scriptOutput != null) {
115                 System.setErr(scriptOutput);
116                 System.setOut(scriptOutput);
117                 engine.setErr(scriptOutput);
118                 engine.setOut(scriptOutput);
119             }
120 
121             if (!Capabilities.haveAccessibility()) {
122                 try {
123                     Capabilities.setAccessibility(true);
124                 } catch (Exception e) {
125                     if (scriptOutput != null) {
126                         e.printStackTrace(scriptOutput);
127                     }
128                 }
129             }
130 
131             engine.setClassLoader(classLoader);
132 
133             if (globalVariables != null) {
134                 for (Map.Entry<String, ?> entry : globalVariables.entrySet()) {
135                     try {
136                         engine.set(entry.getKey(), entry.getValue());
137                     } catch (EvalError e) {
138                         throw new RuntimeException(e);
139                     }
140                 }
141             }
142 
143             try {
144                 return engine.eval(script);
145             } catch (TargetError e) {
146                 throw new ScriptEvaluationException(e.getTarget());
147             } catch (ThreadDeath e) {
148                 throw e;
149             } catch (Throwable e) {
150                 throw new ScriptEvaluationException(e);
151             }
152         } finally {
153             System.setErr(origErr);
154             System.setOut(origOut);
155         }
156     }
157 
158     @Override
159     public void close() throws IOException {
160         classLoader.close();
161     }
162 }