Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
CAMEL-1655 - Add support for saving Groovy and Ruby route code. Viewing
as Groovy or Ruby is TBD.

Thanks to Xueqiang Mi for this contribution!



git-svn-id: https://svn.apache.org/repos/asf/camel/trunk@783847 13f79535-47bb-0310-9956-ffa450edef68
  • Loading branch information
janstey committed Jun 11, 2009
1 parent 8545296 commit c75ef35
Show file tree
Hide file tree
Showing 3 changed files with 244 additions and 45 deletions.
8 changes: 8 additions & 0 deletions components/camel-web/pom.xml
Expand Up @@ -72,6 +72,14 @@
<groupId>org.apache.camel</groupId>
<artifactId>camel-spring</artifactId>
</dependency>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-groovy</artifactId>
</dependency>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-ruby</artifactId>
</dependency>
<dependency>
<groupId>com.sun.jersey.contribs</groupId>
<artifactId>jersey-spring</artifactId>
Expand Down
Expand Up @@ -16,12 +16,15 @@
*/
package org.apache.camel.web.resources;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Collections;
import java.util.List;

import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
Expand All @@ -39,12 +42,17 @@

import com.sun.jersey.api.representation.Form;
import com.sun.jersey.api.view.Viewable;
import groovy.lang.GroovyClassLoader;

import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.impl.DefaultCamelContext;
import org.apache.camel.model.RouteDefinition;
import org.apache.camel.ruby.RubyCamel;
import org.apache.camel.util.ObjectHelper;
import org.apache.camel.view.RouteDotGenerator;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jruby.Main;

/**
* A single Camel Route which is used to implement one or more
Expand All @@ -54,11 +62,23 @@
*/
public class RouteResource extends CamelChildResourceSupport {
private static final transient Log LOG = LogFactory.getLog(RouteResource.class);
private static final String LANGUAGE_XML = "Xml";
private static final String LANGUAGE_GROOVY = "Groovy";
private static final String LANGUAGE_RUBY = "Ruby";
private static final String LANGUAGE_SCALA = "Scala";

private RouteDefinition route;
private String error = "";
private String id;

// what language is used to define this route
private String language = LANGUAGE_XML;

// the route configuration: when language is Xml, the routeDefinition is
// null; when language is Groovy/Scala/Ruby, the routeDefinition contains
// the route definition class. It must be initialized because RouteResource
// is stateless.
private String routeDefinition = "";

public RouteResource(RoutesResource routesResource, RouteDefinition route) {
super(routesResource.getContextResource());
Expand All @@ -83,15 +103,40 @@ public String getRouteXml() throws JAXBException {
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
// TODO fix to use "" namespace prefix
// using this https://jaxb.dev.java.net/nonav/2.1.10/docs/vendorProperties.html#prefixmapper
// using this
// https://jaxb.dev.java.net/nonav/2.1.10/docs/vendorProperties.html#prefixmapper
StringWriter buffer = new StringWriter();
marshaller.marshal(route, buffer);
return buffer.toString();
}

/**
* Returns the Graphviz DOT <a href="http://camel.apache.org/visualisation.html">Visualisation</a>
* of this route
* Returns the language
*/
public String getLanguage() {
return language;
}

/**
* Returns the content of the route definition class
*/
public String getRouteDefinition() {
if (language.equals(LANGUAGE_XML)) {
try {
return getRouteXml();
} catch (JAXBException e) {
// e.printStackTrace();
return "Error on marshal the route definition!";
}
} else {
return routeDefinition;
}
}

/**
* Returns the Graphviz DOT <a
* href="http://camel.apache.org/visualisation.html">Visualisation</a> of
* this route
*/
@GET
@Produces(Constants.DOT_MIMETYPE)
Expand All @@ -100,7 +145,6 @@ public String getDot() throws IOException {
return generator.getRoutesText(getCamelContext());
}


/**
* Allows a route definition to be updated
*/
Expand All @@ -115,45 +159,187 @@ public void postRoute(RouteDefinition routeDefinition) throws Exception {
getCamelContext().addRouteDefinitions(Collections.singletonList(routeDefinition));
}

/**
* Allows a routes builder to be updated
*/
public void postRoutes(RouteBuilder builder) throws Exception {
// remove current route
DefaultCamelContext camelContext = (DefaultCamelContext)getCamelContext();
camelContext.removeRouteDefinition(id);

// lets install the updated routes
camelContext.addRoutes(builder);
}

/**
* Updates a route definition using form encoded data from a web form
*
* @param formData is the form data POSTed typically from a HTML form with the <code>route</code> field used to encode
* the XML text of the new route definition
*
* @param formData is the form data POSTed typically from a HTML form with
* the <code>route</code> field used to encode the XML text of
* the new route definition
*/
@POST
@Consumes("application/x-www-form-urlencoded")
public Response postRouteForm(@Context UriInfo uriInfo, Form formData) throws URISyntaxException {
// TODO replace the Form class with an injected bean?
String xml = formData.getFirst("route", String.class);
String language = formData.getFirst("language", String.class);
String body = formData.getFirst("route", String.class);
if (LOG.isDebugEnabled()) {
LOG.debug("new XML is: " + xml);
LOG.debug("new Route is: " + body);
}
if (xml == null) {
error = "No XML submitted!";
} else {
try {
JAXBContext context = JAXBContext.newInstance(Constants.JAXB_PACKAGES);
Unmarshaller unmarshaller = context.createUnmarshaller();
Object value = unmarshaller.unmarshal(new StringReader(xml));
if (value instanceof RouteDefinition) {
RouteDefinition routeDefinition = (RouteDefinition) value;
postRoute(routeDefinition);
return Response.seeOther(new URI("/routes")).build();
} else {
error = "Posted XML is not a route but is of type " + ObjectHelper.className(value);
}
} catch (JAXBException e) {
error = "Failed to parse XML: " + e.getMessage();
} catch (Exception e) {
error = "Failed to install route: " + e.getMessage();
LOG.info(body);
if (body == null) {
error = "No Route submitted!";
} else if (language.equals(LANGUAGE_XML)) {
return parseXml(body);
} else if (language.equals(LANGUAGE_GROOVY)) {
return parseGroovy(body);
} else if (language.equals(LANGUAGE_RUBY)) {
return parseRuby(body);
} else if (language.equals(LANGUAGE_SCALA)) {
return parseScala(body);
}
error = "Not supproted language!";
return Response.ok(new Viewable("edit", this)).build();

}

/**
* process the route configuration defined in Xml
*/
private Response parseXml(String xml) {
try {
JAXBContext context = JAXBContext.newInstance(Constants.JAXB_PACKAGES);
Unmarshaller unmarshaller = context.createUnmarshaller();
Object value = unmarshaller.unmarshal(new StringReader(xml));
if (value instanceof RouteDefinition) {
RouteDefinition routeDefinition = (RouteDefinition)value;
postRoute(routeDefinition);
return Response.seeOther(new URI("/routes")).build();
} else {
error = "Posted XML is not a route but is of type " + ObjectHelper.className(value);
}
} catch (JAXBException e) {
error = "Failed to parse XML: " + e.getMessage();
} catch (Exception e) {
error = "Failed to install route: " + e.getMessage();
}
// lets re-render the form
return Response.ok(new Viewable("edit", this)).build();
}

/**
* process the route configuration defined in Groovy class
*/
private Response parseGroovy(String route) {
try {
// store the route definition
File file = storeRoute(route, LANGUAGE_GROOVY);

// load the definition class into a RouteBuilder instance
GroovyClassLoader classLoader = new GroovyClassLoader();
Class clazz = classLoader.parseClass(file);
RouteBuilder builder = (RouteBuilder)clazz.newInstance();
LOG.info("Loaded builder: " + builder);

postRoutes(builder);

return Response.seeOther(new URI("/routes")).build();

} catch (IOException e) {
// e.printStackTrace();
error = "Failed to store the route: " + e.getMessage();
} catch (InstantiationException e) {
// e.printStackTrace();
error = "Failed to instantiate the route: " + e.getMessage();

} catch (IllegalAccessException e) {
// e.printStackTrace();
error = "Failed to instantiate the route: " + e.getMessage();
} catch (Exception e) {
// e.printStackTrace();
error = "Failed to edit the route: " + e.getMessage();
}
// lets re-render the form
return Response.ok(new Viewable("edit", this)).build();
}

/**
* process the route configuration defined in Ruby class
*/
private Response parseRuby(String route) {
try {
// add the script of addRouteBuilder into ruby script
route += "\n RubyCamel.addRouteBuilder(RubyRoute.new)";

// store the route definition
File file = storeRoute(route, LANGUAGE_RUBY);

// execute the ruby script, which will store the RouteBuilder
// instances into RubyCamel
String[] args = {file.getAbsolutePath()};
Main.main(args);

// get the route builders from the RubyCamel and add them into this
// route
List<RouteBuilder> list = RubyCamel.getRoutes();
for (RouteBuilder builder : list) {
postRoutes(builder);
}

return Response.seeOther(new URI("/routes")).build();

} catch (IOException e) {
// e.printStackTrace();
error = "Failed to store the route: " + e.getMessage();
} catch (Exception e) {
// e.printStackTrace();
error = "Failed to edit the route: " + e.getMessage();

}
// lets re-render the form
return Response.ok(new Viewable("edit", this)).build();
}

/**
* process the route configuration defined in Scala class
*/
private Response parseScala(String route) {
try {

// store the route definition
File file = storeRoute(route, LANGUAGE_SCALA);

// load the definition class

return Response.seeOther(new URI("/routes")).build();

} catch (IOException e) {
// e.printStackTrace();
error = "Failed to store the route: " + e.getMessage();
} catch (Exception e) {
// e.printStackTrace();
error = "Failed to edit the route: " + e.getMessage();
}
// lets re-render the form
return Response.ok(new Viewable("edit", this)).build();
}

/**
* Stores the route definition class into a file
*/
private File storeRoute(String route, String language) throws IOException {
// create a temporary file to store the route definition class
File file = File.createTempFile("Route-", "." + language);
FileWriter fw = new FileWriter(file);

// write the route into the file
fw.write(route);
fw.flush();
fw.close();
return file;
}

/**
* Looks up an individual route
*/
Expand Down
@@ -1,28 +1,33 @@
<html>
<head>
<script type='text/javascript' src="<c:url value='/js/dojo/dojo.js'/>"></script>
<script type='text/javascript' src="<c:url value='/js/bespin/editor/embed.js'/>"></script>
<script type='text/javascript' src="<c:url value='/js/route.js'/>"></script>
<script type='text/javascript' src="<c:url value='/js/dojo/dojo.js'/>"></script>
<script type='text/javascript'
src="<c:url value='/js/bespin/editor/embed.js'/>"></script>
<script type='text/javascript' src="<c:url value='/js/route.js'/>"></script>

<title>Edit ${it.route.id}</title>
<title>Edit ${it.route.id}</title>
</head>
<body>

<form id="routeForm" action="<c:url value="/routes/${it.route.id}"/>" method="post">
<form id="routeForm" action="<c:url value="/routes/${it.route.id}"/>"
method="post">
<table>
<tr>
<td>
<h2>Edit ${it.route.id}</h2>
</td>
<td>
<input type="submit" value="Save">
</td>
</tr>
<tr>
<td colspan="2">
<textarea id="route" name="route" style="width: 800px; height: 300px; border: 10px solid #ddd; -moz-border-radius: 10px; -webkit-border-radius: 10px;">${it.routeXml}</textarea>
</td>
</tr>
<tr>
<td>
<h2>Edit ${it.route.id}</h2>
</td>
<td><input type="submit" value="Save">&nbsp; as &nbsp;<select id="language" name="language">
<option value="Xml" selected>XML</option>
<option value="Groovy">Groovy</option>
<option value="Ruby">Ruby</option>
<option value="Scala">Scala</option>
</select></td>
</tr>
<tr>
<td colspan="2"><textarea id="route" name="route"
style="width: 800px; height: 300px; border: 10px solid #ddd; -moz-border-radius: 10px; -webkit-border-radius: 10px;">${it.routeDefinition}</textarea>
</td>
</tr>
</table>

<div class="error">${it.error}</div>
Expand Down

0 comments on commit c75ef35

Please sign in to comment.