001/* 002 * Copyright 2002-2017 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.scripting.jruby; 018 019import java.io.IOException; 020import java.lang.reflect.Method; 021 022import org.jruby.RubyException; 023import org.jruby.exceptions.JumpException; 024import org.jruby.exceptions.RaiseException; 025 026import org.springframework.beans.factory.BeanClassLoaderAware; 027import org.springframework.scripting.ScriptCompilationException; 028import org.springframework.scripting.ScriptFactory; 029import org.springframework.scripting.ScriptSource; 030import org.springframework.util.Assert; 031import org.springframework.util.ClassUtils; 032import org.springframework.util.ReflectionUtils; 033 034/** 035 * {@link org.springframework.scripting.ScriptFactory} implementation 036 * for a JRuby script. 037 * 038 * <p>Typically used in combination with a 039 * {@link org.springframework.scripting.support.ScriptFactoryPostProcessor}; 040 * see the latter's javadoc for a configuration example. 041 * 042 * <p>Note: Spring 4.0 supports JRuby 1.5 and higher, with 1.7.x recommended. 043 * As of Spring 4.2, JRuby 9.0.0.0 is supported as well but primarily through 044 * {@link org.springframework.scripting.support.StandardScriptFactory}. 045 * 046 * @author Juergen Hoeller 047 * @author Rob Harrop 048 * @since 2.0 049 * @see JRubyScriptUtils 050 * @see org.springframework.scripting.support.ScriptFactoryPostProcessor 051 * @deprecated in favor of JRuby support via the JSR-223 abstraction 052 * ({@link org.springframework.scripting.support.StandardScriptFactory}) 053 */ 054@Deprecated 055public class JRubyScriptFactory implements ScriptFactory, BeanClassLoaderAware { 056 057 private static final Method getMessageMethod = ClassUtils.getMethodIfAvailable(RubyException.class, "getMessage"); 058 059 060 private final String scriptSourceLocator; 061 062 private final Class<?>[] scriptInterfaces; 063 064 private ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader(); 065 066 067 /** 068 * Create a new JRubyScriptFactory for the given script source. 069 * @param scriptSourceLocator a locator that points to the source of the script. 070 * Interpreted by the post-processor that actually creates the script. 071 * @param scriptInterfaces the Java interfaces that the scripted object 072 * is supposed to implement 073 */ 074 public JRubyScriptFactory(String scriptSourceLocator, Class<?>... scriptInterfaces) { 075 Assert.hasText(scriptSourceLocator, "'scriptSourceLocator' must not be empty"); 076 Assert.notEmpty(scriptInterfaces, "'scriptInterfaces' must not be empty"); 077 this.scriptSourceLocator = scriptSourceLocator; 078 this.scriptInterfaces = scriptInterfaces; 079 } 080 081 082 @Override 083 public void setBeanClassLoader(ClassLoader classLoader) { 084 this.beanClassLoader = classLoader; 085 } 086 087 088 @Override 089 public String getScriptSourceLocator() { 090 return this.scriptSourceLocator; 091 } 092 093 @Override 094 public Class<?>[] getScriptInterfaces() { 095 return this.scriptInterfaces; 096 } 097 098 /** 099 * JRuby scripts do require a config interface. 100 */ 101 @Override 102 public boolean requiresConfigInterface() { 103 return true; 104 } 105 106 /** 107 * Load and parse the JRuby script via JRubyScriptUtils. 108 * @see JRubyScriptUtils#createJRubyObject(String, Class[], ClassLoader) 109 */ 110 @Override 111 public Object getScriptedObject(ScriptSource scriptSource, Class<?>... actualInterfaces) 112 throws IOException, ScriptCompilationException { 113 try { 114 return JRubyScriptUtils.createJRubyObject( 115 scriptSource.getScriptAsString(), actualInterfaces, this.beanClassLoader); 116 } 117 catch (RaiseException ex) { 118 String msg = null; 119 RubyException rubyEx = ex.getException(); 120 if (rubyEx != null) { 121 if (getMessageMethod != null) { 122 // JRuby 9.1.7+ enforces access via getMessage() method 123 msg = ReflectionUtils.invokeMethod(getMessageMethod, rubyEx).toString(); 124 } 125 else { 126 // JRuby 1.7.x: no accessor, just a public message field 127 if (rubyEx.message != null){ 128 msg = rubyEx.message.toString(); 129 } 130 } 131 } 132 throw new ScriptCompilationException(scriptSource, (msg != null ? msg : "Unexpected JRuby error"), ex); 133 } 134 catch (JumpException ex) { 135 throw new ScriptCompilationException(scriptSource, ex); 136 } 137 } 138 139 @Override 140 public Class<?> getScriptedObjectType(ScriptSource scriptSource) 141 throws IOException, ScriptCompilationException { 142 143 return null; 144 } 145 146 @Override 147 public boolean requiresScriptedObjectRefresh(ScriptSource scriptSource) { 148 return scriptSource.isModified(); 149 } 150 151 152 @Override 153 public String toString() { 154 return "JRubyScriptFactory: script source locator [" + this.scriptSourceLocator + "]"; 155 } 156 157}