You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@cocoon.apache.org by st...@apache.org on 2004/06/04 16:02:39 UTC

cvs commit: cocoon-2.1/src/blocks/javaflow/test/org/apache/cocoon/components/flow/javascript JavaScriptFlowTestCase.java JavaScriptFlowTestCase.xtest calc.js

stephan     2004/06/04 07:02:39

  Modified:    src/blocks/javaflow/java/org/apache/cocoon/components/flow/java
                        JavaInterpreter.java
  Added:       src/blocks/javaflow/java/org/apache/cocoon/components/flow/javascript
                        JavaScriptCompilingClassLoader.java
               src/blocks/javaflow/test/org/apache/cocoon/components/flow/javascript
                        JavaScriptFlowTestCase.java
                        JavaScriptFlowTestCase.xtest calc.js
  Log:
  Add first version of the javascript interpreter ontop of javaflow.
  
  Revision  Changes    Path
  1.7       +54 -39    cocoon-2.1/src/blocks/javaflow/java/org/apache/cocoon/components/flow/java/JavaInterpreter.java
  
  Index: JavaInterpreter.java
  ===================================================================
  RCS file: /home/cvs/cocoon-2.1/src/blocks/javaflow/java/org/apache/cocoon/components/flow/java/JavaInterpreter.java,v
  retrieving revision 1.6
  retrieving revision 1.7
  diff -u -r1.6 -r1.7
  --- JavaInterpreter.java	3 Jun 2004 12:43:27 -0000	1.6
  +++ JavaInterpreter.java	4 Jun 2004 14:02:38 -0000	1.7
  @@ -24,12 +24,14 @@
   import org.apache.avalon.framework.configuration.Configurable;
   import org.apache.avalon.framework.configuration.Configuration;
   import org.apache.avalon.framework.configuration.ConfigurationException;
  +import org.apache.avalon.framework.service.ServiceException;
   import org.apache.cocoon.ProcessingException;
   import org.apache.cocoon.components.ContextHelper;
   import org.apache.cocoon.components.flow.AbstractInterpreter;
   import org.apache.cocoon.components.flow.FlowHelper;
   import org.apache.cocoon.components.flow.InvalidContinuationException;
   import org.apache.cocoon.components.flow.WebContinuation;
  +import org.apache.cocoon.components.flow.javascript.JavaScriptCompilingClassLoader;
   import org.apache.cocoon.environment.Redirector;
   import org.apache.cocoon.environment.Request;
   import org.apache.cocoon.environment.Session;
  @@ -44,6 +46,7 @@
   public class JavaInterpreter extends AbstractInterpreter implements Configurable {
   
       private boolean initialized = false;
  +
       private int timeToLive = 600000;
   
       private static final String ACTION_METHOD_PREFIX = "do";
  @@ -53,48 +56,64 @@
        */
       public static final String USER_GLOBAL_SCOPE = "JAVA GLOBAL SCOPE";
   
  -    private ContinuationClassLoader classloader;
  +    private ContinuationClassLoader continuationclassloader;
  +
       private HashMap methods = new HashMap();
   
  +    private JavaScriptCompilingClassLoader javascriptclassloader;
  +
       static {
           JXPathIntrospector.registerDynamicClass(VarMap.class, VarMapHandler.class);
       }
   
       public void configure(Configuration config) throws ConfigurationException {
           super.configure(config);
  -        
  -        classloader = new ContinuationClassLoader(Thread.currentThread().getContextClassLoader());
  -        
  +
  +        javascriptclassloader = new JavaScriptCompilingClassLoader(Thread.currentThread().getContextClassLoader());
  +        try {
  +            javascriptclassloader.service(this.manager);
  +        } catch (ServiceException e) {
  +            throw new ConfigurationException(e.getMessage());
  +        }
  +        continuationclassloader = new ContinuationClassLoader(javascriptclassloader);
  +
           Configuration[] includes = config.getChildren("include");
  -        for(int i=0; i<includes.length; i++)
  -            classloader.addIncludeClass(includes[i].getAttribute("class"));
  +        for (int i = 0; i < includes.length; i++)
  +            continuationclassloader.addIncludeClass(includes[i].getAttribute("class"));
       }
   
       private static String removePrefix(String name) {
           int prefixLen = ACTION_METHOD_PREFIX.length();
  -        return name.substring(prefixLen, prefixLen + 1).toLowerCase()
  -                + name.substring(prefixLen + 1);
  +        return name.substring(prefixLen, prefixLen + 1).toLowerCase() + name.substring(prefixLen + 1);
       }
   
       public void initialize() throws Exception {
   
  -        if (getLogger().isDebugEnabled()) 
  +        if (getLogger().isDebugEnabled())
               getLogger().debug("initialize java flow interpreter");
   
           initialized = true;
   
           for (Iterator scripts = needResolve.iterator(); scripts.hasNext();) {
   
  -            String classname = (String) scripts.next();
  -            if (getLogger().isDebugEnabled()) 
  -                getLogger().debug("registered java class \"" + classname + "\" for flow");
  +            String name = (String) scripts.next();
   
  -            if (!Continuable.class.isAssignableFrom(Class.forName(classname))) {
  -                getLogger().error("java class \"" + classname + "\" doesn't implement Continuable");
  +            if (name.endsWith(".js")) {
  +                javascriptclassloader.addSource(name);
  +                name = JavaScriptCompilingClassLoader.getClassName(name);
  +            }
  +
  +            if (getLogger().isDebugEnabled())
  +                getLogger().debug("registered java class \"" + name + "\" for flow");
  +            
  +            Class clazz = continuationclassloader.loadClass(name);
  +
  +            if (!Continuable.class.isAssignableFrom(clazz)) {
  +                getLogger().error("java class \"" + name + "\" doesn't implement Continuable");
                   continue;
  -            }            
  +            }
   
  -            Class clazz = classloader.loadClass(classname);
  +            //Class clazz = continuationclassloader.loadClass(name);
   
               try {
                   Method[] methods = clazz.getMethods();
  @@ -105,14 +124,15 @@
                           String function = removePrefix(methodName);
                           this.methods.put(function, methods[i]);
   
  -                        if (getLogger().isDebugEnabled()) 
  -                            getLogger().debug("registered method \"" + methodName +
  -                                              "\" as function \"" + function + "\"");
  +                        if (getLogger().isDebugEnabled())
  +                            getLogger().debug(
  +                                    "registered method \"" + methodName + "\" as function \"" + function + "\"");
                       }
                   }
               } catch (Exception e) {
                   throw new ConfigurationException("cannot get methods by reflection", e);
               }
  +
           }
       }
   
  @@ -127,8 +147,7 @@
        * @param redirector
        * @exception Exception if an error occurs
        */
  -    public void callFunction(String function, List params, Redirector redirector)
  -            throws Exception {
  +    public void callFunction(String function, List params, Redirector redirector) throws Exception {
   
           if (!initialized)
               initialize();
  @@ -138,7 +157,7 @@
           if (method == null)
               throw new ProcessingException("No method found for '" + function + "'");
   
  -        if (getLogger().isDebugEnabled()) 
  +        if (getLogger().isDebugEnabled())
               getLogger().debug("calling method \"" + method + "\"");
   
           Request request = ContextHelper.getRequest(this.avalonContext);
  @@ -159,15 +178,14 @@
   
           Continuation continuation = new Continuation(context);
   
  -        WebContinuation wk =
  -                continuationsMgr.createWebContinuation(continuation, null, timeToLive, null);
  +        WebContinuation wk = continuationsMgr.createWebContinuation(continuation, null, timeToLive, null);
           FlowHelper.setWebContinuation(ContextHelper.getObjectModel(this.avalonContext), wk);
   
           continuation.registerThread();
           try {
               if (flow == null) {
                   if (getLogger().isDebugEnabled())
  -                    getLogger().debug("create new instance of \""+method.getDeclaringClass()+"\"");
  +                    getLogger().debug("create new instance of \"" + method.getDeclaringClass() + "\"");
   
                   flow = (Continuable) method.getDeclaringClass().newInstance();
                   context.setObject(flow);
  @@ -177,7 +195,7 @@
   
           } catch (InvocationTargetException ite) {
               if (ite.getTargetException() != null) {
  -                if (ite.getTargetException() instanceof Exception) 
  +                if (ite.getTargetException() instanceof Exception)
                       throw (Exception) ite.getTargetException();
                   else if (ite.getTargetException() instanceof Error)
                       throw new ProcessingException("An internal error occured", ite.getTargetException());
  @@ -199,20 +217,18 @@
           session.setAttribute(USER_GLOBAL_SCOPE, userScopes);
       }
   
  -    public void handleContinuation(String id, List params, Redirector redirector)
  -            throws Exception {
  +    public void handleContinuation(String id, List params, Redirector redirector) throws Exception {
           if (!initialized)
               initialize();
   
           WebContinuation parentwk = continuationsMgr.lookupWebContinuation(id);
   
           if (parentwk == null) {
  -            /*
  -             * Throw an InvalidContinuationException to be handled inside the
  -             * <map:handle-errors> sitemap element.
  -             */
  -            throw new InvalidContinuationException("The continuation ID " + id + " is invalid.");
  -        }
  +        /*
  +         * Throw an InvalidContinuationException to be handled inside the
  +         * <map:handle-errors> sitemap element.
  +         */
  +        throw new InvalidContinuationException("The continuation ID " + id + " is invalid."); }
   
           Continuation parentContinuation = (Continuation) parentwk.getContinuation();
           ContinuationContext parentContext = (ContinuationContext) parentContinuation.getContext();
  @@ -232,8 +248,7 @@
           Continuable flow = (Continuable) context.getObject();
           Method method = context.getMethod();
   
  -        WebContinuation wk =
  -                continuationsMgr.createWebContinuation(continuation, parentwk, timeToLive, null);
  +        WebContinuation wk = continuationsMgr.createWebContinuation(continuation, parentwk, timeToLive, null);
           FlowHelper.setWebContinuation(ContextHelper.getObjectModel(this.avalonContext), wk);
   
           continuation.registerThread();
  @@ -258,11 +273,11 @@
               // remove last object reference, which is not needed to reconstruct
               // the invocation path
               if (continuation.isCapturing())
  -              continuation.getStack().popReference();
  +                continuation.getStack().popReference();
               continuation.deregisterThread();
           }
   
           userScopes.put(method.getDeclaringClass(), flow);
           session.setAttribute(USER_GLOBAL_SCOPE, userScopes);
       }
  -}
  +}
  \ No newline at end of file
  
  
  
  1.1                  cocoon-2.1/src/blocks/javaflow/java/org/apache/cocoon/components/flow/javascript/JavaScriptCompilingClassLoader.java
  
  Index: JavaScriptCompilingClassLoader.java
  ===================================================================
  /*
   * Copyright 1999-2004 The Apache Software Foundation.
   * 
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   * 
   *      http://www.apache.org/licenses/LICENSE-2.0
   * 
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  package org.apache.cocoon.components.flow.javascript;
  
  import java.io.*;
  import java.net.MalformedURLException;
  import java.util.*;
  import java.util.HashMap;
  
  import org.apache.avalon.framework.component.*;
  import org.apache.avalon.framework.component.Composable;
  import org.apache.avalon.framework.service.*;
  import org.apache.avalon.framework.service.Serviceable;
  import org.apache.excalibur.source.*;
  import org.apache.excalibur.source.SourceResolver;
  import org.mozilla.javascript.*;
  import org.mozilla.javascript.tools.ToolErrorReporter;
  
  public class JavaScriptCompilingClassLoader extends ClassLoader implements Serviceable {
  
      private Map sources = new HashMap();
  
      private static ToolErrorReporter reporter;
  
      private SourceResolver resolver;
  
      public JavaScriptCompilingClassLoader(ClassLoader classloader) {
          super(classloader);
      }
  
      public void addSource(String uri) {
          try {
              Source source = resolver.resolveURI(uri);
  
              String classname = getClassName(uri);
              String filename = System.getProperty("java.io.tmpdir") + File.separator + classname;
  
              System.out.println("source=" + uri);
              System.out.println("filename=" + filename);
              System.out.println("classname=" + classname);
              
              System.out.println("resource="+getResource("org/apache/cocoon/components/flow/javascript/calc.js"));
  
              SourceUtil.copy(source.getInputStream(), new FileOutputStream(filename+".js"));
  
              sources.put(classname, filename);
  
          } catch (MalformedURLException e) {
              throw new IllegalArgumentException(e.getMessage());
          } catch (IOException e) {
              throw new IllegalArgumentException(e.getMessage());
          }
      }
  
      protected Class findClass(String classname) throws ClassNotFoundException {
  
          System.out.println("js: find class " + classname);
  
          //byte[] bytes = compile(className);
          String filename = (String) sources.get(classname);
          compile(classname);
  
          try {
              FileInputStream in = new FileInputStream(filename+".class");
              ByteArrayOutputStream out = new ByteArrayOutputStream();
              SourceUtil.copy(in, out);
              byte[] bytes = out.toByteArray();
  
              return defineClass(classname, bytes, 0, bytes.length);
  
          } catch (IOException e) {
              throw new ClassNotFoundException("Could not load class: " + e.getMessage());
          }
      }
  
      private void compile(String classname) throws ClassNotFoundException {
  
          System.out.println("js: compile " + classname);
  
          Context cx = Context.enter();
          reporter = new ToolErrorReporter(true);
          cx.setErrorReporter(reporter);
          
          cx.setGeneratingSource(true);
          cx.setTargetPackage("");
  
          //ClassNameHelper nameHelper = ClassNameHelper.get(cx);
          String filename = ((String) sources.get(classname))+".js";
          
          System.out.println("filename="+filename);
  
          File f = new File(filename);
  
          if (!f.exists()) { throw new ClassNotFoundException("File '" + filename + "' not found"); }
          /*if (!filename.endsWith(".js")) { throw new ClassNotFoundException("File '" + filename
                  + "' doesn't have the extention *.js"); }*/
  
          String out = ((String) sources.get(classname))+".class";
          /*String name = f.getName();
          String nojs = name.substring(0, name.length() - 3);
          String className = getClassName(nojs) + ".class";
          //String out = f.getParent() == null ? className : f.getParent() + File.separator + className;
          String out = System.getProperty("java.io.tmpdir") + File.separator + className;
          //nameHelper.setTargetClassFileName(out);*/
          cx.setTargetClassFileName(out);
  
          System.out.println("out=" + out);
  
          try {
              Reader in = new FileReader(filename);
              cx.compileReader(null, in, filename, 1, null);
          } catch (FileNotFoundException ex) {
              throw new ClassNotFoundException("File '" + filename + "' cannot open");
          } catch (IOException ioe) {
              Context.reportError(ioe.toString());
          }
          Context.exit();
      }
  
      /**
       * Verify that class file names are legal Java identifiers.  Substitute
       * illegal characters with underscores, and prepend the name with an
       * underscore if the file name does not begin with a JavaLetter.
       */
      public static String getClassName(String name) {
          char[] s = new char[name.length() + 1];
          char c;
          int j = 0;
  
          if (!Character.isJavaIdentifierStart(name.charAt(0))) {
              s[j++] = '_';
          }
          for (int i = 0; i < name.length(); i++, j++) {
              c = name.charAt(i);
              if (Character.isJavaIdentifierPart(c)) {
                  s[j] = c;
              } else {
                  s[j] = '_';
              }
          }
          return (new String(s)).trim();
      }
  
      public void service(ServiceManager manager) throws ServiceException {
          resolver = (SourceResolver) manager.lookup(SourceResolver.ROLE);
      }
  }
  
  
  
  
  1.1                  cocoon-2.1/src/blocks/javaflow/test/org/apache/cocoon/components/flow/javascript/JavaScriptFlowTestCase.java
  
  Index: JavaScriptFlowTestCase.java
  ===================================================================
  /*
   * Copyright 1999-2004 The Apache Software Foundation.
   * 
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   * 
   *      http://www.apache.org/licenses/LICENSE-2.0
   * 
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  package org.apache.cocoon.components.flow.javascript;
  
  import java.util.*;
  
  import org.apache.avalon.framework.component.*;
  import org.apache.avalon.framework.container.ContainerUtil;
  import org.apache.avalon.framework.logger.Logger;
  import org.apache.cocoon.SitemapComponentTestCase;
  import org.apache.cocoon.components.flow.*;
  import org.apache.excalibur.event.Queue;
  import org.apache.excalibur.event.command.CommandManager;
  import org.apache.excalibur.source.SourceResolver;
  
  /**
   *
   *
   * @author <a href="mailto:stephan@apache.org">Stephan Michels </a>
   * @version CVS $Id: JavaScriptFlowTestCase.java,v 1.1 2004/06/04 14:02:38 stephan Exp $
   */
  public class JavaScriptFlowTestCase extends SitemapComponentTestCase {
  
      public JavaScriptFlowTestCase(String name) {
          super(name);
      }
      
      /*public void setUp() {
          CommandManager commands = new CommandManager();
          ContainerUtil.enableLogging(commands, (Logger) getLogger());
          //this.threads.register(this.commands);
          
          getContext(). put(Queue.ROLE, commands.getCommandSink());
      }*/
  
      public void testCalculator() throws Exception {
          String source = "resource://org/apache/cocoon/components/flow/javascript/calc.js";
          callFunction("java", source, "calculator", new ArrayList());
      }
  
      public void callFunction(String type, String source, String function, List params) throws Exception {
          
          ComponentSelector selector = null;
          Interpreter interpreter = null;
          SourceResolver resolver = null;
  
          try {
              selector = (ComponentSelector) this.manager.lookup(Interpreter.ROLE);
              assertNotNull("Test lookup of interpreter selector", selector);
  
              resolver = (SourceResolver) this.manager.lookup(SourceResolver.ROLE);
              assertNotNull("Test lookup of source resolver", resolver);
  
              assertNotNull("Test if interpreter name is not null", type);
              interpreter = (Interpreter) selector.select(type);
              assertNotNull("Test lookup of interpreter", interpreter);
  
              ((AbstractInterpreter)interpreter).register(source);
              interpreter.callFunction(function, params, getRedirector());
  
          } catch (ComponentException ce) {
              getLogger().error("Could not retrieve interpeter", ce);
              fail("Could not retrieve interpreter: " + ce.toString());
          } finally {
              if (interpreter != null) {
                  selector.release((Component) interpreter);
              }
              this.manager.release(selector);
              this.manager.release(resolver);
          }
      }
      
      public void handleContinuation(String type, String source, String id, List params) throws Exception {
          
          ComponentSelector selector = null;
          Interpreter interpreter = null;
          SourceResolver resolver = null;
  
          try {
              selector = (ComponentSelector) this.manager.lookup(Interpreter.ROLE);
              assertNotNull("Test lookup of interpreter selector", selector);
  
              resolver = (SourceResolver) this.manager.lookup(SourceResolver.ROLE);
              assertNotNull("Test lookup of source resolver", resolver);
  
              assertNotNull("Test if interpreter name is not null", type);
              interpreter = (Interpreter) selector.select(type);
              assertNotNull("Test lookup of interpreter", interpreter);
  
              ((AbstractInterpreter)interpreter).register(source);
              interpreter.handleContinuation(id, params, getRedirector());
  
          } catch (ComponentException ce) {
              getLogger().error("Could not retrieve interpreter", ce);
              fail("Could not retrieve interpreter: " + ce.toString());
          } finally {
              if (interpreter != null) {
                  selector.release((Component) interpreter);
              }
              this.manager.release(selector);
              this.manager.release(resolver);
          }
      }
  }
  
  
  
  1.1                  cocoon-2.1/src/blocks/javaflow/test/org/apache/cocoon/components/flow/javascript/JavaScriptFlowTestCase.xtest
  
  Index: JavaScriptFlowTestCase.xtest
  ===================================================================
  <?xml version="1.0" ?>
  <!--
    Copyright 1999-2004 The Apache Software Foundation
  
    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at
  
        http://www.apache.org/licenses/LICENSE-2.0
  
    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.
  -->
  <testcase>
   <annotation>
    Test Cases: JavaScriptFlowTestCase
   </annotation>
  
   <logkit>
    <factories>
     <factory type="stream" class="org.apache.avalon.excalibur.logger.factory.StreamTargetFactory"/>
    </factories>
    <targets>
     <stream id="root">
      <stream>System.out</stream>
      <format type="extended">
       %7.7{priority} %5.5{time}   [%9.9{category}] (%{context}): %{message}\n%{throwable}
      </format>
     </stream>
    </targets>
    <categories>
     <category name="test" log-level="DEBUG">
      <log-target id-ref="root"/>
     </category>
    </categories>
   </logkit>
  
   <context>
    <entry name="org.apache.excalibur.event.Queue" 
    	     class="org.apache.excalibur.event.impl.DefaultQueue"/>
    <entry name="environment-context" 
    	     class="org.apache.cocoon.environment.mock.MockContext"/>  	     
   </context>
   
  
   <roles>
    <role name="org.apache.excalibur.source.SourceFactorySelector"
          shorthand="source-factories"
          default-class="org.apache.avalon.excalibur.component.ExcaliburComponentSelector"/>
  
    <role name="org.apache.excalibur.source.SourceResolver"
          shorthand="source-resolver"
          default-class="org.apache.excalibur.source.impl.SourceResolverImpl"/>
  
    <role name="org.apache.cocoon.acting.ActionSelector"
          shorthand="actions"
          default-class="org.apache.cocoon.components.ExtendedComponentSelector"/>
  
    <role name="org.apache.cocoon.components.flow.Interpreter"
          shorthand="flow-interpreters"
          default-class="org.apache.cocoon.components.flow.InterpreterSelector"/>
  
    <role name="org.apache.cocoon.components.flow.ContinuationsManager"
          shorthand="continuations-manager"
          default-class="org.apache.cocoon.components.flow.ContinuationsManagerImpl"/>
  
   </roles>
  
   <components>
    <source-factories>
     <component-instance class="org.apache.excalibur.source.impl.ResourceSourceFactory" name="resource"/>
     <component-instance class="org.apache.excalibur.source.impl.URLSourceFactory" name="*"/>
    </source-factories>
  
    <source-resolver class="org.apache.excalibur.source.impl.SourceResolverImpl"/>
  
    <flow-interpreters default="java">
      <component-instance class="org.apache.cocoon.components.flow.java.JavaInterpreter" name="java"/>
    </flow-interpreters>
  
    <continuations-manager time-to-live="3600000">
      <expirations-check type="periodic">
        <offset>180000</offset>
        <period>180000</period>
      </expirations-check>
    </continuations-manager>
   </components>
  
  </testcase>
  
  
  
  1.1                  cocoon-2.1/src/blocks/javaflow/test/org/apache/cocoon/components/flow/javascript/calc.js
  
  Index: calc.js
  ===================================================================
  /*
  * Copyright 1999-2004 The Apache Software Foundation
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
  *     http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
  var a, b, op;
  
  function calculator()
  {
    a = getNumber("a");
    b = getNumber("b", a);
    op = getOperator(a, b);
  
    if (op == "plus")
      sendResult(a, b, op, a + b);
    else if (op == "minus")
      sendResult(a, b, op, a - b);
    else if (op == "multiply")
      sendResult(a, b, op, a * b);
    else if (op == "divide")
      sendResult(a, b, op, a / b);
    else
      sendResult("Error: Unkown operator!");
  }
  
  function getNumber(name, a, b)
  {
    var uri = "page/getNumber" + name.toUpperCase();
    cocoon.sendPageAndWait(uri, { "a" : a, "b" : b });
    return parseFloat(cocoon.request.getParameter(name));
  }
  
  function getOperator(a, b)
  {
    cocoon.sendPageAndWait("page/getOperator", { "a" : a, "b" : b });
    return cocoon.request.operator;
  }
  
  function sendResult(a, b, op, result)
  {
    cocoon.sendPage("page/displayResult",
             { 
                  "a" : a, 
                  "b" : b, 
                  "operator" : op, 
                  "result" : result 
             }
    );
  }