2014-12-07 2 views
14

자바 응용 프로그램에서 작성된 모든 서버 측 자바 스크립트 코드를 평가하기 위해 Nashorn 자바 스크립트 엔진을 사용하고 있습니다. 성능을 향상 시키려면 시작시 JsEngine을 초기화하기 위해 봄을 사용하고 & 캐시는 콧수염과 같은 모든 핵심 도구를 캐시합니다. 공통 JS 도구. 그러면 화면이 렌더링 될 때마다 미리 평가 된 JsEngine이 페이지 별 JavaScript 코드를 평가하는 데 사용됩니다. 언젠가 정상적으로 작동합니다. 즉, 페이지를 예상대로 렌더링하지만 동일한 URL을 계속 방문 할 때 다음 예외를 던지기 시작합니다.Nashorn의 IllegalArgumentException 예외 - Java 8의 버그입니까?

문제점의 근본 원인을 찾을 수 없습니다. 아래로 사용

@Component 
public class JsEngine { 

    private ScriptEngine scriptEngine; 

    @PostConstruct 
    public void init() throws ScriptException, IOException{ 
     scriptEngine = new ScriptEngineManager().getEngineByName("nashorn"); 
     this.cacheAllCoreEngines(); 
     for(String key: defaultEngineSource.keySet()){ 
      scriptEngine.eval(defaultEngineSource.get(key)); 
     } 
    } 

    private void cacheAllCoreEngines()throws IOException{ 
     //read all core files such as mustache, etc. 
     defaultEngineSource.put("mustache", FileUtil.readFileFromDisk("<actual path..>/mustache.js")); 
    } 

    public Object eval(String source) throws ScriptException{ 
      .... code to handle exceptions 
      return scriptEngine.eval (source); 
    } 

} 

JsEngine, 몇 사이클을 렌더링 후

public class AppRendererImpl implements AppRenderer { 

    @Autowired 
    JsEngine jsEngine; 

    public String render(){ 
    .... 
    .... //Read source from disk or cache 
    jsEngine.eval(source);.... 
    }  
} 

예외,

예외 :
java.lang.IllegalArgumentException가 : 목표 및 필터 유형이 일치하지 않는 : (ScriptObject) 객체, (Object) 객체
at java.lang.invoke.MethodHandleStatics.newIllegalArgumentException (MethodHa ndleStatics.java:115) java.lang.invoke.MethodHandles.filterArgument에서
(MethodHandles.java:2416) java.lang.invoke.MethodHandles.filterArguments (MethodHandles.java:2403)
jdk.nashorn에서의
.internal.lookup.MethodHandleFactory $ StandardMethodHandleFunctionality.filterArguments (MethodHandleFactory.java:277) jdk.nashorn.internal.runtime.WithObject.filter (WithObject.java:270)에서

jdk.nashorn.internal.runtime.WithObject에서 .fixExpressionCallSite (WithObject.java:249)
at jdk.nashorn.internal.runtime.WithObject.lookup (WithObject.java:169)
at jdk.nashorn.internal.runtime.link er.NashornLinker.getGuardedInvocation (NashornLinker.java:96) jdk.internal.dynalink.support.CompositeTypeBasedGuardingDynamicLinker.getGuardedInvocation (CompositeTypeBasedGuardingDynamicLinker.java:176) jdk.internal.dynalink.support.CompositeGuardingDynamicLinker.getGuardedInvocation에서
(CompositeGuardingDynamicLinker에서
. 자바 : 124)에 jdk.internal.dynalink.support.LinkerServicesImpl.getGuardedInvocation
(LinkerServicesImpl.java:144) jdk.internal.dynalink.DynamicLinker.relink에서
(DynamicLinker.java:232)
jdk.nashorn에서 .internal.scripts.Script $ \^eval _._ L6 $ _L8 (: 21)
at jdk.nashorn.internal.scripts.Script $ \^eval _._ L6 $ _L40 (: 41)jdk.nashorn.internal.scripts.Script $ \^eval_.runScript에서 (1) jdk.nashorn.internal.runtime.ScriptFunctionData.invoke (ScriptFunctionData.java:498) jdk.nashorn에서
에서
. internal.runtime.ScriptFunction.invoke (ScriptFunction.java:206) jdk.nashorn.internal.runtime.ScriptRuntime.apply에서
(ScriptRuntime.java:378) jdk.nashorn.api.scripting.NashornScriptEngine.evalImpl에서
(NashornScriptEngine.java:546) jdk.nashorn.api.scripting.NashornScriptEngine.evalImpl에서 jdk.nashorn.api.scripting.NashornScriptEngine.evalImpl (NashornScriptEngine.java:528)
(NashornScriptEngine에서
.자바 : 524)에 jdk.nashorn.api.scripting.NashornScriptEngine.eval
(NashornScriptEngine.java:194) javax.script.AbstractScriptEngine.eval에서
(AbstractScriptEngine.java:264)
com.nube.portal에서 .engines.js.JsEngine.eval (JsEngine.java:111)
at com.nube.portal.engines.html.tags.HtmlTagScript.eval (HtmlTagScript.java:66)
........

모든 전역 개체를 다른지도로 복사하는 데 추가 된 사용자 지정 코드가 있습니다. 이것은 모든 전역 객체에 "nube"로 액세스해야하는 몇 가지 다른 요구 사항을 촉진하기위한 것입니다. 이 코드가 자주 실행될 때 어떤 문제가 발생할지 모르겠습니다. 컨텍스트에서 개체를 제거하지 않는 것을 명심하십시오.

public void movePublicObjects(String prefix) throws NubeException{ 
    Bindings b1 = scriptEngine.getContext().getBindings(ScriptContext.ENGINE_SCOPE); 
    Map<String, Object> nubeObjects = new HashMap<String, Object>(); 
    for(Entry<String, Object> entry: b1.entrySet()){ 
     if(!entry.getKey().equals("nube")){ 
      nubeObjects.put(entry.getKey(), entry.getValue()); 
     } 
    } 
    b1.put("nube", nubeObjects); 
    return; 
} 

이 코드는 JsEngine을 프로토 타입으로 정의했지만 성능이 좋지 않을 때 완벽하게 작동합니다. 이것이 Nashorn의 버그라고 생각하십니까?

답변

1

영구 JS 컨텍스트에서 무언가가 손상되어 옵티 마이저의 가정을 위반하거나 플랫 결함입니다. 스택 추적은 코드가 메서드를 호출하고 스크립트 런타임의 코드 경로가 최적화 된 버전의 함수가 해당 가정과 일치하지 않는 인수 (해당 인수 형식 필터)이 예외와 함께 실패합니다. 코드 경로는 JS 코드에 관계없이 일관된 런타임에서 따라 와서는 안됩니다.

+0

귀하의 답변은 JS 컨텍스트에서 수행하고있는 추가 단계를 하나 추가하라고 상기시켜줍니다. 나는 코드로 질문을 갱신 할 것이다 – kamoor

3

IIRC, 스프링 @Component은 기본적으로 싱글 톤 범위이므로 JsEngine의 단일 ScriptEngine 인스턴스는 스레드간에 공유됩니다.

:

나는이 말을했다 스레드 안전에 관한 Nashorn 개발자 메일 링리스트에 a discussion을 발견했다. . . Nashorn은 설계 상 스레드로부터 안전하지 않습니다. 사실, 당신이 평가되는 경우

new NashornScriptEngineFactory().getParameter("THREADING")

는 "이 엔진 구현, 스레드 안전하지 않고 여러 스레드에서 동시에 스크립트를 실행하는 데 사용할 수 없습니다"의미, null를 돌려거야 - 볼 http://docs.oracle.com/javase/7/docs/api/javax/script/ScriptEngineFactory.html#getParameter(java.lang.String)

Nashorn 라이브러리 내부는 스레드로부터 안전합니다. . . 단일 엔진 인스턴스 내에서 실행되는 JavaScript 프로그램은 스레드로부터 안전하지 않습니다.

이전에 시도한 적이 있다면 different from Rhino입니다. Nashorn을 사용하면 아마도 ScriptEngine을 동시 액세스로부터 보호하기위한 조치를 취해야합니다. 이는 예측할 수없는 동작을 설명 할 수 있습니다.

풀을 설정하거나 스레드 로컬 인 엔진 인스턴스에 액세스하거나 구성 요소 범위를 싱글 톤 대신 프로토 타입으로 변경하십시오.여기


초기화 스크립트 엔진의 스레드 로컬 인스턴스를 사용하여 잠재적 인 솔루션입니다 :

@Component 
public class JsEngine { 

    private final ThreadLocal<ScriptEngine> threadEngines = 
     new ThreadLocal<ScriptEngine>() { 
      @Override 
      protected ScriptEngine initialValue() { 
       ScriptEngine engine = 
        new ScriptEngineManager().getEngineByName("nashorn"); 
       for (String key: defaultEngineSource.keySet()) { 
        engine.eval(defaultEngineSource.get(key)); 
       } 
       return engine; 
      } 
     }; 

    @PostConstruct 
    public void init() throws ScriptException, IOException { 
     this.cacheAllCoreEngines(); 
     // engine initialization moved to per-thread via ThreadLocal 
    } 

    private void cacheAllCoreEngines() throws IOException{ 
     //read all core files such as mustache, etc. 
     defaultEngineSource.put("mustache", FileUtil.readFileFromDisk("<actual path..>/mustache.js")); 
    } 

    public Object eval(String source) throws ScriptException{ 
     // .... code to handle exceptions 
     return threadEngines.get().eval(source); 
    } 
} 

여전히 각각의 새에 대한 (등 콧수염, 코드) 엔진을 초기화 오버 헤드가있을 것입니다 귀하의 구성 요소를 사용하는 스레드. 그러나 응용 프로그램이 스레드 풀을 사용하는 경우 동일한 스레드를 재사용해야하며 프로토 타입 범위와 마찬가지로 호출 할 때마다 비용을 지불하지 않아도됩니다.

+0

다른 경로를 보여주는 것에 감사하는 William Price. 나는 그것이 스레드 안전 문제라고 생각하지 않습니다. 나는 계속 탐구 할 것이다. – kamoor