001    /*
002     * Copyright (C) 2012 eXo Platform SAS.
003     *
004     * This is free software; you can redistribute it and/or modify it
005     * under the terms of the GNU Lesser General Public License as
006     * published by the Free Software Foundation; either version 2.1 of
007     * the License, or (at your option) any later version.
008     *
009     * This software is distributed in the hope that it will be useful,
010     * but WITHOUT ANY WARRANTY; without even the implied warranty of
011     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012     * Lesser General Public License for more details.
013     *
014     * You should have received a copy of the GNU Lesser General Public
015     * License along with this software; if not, write to the Free
016     * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
017     * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
018     */
019    
020    package org.crsh.lang.groovy.command;
021    
022    import groovy.lang.Closure;
023    import groovy.lang.GroovyObject;
024    import groovy.lang.GroovyRuntimeException;
025    import groovy.lang.MetaClass;
026    import groovy.lang.MissingMethodException;
027    import groovy.lang.MissingPropertyException;
028    import org.codehaus.groovy.runtime.InvokerHelper;
029    import org.codehaus.groovy.runtime.InvokerInvocationException;
030    import org.crsh.cli.impl.descriptor.IntrospectionException;
031    import org.crsh.command.BaseCommand;
032    import org.crsh.command.InvocationContext;
033    import org.crsh.command.NoSuchCommandException;
034    import org.crsh.command.ScriptException;
035    import org.crsh.command.ShellCommand;
036    import org.crsh.lang.groovy.closure.PipeLineClosure;
037    import org.crsh.lang.groovy.closure.PipeLineInvoker;
038    import org.crsh.shell.impl.command.CRaSH;
039    
040    import java.io.IOException;
041    import java.lang.reflect.UndeclaredThrowableException;
042    
043    public abstract class GroovyCommand extends BaseCommand implements GroovyObject {
044    
045      // never persist the MetaClass
046      private transient MetaClass metaClass;
047    
048      protected GroovyCommand() throws IntrospectionException {
049        this.metaClass = InvokerHelper.getMetaClass(this.getClass());
050      }
051    
052      @Override
053      public UndeclaredThrowableException toScript(Throwable cause) {
054        if (cause instanceof groovy.util.ScriptException) {
055          cause = unwrap((groovy.util.ScriptException)cause);
056        }
057        return super.toScript(cause);
058      }
059    
060      public static ScriptException unwrap(groovy.util.ScriptException cause) {
061        // Special handling for groovy.util.ScriptException
062        // which may be thrown by scripts because it is imported by default
063        // by groovy imports
064        String msg = cause.getMessage();
065        ScriptException translated;
066        if (msg != null) {
067          translated = new ScriptException(msg);
068        } else {
069          translated = new ScriptException();
070        }
071        translated.setStackTrace(cause.getStackTrace());
072        return translated;
073      }
074    
075      public static ScriptException unwrap(Throwable cause) {
076        if (cause instanceof ScriptException) {
077          return (ScriptException)cause;
078        } if (cause instanceof groovy.util.ScriptException) {
079          return unwrap((groovy.util.ScriptException)cause);
080        } else {
081          return new ScriptException(cause);
082        }
083      }
084    
085      public final Object invokeMethod(String name, Object args) {
086        try {
087          return getMetaClass().invokeMethod(this, name, args);
088        }
089        catch (MissingMethodException missing) {
090          if (context instanceof InvocationContext) {
091            CRaSH crash = (CRaSH)context.getSession().get("crash");
092            if (crash != null) {
093              ShellCommand cmd;
094              try {
095                cmd = crash.getCommand(name);
096              }
097              catch (NoSuchCommandException ce) {
098                throw new InvokerInvocationException(ce);
099              }
100              if (cmd != null) {
101                InvocationContext<Object> ic = (InvocationContext<Object>)peekContext();
102                PipeLineClosure closure = new PipeLineClosure(ic, name, cmd);
103                PipeLineInvoker evaluation = closure.bind(args);
104                try {
105                  evaluation.invoke(ic);
106                  return null;
107                }
108                catch (IOException e) {
109                  throw new GroovyRuntimeException(e);
110                }
111                catch (UndeclaredThrowableException e) {
112                  throw new GroovyRuntimeException(e.getCause());
113                }
114              }
115            }
116          }
117    
118          //
119          Object o = context.getSession().get(name);
120          if (o instanceof Closure) {
121            Closure closure = (Closure)o;
122            if (args instanceof Object[]) {
123              Object[] array = (Object[])args;
124              if (array.length == 0) {
125                return closure.call();
126              } else {
127                return closure.call(array);
128              }
129            } else {
130              return closure.call(args);
131            }
132          } else {
133            throw missing;
134          }
135        }
136      }
137    
138      public final Object getProperty(String property) {
139        if (context instanceof InvocationContext<?>) {
140          CRaSH crash = (CRaSH)context.getSession().get("crash");
141          if (crash != null) {
142            try {
143              ShellCommand cmd = crash.getCommand(property);
144              if (cmd != null) {
145                InvocationContext<Object> ic = (InvocationContext<Object>)peekContext();
146                return new PipeLineClosure(ic, property, cmd);
147              }
148            } catch (NoSuchCommandException e) {
149              throw new InvokerInvocationException(e);
150            }
151          }
152        }
153    
154        //
155        try {
156          return getMetaClass().getProperty(this, property);
157        }
158        catch (MissingPropertyException e) {
159          return context.getSession().get(property);
160        }
161      }
162    
163      public final void setProperty(String property, Object newValue) {
164        try {
165          getMetaClass().setProperty(this, property, newValue);
166        }
167        catch (MissingPropertyException e) {
168          context.getSession().put(property, newValue);
169        }
170      }
171    
172      public MetaClass getMetaClass() {
173        if (metaClass == null) {
174          metaClass = InvokerHelper.getMetaClass(getClass());
175        }
176        return metaClass;
177      }
178    
179      public void setMetaClass(MetaClass metaClass) {
180        this.metaClass = metaClass;
181      }
182    }