001 /***************************************************************************** 002 * Copyright (C) PicoContainer Organization. All rights reserved. * 003 * ------------------------------------------------------------------------- * 004 * The software in this package is published under the terms of the BSD * 005 * style license a copy of which has been included with this distribution in * 006 * the LICENSE.txt file. * 007 * Original code by Nick Sieger * 008 *****************************************************************************/ 009 010 package org.picocontainer.script.jruby; 011 012 import java.io.IOException; 013 import java.io.Reader; 014 import java.io.StringWriter; 015 import java.util.Collections; 016 017 import org.jruby.Ruby; 018 import org.jruby.RubyInstanceConfig; 019 import org.jruby.exceptions.RaiseException; 020 import org.jruby.javasupport.JavaEmbedUtils; 021 import org.jruby.runtime.builtin.IRubyObject; 022 import org.picocontainer.PicoCompositionException; 023 import org.picocontainer.PicoContainer; 024 import org.picocontainer.DefaultPicoContainer; 025 import org.picocontainer.containers.EmptyPicoContainer; 026 import org.picocontainer.classname.DefaultClassLoadingPicoContainer; 027 import org.picocontainer.script.LifecycleMode; 028 import org.picocontainer.script.ScriptedPicoContainerMarkupException; 029 import org.picocontainer.script.ScriptedContainerBuilder; 030 import org.picocontainer.behaviors.Caching; 031 032 /** 033 * The script uses the {@code scriptedcontainer.rb} script to create an instance of 034 * {@link PicoContainer}. There are implicit variables named "$parent" and 035 * "$assembly_scope". 036 * 037 * @author Nick Sieger 038 */ 039 public final class JRubyContainerBuilder extends ScriptedContainerBuilder { 040 public static final String MARKUP_EXCEPTION_PREFIX = "scriptedbuilder: "; 041 042 private final String script; 043 044 public JRubyContainerBuilder(Reader script, ClassLoader classLoader) { 045 this(script,classLoader, LifecycleMode.AUTO_LIFECYCLE); 046 } 047 048 049 public JRubyContainerBuilder(Reader script, ClassLoader classLoader, LifecycleMode lifecycle) { 050 super(script, classLoader, lifecycle); 051 this.script = toString(script); 052 } 053 054 private String toString(Reader script) { 055 int charsRead; 056 char[] chars = new char[1024]; 057 StringWriter writer = new StringWriter(); 058 try { 059 while ((charsRead = script.read(chars)) != -1) { 060 writer.write(chars, 0, charsRead); 061 } 062 } catch (IOException e) { 063 throw new RuntimeException("unable to read script from reader", e); 064 } 065 return writer.toString(); 066 } 067 068 /** 069 * {@inheritDoc} 070 * <p>Latest method of invoking jruby script have been adapted from <a 071 * href="http://wiki.jruby.org/wiki/Java_Integration" title="Click to visit JRuby Wiki"> 072 * JRuby wiki:</a></p> 073 * @todo create a way to prevent initialization and shutdown with each script invocation. 074 */ 075 protected PicoContainer createContainerFromScript(PicoContainer parentContainer, Object assemblyScope) { 076 if (parentContainer == null) { 077 parentContainer = new EmptyPicoContainer(); 078 } 079 parentContainer = new DefaultClassLoadingPicoContainer(getClassLoader(), new DefaultPicoContainer(new Caching(), 080 parentContainer)); 081 082 083 084 RubyInstanceConfig rubyConfig = new RubyInstanceConfig(); 085 rubyConfig.setLoader(this.getClassLoader()); 086 Ruby ruby = JavaEmbedUtils.initialize(Collections.EMPTY_LIST, rubyConfig); 087 ruby.getLoadService().require("org/picocontainer/script/jruby/scriptedbuilder"); 088 ruby.defineReadonlyVariable("$parent", JavaEmbedUtils.javaToRuby(ruby, parentContainer)); 089 ruby.defineReadonlyVariable("$assembly_scope", JavaEmbedUtils.javaToRuby(ruby, assemblyScope)); 090 091 092 try { 093 094 //IRubyObject result = ruby.executeScript(script); 095 IRubyObject result = JavaEmbedUtils.newRuntimeAdapter().eval(ruby, script); 096 return (PicoContainer) JavaEmbedUtils.rubyToJava(ruby, result, PicoContainer.class); 097 } catch (RaiseException re) { 098 if (re.getCause() instanceof ScriptedPicoContainerMarkupException) { 099 throw (ScriptedPicoContainerMarkupException) re.getCause(); 100 } 101 String message = (String) JavaEmbedUtils.rubyToJava(ruby, re.getException().message, String.class); 102 if (message.startsWith(MARKUP_EXCEPTION_PREFIX)) { 103 throw new ScriptedPicoContainerMarkupException(message.substring(MARKUP_EXCEPTION_PREFIX.length())); 104 } else { 105 throw new PicoCompositionException(message, re); 106 } 107 } finally { 108 JavaEmbedUtils.terminate(ruby); 109 } 110 } 111 }