Skip to content
This repository was archived by the owner on Aug 18, 2020. It is now read-only.

Commit 12795ba

Browse files
committed
Added framework support and rest API for creating and deleting plugin instances.
1 parent 14d5d90 commit 12795ba

File tree

7 files changed

+124
-5
lines changed

7 files changed

+124
-5
lines changed

src/main/scala/org/codeoverflow/chatoverflow/framework/PluginFramework.scala

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,17 @@ class PluginFramework(pluginDirectoryPath: String) extends WithLogger {
1717
private val pluginTypes: ListBuffer[PluginType] = ListBuffer[PluginType]()
1818
private val loadedJarPaths: ListBuffer[String] = ListBuffer[String]()
1919

20+
/**
21+
* Returns, if a plugin type exists.
22+
*
23+
* @param pluginName the name of the plugin set in the plugin jar file
24+
* @param pluginAuthor the author of the plugin
25+
* @return true, if the specified plugin type exists
26+
*/
27+
def pluginExists(pluginName: String, pluginAuthor: String): Boolean = {
28+
getPlugin(pluginName, pluginAuthor).isDefined
29+
}
30+
2031
/**
2132
* Returns the plugin type specified by name and author.
2233
*

src/main/scala/org/codeoverflow/chatoverflow/instance/PluginInstance.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,8 @@ class PluginInstance(val instanceName: String, pluginType: PluginType) extends W
168168
}
169169
}
170170

171+
// TODO: Also connectors & input/output should be shutdown somewhere (if the plugin ends OR is ended)
172+
171173
// After the loop (or setup) the plugin should end
172174
plugin.get.shutdown()
173175

src/main/scala/org/codeoverflow/chatoverflow/instance/PluginInstanceRegistry.scala

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,29 @@ class PluginInstanceRegistry extends WithLogger {
5252
pluginInstances.get(instanceName)
5353
}
5454

55+
/**
56+
* Removes a plugin specified by its name, if possible.
57+
*
58+
* @param instanceName the name of the instance to remove
59+
* @return true, if the removing process was possible
60+
*/
61+
def removePluginInstance(instanceName: String): Boolean = {
62+
if (pluginInstances.contains(instanceName) && pluginInstances(instanceName).isRunning) {
63+
false
64+
} else {
65+
pluginInstances -= instanceName
66+
true
67+
}
68+
}
69+
70+
/**
71+
* Returns if the registry contains a specified instanceName (key).
72+
*
73+
* @param instanceName the name to search for
74+
* @return true, if a plugin instance with the given name exists
75+
*/
76+
def pluginInstanceExists(instanceName: String): Boolean = pluginInstances.contains(instanceName)
77+
5578
/**
5679
* Returns a list of all plugin instances. Can be used to serialize the instance content.
5780
*

src/main/scala/org/codeoverflow/chatoverflow/ui/web/JsonServlet.scala

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package org.codeoverflow.chatoverflow.ui.web
22

3+
import javax.servlet.http.HttpServletRequest
4+
import org.codeoverflow.chatoverflow.ui.web.rest.DTOs.ResultMessage
35
import org.codeoverflow.chatoverflow.{ChatOverflow, Launcher}
46
import org.json4s.{DefaultFormats, Formats}
57
import org.scalatra.json.JacksonJsonSupport
@@ -16,4 +18,31 @@ abstract class JsonServlet extends ScalatraServlet with JacksonJsonSupport with
1618
before() {
1719
contentType = formats("json")
1820
}
21+
22+
/**
23+
* Utility function to parse incoming json-body-arguments. Uses a lot of scala magic. Magical!
24+
*
25+
* @param func the function (taking an object T, returning a ResultMessage) to call, if the parsing process was successful
26+
* @param request the request, implicitly provided by the scalatra environment
27+
* @tparam T the type of body you want (probably a case class)
28+
* @return a ResultMessage-object containing only true if successful, otherwise false and a error message
29+
*/
30+
protected def parsedAs[T: Manifest](func: T => ResultMessage)(implicit request: HttpServletRequest): ResultMessage = {
31+
val errorMessage = ResultMessage(success = false, "Unable to parse. Wrong body-format.")
32+
33+
if (request.body == "") {
34+
// No body provided
35+
errorMessage
36+
} else {
37+
// Parse json, extract case class, execute func
38+
val json = parse(request.body)
39+
val data = json.extractOpt[T](DefaultFormats, manifest[T])
40+
41+
if (data.isEmpty) {
42+
errorMessage
43+
} else {
44+
func(data.get)
45+
}
46+
}
47+
}
1948
}

src/main/scala/org/codeoverflow/chatoverflow/ui/web/rest/DTOs.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,8 @@ object DTOs {
2424
areCredentialsSet: Boolean, isRunning: Boolean, requiredCredentialKeys: Seq[String],
2525
optionalCredentialKeys: Seq[String])
2626

27+
case class PluginInstanceRef(instanceName: String, pluginName: String, pluginAuthor: String)
28+
29+
case class ResultMessage(success: Boolean, message: String = "")
30+
2731
}

src/main/scala/org/codeoverflow/chatoverflow/ui/web/rest/plugin/PluginInstanceController.scala

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ package org.codeoverflow.chatoverflow.ui.web.rest.plugin
33
import org.codeoverflow.chatoverflow.api.io
44
import org.codeoverflow.chatoverflow.api.plugin.configuration
55
import org.codeoverflow.chatoverflow.ui.web.JsonServlet
6-
import org.codeoverflow.chatoverflow.ui.web.rest.DTOs.{PluginInstance, Requirement}
6+
import org.codeoverflow.chatoverflow.ui.web.rest.DTOs.{PluginInstance, PluginInstanceRef, Requirement, ResultMessage}
77
import org.scalatra.swagger.Swagger
88

99
import scala.collection.JavaConverters._
@@ -45,15 +45,54 @@ class PluginInstanceController(implicit val swagger: Swagger) extends JsonServle
4545
val index = startIndex.getOrElse("0")
4646
val msg = logMessages.toArray.drop(Integer.parseInt(index))
4747
msg.toSeq
48+
}
4849

50+
post("/", operation(postInstance)) {
51+
parsedAs[PluginInstanceRef] {
52+
case PluginInstanceRef(instanceName, pluginName, pluginAuthor) =>
53+
// Check existence of key first
54+
if (chatOverflow.pluginInstanceRegistry.pluginInstanceExists(instanceName)) {
55+
ResultMessage(success = false, "Plugin instance already exists.")
56+
57+
} else if (!chatOverflow.pluginFramework.pluginExists(pluginName, pluginAuthor)) {
58+
ResultMessage(success = false, "Plugin type does not exist.")
59+
60+
} else {
61+
val pluginType = chatOverflow.pluginFramework.getPlugin(pluginName, pluginAuthor)
62+
63+
if (!chatOverflow.pluginInstanceRegistry.addPluginInstance(instanceName, pluginType.get)) {
64+
ResultMessage(success = false, "Unable to create new plugin instance.")
65+
} else {
66+
chatOverflow.save()
67+
ResultMessage(success = true)
68+
}
69+
}
70+
}
71+
}
72+
73+
delete("/:instanceName", operation(deleteInstance)) {
74+
val instanceName = params("instanceName")
75+
76+
val pluginInstance = chatOverflow.pluginInstanceRegistry.getPluginInstance(instanceName)
77+
78+
if (pluginInstance.isEmpty) {
79+
ResultMessage(success = false, "Plugin instance not found.")
80+
81+
} else if (pluginInstance.get.isRunning) {
82+
ResultMessage(success = false, "Plugin instance is running.")
83+
84+
} else if (!chatOverflow.pluginInstanceRegistry.removePluginInstance(instanceName)) {
85+
ResultMessage(success = false, "Unable to remove plugin instance.")
86+
87+
} else {
88+
chatOverflow.save()
89+
ResultMessage(success = true)
90+
}
4991
}
5092

5193
private def pluginInstanceToDTO(pluginInstance: org.codeoverflow.chatoverflow.instance.PluginInstance) = {
5294
PluginInstance(pluginInstance.instanceName, pluginInstance.getPluginTypeName,
5395
pluginInstance.getPluginTypeAuthor, pluginInstance.isRunning,
5496
pluginInstance.getRequirements.getRequirementMap.keySet().asScala.toList)
55-
5697
}
57-
58-
5998
}

src/main/scala/org/codeoverflow/chatoverflow/ui/web/rest/plugin/PluginInstanceControllerDefinition.scala

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package org.codeoverflow.chatoverflow.ui.web.rest.plugin
22

3-
import org.codeoverflow.chatoverflow.ui.web.rest.DTOs.{PluginInstance, Requirement}
3+
import org.codeoverflow.chatoverflow.ui.web.rest.DTOs.{PluginInstance, PluginInstanceRef, Requirement, ResultMessage}
44
import org.scalatra.swagger.SwaggerSupport
55
import org.scalatra.swagger.SwaggerSupportSyntax.OperationBuilder
66

@@ -30,6 +30,17 @@ trait PluginInstanceControllerDefinition extends SwaggerSupport {
3030
parameter pathParam[String]("instanceName").description("The name of the plugin instance.")
3131
parameter queryParam[Option[String]]("startIndex").description("The start index of the message stream").optional)
3232

33+
val postInstance: OperationBuilder =
34+
(apiOperation[ResultMessage]("postInstance")
35+
summary "Creates a new plugin instance."
36+
description "Creates a new plugin instance with given name and plugin type."
37+
parameter bodyParam[PluginInstanceRef]("body").description("Requires new instance name and PluginType (name and author)."))
38+
39+
val deleteInstance: OperationBuilder =
40+
(apiOperation[PluginInstance]("deleteInstance")
41+
summary "Removes a plugin instance."
42+
description "Removes a plugin instance specified by its name, if possible."
43+
parameter pathParam[String]("instanceName").description("The name of the plugin instance."))
3344

3445
override protected def applicationDescription: String = "Handles plugin instances and requirements."
3546

0 commit comments

Comments
 (0)