Skip to content
This repository has been archived by the owner on Mar 16, 2022. It is now read-only.

Commit

Permalink
Adds Java Controller and Metadata support
Browse files Browse the repository at this point in the history
I've only added metadata support inputs for event sourcing and crdts, I
haven't added it for outputs yet.

Controller support is in full.
  • Loading branch information
jroper committed Jun 30, 2020
1 parent 961f4c8 commit fa7bb50
Show file tree
Hide file tree
Showing 37 changed files with 2,636 additions and 99 deletions.
5 changes: 3 additions & 2 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -613,7 +613,7 @@ lazy val `java-support` = (project in file("java-support"))
"com.fasterxml.jackson.core" % "jackson-databind" % "2.9.9.3"
),
javacOptions in Compile ++= Seq("-encoding", "UTF-8"),
javacOptions in (Compile, compile) ++= Seq("-source", "1.8", "-target", "1.8"),
javacOptions in (Compile, compile) ++= Seq("-source", "11", "-target", "11"),
akkaGrpcGeneratedSources in Compile := Seq(AkkaGrpc.Server),
akkaGrpcGeneratedLanguages in Compile := Seq(AkkaGrpc.Scala), // FIXME should be Java, but here be dragons

Expand All @@ -635,7 +635,8 @@ lazy val `java-support` = (project in file("java-support"))
sbtprotoc.ProtocPlugin.protobufConfigSettings ++ Seq(
PB.protoSources ++= {
val baseDir = (baseDirectory in ThisBuild).value / "protocols"
Seq(baseDir / "example")
val testSources = sourceDirectory.value / "proto"
Seq(baseDir / "example", testSources)
},
PB.targets := Seq(
PB.gens.java -> crossTarget.value / "akka-grpc" / "test"
Expand Down
3 changes: 2 additions & 1 deletion docs/src/main/paradox/user/lang/java/gettingstarted.md
Original file line number Diff line number Diff line change
Expand Up @@ -180,4 +180,5 @@ Exactly which context parameters are available depend on the type of entity and
|--------------------------------------------------------|-----------------------|-----------------------|
| @javadoc[`Context`](io.cloudstate.javasupport.Context) | | The super type of all Cloudstate contexts. Every invoker makes a subtype of this available for injection, and method or constructor may accept that sub type, or any super type of that subtype that is a subtype of `Context`. |
| `java.lang.String` | @javadoc[`@EntityId`](io.cloudstate.javasupport.EntityId) | The ID of the entity. |

| @javadoc[`Metadata`](io.cloudstate.javasupport.Metadata) | | The metadata associated with the command. |
| @javadoc[`CloudEvent`](io.cloudstate.javasupport.CloudEvent) | | The CloudEvent metadata associated with the command. May be wrapped in `java.util.Optional`. |
171 changes: 171 additions & 0 deletions java-support/src/main/java/io/cloudstate/javasupport/CloudEvent.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
package io.cloudstate.javasupport;

import java.net.URI;
import java.time.ZonedDateTime;
import java.util.Optional;

/** CloudEvent representation of Metadata. */
public interface CloudEvent {

/**
* The CloudEvent spec version.
*
* @return The spec version.
*/
String specversion();

/**
* The id of this CloudEvent.
*
* @return The id.
*/
String id();

/**
* Return a new CloudEvent with the given id.
*
* @param id The id to set.
* @return A copy of this CloudEvent.
*/
CloudEvent withId(String id);

/**
* The source of this CloudEvent.
*
* @return The source.
*/
URI source();

/**
* Return a new CloudEvent with the given source.
*
* @param source The source to set.
* @return A copy of this CloudEvent.
*/
CloudEvent withSource(URI source);

/**
* The type of this CloudEvent.
*
* @return The type.
*/
String type();

/**
* Return a new CloudEvent with the given type.
*
* @param type The type to set.
* @return A copy of this CloudEvent.
*/
CloudEvent withType(String type);

/**
* The data content type of this CloudEvent.
*
* @return The data content type, if set.
*/
Optional<String> datacontenttype();

/**
* Return a new CloudEvent with the given data content type.
*
* @param datacontenttype The data content type to set.
* @return A copy of this CloudEvent.
*/
CloudEvent withDatacontenttype(String datacontenttype);

/**
* Clear the data content type of this CloudEvent, if set.
*
* @return A copy of this CloudEvent.
*/
CloudEvent clearDatacontenttype();

/**
* The data schema of this CloudEvent.
*
* @return The data schema, if set.
*/
Optional<URI> dataschema();

/**
* Return a new CloudEvent with the given data schema.
*
* @param dataschema The data schema to set.
* @return A copy of this CloudEvent.
*/
CloudEvent withDataschema(URI dataschema);

/**
* Clear the data schema of this CloudEvent, if set.
*
* @return A copy of this CloudEvent.
*/
CloudEvent clearDataschema();

/**
* The subject of this CloudEvent.
*
* @return The subject, if set.
*/
Optional<String> subject();

/**
* Return a new CloudEvent with the given subject.
*
* @param subject The subject to set.
* @return A copy of this CloudEvent.
*/
CloudEvent withSubject(String subject);

/**
* Clear the subject of this CloudEvent, if set.
*
* @return A copy of this CloudEvent.
*/
CloudEvent clearSubject();

/**
* The time of this CloudEvent.
*
* @return The time, if set.
*/
Optional<ZonedDateTime> time();

/**
* Return a new CloudEvent with the given time.
*
* @param time The time to set.
* @return A copy of this CloudEvent.
*/
CloudEvent withTime(ZonedDateTime time);

/**
* Clear the time of this CloudEvent, if set.
*
* @return A copy of this CloudEvent.
*/
CloudEvent clearTime();

/**
* Return this CloudEvent represented as Metadata.
*
* <p>If this CloudEvent was created by {{@link Metadata#asCloudEvent()}}, then any non CloudEvent
* metadata that was present will still be present.
*
* @return This CloudEvent expressed as Cloudstate metadata.
*/
Metadata asMetadata();

/**
* Create a CloudEvent.
*
* @param id The id of the CloudEvent.
* @param source The source of the CloudEvent.
* @param type The type of the CloudEvent.
* @return The newly created CloudEvent.
*/
static CloudEvent of(String id, URI source, String type) {
return Metadata.EMPTY.asCloudEvent(id, source, type);
}
}
110 changes: 95 additions & 15 deletions java-support/src/main/java/io/cloudstate/javasupport/CloudState.java
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
package io.cloudstate.javasupport;

import akka.actor.ActorSystem;
import akka.stream.Materializer;
import com.typesafe.config.Config;
import com.google.protobuf.Descriptors;
import io.cloudstate.javasupport.controller.Controller;
import io.cloudstate.javasupport.controller.ControllerHandler;
import io.cloudstate.javasupport.crdt.CrdtEntity;
import io.cloudstate.javasupport.crdt.CrdtEntityFactory;
import io.cloudstate.javasupport.eventsourced.EventSourcedEntity;
import io.cloudstate.javasupport.eventsourced.EventSourcedEntityFactory;
import io.cloudstate.javasupport.impl.AnySupport;
import io.cloudstate.javasupport.impl.controller.AnnotationBasedControllerSupport;
import io.cloudstate.javasupport.impl.controller.ControllerService;
import io.cloudstate.javasupport.impl.crdt.AnnotationBasedCrdtSupport;
import io.cloudstate.javasupport.impl.crdt.CrdtStatefulService;
import io.cloudstate.javasupport.impl.eventsourced.AnnotationBasedEventSourcedSupport;
Expand All @@ -16,13 +22,14 @@
import java.util.concurrent.CompletionStage;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;

/**
* The CloudState class is the main interface to configuring entities to deploy, and subsequently
* starting a local server which will expose these entities to the CloudState Proxy Sidecar.
*/
public final class CloudState {
private final Map<String, StatefulService> services = new HashMap<>();
private final Map<String, Function<ActorSystem, Service>> services = new HashMap<>();
private ClassLoader classLoader = getClass().getClassLoader();
private String typeUrlPrefix = AnySupport.DefaultTypeUrlPrefix();
private AnySupport.Prefer prefer = AnySupport.PREFER_JAVA();
Expand Down Expand Up @@ -108,14 +115,15 @@ public CloudState registerEventSourcedEntity(

final AnySupport anySupport = newAnySupport(additionalDescriptors);

services.put(
descriptor.getFullName(),
EventSourcedStatefulService service =
new EventSourcedStatefulService(
new AnnotationBasedEventSourcedSupport(entityClass, anySupport, descriptor),
descriptor,
anySupport,
persistenceId,
snapshotEvery));
snapshotEvery);

services.put(descriptor.getFullName(), system -> service);

return this;
}
Expand Down Expand Up @@ -144,12 +152,13 @@ public CloudState registerEventSourcedEntity(
Descriptors.FileDescriptor... additionalDescriptors) {
services.put(
descriptor.getFullName(),
new EventSourcedStatefulService(
factory,
descriptor,
newAnySupport(additionalDescriptors),
persistenceId,
snapshotEvery));
service ->
new EventSourcedStatefulService(
factory,
descriptor,
newAnySupport(additionalDescriptors),
persistenceId,
snapshotEvery));

return this;
}
Expand Down Expand Up @@ -178,18 +187,19 @@ public CloudState registerCrdtEntity(

final AnySupport anySupport = newAnySupport(additionalDescriptors);

services.put(
descriptor.getFullName(),
CrdtStatefulService service =
new CrdtStatefulService(
new AnnotationBasedCrdtSupport(entityClass, anySupport, descriptor),
descriptor,
anySupport));
anySupport);

services.put(descriptor.getFullName(), system -> service);

return this;
}

/**
* Register an CRDt entity factory.
* Register a CRDT entity factory.
*
* <p>This is a low level API intended for custom (eg, non reflection based) mechanisms for
* implementing the entity.
Expand All @@ -206,7 +216,77 @@ public CloudState registerCrdtEntity(
Descriptors.FileDescriptor... additionalDescriptors) {
services.put(
descriptor.getFullName(),
new CrdtStatefulService(factory, descriptor, newAnySupport(additionalDescriptors)));
system ->
new CrdtStatefulService(factory, descriptor, newAnySupport(additionalDescriptors)));

return this;
}

/**
* Register an annotated Controller service.
*
* <p>The controller class must be annotated with {@link
* io.cloudstate.javasupport.controller.Controller}.
*
* @param controller The controller object.
* @param descriptor The descriptor for the service that this controller implements.
* @param additionalDescriptors Any additional descriptors that should be used to look up protobuf
* types when needed.
* @return This Cloudstate builder.
*/
public CloudState registerController(
Object controller,
Descriptors.ServiceDescriptor descriptor,
Descriptors.FileDescriptor... additionalDescriptors) {

Controller controllerAnnotation = controller.getClass().getAnnotation(Controller.class);
if (controllerAnnotation == null) {
throw new IllegalArgumentException(
controller.getClass() + " does not declare an " + Controller.class + " annotation!");
}

final AnySupport anySupport = newAnySupport(additionalDescriptors);

services.put(
descriptor.getFullName(),
system ->
new ControllerService(
new AnnotationBasedControllerSupport(
controller, anySupport, descriptor, Materializer.createMaterializer(system)),
descriptor,
anySupport));

return this;
}

/**
* Register a Controller handler.
*
* <p>This is a low level API intended for custom (eg, non reflection based) mechanisms for
* implementing the controller.
*
* @param controller The controller handler.
* @param descriptor The descriptor for the service that this controller implements.
* @param additionalDescriptors Any additional descriptors that should be used to look up protobuf
* types when needed.
* @return This Cloudstate builder.
*/
public CloudState registerController(
ControllerHandler controller,
Descriptors.ServiceDescriptor descriptor,
Descriptors.FileDescriptor... additionalDescriptors) {

Controller controllerAnnotation = controller.getClass().getAnnotation(Controller.class);
if (controllerAnnotation == null) {
throw new IllegalArgumentException(
controller.getClass() + " does not declare an " + Controller.class + " annotation!");
}

final AnySupport anySupport = newAnySupport(additionalDescriptors);

ControllerService service = new ControllerService(controller, descriptor, anySupport);

services.put(descriptor.getFullName(), system -> service);

return this;
}
Expand Down
Loading

0 comments on commit fa7bb50

Please sign in to comment.