You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@activemq.apache.org by ch...@apache.org on 2010/12/15 03:12:02 UTC

svn commit: r1049386 - in /activemq/activemq-apollo/trunk: apollo-broker/src/main/scala/org/apache/activemq/apollo/broker/ apollo-broker/src/test/scala/org/apache/activemq/apollo/web/ apollo-dto/src/main/java/org/apache/activemq/apollo/dto/ apollo-dto/...

Author: chirino
Date: Wed Dec 15 02:12:02 2010
New Revision: 1049386

URL: http://svn.apache.org/viewvc?rev=1049386&view=rev
Log:
Simplifying the BrokerDTO and FileConfigStore

Added:
    activemq/activemq-apollo/trunk/apollo-broker/src/test/scala/org/apache/activemq/apollo/web/
    activemq/activemq-apollo/trunk/apollo-broker/src/test/scala/org/apache/activemq/apollo/web/FileConfigStoreTest.scala
      - copied, changed from r1049385, activemq/activemq-apollo/trunk/apollo-web/src/test/scala/org/apache/activemq/apollo/web/FileConfigStoreTest.scala
    activemq/activemq-apollo/trunk/apollo-dto/src/main/java/org/apache/activemq/apollo/dto/ValueDTO.java
      - copied, changed from r1049385, activemq/activemq-apollo/trunk/apollo-web/src/test/scala/org/apache/activemq/apollo/web/FileConfigStoreTest.scala
    activemq/activemq-apollo/trunk/apollo-web/src/main/webapp/WEB-INF/org/apache/activemq/apollo/web/resources/EditConfig.jade
      - copied, changed from r1049385, activemq/activemq-apollo/trunk/apollo-web/src/main/webapp/WEB-INF/org/apache/activemq/apollo/dto/BrokerSummaryDTO.jade
Removed:
    activemq/activemq-apollo/trunk/apollo-web/src/test/scala/org/apache/activemq/apollo/web/FileConfigStoreTest.scala
Modified:
    activemq/activemq-apollo/trunk/apollo-broker/src/main/scala/org/apache/activemq/apollo/broker/ConfigStore.scala
    activemq/activemq-apollo/trunk/apollo-dto/src/main/java/org/apache/activemq/apollo/dto/BrokerDTO.java
    activemq/activemq-apollo/trunk/apollo-dto/src/main/resources/org/apache/activemq/apollo/dto/jaxb.index
    activemq/activemq-apollo/trunk/apollo-web/src/main/scala/org/apache/activemq/apollo/web/resources/ConfigurationResource.scala
    activemq/activemq-apollo/trunk/apollo-web/src/main/webapp/WEB-INF/org/apache/activemq/apollo/dto/BrokerDTO.jade
    activemq/activemq-apollo/trunk/apollo-web/src/main/webapp/WEB-INF/org/apache/activemq/apollo/dto/BrokerSummaryDTO.jade

Modified: activemq/activemq-apollo/trunk/apollo-broker/src/main/scala/org/apache/activemq/apollo/broker/ConfigStore.scala
URL: http://svn.apache.org/viewvc/activemq/activemq-apollo/trunk/apollo-broker/src/main/scala/org/apache/activemq/apollo/broker/ConfigStore.scala?rev=1049386&r1=1049385&r2=1049386&view=diff
==============================================================================
--- activemq/activemq-apollo/trunk/apollo-broker/src/main/scala/org/apache/activemq/apollo/broker/ConfigStore.scala (original)
+++ activemq/activemq-apollo/trunk/apollo-broker/src/main/scala/org/apache/activemq/apollo/broker/ConfigStore.scala Wed Dec 15 02:12:02 2010
@@ -16,24 +16,17 @@
  */
 package org.apache.activemq.apollo.broker
 
-import org.apache.activemq.apollo.broker.jaxb.PropertiesReader
-import org.apache.activemq.apollo.dto.{XmlCodec, ConnectorDTO, VirtualHostDTO, BrokerDTO}
-import java.util.regex.Pattern
-import javax.xml.stream.{XMLOutputFactory, XMLInputFactory}
+import org.apache.activemq.apollo.dto.{XmlCodec, BrokerDTO}
 import org.fusesource.hawtdispatch._
-import _root_.org.fusesource.hawtdispatch.ScalaDispatchHelpers._
 import java.util.concurrent.{TimeUnit, ExecutorService, Executors}
 import org.fusesource.hawtbuf.{ByteArrayInputStream, ByteArrayOutputStream}
-import javax.xml.bind.{Marshaller, JAXBContext}
 import security.EncryptionSupport
 import XmlCodec._
 import org.apache.activemq.apollo.util._
-import scala.util.continuations._
-import org.fusesource.hawtdispatch.DispatchQueue
 import java.util.Arrays
 import FileSupport._
 import java.util.Properties
-import java.io.{FileInputStream, OutputStreamWriter, File}
+import java.io.{FileOutputStream, FileInputStream, File}
 
 object ConfigStore {
 
@@ -54,9 +47,12 @@ object ConfigStore {
  */
 trait ConfigStore {
 
+  def read(): String
+  def write(value:String): Unit
+
   def load(eval:Boolean): BrokerDTO
 
-  def store(config:BrokerDTO): Boolean
+  def store(config:BrokerDTO): Unit
 
   def can_write:Boolean
 
@@ -79,13 +75,7 @@ object FileConfigStore extends Log
 class FileConfigStore extends ConfigStore {
   import FileConfigStore._
 
-  object StoredBrokerModel {
-    def apply(config:BrokerDTO) = {
-      val data = marshall(config)
-      new StoredBrokerModel(config.rev, data, 0)
-    }
-  }
-  case class StoredBrokerModel(rev:Int, data:Array[Byte], lastModified:Long)
+  case class StoredBrokerModel(data:Array[Byte], lastModified:Long)
 
   var file:File = new File("activemq.xml")
 
@@ -106,29 +96,20 @@ class FileConfigStore extends ConfigStor
     running = true
 
     file = file.getCanonicalFile;
-    file.getParentFile.mkdir
-    // Find the latest rev
-    val files = file.getParentFile.listFiles
-    val regex = (Pattern.quote(file.getName+".")+"""(\d)$""").r
-    var revs:List[Int] = Nil
-    if( files!=null ) {
-      for( file <- files) {
-        file.getName match {
-          case regex(ver)=>
-            revs ::= Integer.parseInt(ver)
-          case _ =>
-        }
-      }
-    }
-    revs = revs.sortWith((x,y)=> x < y)
 
-    val last = if( file.exists ) {
-      read(1, file)
-    } else {
-      write(StoredBrokerModel(defaultConfig(1)))
+    if( !file.exists ) {
+      try {
+        // try to create a default version of the file.
+        store(Broker.defaultConfig)
+      } catch {
+        case e:Throwable =>
+      }
+      if( !file.exists ) {
+        throw new Exception("The '%s' configuration file does not exist.".format(file.getPath))
+      }
     }
 
-    latest = last
+    latest = read(file)
     schedualNextUpdateCheck
   }
 
@@ -141,38 +122,36 @@ class FileConfigStore extends ConfigStor
     unmarshall(latest.data, eval)
   }
 
+  def read() = {
+    new String(latest.data)
+  }
+
   def can_write:Boolean = file.canWrite
 
-  def store(config:BrokerDTO) = {
-    debug("storing broker model: %s ver %d", config.id, config.rev)
-    if( latest.rev+1 != config.rev ) {
-      debug("update request does not match next revision: %d", latest.rev+1)
-      false
-    } else {
-      latest = write(StoredBrokerModel(config))
-      true
-    }
+  def store(config:BrokerDTO):Unit = {
+    val data = marshall(config)
+    latest = write(StoredBrokerModel(data, 0))
   }
 
-  private def fileRev(rev:Int) = new File(file.getParent, file.getName+"."+rev)
+  def write(value:String) = {
+    val m = StoredBrokerModel(value.getBytes, 0)
+    unmarshall(m.data)
+    latest = write(m)
+  }
 
   private def schedualNextUpdateCheck:Unit = dispatchQueue.after(1, TimeUnit.SECONDS) {
     if( running ) {
       val lastModified = latest.lastModified
       val latestData = latest.data
-      val nextRev = latest.rev+1
       ioWorker {
         try {
           val l = file.lastModified
           if( l != lastModified ) {
-            val config = read(nextRev, file)
+            val config = read(file)
             if ( !Arrays.equals(latestData, config.data) ) {
-              val c = unmarshall(config.data)
-              c.rev = config.rev
-              store(c)
-            } else {
-              latest = latest.copy(lastModified = l)
+              // TODO: trigger reloading the config file.
             }
+            latest = config
           }
           schedualNextUpdateCheck
         }
@@ -185,24 +164,24 @@ class FileConfigStore extends ConfigStor
     }
   }
 
-
-
-  private def defaultConfig(rev:Int) = {
-    val config = Broker.defaultConfig
-    config.rev = rev
-    config
-  }
-
-  private def read(rev:Int, file: File) ={
+  private def read(file: File) ={
     val data = IOHelper.readBytes(file)
     val config = unmarshall(data) // validates the xml
-    StoredBrokerModel(rev, data, file.lastModified)
+    StoredBrokerModel(data, file.lastModified)
   }
 
   private  def write(config:StoredBrokerModel) = {
-    // write to the files..
+
+    // backup the config file...
+    if(file.exists()) {
+      using(new FileInputStream(file)) { in =>
+        using(new FileOutputStream(file.getParentFile / ("~"+file.getName))) { out =>
+          copy(in, out)
+        }
+      }
+    }
+
     IOHelper.writeBinaryFile(file, config.data)
-    IOHelper.writeBinaryFile(fileRev(config.rev), config.data)
     config.copy(lastModified = file.lastModified)
   }
 

Copied: activemq/activemq-apollo/trunk/apollo-broker/src/test/scala/org/apache/activemq/apollo/web/FileConfigStoreTest.scala (from r1049385, activemq/activemq-apollo/trunk/apollo-web/src/test/scala/org/apache/activemq/apollo/web/FileConfigStoreTest.scala)
URL: http://svn.apache.org/viewvc/activemq/activemq-apollo/trunk/apollo-broker/src/test/scala/org/apache/activemq/apollo/web/FileConfigStoreTest.scala?p2=activemq/activemq-apollo/trunk/apollo-broker/src/test/scala/org/apache/activemq/apollo/web/FileConfigStoreTest.scala&p1=activemq/activemq-apollo/trunk/apollo-web/src/test/scala/org/apache/activemq/apollo/web/FileConfigStoreTest.scala&r1=1049385&r2=1049386&rev=1049386&view=diff
==============================================================================
--- activemq/activemq-apollo/trunk/apollo-web/src/test/scala/org/apache/activemq/apollo/web/FileConfigStoreTest.scala (original)
+++ activemq/activemq-apollo/trunk/apollo-broker/src/test/scala/org/apache/activemq/apollo/web/FileConfigStoreTest.scala Wed Dec 15 02:12:02 2010
@@ -1,3 +1,5 @@
+package org.apache.activemq.apollo.web
+
 /**
  * Licensed to the Apache Software Foundation (ASF) under one or more
  * contributor license agreements.  See the NOTICE file distributed with
@@ -14,8 +16,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.activemq.apollo.web
-
 import java.io.File
 import org.apache.activemq.apollo.util._
 import org.apache.activemq.apollo.broker.FileConfigStore
@@ -41,5 +41,4 @@ class FileConfigStoreTest extends FunSui
 
     store.stop
   }
-}
-
+}
\ No newline at end of file

Modified: activemq/activemq-apollo/trunk/apollo-dto/src/main/java/org/apache/activemq/apollo/dto/BrokerDTO.java
URL: http://svn.apache.org/viewvc/activemq/activemq-apollo/trunk/apollo-dto/src/main/java/org/apache/activemq/apollo/dto/BrokerDTO.java?rev=1049386&r1=1049385&r2=1049386&view=diff
==============================================================================
--- activemq/activemq-apollo/trunk/apollo-dto/src/main/java/org/apache/activemq/apollo/dto/BrokerDTO.java (original)
+++ activemq/activemq-apollo/trunk/apollo-dto/src/main/java/org/apache/activemq/apollo/dto/BrokerDTO.java Wed Dec 15 02:12:02 2010
@@ -21,6 +21,8 @@ import java.util.ArrayList;
 import java.util.List;
 
 /**
+ * This is the root container for a broker's configuration.
+ *
  * @author <a href="http://hiramchirino.com">Hiram Chirino</a>
  */
 @XmlRootElement(name="broker")
@@ -28,18 +30,6 @@ import java.util.List;
 public class BrokerDTO extends ServiceDTO<String> {
 
     /**
-     * Used to track config revisions.
-     */
-    @XmlAttribute
-    public int rev;
-
-    /**
-     * Used to track who last modified the configuration.
-     */
-    @XmlAttribute(name="modified-by")
-    public String modified_by;
-
-    /**
      * Used to store any configuration notes.
      */
     @XmlElement

Copied: activemq/activemq-apollo/trunk/apollo-dto/src/main/java/org/apache/activemq/apollo/dto/ValueDTO.java (from r1049385, activemq/activemq-apollo/trunk/apollo-web/src/test/scala/org/apache/activemq/apollo/web/FileConfigStoreTest.scala)
URL: http://svn.apache.org/viewvc/activemq/activemq-apollo/trunk/apollo-dto/src/main/java/org/apache/activemq/apollo/dto/ValueDTO.java?p2=activemq/activemq-apollo/trunk/apollo-dto/src/main/java/org/apache/activemq/apollo/dto/ValueDTO.java&p1=activemq/activemq-apollo/trunk/apollo-web/src/test/scala/org/apache/activemq/apollo/web/FileConfigStoreTest.scala&r1=1049385&r2=1049386&rev=1049386&view=diff
==============================================================================
--- activemq/activemq-apollo/trunk/apollo-web/src/test/scala/org/apache/activemq/apollo/web/FileConfigStoreTest.scala (original)
+++ activemq/activemq-apollo/trunk/apollo-dto/src/main/java/org/apache/activemq/apollo/dto/ValueDTO.java Wed Dec 15 02:12:02 2010
@@ -14,32 +14,24 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.activemq.apollo.web
+package org.apache.activemq.apollo.dto;
 
-import java.io.File
-import org.apache.activemq.apollo.util._
-import org.apache.activemq.apollo.broker.FileConfigStore
-import org.fusesource.hawtdispatch._
+import javax.xml.bind.annotation.*;
+import java.util.ArrayList;
+import java.util.List;
 
 /**
- * <p>
- * </p>
+ * This is the root container for a broker's configuration.
  *
  * @author <a href="http://hiramchirino.com">Hiram Chirino</a>
  */
-class FileConfigStoreTest extends FunSuiteSupport {
-  test("file config store") {
-
-    val store = new FileConfigStore
-    store.file = new File("activemq.xml")
-
-    store.start
-
-    expect("default") {
-      store.load(false).id
-    }
-
-    store.stop
-  }
+@XmlRootElement(name="value")
+@XmlAccessorType(XmlAccessType.FIELD)
+public class ValueDTO {
+
+    /**
+     * Holds a value
+     */
+    @XmlValue
+    public String value;
 }
-

Modified: activemq/activemq-apollo/trunk/apollo-dto/src/main/resources/org/apache/activemq/apollo/dto/jaxb.index
URL: http://svn.apache.org/viewvc/activemq/activemq-apollo/trunk/apollo-dto/src/main/resources/org/apache/activemq/apollo/dto/jaxb.index?rev=1049386&r1=1049385&r2=1049386&view=diff
==============================================================================
--- activemq/activemq-apollo/trunk/apollo-dto/src/main/resources/org/apache/activemq/apollo/dto/jaxb.index (original)
+++ activemq/activemq-apollo/trunk/apollo-dto/src/main/resources/org/apache/activemq/apollo/dto/jaxb.index Wed Dec 15 02:12:02 2010
@@ -45,4 +45,5 @@ QueueDTO
 DestinationDTO
 LinkDTO
 QueueConsumerStatusDTO
-StompDTO
\ No newline at end of file
+StompDTO
+ValueDTO
\ No newline at end of file

Modified: activemq/activemq-apollo/trunk/apollo-web/src/main/scala/org/apache/activemq/apollo/web/resources/ConfigurationResource.scala
URL: http://svn.apache.org/viewvc/activemq/activemq-apollo/trunk/apollo-web/src/main/scala/org/apache/activemq/apollo/web/resources/ConfigurationResource.scala?rev=1049386&r1=1049385&r2=1049386&view=diff
==============================================================================
--- activemq/activemq-apollo/trunk/apollo-web/src/main/scala/org/apache/activemq/apollo/web/resources/ConfigurationResource.scala (original)
+++ activemq/activemq-apollo/trunk/apollo-web/src/main/scala/org/apache/activemq/apollo/web/resources/ConfigurationResource.scala Wed Dec 15 02:12:02 2010
@@ -23,8 +23,10 @@ import Response.Status._
 import Response._
 import java.net.URI
 import java.io.ByteArrayInputStream
-import org.apache.activemq.apollo.dto.{XmlCodec, BrokerDTO}
 import org.apache.activemq.apollo.broker.ConfigStore
+import org.apache.activemq.apollo.dto.{ValueDTO, XmlCodec, BrokerDTO}
+
+case class EditConfig(config:String)
 
 /**
  * A broker resource is used to represent the configuration of a broker.
@@ -32,45 +34,47 @@ import org.apache.activemq.apollo.broker
 @Produces(Array("application/json", "application/xml","text/xml", "text/html;qs=5"))
 case class ConfigurationResource(parent:BrokerResource) extends Resource(parent) {
 
-  lazy val config = {
-    val store = ConfigStore()
-    if( store.can_write ) {
-      store.load(false)
+  lazy val store = {
+    val rc = ConfigStore()
+    if( rc.can_write ) {
+      rc
     } else {
       None
     }.getOrElse(result(NOT_FOUND))
   }
 
+  @GET
+  def get() = store.load(false)
 
+  @Produces(Array("text/html"))
   @GET
-  def get(@Context uriInfo:UriInfo) = {
-    val ub = uriInfo.getAbsolutePathBuilder()
-    seeOther(path(config.rev)).build
+  @Path("edit")
+  def edit_html() = {
+    EditConfig(store.read)
   }
 
-  @GET @Path("{rev}")
-  def getConfig(@PathParam("rev") rev:Int):BrokerDTO = {
-    // that rev may have gone away..
-    config.rev==rev || result(NOT_FOUND)
-    config
+  @POST
+  @Path("edit")
+  def edit_post(@FormParam("config") config:String) = {
+    val rc = new ValueDTO
+    rc.value = config
+    edit_put(rc)
+    result(path("../.."))
   }
 
-  @POST @Path("{rev}")
-  def post(@PathParam("rev") rev:Int, @FormParam("config") config:String) = {
-    val dto = XmlCodec.unmarshalBrokerDTO(new ByteArrayInputStream(config.getBytes("UTF-8")))
-    put(rev, dto)
-    result(path("../"+dto.rev))
+  @Produces(Array("application/json", "application/xml","text/xml"))
+  @GET
+  @Path("edit")
+  def edit() = {
+    val rc = new ValueDTO
+    rc.value = store.read
+    rc
   }
 
-  @PUT @Path("{rev}")
-  def put(@PathParam("rev") rev:Int, config:BrokerDTO) = {
-    config.rev = rev
-    val store = ConfigStore()
-    if( store.can_write ) {
-      store.store(config)
-    } else {
-      false
-    } || result(NOT_FOUND)
+  @PUT
+  @Path("edit")
+  def edit_put(config:ValueDTO) = {
+    store.write(config.value)
   }
 
 }

Modified: activemq/activemq-apollo/trunk/apollo-web/src/main/webapp/WEB-INF/org/apache/activemq/apollo/dto/BrokerDTO.jade
URL: http://svn.apache.org/viewvc/activemq/activemq-apollo/trunk/apollo-web/src/main/webapp/WEB-INF/org/apache/activemq/apollo/dto/BrokerDTO.jade?rev=1049386&r1=1049385&r2=1049386&view=diff
==============================================================================
--- activemq/activemq-apollo/trunk/apollo-web/src/main/webapp/WEB-INF/org/apache/activemq/apollo/dto/BrokerDTO.jade (original)
+++ activemq/activemq-apollo/trunk/apollo-web/src/main/webapp/WEB-INF/org/apache/activemq/apollo/dto/BrokerDTO.jade Wed Dec 15 02:12:02 2010
@@ -13,21 +13,11 @@
 -# See the License for the specific language governing permissions and
 -# limitations under the License.
 
+- import it._
 - val helper = new org.apache.activemq.apollo.web.resources.ViewHelper
 - import helper._
-- import org.fusesource.hawtbuf._
-- import org.apache.activemq.apollo.dto.XmlCodec._
 
 .breadcumbs
   a(href={strip_resolve("..")}) Back
 
-form(method="post" action={it.rev+1})
-  div
-    input(type="submit" value="Update")
-  div
-    textarea(rows="40" cols="80" name="config")<
-      - val baos = new ByteArrayOutputStream
-      - marshalBrokerDTO(it, baos, true)
-      ~~ new String(baos.toByteArray, "UTF-8")
-  div
-    input(type="submit" value="Update")
\ No newline at end of file
+a(href="config/edit") Edit

Modified: activemq/activemq-apollo/trunk/apollo-web/src/main/webapp/WEB-INF/org/apache/activemq/apollo/dto/BrokerSummaryDTO.jade
URL: http://svn.apache.org/viewvc/activemq/activemq-apollo/trunk/apollo-web/src/main/webapp/WEB-INF/org/apache/activemq/apollo/dto/BrokerSummaryDTO.jade?rev=1049386&r1=1049385&r2=1049386&view=diff
==============================================================================
--- activemq/activemq-apollo/trunk/apollo-web/src/main/webapp/WEB-INF/org/apache/activemq/apollo/dto/BrokerSummaryDTO.jade (original)
+++ activemq/activemq-apollo/trunk/apollo-web/src/main/webapp/WEB-INF/org/apache/activemq/apollo/dto/BrokerSummaryDTO.jade Wed Dec 15 02:12:02 2010
@@ -25,5 +25,5 @@ h1 Broker: #{id}
 
 - if (configurable)
   p
-    a(href={path("config")}) configuration
+    a(href={path("config/edit")}) configuration
 

Copied: activemq/activemq-apollo/trunk/apollo-web/src/main/webapp/WEB-INF/org/apache/activemq/apollo/web/resources/EditConfig.jade (from r1049385, activemq/activemq-apollo/trunk/apollo-web/src/main/webapp/WEB-INF/org/apache/activemq/apollo/dto/BrokerSummaryDTO.jade)
URL: http://svn.apache.org/viewvc/activemq/activemq-apollo/trunk/apollo-web/src/main/webapp/WEB-INF/org/apache/activemq/apollo/web/resources/EditConfig.jade?p2=activemq/activemq-apollo/trunk/apollo-web/src/main/webapp/WEB-INF/org/apache/activemq/apollo/web/resources/EditConfig.jade&p1=activemq/activemq-apollo/trunk/apollo-web/src/main/webapp/WEB-INF/org/apache/activemq/apollo/dto/BrokerSummaryDTO.jade&r1=1049385&r2=1049386&rev=1049386&view=diff
==============================================================================
--- activemq/activemq-apollo/trunk/apollo-web/src/main/webapp/WEB-INF/org/apache/activemq/apollo/dto/BrokerSummaryDTO.jade (original)
+++ activemq/activemq-apollo/trunk/apollo-web/src/main/webapp/WEB-INF/org/apache/activemq/apollo/web/resources/EditConfig.jade Wed Dec 15 02:12:02 2010
@@ -4,9 +4,9 @@
 -# The ASF licenses this file to You 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.
@@ -17,13 +17,14 @@
 - val helper = new org.apache.activemq.apollo.web.resources.ViewHelper
 - import helper._
 
-h1 Broker: #{id}
-
-- if (manageable)
-  p
-    a(href={path("runtime")}) manage
-
-- if (configurable)
-  p
-    a(href={path("config")}) configuration
+.breadcumbs
+  a(href={strip_resolve("../..")}) Back
 
+form(method="post" action="edit")
+  div
+    input(type="submit" value="Update")
+  div
+    textarea(rows="40" cols="80" name="config")<
+      ~~ config
+  div
+    input(type="submit" value="Update")
\ No newline at end of file