001 /***************************************************************************** 002 * Copyright (C) NanoContainer 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.nanocontainer.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.exceptions.RaiseException; 019 import org.jruby.javasupport.JavaEmbedUtils; 020 import org.jruby.runtime.builtin.IRubyObject; 021 import org.nanocontainer.integrationkit.PicoCompositionException; 022 import org.nanocontainer.reflection.DefaultNanoPicoContainer; 023 import org.nanocontainer.script.NanoContainerMarkupException; 024 import org.nanocontainer.script.ScriptedContainerBuilder; 025 import org.picocontainer.PicoContainer; 026 import org.picocontainer.alternatives.EmptyPicoContainer; 027 028 /** 029 * The script uses the {@code nanocontainer.rb} script to create an instance of 030 * {@link PicoContainer}. 031 * There are implicit variables named "$parent" and "$assembly_scope". 032 * 033 * @author Nick Sieger 034 */ 035 public class JRubyContainerBuilder extends ScriptedContainerBuilder { 036 public static final String MARKUP_EXCEPTION_PREFIX = "nanobuilder: "; 037 038 private String script; 039 040 public JRubyContainerBuilder(Reader script, ClassLoader classLoader) { 041 super(script, classLoader); 042 this.script = toString( script ); 043 } 044 045 private String toString(Reader script) { 046 int charsRead; 047 char[] chars = new char[1024]; 048 StringWriter writer = new StringWriter(); 049 try { 050 while ((charsRead = script.read(chars)) != -1) { 051 writer.write(chars, 0, charsRead); 052 } 053 } catch (IOException e) { 054 throw new RuntimeException("unable to read script from reader", e); 055 } 056 return writer.toString(); 057 } 058 059 protected PicoContainer createContainerFromScript(PicoContainer parentContainer, Object assemblyScope) { 060 if (parentContainer == null) { 061 parentContainer = new EmptyPicoContainer(); 062 } 063 parentContainer = new DefaultNanoPicoContainer(getClassLoader(), parentContainer); 064 065 Ruby ruby = JavaEmbedUtils.initialize(Collections.EMPTY_LIST); 066 ruby.getLoadService().require("org/nanocontainer/script/jruby/nanobuilder"); 067 ruby.defineReadonlyVariable("$parent", JavaEmbedUtils.javaToRuby(ruby, parentContainer)); 068 ruby.defineReadonlyVariable("$assembly_scope", JavaEmbedUtils.javaToRuby(ruby, assemblyScope)); 069 try { 070 IRubyObject result = ruby.evalScript(script); 071 return (PicoContainer) JavaEmbedUtils.rubyToJava(ruby, result, PicoContainer.class); 072 } catch (RaiseException re) { 073 if (re.getCause() instanceof NanoContainerMarkupException) { 074 throw (NanoContainerMarkupException) re.getCause(); 075 } 076 String message = (String) JavaEmbedUtils.rubyToJava(ruby, re.getException().message, String.class); 077 if (message.startsWith(MARKUP_EXCEPTION_PREFIX)) { 078 throw new NanoContainerMarkupException(message.substring(MARKUP_EXCEPTION_PREFIX.length())); 079 } else { 080 throw new PicoCompositionException(message, re); 081 } 082 } 083 } 084 }