org.embulk.jruby.LazyScriptingContainerDelegate Maven / Gradle / Ivy
package org.embulk.jruby;
import com.google.inject.Injector;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.embulk.EmbulkSystemProperties;
import org.slf4j.Logger;
/**
* Indirects onto JRuby with initializing ScriptingContainer lazily.
*/
@SuppressWarnings("checkstyle:AbbreviationAsWordInName")
public final class LazyScriptingContainerDelegate extends ScriptingContainerDelegate {
public LazyScriptingContainerDelegate(
final ClassLoader classLoader,
final LocalContextScope delegateLocalContextScope,
final LocalVariableBehavior delegateLocalVariableBehavior,
final JRubyInitializer initializer) {
this.impl = null;
this.classLoader = classLoader;
this.delegateLocalContextScope = delegateLocalContextScope;
this.delegateLocalVariableBehavior = delegateLocalVariableBehavior;
this.initializer = initializer;
}
public static LazyScriptingContainerDelegate withGems(
final Logger logger,
final EmbulkSystemProperties embulkSystemProperties) {
return of(false, true, null, logger, embulkSystemProperties);
}
public static LazyScriptingContainerDelegate withGemsIgnored(
final Logger logger,
final EmbulkSystemProperties embulkSystemProperties) {
return of(false, false, null, logger, embulkSystemProperties);
}
public static LazyScriptingContainerDelegate withInjector(
final Injector injector,
final Logger logger,
final EmbulkSystemProperties embulkSystemProperties) {
return of(true, true, injector, logger, embulkSystemProperties);
}
private static LazyScriptingContainerDelegate of(
final boolean isEmbulkSpecific,
final boolean initializesGem,
final Injector injector,
final Logger logger,
final EmbulkSystemProperties embulkSystemProperties) {
// use_global_ruby_runtime is valid only when it's guaranteed that just one Injector is
// instantiated in this JVM.
final boolean useGlobalRubyRuntime = embulkSystemProperties.getPropertyAsBoolean("use_global_ruby_runtime", false);
final String jrubyProperty = embulkSystemProperties.getProperty("jruby");
final ArrayList jrubyUrlsBuilt = new ArrayList<>();
if (jrubyProperty != null && !jrubyProperty.isEmpty()) {
// File.pathSeparator is not available here because the property "jruby" expects a URL-like style:
// "file:" and "mvn:". It now supports only "file:", though.
//
// Semicolons basically do not appear in URLs without quoting except for the "optional fields and values"
// case described in RFC 1738. We don't need to take care of the "optional fields and values" case here.
// https://tools.ietf.org/html/rfc1738
for (final String jarLocator : jrubyProperty.split("\\;")) {
if (jarLocator.startsWith("file:")) {
// TODO: Validate the path more.
final URL jarUrl;
try {
jarUrl = new URL(jarLocator);
} catch (final MalformedURLException ex) {
throw new JRubyInvalidRuntimeException("Embulk system property \"jruby\" is invalid: " + jrubyProperty, ex);
}
jrubyUrlsBuilt.add(jarUrl);
} else {
throw new JRubyInvalidRuntimeException("Embulk system property \"jruby\" is invalid: " + jrubyProperty);
}
}
}
final List jrubyUrls = Collections.unmodifiableList(jrubyUrlsBuilt);
final JRubyInitializer initializer = JRubyInitializer.of(
isEmbulkSpecific,
initializesGem,
isEmbulkSpecific ? injector : null,
logger,
embulkSystemProperties);
final JRubyClassLoader jrubyClassLoader;
try {
jrubyClassLoader = new JRubyClassLoader(jrubyUrls, LazyScriptingContainerDelegate.class.getClassLoader());
} catch (final RuntimeException ex) {
return null;
}
try {
jrubyClassLoader.loadClass("org.jruby.Main");
} catch (final ClassNotFoundException ex) {
return null;
}
try {
final LazyScriptingContainerDelegate jruby = new LazyScriptingContainerDelegate(
jrubyClassLoader,
useGlobalRubyRuntime
? ScriptingContainerDelegate.LocalContextScope.SINGLETON
: ScriptingContainerDelegate.LocalContextScope.SINGLETHREAD,
ScriptingContainerDelegate.LocalVariableBehavior.PERSISTENT,
initializer);
if (useGlobalRubyRuntime) {
// In case the global JRuby instance is used, the instance should be always initialized.
// Ruby tests (in embulk-ruby/) are examples.
jruby.getInitialized();
}
return jruby;
} catch (Exception ex) {
return null;
}
}
@Override
public String getJRubyVersion() {
return getInitialized().getJRubyVersion();
}
@Override
public String getRubyVersion() {
return getInitialized().getRubyVersion();
}
// It is intentionally package-private. It is just for logging from JRubyScriptingModule.
@Override
String getGemHome() throws JRubyInvalidRuntimeException {
return getInitialized().getGemHome();
}
// It is intentionally package-private. It is just for logging from JRubyScriptingModule.
@Override
String getGemPathInString() throws JRubyInvalidRuntimeException {
return getInitialized().getGemPathInString();
}
@Override
public void clearGemPaths() throws JRubyInvalidRuntimeException {
getInitialized().clearGemPaths();
}
@Override
public void setGemPaths(final String gemHome) throws JRubyInvalidRuntimeException {
getInitialized().setGemPaths(gemHome);
}
@Override
public void setGemPaths(final String gemHome, final String gemPath) throws JRubyInvalidRuntimeException {
getInitialized().setGemPaths(gemHome, gemPath);
}
@Override
public boolean isBundleGemfileDefined() throws JRubyInvalidRuntimeException {
return getInitialized().isBundleGemfileDefined();
}
@Override
public String getBundleGemfile() throws JRubyInvalidRuntimeException {
return getInitialized().getBundleGemfile();
}
@Override
public void setBundleGemfile(final String gemfilePath) throws JRubyInvalidRuntimeException {
getInitialized().setBundleGemfile(gemfilePath);
}
@Override
public void unsetBundleGemfile() throws JRubyInvalidRuntimeException {
getInitialized().unsetBundleGemfile();
}
// It is intentionally private. It should return RubyObject while it is Object in the signature.
@Override
Object getGemPaths() throws JRubyInvalidRuntimeException {
return getInitialized().getGemPaths();
}
@Override
public void processJRubyOption(final String jrubyOption)
throws JRubyInvalidRuntimeException, UnrecognizedJRubyOptionException, NotWorkingJRubyOptionException {
getInitialized().processJRubyOption(jrubyOption);
}
@Override
public Object callMethodArray(
final Object receiver,
final String methodName,
final Object[] args) throws JRubyInvalidRuntimeException {
return getInitialized().callMethodArray(receiver, methodName, args);
}
@Override
public Object callMethod(
final Object receiver,
final String methodName,
final Object... args) throws JRubyInvalidRuntimeException {
return getInitialized().callMethod(receiver, methodName, args);
}
/*
@Override
public Object callMethod(
final Object receiver,
final String methodName,
final Block block,
final Object... args) throws JRubyInvalidRuntimeException;
*/
@Override
public T callMethod(
final Object receiver,
final String methodName,
final Class returnType) throws JRubyInvalidRuntimeException {
return getInitialized().callMethod(receiver, methodName, returnType);
}
@Override
public T callMethod(
final Object receiver,
final String methodName,
final Object singleArg,
final Class returnType) throws JRubyInvalidRuntimeException {
return getInitialized().callMethod(receiver, methodName, singleArg, returnType);
}
/*
@Override
public T callMethod(
final Object receiver,
final String methodName,
final Object[] args,
final Class returnType) throws JRubyInvalidRuntimeException;
*/
/*
@Override
public T callMethod(
final Object receiver,
final String methodName,
final Object[] args,
final Block block,
final Class returnType) throws JRubyInvalidRuntimeException;
*/
/*
@Override
public T callMethod(
final Object receiver,
final String methodName,
final Class returnType,
final EmbedEvalUnit unit) throws JRubyInvalidRuntimeException;
*/
/*
@Override
public T callMethod(
final Object receiver,
final String methodName,
final Object[] args,
final Class returnType,
final EmbedEvalUnit unit) throws JRubyInvalidRuntimeException;
*/
/*
@Override
public T callMethod(
final Object receiver,
final String methodName,
final Object[] args,
final Block block,
final Class returnType,
final EmbedEvalUnit unit) throws JRubyInvalidRuntimeException;
*/
// It is intentionally private. It should return LocalContextProvider while it is Object in the signature.
@Override
Object getProvider() throws JRubyInvalidRuntimeException {
return getInitialized().getProvider();
}
@Override
public Object put(final String key, final Object value) throws JRubyInvalidRuntimeException {
return getInitialized().put(key, value);
}
@Override
public Object remove(final String key) throws JRubyInvalidRuntimeException {
return getInitialized().remove(key);
}
@Override
public Object runScriptlet(final String script) throws JRubyInvalidRuntimeException {
return getInitialized().runScriptlet(script);
}
// It is intentionally private. It should return RubyInstanceConfig while it is Object in the signature.
@Override
Object getRubyInstanceConfig() throws JRubyInvalidRuntimeException {
return getInitialized().getRubyInstanceConfig();
}
// It is intentionally private. It should return Runtime while it is Object in the signature.
@Override
Object getRuntime() throws JRubyInvalidRuntimeException {
return getInitialized().getRuntime();
}
synchronized ScriptingContainerDelegateImpl getInitialized() {
if (this.impl == null) {
this.impl = ScriptingContainerDelegateImpl.create(
this.classLoader, this.delegateLocalContextScope, this.delegateLocalVariableBehavior);
if (this.initializer != null) {
this.initializer.initialize(this.impl);
}
}
return this.impl;
}
private ScriptingContainerDelegateImpl impl;
private final ClassLoader classLoader;
private final LocalContextScope delegateLocalContextScope;
private final LocalVariableBehavior delegateLocalVariableBehavior;
private final JRubyInitializer initializer;
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy