{"id":13752769,"url":"https://github.com/oracle/wookiee","last_synced_at":"2025-05-09T20:34:19.475Z","repository":{"id":31489078,"uuid":"35053247","full_name":"oracle/wookiee","owner":"oracle","description":"Scala based lightweight service framework using zookeeper, gRPC, and other popular technologies.","archived":true,"fork":false,"pushed_at":"2024-02-20T17:57:46.000Z","size":9832,"stargazers_count":144,"open_issues_count":0,"forks_count":47,"subscribers_count":30,"default_branch":"master","last_synced_at":"2024-11-16T05:32:14.298Z","etag":null,"topics":["framework","lightweight","microservice","scala","wookiee"],"latest_commit_sha":null,"homepage":"","language":"Scala","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/oracle.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2015-05-04T18:56:30.000Z","updated_at":"2024-09-02T02:16:22.000Z","dependencies_parsed_at":"2023-10-30T04:35:29.709Z","dependency_job_id":"b167db50-255a-4046-9367-811dafa86ad0","html_url":"https://github.com/oracle/wookiee","commit_stats":null,"previous_names":[],"tags_count":71,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oracle%2Fwookiee","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oracle%2Fwookiee/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oracle%2Fwookiee/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oracle%2Fwookiee/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/oracle","download_url":"https://codeload.github.com/oracle/wookiee/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253321745,"owners_count":21890458,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["framework","lightweight","microservice","scala","wookiee"],"created_at":"2024-08-03T09:01:10.755Z","updated_at":"2025-05-09T20:34:14.449Z","avatar_url":"https://github.com/oracle.png","language":"Scala","readme":"# Wookiee Platform\n\n[![Latest Release](https://img.shields.io/github/release/oracle/wookiee.svg)](https://github.com/oracle/wookiee/releases) [![License](http://img.shields.io/:license-Apache%202-red.svg)](http://www.apache.org/licenses/LICENSE-2.0.txt)\n\nFastest way to get going with Wookiee check out the [Quickstart Guide](docs/quickstart.md).\n\nWookiee is Licensed under the Apache 2.0 License, for more information see [LICENSE](LICENSE)\n\n## Usage\n\nWookiee is meant to save you from the endless tedium of creating yet another micro service. \nIt provides a common Main class ([HarnessService](wookiee-core/src/main/scala/com/oracle/infy/wookiee/app/HarnessService.scala)) \nand tacks on a ton of out of the box conveniences.\n\n\u003cb\u003eSo think of Wookiee when you...\u003c/b\u003e\n\u003ci\u003e\n* ...are trying to track down what library you put the eleventh health check \nimplementation of your career in for you to copy paste\n* ...just aren't sure whether you want to use Colossus or Akka Http and you'd like to be\nable to swap between the two in a few minutes or just run both at once!\n* ...need to get metrics recording in your service and reporting out to Graphite \nand you want to be able to do it with zero lines of setup code\n* ...have found that interacting with the old school Scala(Java) main() method reminds\nyou too much of being in college and you begin to doubt you've improved at all\n* ...don't have the patience to throw together an artisanal configuration reader for the\nhundredth time because your cycles are more important, dang it!\n* ...want the new intern to be able to create their own new Services using a really simple\ntemplate with tons of examples since your company runs all their Services on one framework\n* ...would rather focus on the functionality of your Actors than worrying about\nlinking up health checks, starting everything up, and sending out PoisonPills on shutdown\n* ...have no appetite for creating a new logger variable for every single class you want to hear from\n* ...need to integrate a new technology but find it unsavory to write thirty lines of\n\"hotNewTech.start(config); ...; hotNewTech.whatever(); ...; hotNewTech.close()\" in every codebase\n* ...just want to be able to get straight to the fun stuff!\n\n\u003c/i\u003e\n\n### Adding to Pom\n\nAdd [latest version](https://github.com/oracle/wookiee/releases/latest) of wookiee, either Scala 2.12 or 2.13 varietals:\n~~~~\n\u003cdependency\u003e\n    \u003cgroupId\u003ecom.oracle.infy\u003c/groupId\u003e\n    \u003cartifactId\u003ewookiee-core_${scala.artifact.version}\u003c/artifactId\u003e\n    \u003cversion\u003e${wookiee.version}\u003c/version\u003e\n\u003c/dependency\u003e\n\u003cdependency\u003e\n    \u003cgroupId\u003ecom.oracle.infy\u003c/groupId\u003e\n    \u003cartifactId\u003ewookiee-test_${scala.artifact.version}\u003c/artifactId\u003e\n    \u003cversion\u003e${wookiee.version}\u003c/version\u003e\n\u003c/dependency\u003e\n~~~~\n```sbt\nlibraryDependencies += \"com.oracle.infy\" %% \"wookiee-core\" % \"${wookiee.version}\"\nlibraryDependencies += \"com.oracle.infy\" %% \"wookiee-test\" % \"${wookiee.version}\"\n```\n\n### What's Included\n\nThe Wookiee platform repository contains the core, supporting components and a test library. It is built primarily on Scala and [Akka](http://akka.io). It contains example projects as well as Maven archetypes for creating various service and creating a component. Wookiee is split into 2 primary components, the Wookiee library and the system components. The Wookiee library is comprised of the of the following components:\n\n* [Command Executive](wookiee-libs/src/main/scala/com/oracle/infy/wookiee/command/WookieeCommandExecutive.scala) - Adds and Executes commands. \n* [Component Manager](wookiee-core/src/main/scala/com/oracle/infy/wookiee/component/ComponentManager.scala) - Loads up component Jars and managers\n* [Service Manager](wookiee-core/src/main/scala/com/oracle/infy/wookiee/service/ServiceManager.scala) - Loads up user services, this is where the primary business logic for the application would reside\n* [Health Provider](wookiee-libs/src/main/scala/com/oracle/infy/wookiee/health/WookieeMonitor.scala) - provides framework for health in components and services\n* [Logging](wookiee-libs/src/main/scala/com/oracle/infy/wookiee/logging/LoggingAdapter.scala) - provides basic logging capability for components and services\n* [Utilities](wookiee-libs/src/main/scala/com/oracle/infy/wookiee/utils) - Utility libraries for common functions in the code.\n\n### Command Executive\nThe command manager is the central actor for routing all execution of commands in our system. A command is simply the primary execution point for specific set of work. \nFor more information see the [Commands](docs/Commands.md) documentation.\n\n### Loading System Components\nThe Wookiee library loads various components when it starts up. Each component is derived through 1 of 4 methods:\n\n1. It checks the sub folders found in the root folder defined in the application config under the key \"components.component-path\". The name of the folder will be the name of the actor that is initialized by the component manager. Each component folder will contain a lib folder with all the jars that the component uses as well as the component jar. A conf file must be located in the component folder for configuration of the component. For specifics around individual components, the configuration file and the expected pattern for the component see Components section in this doc.\n\n2. It checks for jars in the aforementioned folder under the key \"components.component-path\". The jars there are expected to be shaded jars that contain all the needed libraries and config for the component in the jar. Any configuration can be overridden in your primary conf file using the config from the reference conf in the jar.\n\n3. It loads a component from a class based on the configuration that is loaded into the system. This list of components are found in the main configuration under the key \"components.lib-components\". The value of the key is a list of strings that simply point to the config for the component.\n\n4. It loads a component automatically based on a key in the config file for that component \"dynamic-component\". If the key is set to true in the config it will load up the component.*\n\n* Note: If a list of components are set under the key \"component.lib-components\" there would be no components loaded automatically, essentially the last type would be switched off.\n\n### Loading Services\nServices that are built for Wookiee are also loaded into memory by the core. The exception to this would be if Wookiee is used just as a library or embedded in an application. In this case the service in this context is not really a service but rather an application. In general however, if Wookiee is to be used as a library it would most likely be more beneficial to use the individual system components in search for the specific functionality that your App requires. Services are loaded in a similar fashion to the components, where the services are located in sub folders found in the root folder defined in the application config under the key \"services.service-path\". The primary difference being that classes are loaded into a separate class loader instead of the root Wookiee class loader. (** note ** not the system class loader)\n\nIn most cases services will be loaded as mentioned above, however one can also load the service dynamically which will be described below:\n\n### Local Messaging\nThere is a cluster component which allows for messaging across a cluster. However, by default Wookiee will include local messaging. The messaging works identically to how clustered messaging works, and is based on a simple PubSub methodology.\n\n### Health\nStandardized health checks is provided by the library. The WookieeMonitor trait will apply default health functionality to any class that leverages the trait. By default, a developer would only have to insert the following code into their class\n```scala\nimport com.typesafe.config.Config\nimport com.oracle.infy.wookiee.service.ServiceV2\nimport scala.concurrent.Future\nimport com.oracle.infy.wookiee.health.{WookieeMonitor, HealthComponent, ComponentState}\n\n// ServiceV2 is a trait that extends WookieeMonitor\nclass MyService(config: Config) extends ServiceV2(config) {\n  val underClass = new MyUnderClass()\n\t\n  override def getHealth: Future[HealthComponent] = {\n    Future.successful(HealthComponent(name, ComponentState.NORMAL, \"MyService is healthy\"))\n  }\n  \n  override def getDependents: Iterable[WookieeMonitor] = List(underClass)\n}\n\n// WookieeMonitor is a trait that provides health functionality\n// If the parent that creates this class extends WookieeMonitor and implements the getDependents function\n// then the getHealth function will be called on the dependents\nclass MyUnderClass() extends WookieeMonitor {\n  override def getHealth: Future[HealthComponent] = {\n    Future.successful(HealthComponent(name, ComponentState.NORMAL, \"MyUnderClass is healthy\"))\n  }\n}\n```\nThe above code will give the actor basic health functionality, this will do two things:\n\n* receive health check requests in MyService and return a healthcheck status\n* iterate through the WookieeMonitor dependents and call the getHealth function on them\n\nGenerally a developer would want to override the getHealth function to give customized health check status for the class. Example:\n```scala\n\toverride def getHealth : Future[HealthComponent] = {\n\t\tFuture {\n\t\t\tMath.random.toInt match {\n\t\t\t\tcase s if s \u003e 50 =\u003e\n\t\t\t\t\tHealthComponent(self.path.name, ComponentState.NORMAL, \"Random health check is NORMAL\")\n\t\t\t\tcase s if s \u003e 10 \u0026\u0026 s \u003c= 50 =\u003e\n\t\t\t\t\tHealthComponent(self.path.name, ComponentState.DEGRADED, \"Random health check is DEGRADED\")\n\t\t\t\tcase s if s \u003c= 10 =\u003e\n\t\t\t\t\tHealthComponent(self.path.name, ComponentState.CRITICAL, \"Random health check is CRITICAL\")\n\t\t\t}\n\t\t}\n\t}\n```\nThis code will create a random health check result based on the value of the random int. As shown the ComponentState can be either NORMAL, DEGRADED or CRITICAL. \nLastly if needed a developer can override the checkHealth function that will handle the message, which will by default use getHealth to get the health of the current actor and then traverse the children to get their health, however if there is a requirement to modify this behavior you can simply override it.\n\n### Configuration Watching\nThe file specified in config.file will actually be watched for any changes to it and a message\nwill be sent to all components and services (which will then forward it on to their getDependents() lists). \nTo hook into this most easily, extend the ConfigHelperV2 class like so:\n\n```scala\nimport com.oracle.infy.wookiee.config.ConfigHelperV2\n\nclass ConfigWatchingClass extends ConfigHelperV2 {\n  override def renewConfiguration(): Unit = {\n      super.renewConfiguration()\n      renewableConfig // Do something with your updated config object\n  }\n}\n\nclass ConfigWatchingService extends ServiceV2 {\n  val configWatchingClass = new ConfigWatchingClass()\n    \n  override def getDependents: Iterable[WookieeMonitor] = List(configWatchingClass)\n}\n```\n\n### Logging\nStandardized logging is provided by the library. This can be applied to any class using the trait LoggingAdapter. This will give you the \"log\" variable which will allow you to write info, debug, warn, error and trace messages to the log.\n```scala\nclass AnyClass extends LoggingAdapter {\n  log.info(\"This is a log message\")\n}\n```\n\nWookiee can be used as both a library and a service. To use it as a service a developer would be required to simply execute the HarnessService app, for use as a library the developer would be required to add a dependency in the project's pom and then initialize Wookiee manually. Alternatively the developer could add a dependency for a single component to the POM and use it separately. For more information on leveraging a single component see the doc specific to that component.\n\n[**Releases**](https://github.com/oracle/wookiee/releases)\n\n# How To\nThere are several aspects to utilizing the functionality contained with Wookiee and its supporting libraries. This\nsection outlines the available functionality and how to best utilize the Wookiee Platform.\n\n### Increasing Artifact Version\nTo bump the version of Wookiee Core/Test simply increase it in the VERSION file.\nThe version that is published will be the literal contents of that file.\n\n### Creating a service\nAs services are what provide functionality to the Wookiee container, this section provides information on how to\ncreate a basic service.\n\n[Example](examples/basic-service/src/main/scala/com/oracle/infy/qa/BasicService.scala)\n\n### Creating a component\nComponents provide pluggable core functionality in to Wookiee. This allows developers to pick and choose the kind of functionality that they want.\n\n[Example](examples/basic-extension/src/main/scala/com/oracle/infy/wookiee/qa/BasicExtension.scala)\n\n### Components\nA component is dynamically loaded in Wookiee. This allows for a developer to then only load the components that they wish to use as part of the Wookiee Platform. A component is defined by a class object with the Component trait found in the wookiee-core project. Wookiee will start up any component that is found in location that is defined by the component-path key in the harness configuration file.\n```\nwookiee-system {\n  # This is the path to the location of the components (defaults to \"components\")\n  # Should just contain the jar for the component\n  component-path = \"components\"\n  ...\n}\n```\n\n\n* [Webservice Component](wookiee-web)\n  * Most modern Component for HTTP/WS services\n* [Cache Component](wookiee-cache)\n  * Provides a Cache implementation for Wookiee\n* [Memcache Component](wookiee-cache-memcache)\n  * Extends the Cache Component to provide a Memcache implementation\n* [gRPC Component](wookiee-grpc-component)\n  * Allows for creation and querying of gRPC services\n* [Metrics Component](wookiee-metrics)\n  * Metrics that can be attached anywhere and sent to a metrics service\n* [Zookeeper Component](wookiee-zookeeper)\n  * Provides Zookeeper connection management and service discovery\n\n### Configuring a component\nEach component loaded in Wookiee should provide a default configuration that will fit most situations.  The Wookiee Platform\nuses Typesafe Config to load configurations at runtime in layers.  A component's default configuration should be given the lowest\npriority, the reference conf, following the layered priority schema set by Typesafe Config.  This can be problematic at times, as third party libs\nand components with equally prioritized, overlapping configurations are combined in the application.  To ensure component \nconfigurations take precedence, place components jars at the beginning of the classpath.  One approach is to separate components\nfrom third party libs in the distribution.\n\nMaven dist.xml\n```xml\n\u003cassembly\u003e\n    \u003cid\u003ebin\u003c/id\u003e\n    \u003cincludeBaseDirectory\u003efalse\u003c/includeBaseDirectory\u003e\n    \u003cformats\u003e\n        \u003cformat\u003etar.gz\u003c/format\u003e\n    \u003c/formats\u003e\n    \u003cfiles\u003e\n        \u003cfile\u003e\n            \u003csource\u003e${project.build.directory}/${project.build.finalName}.jar\u003c/source\u003e\n        \u003c/file\u003e\n    \u003c/files\u003e\n    \u003cdependencySets\u003e\n        \u003cdependencySet\u003e\n            \u003cuseProjectArtifact\u003efalse\u003c/useProjectArtifact\u003e\n            \u003coutputDirectory\u003e/lib/thirdparty\u003c/outputDirectory\u003e\n            \u003cscope\u003eruntime\u003c/scope\u003e\n            \u003cexcludes\u003e\n                \u003cexclude\u003e*:wookiee*:jar\u003c/exclude\u003e\n            \u003c/excludes\u003e\n        \u003c/dependencySet\u003e\n        \u003cdependencySet\u003e\n            \u003cuseProjectArtifact\u003efalse\u003c/useProjectArtifact\u003e\n            \u003coutputDirectory\u003e/lib/components\u003c/outputDirectory\u003e\n            \u003cscope\u003eruntime\u003c/scope\u003e\n            \u003cincludes\u003e\n                \u003cinclude\u003e*:wookiee*:jar\u003c/include\u003e\n            \u003c/includes\u003e\n        \u003c/dependencySet\u003e\n    \u003c/dependencySets\u003e\n\u003c/assembly\u003e\n```\n\nRun command:\n```\njava -cp *:lib/components/*:lib/thirdparty/* com.oracle.infy.wookiee.app.HarnessService\n```\n\n## Discoverability\nWookiee provides a service discovery mechanism that allows services to register themselves with the Wookiee Platform.\nThis gives us the ability to message services that are running in the Wookiee Platform without having to know the\nexact host that those services are on. \n* [Wookiee Discovery](wookiee-discovery)\n* [Example of Usage](examples/advanced-communication)\n\n## Wookiee Actors\nWookiee formerly ran on akka Actors (and still will until version 2.5). There is an interface called the WookieeActor\nthat emulates the functionality of an akka Actor. This can be used from anywhere in the Wookiee Platform and has all\nthe same methods and behavior as an akka Actor. This is useful for when you want to use the Wookiee Platform but don't\nwant to use akka Actors.\n\n```scala\nimport com.oracle.infy.wookiee.actors.WookieeActor\n\nclass MyActor extends WookieeActor {\n  override def receive: Receive = {\n    case \"hello\" =\u003e sender() ! \"world\"\n  }\n}\n```\nThese WookieeActors have all the akka functionality including the ability to `become(receiver)` and use schedulers.\nTo spin up an actor is easy and doesn't require an actor system or actor context.\n\n```scala\nimport com.oracle.infy.wookiee.actors.WookieeActor\n\nval myActor = WookieeActor.actorOf(new MyActor)\n```\n\nOne can also create a router of actors using the `WookieeActor.withRouter` method.\n\n```scala\nimport com.oracle.infy.wookiee.actors.WookieeActor\nimport com.oracle.infy.wookiee.actors.router.RoundRobinRouter\n\n// Will create 10 instances of MyActor and round robin between them\nWookieeActor.withRouter(new MyActor, new RoundRobinRouter(10))\n```\n\n\n# wookiee-grpc\n* [wookiee-grpc](#wookiee-grpc)\n## Installation\nwookiee-grpc is available for Scala 2.12 and 2.13. There are no plans to support scala 2.11 or lower.\n```scala\nlibraryDependencies += \"com.oracle.infy\" %% \"wookiee-grpc\" % \"2.2.8\"\n```\n\n## Setup ScalaPB\nWe use [ScalaPB](https://github.com/scalapb/ScalaPB) to generate source code from a `.proto` file. You can use\nother plugins/code generators if you wish. wookiee-grpc will work as long as you have `io.grpc.ServerServiceDefinition`\nfor the server and something that accept `io.grpc.ManagedChannel` for the client.\n\nDeclare your gRPC service using proto3 syntax and save it in `src/main/protobuf/myService.proto`\n```proto\nsyntax = \"proto3\";\n\npackage com.oracle.infy.wookiee;\n\nmessage HelloRequest {\n  string name = 1;\n}\n\nmessage HelloResponse {\n  string resp = 1;\n}\n\nservice MyService {\n  rpc greet(HelloRequest) returns (HelloResponse) {}\n}\n\n```\n\nAdd ScalaPB plugin to `plugin.sbt` file\n```sbt\naddSbtPlugin(\"com.thesamet\" % \"sbt-protoc\" % \"1.0.6\")\nlibraryDependencies += \"com.thesamet.scalapb\" %% \"compilerplugin\" % \"0.11.8\"\n\n```\n\nConfigure the project in `build.sbt` so that ScalaPB can generate code\n```sbt\n    Compile / PB.targets := Seq(\n      scalapb.gen() -\u003e (Compile / sourceManaged).value / \"scalapb\"\n    ),\n    libraryDependencies ++= Seq(\n      \"io.grpc\" % \"grpc-netty\" % scalapb.compiler.Version.grpcJavaVersion,\n      \"com.thesamet.scalapb\" %% \"scalapb-runtime-grpc\" % scalapb.compiler.Version.scalapbVersion\n    )\n  )\n\n```\n\nIn the sbt shell, type `protocGenerate` to generate scala code based on the `.proto` file. ScalaPB will generate\ncode and put it under `target/scala-2.13/src_managed/main`.\n\n## Using wookiee-grpc\nAfter the code has been generated by ScalaPB, you can use wookiee-grpc for service discoverability and load balancing.\n\nwookiee-grpc is written using functional concepts. One key concept is side-effect management/referential transparency.\nWe use cats-effect (https://typelevel.org/cats-effect/) internally.\nIf you want to use cats-effect, you can use the methods that return `IO[_]`. Otherwise, use the methods prefixed with `unsafe`.\nWhen using `unsafe` methods, you are expected to handle any exceptions\n\n### Imports\nAdd the following imports:\n```sbt\nimport com.oracle.infy.wookiee.grpc.model.{Host, HostMetadata}\nimport com.oracle.infy.wookiee.grpc.settings._\nimport com.oracle.infy.wookiee.grpc._\nimport com.oracle.infy.wookiee.grpc.model.LoadBalancers._\nimport io.grpc._\n\n```\n\n### Creating a Server\n```sbt\n    val serverSettingsF: ServerSettings = ServerSettings(\n      discoveryPath = zookeeperDiscoveryPath,\n      serverServiceDefinition = ssd,\n      // This is an optional arg. wookiee-grpc will try to resolve the address automatically.\n      // If you are running this locally, its better to explicitly set the hostname\n      host = Host(0, \"localhost\", 9091, HostMetadata(0, quarantined = false)),\n      authSettings = None,\n      sslServerSettings = None,\n      bossExecutionContext = mainEC,\n      workerExecutionContext = mainEC,\n      applicationExecutionContext = mainEC,\n      bossThreads = bossThreads,\n      workerThreads = mainECParallelism,\n      curatorFramework = curator\n    )\n\n    val serverF: Future[WookieeGrpcServer] = WookieeGrpcServer.start(serverSettingsF).unsafeToFuture()\n\n```\n\n\n### Creating a Client Channel\n```sbt\n    val wookieeGrpcChannel: WookieeGrpcChannel = WookieeGrpcChannel\n      .of(\n        ChannelSettings(\n          serviceDiscoveryPath = zookeeperDiscoveryPath,\n          eventLoopGroupExecutionContext = blockingEC,\n          channelExecutionContext = mainEC,\n          offloadExecutionContext = blockingEC,\n          eventLoopGroupExecutionContextThreads = bossThreads,\n//           Load Balancing Policy\n//             One of:\n//               RoundRobinPolicy\n//               RoundRobinWeightedPolicy\n//               RoundRobinHashedPolicy\n          lbPolicy = RoundRobinPolicy,\n          curatorFramework = curator,\n          sslClientSettings = None,\n          clientAuthSettings = None\n        )\n      )\n      .unsafeRunSync()\n\n    val stub: MyServiceGrpc.MyServiceStub = MyServiceGrpc.stub(wookieeGrpcChannel.managedChannel)\n\n```\n\n### Executing a gRPC Call\n```sbt\n    val gRPCResponseF: Future[HelloResponse] = for {\n      server \u003c- serverF\n      resp \u003c- stub\n        .withInterceptors(new ClientInterceptor {\n          override def interceptCall[ReqT, RespT](\n              method: MethodDescriptor[ReqT, RespT],\n              callOptions: CallOptions,\n              next: Channel\n          ): ClientCall[ReqT, RespT] = {\n            next.newCall(\n              method,\n              // Set the WookieeGrpcChannel.hashKeyCallOption when using RoundRobinHashedPolicy\n              callOptions.withOption(WookieeGrpcChannel.hashKeyCallOption, \"Some hash\")\n            )\n          }\n        })\n        .greet(HelloRequest(\"world!\"))\n      _ \u003c- wookieeGrpcChannel.shutdown().unsafeToFuture()\n      _ \u003c- server.shutdown().unsafeToFuture()\n    } yield resp\n\n    println(Await.result(gRPCResponseF, Duration.Inf))\n    curator.close()\n    zkFake.close()\n    ()\n\n```\n\n\n### Setting up load balancing methods in channel settings\n\nThere are three load balancing policies that ship with wookiee-grpc.\nThe load balancing policies are set up within the gRPC Channel Settings.\n\n* **Round Robin**\n\n  A simple round robin policy that alternates between hosts as calls are executed. It's fairly simplistic.\n\n\n* **Round Robin Weighted**\n\n  This load balancer takes server load into consideration and distributes calls to the server with the lowest\n  current usage. If all loads are equivalent, it defaults to simple Round Robin behavior.\n\n\n* **Round Robin Hashed**\n\n  Provides \"stickiness\" for the gRPC host. If you want a particular host to serve the request for all the calls with a\n  particular key, you can use this policy. For example, if you want a single server to service all requests that use\n  the key \"foo\", you can set the `WookieeGrpcChannel.hashKeyCallOption` on every call. This will ensure that all gRPC calls using the same\n  hash will be executed on the same server.\n\n```sbt\n    val gRPCResponseF: Future[HelloResponse] = for {\n      server \u003c- serverF\n      resp \u003c- stub\n        .withInterceptors(new ClientInterceptor {\n          override def interceptCall[ReqT, RespT](\n              method: MethodDescriptor[ReqT, RespT],\n              callOptions: CallOptions,\n              next: Channel\n          ): ClientCall[ReqT, RespT] = {\n            next.newCall(\n              method,\n              // Set the WookieeGrpcChannel.hashKeyCallOption when using RoundRobinHashedPolicy\n              callOptions.withOption(WookieeGrpcChannel.hashKeyCallOption, \"Some hash\")\n            )\n          }\n        })\n        .greet(HelloRequest(\"world!\"))\n      _ \u003c- wookieeGrpcChannel.shutdown().unsafeToFuture()\n      _ \u003c- server.shutdown().unsafeToFuture()\n    } yield resp\n\n    println(Await.result(gRPCResponseF, Duration.Inf))\n    curator.close()\n    zkFake.close()\n    ()\n\n```\n\n## Putting it all together\n\nHere is an example of a complete gRPC solution\n\n```scala\nimport java.lang.Thread.UncaughtExceptionHandler\nimport java.util.concurrent.{Executors, ForkJoinPool, ThreadFactory}\nimport cats.effect.{Blocker, ContextShift, IO, Timer}\n//wookiee-grpc imports\nimport com.oracle.infy.wookiee.grpc.model.{Host, HostMetadata}\nimport com.oracle.infy.wookiee.grpc.settings._\nimport com.oracle.infy.wookiee.grpc._\nimport com.oracle.infy.wookiee.grpc.model.LoadBalancers._\nimport io.grpc._\n//wookiee-grpc imports\nimport org.typelevel.log4cats.Logger\n// This is from ScalaPB generated code\nimport com.oracle.infy.wookiee.myService.MyServiceGrpc.MyService\nimport com.oracle.infy.wookiee.myService.{HelloRequest, HelloResponse, MyServiceGrpc}\nimport org.typelevel.log4cats.slf4j.Slf4jLogger\nimport io.grpc.ServerServiceDefinition\nimport org.apache.curator.test.TestingServer\n\nimport scala.concurrent.duration._\nimport scala.concurrent.{Await, ExecutionContext, Future}\n\nobject Example {\n\n  def main(args: Array[String]): Unit = {\n    val bossThreads = 10\n    val mainECParallelism = 10\n\n    // wookiee-grpc is written using functional concepts. One key concept is side-effect management/referential transparency\n    // We use cats-effect (https://typelevel.org/cats-effect/) internally.\n    // If you want to use cats-effect, you can use the methods that return IO[_]. Otherwise, use the methods prefixed with `unsafe`.\n    // When using `unsafe` methods, you are expected to handle any exceptions\n\n    val uncaughtExceptionHandler = new UncaughtExceptionHandler {\n      override def uncaughtException(t: Thread, e: Throwable): Unit = {\n        System.err.println(\"Got an uncaught exception on thread \" ++ t.getName ++ \" \" ++ e.toString)\n      }\n    }\n\n    val tf = new ThreadFactory {\n      override def newThread(r: Runnable): Thread = {\n        val t = new Thread(r)\n        t.setName(\"blocking-\" ++ t.getId.toString)\n        t.setUncaughtExceptionHandler(uncaughtExceptionHandler)\n        t.setDaemon(true)\n        t\n      }\n    }\n\n    // The blocking execution context must create daemon threads if you want your app to shutdown\n    val blockingEC = ExecutionContext.fromExecutorService(Executors.newCachedThreadPool(tf))\n    // This is the execution context used to execute your application specific code\n    implicit val mainEC: ExecutionContext = ExecutionContext.fromExecutor(\n      new ForkJoinPool(\n        mainECParallelism,\n        ForkJoinPool.defaultForkJoinWorkerThreadFactory,\n        uncaughtExceptionHandler,\n        true\n      )\n    )\n\n    // Use a separate execution context for the timer\n    val timerEC = ExecutionContext.fromExecutorService(Executors.newSingleThreadExecutor())\n\n    implicit val cs: ContextShift[IO] = IO.contextShift(mainEC)\n    implicit val blocker: Blocker = Blocker.liftExecutionContext(blockingEC)\n    implicit val timer: Timer[IO] = IO.timer(timerEC)\n    implicit val logger: Logger[IO] = Slf4jLogger.create[IO].unsafeRunSync()\n\n    val zookeeperDiscoveryPath = \"/discovery\"\n\n    // This is just to demo, use an actual Zookeeper quorum.\n    val zkFake = new TestingServer()\n    val connStr = zkFake.getConnectString\n\n    val curator = WookieeGrpcUtils.createCurator(connStr, 5.seconds, blockingEC).unsafeRunSync()\n    curator.start()\n\n    val ssd: ServerServiceDefinition = MyService.bindService(\n      (request: HelloRequest) =\u003e {\n        println(\"received request\")\n        Future.successful(HelloResponse(\"Hello \" ++ request.name))\n      },\n      mainEC\n    )\n\n    //Creating a Server\n    val serverSettingsF: ServerSettings = ServerSettings(\n      discoveryPath = zookeeperDiscoveryPath,\n      serverServiceDefinition = ssd,\n      // This is an optional arg. wookiee-grpc will try to resolve the address automatically.\n      // If you are running this locally, its better to explicitly set the hostname\n      host = Host(0, \"localhost\", 9091, HostMetadata(0, quarantined = false)),\n      authSettings = None,\n      sslServerSettings = None,\n      bossExecutionContext = mainEC,\n      workerExecutionContext = mainEC,\n      applicationExecutionContext = mainEC,\n      bossThreads = bossThreads,\n      workerThreads = mainECParallelism,\n      curatorFramework = curator\n    )\n\n    val serverF: Future[WookieeGrpcServer] = WookieeGrpcServer.start(serverSettingsF).unsafeToFuture()\n    //Creating a Server\n\n    //channelSettings\n    val wookieeGrpcChannel: WookieeGrpcChannel = WookieeGrpcChannel\n      .of(\n        ChannelSettings(\n          serviceDiscoveryPath = zookeeperDiscoveryPath,\n          eventLoopGroupExecutionContext = blockingEC,\n          channelExecutionContext = mainEC,\n          offloadExecutionContext = blockingEC,\n          eventLoopGroupExecutionContextThreads = bossThreads,\n//           Load Balancing Policy\n//             One of:\n//               RoundRobinPolicy\n//               RoundRobinWeightedPolicy\n//               RoundRobinHashedPolicy\n          lbPolicy = RoundRobinPolicy,\n          curatorFramework = curator,\n          sslClientSettings = None,\n          clientAuthSettings = None\n        )\n      )\n      .unsafeRunSync()\n\n    val stub: MyServiceGrpc.MyServiceStub = MyServiceGrpc.stub(wookieeGrpcChannel.managedChannel)\n    //channelSettings\n\n    //grpcCall\n    val gRPCResponseF: Future[HelloResponse] = for {\n      server \u003c- serverF\n      resp \u003c- stub\n        .withInterceptors(new ClientInterceptor {\n          override def interceptCall[ReqT, RespT](\n              method: MethodDescriptor[ReqT, RespT],\n              callOptions: CallOptions,\n              next: Channel\n          ): ClientCall[ReqT, RespT] = {\n            next.newCall(\n              method,\n              // Set the WookieeGrpcChannel.hashKeyCallOption when using RoundRobinHashedPolicy\n              callOptions.withOption(WookieeGrpcChannel.hashKeyCallOption, \"Some hash\")\n            )\n          }\n        })\n        .greet(HelloRequest(\"world!\"))\n      _ \u003c- wookieeGrpcChannel.shutdown().unsafeToFuture()\n      _ \u003c- server.shutdown().unsafeToFuture()\n    } yield resp\n\n    println(Await.result(gRPCResponseF, Duration.Inf))\n    curator.close()\n    zkFake.close()\n    ()\n    //grpcCall\n  }\n}\n\nExample.main(Array.empty[String])\n// received request\n// HelloResponse(Hello world!,UnknownFieldSet(Map()))\n```\n## Contributing\nThis project is not accepting external contributions at this time. For bugs or enhancement requests, please file a GitHub issue unless it’s security related. When filing a bug remember that the better written the bug is, the more likely it is to be fixed. If you think you’ve found a security vulnerability, do not raise a GitHub issue and follow the instructions in our [security policy](./SECURITY.md).\n\n## Security\nPlease consult the [security guide](./SECURITY.md) for our responsible security vulnerability disclosure process\n\n## License\nCopyright (c) 2004, 2023 Oracle and/or its affiliates.\nReleased under the Apache License Version 2.0\n","funding_links":[],"categories":["scala"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Foracle%2Fwookiee","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Foracle%2Fwookiee","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Foracle%2Fwookiee/lists"}