activemq-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From chir...@apache.org
Subject svn commit: r1366431 [2/2] - in /activemq/activemq-apollo/trunk: ./ apollo-amqp/ apollo-broker/src/main/scala/org/apache/activemq/apollo/broker/ apollo-broker/src/main/scala/org/apache/activemq/apollo/broker/protocol/ apollo-broker/src/test/scala/ apol...
Date Fri, 27 Jul 2012 16:04:02 GMT
Added: activemq/activemq-apollo/trunk/apollo-util/src/test/scala/org/scalatest/ParallelBeforeAndAfterAll.scala
URL: http://svn.apache.org/viewvc/activemq/activemq-apollo/trunk/apollo-util/src/test/scala/org/scalatest/ParallelBeforeAndAfterAll.scala?rev=1366431&view=auto
==============================================================================
--- activemq/activemq-apollo/trunk/apollo-util/src/test/scala/org/scalatest/ParallelBeforeAndAfterAll.scala
(added)
+++ activemq/activemq-apollo/trunk/apollo-util/src/test/scala/org/scalatest/ParallelBeforeAndAfterAll.scala
Fri Jul 27 16:04:01 2012
@@ -0,0 +1,253 @@
+/*
+ * Copyright 2001-2009 Artima, Inc.
+ *
+ * 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.scalatest
+
+import tools.ConcurrentDistributor
+
+/**
+ * Trait that can be mixed into suites that need methods invoked before and after executing
the
+ * suite. This trait allows code to be executed before and/or after all the tests and nested
suites of a
+ * suite are run. This trait overrides <code>run</code> (the main <code>run</code>
method that
+ * takes seven parameters, an optional test name, reporter, stopper, filter, configMap, optional
distributor,
+ * and tracker) and calls the
+ * <code>beforeAll</code> method, then calls <code>super.run</code>.
After the <code>super.run</code>
+ * invocation completes, whether it returns normally or completes abruptly with an exception,
+ * this trait's <code>run</code> method will invoke <code>afterAll</code>.
+ *
+ * <p>
+ * Trait <code>BeforeAndAfterAll</code> defines two overloaded variants  each
of <code>beforeAll</code>
+ * and <code>afterAll</code>, one that takes a <code>configMap</code>
and another that takes no
+ * arguments. This traits implemention of the variant that takes the <code>configMap</code>
+ * simply invokes the variant that takes no parameters, which does nothing. Thus you can
override
+ * whichever variant you want. If you need something from the <code>configMap</code>
before
+ * all tests and nested suites are run, override <code>beforeAll(Map[String, Any])</code>.
Otherwise,
+ * override <code>beforeAll()</code>.
+ * </p>
+ *
+ * <p>
+ * For example, the following <code>MasterSuite</code> mixes in <code>BeforeAndAfterAll</code>
and
+ * in <code>beforeAll</code>, creates and writes to a temp file, taking the name
of the temp file
+ * from the <code>configMap</code>. This same <code>configMap</code>
is then passed to the <code>run</code>
+ * methods of the nested suites, <code>OneSuite</code>, <code>TwoSuite</code>,
<code>RedSuite</code>,
+ * and <code>BlueSuite</code>, so those suites can access the filename and, therefore,
the file's
+ * contents. After all of the nested suites have executed, <code>afterAll</code>
is invoked, which
+ * again grabs the file name from the <code>configMap</code> and deletes the
file:
+ * </p>
+ *
+ * <pre class="stHighlight">
+ * import org.scalatest.SuperSuite
+ * import org.scalatest.BeforeAndAfterAll
+ * import java.io.FileReader
+ * import java.io.FileWriter
+ * import java.io.File
+ *
+ * class MasterSuite extends Suite with BeforeAndAfterAll {
+ *
+ * private val FileNameKeyInGoodies = "tempFileName"
+ *
+ * // Set up the temp file needed by the test, taking
+ * // a file name from the configMap
+ * override def beforeAll(configMap: Map[String, Any]) {
+ *
+ * require(
+ * configMap.isDefinedAt(FileNameKeyInGoodies),
+ * "must place a temp file name in the configMap under the key: " + FileNameKeyInGoodies
+ * )
+ *
+ * val fileName = configMap(tempFileName)
+ *
+ * val writer = new FileWriter(fileName)
+ * try {
+ * writer.write("Hello, suite of tests!")
+ * }
+ * finally {
+ * writer.close()
+ * }
+ * }
+ *
+ * override def nestedSuites =
+ * List(new OneSuite, new TwoSuite, new RedSuite, new BlueSuite)
+ *
+ * // Delete the temp file
+ * override def afterAll(configMap: Map[String, Any]) {
+ * // No need to require that configMap contains the key again because it won't get
+ * // here if it didn't contain the key in beforeAll
+ * val fileName = configMap(tempFileName))
+ * val file = new File(fileName)
+ * file.delete()
+ * }
+ * }
+ * </pre>
+ *
+ * <p>
+ * Because the <code>BeforeAndAfterAll</code> trait invokes <code>super.run</code>
to run the suite, you may need to
+ * mix this trait in last to get the desired behavior. For example, this won't
+ * work, because <code>BeforeAndAfterAll</code> is "super" to </code>FunSuite</code>:
+ * </p>
+ * <pre class="stHighlight">
+ * class MySuite extends BeforeAndAfterAll with FunSuite
+ * </pre>
+ * <p>
+ * You'd need to turn it around, so that <code>FunSuite</code> is "super" to
<code>BeforeAndAfterAll</code>, like this:
+ * </p>
+ * <pre class="stHighlight">
+ * class MySuite extends FunSuite with BeforeAndAfterAll
+ * </pre>
+ *
+ * <strong>Note: This trait does not currently ensure that the code in <code>afterAll</code>
is executed after
+ * all the tests and nested suites are executed if a <code>Distributor</code>
is passed. The
+ * plan is to do that eventually, but in the meantime, be aware that <code>afterAll</code>
is
+ * guaranteed to be run after all the tests and nested suites only when they are run
+ * sequentially.</strong>
+ *
+ * @author Bill Venners
+ */
+trait ParallelBeforeAndAfterAll extends AbstractSuite {
+
+  this: Suite =>
+
+  /**
+   * Defines a method to be run before any of this suite's tests or nested suites are run.
+   *
+   * <p>
+   * This trait's implementation
+   * of <code>run</code> invokes the overloaded form of this method that
+   * takes a <code>configMap</code> before executing
+   * any tests or nested suites. This trait's implementation of that <code>beforeAll(Map[String,
Any])</code>
+   * method simply invokes this <code>beforeAll()</code>
+   * method. Thus this method can be used to set up a test fixture
+   * needed by the entire suite, when you don't need anything from the <code>configMap</code>.
+   * This trait's implementation of this method does nothing.
+   * </p>
+   */
+  protected def beforeAll() = ()
+
+  /**
+   * Defines a method (that takes a <code>configMap</code>) to be run before
any
+   * of this suite's tests or nested suites are run.
+   *
+   * <p>
+   * This trait's implementation
+   * of <code>run</code> invokes this method before executing
+   * any tests or nested suites (passing in the <code>configMap</code> passed
to it), thus this
+   * method can be used to set up a test fixture
+   * needed by the entire suite. This trait's implementation of this method invokes the
+   * overloaded form of <code>beforeAll</code> that takes no <code>configMap</code>.
+   * </p>
+   */
+  protected def beforeAll(configMap: Map[String, Any]) {
+    beforeAll()
+  }
+
+  /**
+   * Defines a method to be run after all of this suite's tests and nested suites have
+   * been run.
+   *
+   * <p>
+   * This trait's implementation
+   * of <code>run</code> invokes the overloaded form of this method that
+   * takes a <code>configMap</code> after executing
+   * all tests and nested suites. This trait's implementation of that <code>afterAll(Map[String,
Any])</code> method simply invokes this
+   * <code>afterAll()</code> method. Thus this method can be used to tear down
a test fixture
+   * needed by the entire suite, when you don't need anything from the <code>configMap</code>.
+   * This trait's implementation of this method does nothing.
+   * </p>
+   */
+  protected def afterAll() = ()
+
+  /**
+   * Defines a method (that takes a <code>configMap</code>) to be run after
+   * all of this suite's tests and nested suites have been run.
+   *
+   * <p>
+   * This trait's implementation
+   * of <code>run</code> invokes this method after executing all tests
+   * and nested suites (passing in the <code>configMap</code> passed to it),
thus this
+   * method can be used to tear down a test fixture
+   * needed by the entire suite. This trait's implementation of this method invokes the
+   * overloaded form of <code>afterAll</code> that takes no <code>configMap</code>.
+   * </p>
+   */
+  protected def afterAll(configMap: Map[String, Any]) {
+    afterAll()
+  }
+
+  /**
+   * Execute a suite surrounded by calls to <code>beforeAll</code> and <code>afterAll</code>.
+   *
+   * <p>
+   * This trait's implementation of this method ("this method") invokes <code>beforeAll(Map[String,
Any])</code>
+   * before executing any tests or nested suites and <code>afterAll(Map[String, Any])</code>
+   * after executing all tests and nested suites. It runs the suite by invoking <code>super.run</code>,
passing along
+   * the seven parameters passed to it.
+   * </p>
+   *
+   * <p>
+   * If any invocation of <code>beforeAll</code> completes abruptly with an exception,
this
+   * method will complete abruptly with the same exception. If any call to
+   * <code>super.run</code> completes abruptly with an exception, this method
+   * will complete abruptly with the same exception, however, before doing so, it will
+   * invoke <code>afterAll</code>. If <cod>afterAll</code> <em>also</em>
completes abruptly with an exception, this
+   * method will nevertheless complete abruptly with the exception previously thrown by <code>super.run</code>.
+   * If <code>super.run</code> returns normally, but <code>afterAll</code>
completes abruptly with an
+   * exception, this method will complete abruptly with the same exception.
+   * </p>
+   */
+  abstract override def run(testName: Option[String], reporter: Reporter, stopper: Stopper,
filter: Filter,
+                            configMap: Map[String, Any], distributor: Option[Distributor],
tracker: Tracker) {
+    var thrownException: Option[Throwable] = None
+
+    def waitUntilDistributorIsDone = {
+      // If you are using a concurrent distributor.. wait for all
+      // concurrent tasks to complete before executing the afterAll.
+      distributor match {
+        case Some(distributor) => distributor match {
+          case distributor: ConcurrentDistributor =>
+            distributor.waitUntilDone();
+          case _ =>
+        }
+        case _ =>
+      }
+    }
+
+    beforeAll(configMap)
+    waitUntilDistributorIsDone
+    try {
+      super.run(testName, reporter, stopper, filter, configMap, distributor, tracker)
+    }
+    catch {
+      case e: Exception => thrownException = Some(e)
+    }
+    finally {
+      waitUntilDistributorIsDone
+      try {
+        afterAll(configMap) // Make sure that afterAll is called even if run completes abruptly.
+        thrownException match {
+          case Some(e) => throw e
+          case None =>
+        }
+      }
+      catch {
+        case laterException: Exception =>
+          thrownException match {
+            // If both run and afterAll throw an exception, report the test exception
+            case Some(earlierException) => throw earlierException
+            case None => throw laterException
+          }
+      }
+    }
+  }
+}

Added: activemq/activemq-apollo/trunk/apollo-util/src/test/scala/org/scalatest/junit/ParallelJUnitRunner.scala
URL: http://svn.apache.org/viewvc/activemq/activemq-apollo/trunk/apollo-util/src/test/scala/org/scalatest/junit/ParallelJUnitRunner.scala?rev=1366431&view=auto
==============================================================================
--- activemq/activemq-apollo/trunk/apollo-util/src/test/scala/org/scalatest/junit/ParallelJUnitRunner.scala
(added)
+++ activemq/activemq-apollo/trunk/apollo-util/src/test/scala/org/scalatest/junit/ParallelJUnitRunner.scala
Fri Jul 27 16:04:01 2012
@@ -0,0 +1,153 @@
+/*
+ * Copyright 2001-2008 Artima, Inc.
+ *
+ * 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.scalatest.junit
+
+import org.junit.runner.{Description, RunWith}
+import org.scalatest._
+import org.junit.runner.notification.RunNotifier
+import tools.ConcurrentDistributor
+import java.util.concurrent.{ExecutorService, Executors}
+import scala.Some
+
+/*
+ I think that Stopper really should be a no-op, like it is, because the user has
+ no way to stop it. This is wierd, because it will call nested suites. So the tests
+ just pile up. Oh, I see, the darn information about which test it is is in the
+ stupid Description displayName. We probably need to add optional test name and
+ suite class name to Report, just to satisfy JUnit integration.
+*/
+/**
+ * A JUnit <code>Runner</code> that knows how to run any ScalaTest <code>Suite</code>
+ * (or <code>Spec</code>, which extends <code>Suite</code>).
+ * This enables you to provide a JUnit <code>RunWith</code> annotation on any
+ * ScalaTest <code>Suite</code>. Here's an example:
+ *
+ * <pre>
+ * import org.junit.runner.RunWith
+ * import org.scalatest.junit.JUnitRunner
+ * import org.scalatest.FunSuite
+ *
+ * @RunWith( c l a s s O f[ J U n i t R u n n e r ] )
+ *         class MySuite extends FunSuite {
+ *         // ...
+ *         }
+ *         </pre>
+ *
+ *         <p>
+ *         This <code>RunWith</code> annotation will enable the <code>MySuite</code>
class
+ *         to be run by JUnit 4.
+ *         </p>
+ *
+ * @author Bill Venners
+ * @author Daniel Watson
+ * @author Jon-Anders Teigen
+ * @author Colin Howe
+ */
+@RunWith(classOf[JUnitRunner])
+final class ParallelJUnitRunner(suiteClass: java.lang.Class[Suite]) extends org.junit.runner.Runner
{
+
+  private val canInstantiate = Suite.checkForPublicNoArgConstructor(suiteClass)
+  require(canInstantiate, "Must pass an org.scalatest.Suite with a public no-arg constructor")
+
+  private val suiteToRun = suiteClass.newInstance
+
+  /**
+   * Get a JUnit <code>Description</code> for this ScalaTest <code>Suite</code>
of tests.
+   *
+   * return a <code>Description</code> of this suite of tests
+   */
+  val getDescription = createDescription(suiteToRun)
+
+  private def createDescription(suite: Suite): Description = {
+    val description = Description.createSuiteDescription(suite.getClass)
+    // If we don't add the testNames and nested suites in, we get
+    // Unrooted Tests show up in Eclipse
+    for (name <- suite.testNames) {
+      description.addChild(Description.createTestDescription(suite.getClass, name))
+    }
+    for (nestedSuite <- suite.nestedSuites) {
+      description.addChild(createDescription(nestedSuite))
+    }
+    description
+  }
+
+  /**
+   * Run this <code>Suite</code> of tests, reporting results to the passed <code>RunNotifier</code>.
+   * This class's implementation of this method invokes <code>run</code> on an
instance of the
+   * <code>suiteClass</code> <code>Class</code> passed to the primary
constructor, passing
+   * in a <code>Reporter</code> that forwards to the  <code>RunNotifier</code>
passed to this
+   * method as <code>notifier</code>.
+   *
+   * @param notifier the JUnit <code>RunNotifier</code> to which to report the
results of executing
+   *                 this suite of tests
+   */
+  def run(notifier: RunNotifier) {
+    val reporter = new org.scalatest.junit.RunNotifierReporter(notifier)
+    var stopper = new Stopper {}
+    var filter = Filter()
+    var configMap = Map[String, Any]()
+    ParallelJUnitRunner.withExecutor { executor =>
+      val distributor = new ConcurrentDistributor(reporter, stopper, filter, configMap, executor)
+      try {
+        suiteToRun.run(None, reporter, stopper, filter, configMap, Some(distributor), new
Tracker)
+      } finally {
+        distributor.waitUntilDone()
+      }
+    }
+  }
+
+  /**
+   * Returns the number of tests that are expected to run when this ScalaTest <code>Suite</code>
+   * is run.
+   *
+   * @return the expected number of tests that will run when this suite is run
+   */
+  override def testCount() = suiteToRun.expectedTestCount(Filter())
+}
+
+object ParallelJUnitRunner {
+
+  private var useCounter = 0
+  private var executor:ExecutorService = _
+
+  // We share an executor across multiple concurrent test executions.
+  def withExecutor[T](fun: (ExecutorService)=>T):T= {
+    val value = this.synchronized {
+      useCounter+=1
+      if( executor == null) {
+        executor = Executors.newFixedThreadPool(Integer.getInteger("test.threads", Runtime.getRuntime.availableProcessors));
+      }
+      executor
+    }
+    try {
+      fun(value)
+    } finally {
+      val shutdown = this.synchronized {
+        useCounter-=1
+        if( useCounter == 0) {
+          executor = null
+          true
+        } else {
+          false
+        }
+      }
+      if ( shutdown ) {
+        value.shutdown()
+      }
+    }
+  }
+
+}
\ No newline at end of file

Modified: activemq/activemq-apollo/trunk/apollo-web/src/main/scala/org/apache/activemq/apollo/web/resources/BrokerResource.scala
URL: http://svn.apache.org/viewvc/activemq/activemq-apollo/trunk/apollo-web/src/main/scala/org/apache/activemq/apollo/web/resources/BrokerResource.scala?rev=1366431&r1=1366430&r2=1366431&view=diff
==============================================================================
--- activemq/activemq-apollo/trunk/apollo-web/src/main/scala/org/apache/activemq/apollo/web/resources/BrokerResource.scala
(original)
+++ activemq/activemq-apollo/trunk/apollo-web/src/main/scala/org/apache/activemq/apollo/web/resources/BrokerResource.scala
Fri Jul 27 16:04:01 2012
@@ -35,6 +35,7 @@ import org.apache.activemq.apollo.dto._
 import javax.ws.rs.core.MediaType._
 import com.wordnik.swagger.core._
 import javax.servlet.http.HttpServletResponse
+import FutureResult._
 
 @Path(          "/api/json/broker")
 @Api(value =    "/api/json/broker",

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=1366431&r1=1366430&r2=1366431&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
Fri Jul 27 16:04:01 2012
@@ -24,6 +24,8 @@ import javax.ws.rs._
 import javax.ws.rs.core.Response.Status._
 import javax.ws.rs.core.MediaType._
 import com.wordnik.swagger.core.{ApiOperation, Api}
+import org.apache.activemq.apollo.util.FutureResult
+import FutureResult._
 
 case class EditConfig(file:String, config:String, can_write:Boolean)
 case class ListConfigs(files:Array[String])

Modified: activemq/activemq-apollo/trunk/apollo-web/src/main/scala/org/apache/activemq/apollo/web/resources/SessionResource.scala
URL: http://svn.apache.org/viewvc/activemq/activemq-apollo/trunk/apollo-web/src/main/scala/org/apache/activemq/apollo/web/resources/SessionResource.scala?rev=1366431&r1=1366430&r2=1366431&view=diff
==============================================================================
--- activemq/activemq-apollo/trunk/apollo-web/src/main/scala/org/apache/activemq/apollo/web/resources/SessionResource.scala
(original)
+++ activemq/activemq-apollo/trunk/apollo-web/src/main/scala/org/apache/activemq/apollo/web/resources/SessionResource.scala
Fri Jul 27 16:04:01 2012
@@ -28,6 +28,7 @@ import org.apache.activemq.apollo.dto._
 import javax.ws.rs.core.MediaType._
 import org.apache.activemq.apollo.broker.security.SecurityContext
 import com.wordnik.swagger.core._
+import FutureResult._
 
 @Path(          "/api/json/session")
 @Api(value =    "/api/json/session",

Modified: activemq/activemq-apollo/trunk/apollo-web/src/main/scala/org/apache/activemq/apollo/web/resources/Support.scala
URL: http://svn.apache.org/viewvc/activemq/activemq-apollo/trunk/apollo-web/src/main/scala/org/apache/activemq/apollo/web/resources/Support.scala?rev=1366431&r1=1366430&r2=1366431&view=diff
==============================================================================
--- activemq/activemq-apollo/trunk/apollo-web/src/main/scala/org/apache/activemq/apollo/web/resources/Support.scala
(original)
+++ activemq/activemq-apollo/trunk/apollo-web/src/main/scala/org/apache/activemq/apollo/web/resources/Support.scala
Fri Jul 27 16:04:01 2012
@@ -88,7 +88,7 @@ abstract class Resource(parent:Resource=
   }
   
   def ok[T](value:FutureResult[T]):Unit = {
-    unwrap_future_result(value)
+    FutureResult.unwrap_future_result(value)
     throw new WebApplicationException(Response.ok().build)
   }
 

Modified: activemq/activemq-apollo/trunk/pom.xml
URL: http://svn.apache.org/viewvc/activemq/activemq-apollo/trunk/pom.xml?rev=1366431&r1=1366430&r2=1366431&view=diff
==============================================================================
--- activemq/activemq-apollo/trunk/pom.xml (original)
+++ activemq/activemq-apollo/trunk/pom.xml Fri Jul 27 16:04:01 2012
@@ -41,7 +41,7 @@
 
     <!-- dependencies that track scala version changes -->
     <scala-version>2.9.1-1</scala-version>
-    <scalatest-version>1.6.1</scalatest-version>
+    <scalatest-version>1.8</scalatest-version>
     
     <activemq-version>5.4.1</activemq-version>
     <servicemix.kernel.version>1.1.0</servicemix.kernel.version>
@@ -72,7 +72,7 @@
     <jdom-version>1.0</jdom-version>
     <jetty-version>7.6.1.v20120215</jetty-version>
     <jmock-version>1.0.1</jmock-version>
-    <junit-version>4.7</junit-version>
+    <junit-version>4.10</junit-version>
     <jxta-version>2.0</jxta-version>
     <log4j-version>1.2.14</log4j-version>
     <nlog4j-version>1.5.2</nlog4j-version>
@@ -120,7 +120,7 @@
       
     <!-- maven plugin versions -->
     <maven-scala-plugin-version>2.15.2</maven-scala-plugin-version>
-    <maven-surefire-plugin-version>2.4.3</maven-surefire-plugin-version>
+    <maven-surefire-plugin-version>2.12</maven-surefire-plugin-version>
     <jetty-plugin-version>7.0.1.v20091125</jetty-plugin-version>
     
     <!-- disable unique version timestamps in snapshots -->
@@ -288,7 +288,7 @@
           <configuration>
             <redirectTestOutputToFile>false</redirectTestOutputToFile>
             <forkMode>once</forkMode>
-            <argLine>-enableassertions</argLine>
+            <argLine>-enableassertions -Xmx500m</argLine>
             <failIfNoTests>false</failIfNoTests>
             <workingDirectory>${project.build.directory}</workingDirectory>
             <includes>
@@ -299,10 +299,9 @@
               <exclude>**/legacy/**</exclude>
               <exclude>**/jaxb/**</exclude>
             </excludes>
-            <!--
-            <parallel>methods</parallel>
-            <threadCount>16</threadCount>
-            -->
+            <parallel>classes</parallel>
+            <perCoreThreadCount>false</perCoreThreadCount>
+            <threadCount>2</threadCount> 
           </configuration>
         </plugin>
         



Mime
View raw message