ActorTypeInformation with parsing logic + some stubs.

This commit is contained in:
Artur Souza 2019-12-11 16:42:56 -08:00
parent 937224e2f3
commit 9dd68c8f71
8 changed files with 419 additions and 0 deletions

View File

@ -171,6 +171,7 @@ class DaprHttpAsyncClient implements DaprAsyncClient {
.addHeader(Constants.HEADER_DAPR_REQUEST_ID, requestId)
.build();
// TODO: make this call async as well.
Response response = this.httpClient.newCall(request).execute();
if (!response.isSuccessful())
{

View File

@ -0,0 +1,12 @@
/*
* Copyright (c) Microsoft Corporation.
* Licensed under the MIT License.
*/
package io.dapr.actors.runtime;
/**
* TODO
*/
public abstract class AbstractActor {
}

View File

@ -0,0 +1,12 @@
/*
* Copyright (c) Microsoft Corporation.
* Licensed under the MIT License.
*/
package io.dapr.actors.runtime;
/**
* TODO
*/
public interface Actor {
}

View File

@ -0,0 +1,20 @@
/*
* Copyright (c) Microsoft Corporation.
* Licensed under the MIT License.
*/
package io.dapr.actors.runtime;
import java.lang.annotation.*;
/**
* Annotation to override default behavior of Actor class.
*/
@Documented
@Target(ElementType.TYPE_USE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ActorType {
String Name();
}

View File

@ -0,0 +1,181 @@
/*
* The MIT License
*
* Copyright 2019 Swen Schisler <swen.schisler@fourtytwosoft.io>.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package io.dapr.actors.runtime;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
/**
* Contains the information about the class implementing an actor.
*
* @author Swen Schisler <swen.schisler@fourtytwosoft.io>
* @author Artur Souza <artur.barbalho@microsoft.com>
*/
public final class ActorTypeInformation {
/**
* Actor type's name.
*/
private final String name;
/**
* Actor's implementation class.
*/
private final Class implementationClass;
/**
* Actor's immediate interfaces.
*/
private final Collection<Class> interfaces;
/**
* Whether Actor type is abstract.
*/
private final boolean abstractClass;
/**
* Whether Actor type is remindable.
*/
private final boolean remindable;
/**
* Instantiates a new {@link ActorTypeInformation}
* @param name Actor type's name.
* @param implementationClass Actor's implementation class.
* @param interfaces Actor's immediate interfaces.
* @param abstractClass Whether Actor type is abstract.
* @param remindable Whether Actor type is remindable.
*/
private ActorTypeInformation(String name,
Class implementationClass,
Collection<Class> interfaces,
boolean abstractClass,
boolean remindable) {
this.name = name;
this.implementationClass = implementationClass;
this.interfaces = interfaces;
this.abstractClass = abstractClass;
this.remindable = remindable;
}
/**
* Returns the name of this ActorType.
* @return ActorType's name.
*/
public String getName() {
return this.name;
}
/**
* Gets the type of the class implementing the actor.
*
* @return The {@link Class} of implementing the actor.
*/
public Class getImplementationClass() {
return this.implementationClass;
}
/**
* Gets the actor interfaces which derive from {@link Actor} and implemented by actor class.
*
* @return Collection of actor interfaces.
*/
public Collection<Class> getInterfaces() {
return Collections.unmodifiableCollection(this.interfaces);
}
/**
* Gets a value indicating whether the class implementing actor is abstract.
*
* @return true if the class implementing actor is abstract, otherwise false.
*/
public boolean isAbstractClass() {
return this.abstractClass;
}
/**
* Gets a value indicating whether the actor class implements {@link Remindable}.
*
* @return true if the actor class implements {@link Remindable}.
*/
public boolean isRemindable() {
return this.remindable;
}
/**
* Creates the {@link ActorTypeInformation} from given Class.
*
* @param actorClass The type of class implementing the actor to create ActorTypeInformation for.
* @return ActorTypeInformation if successfully created for actorType or null.
*/
public static ActorTypeInformation tryCreate(Class actorClass) {
try {
return create(actorClass);
} catch (IllegalArgumentException e) {
return null;
}
}
/**
* Creates an {@link #ActorTypeInformation} from actorType.
*
* @param actorClass The class implementing the actor to create ActorTypeInformation for.
* @return {@link #ActorTypeInformation} created from actorType.
*/
public static ActorTypeInformation create(Class actorClass) {
if (!ActorTypeUtilities.isActor(actorClass)) {
throw new IllegalArgumentException(
String.format(
"The type '%s' is not an Actor. An actor type must derive from '%s'.",
actorClass == null ? "" : actorClass.getCanonicalName(),
Actor.class.getCanonicalName()));
}
// get all actor interfaces
Class<?>[] actorInterfaces = actorClass.getInterfaces();
boolean isAbstract = Modifier.isAbstract(actorClass.getModifiers());
// ensure that the if the actor type is not abstract it implements at least one actor interface
if ((actorInterfaces.length == 0) && !isAbstract) {
throw new IllegalArgumentException(
String.format(
"The actor type '%s' does not implement any actor interfaces or one of the " +
"interfaces implemented is not an actor interface. " +
"All interfaces(including its parent interface) implemented by actor type must " +
"be actor interface. An actor interface is the one that ultimately derives " +
"from '%s' type.",
actorClass == null ? "" : actorClass.getCanonicalName(),
Actor.class.getCanonicalName()));
}
boolean isRemindable = ActorTypeUtilities.isRemindableActor(actorClass);
ActorType actorTypeAnnotation = (ActorType) actorClass.getAnnotation(ActorType.class);
String typeName = actorTypeAnnotation != null ? actorTypeAnnotation.Name() : actorClass.getSimpleName();
return new ActorTypeInformation(typeName, actorClass, Arrays.asList(actorInterfaces), isAbstract, isRemindable);
}
}

View File

@ -0,0 +1,87 @@
/*
* Copyright (c) Microsoft Corporation.
* Licensed under the MIT License.
*/
package io.dapr.actors.runtime;
import java.util.Arrays;
/**
* Utility class to extract information on Actor type.
*/
final class ActorTypeUtilities {
/**
* Gets all interfaces that extend Actor.
* @param clazz Actor class.
* @return Array of Actor interfaces.
*/
public static Class[] getActorInterfaces(Class clazz) {
if (clazz == null) {
return new Class[0];
}
return Arrays.stream(clazz.getInterfaces())
.filter(t -> Actor.class.isAssignableFrom(t))
.filter(t -> getNonActorParentClass(t) == null)
.toArray(Class[]::new);
}
/**
* Determines if given class is an Actor interface.
* @param clazz Actor interface candidate.
* @return Whether this is an Actor interface.
*/
public static boolean isActorInterface(Class clazz) {
return (clazz != null) && clazz.isInterface() && (getNonActorParentClass(clazz) == null);
}
/**
* Determines whether this is an Actor class.
* @param clazz Actor class candidate.
* @return Whether this is an Actor class.
*/
public static boolean isActor(Class clazz) {
if (clazz == null) {
return false;
}
return AbstractActor.class.isAssignableFrom(clazz);
}
/**
* Determines whether this is an remindable Actor.
* @param clazz Actor class.
* @return Whether this is an remindable Actor.
*/
public static boolean isRemindableActor(Class clazz) {
return (clazz != null) && isActor(clazz) && (Arrays.stream(clazz.getInterfaces()).filter(t -> t.equals(Remindable.class)).count() > 0);
}
/**
* Returns the parent class if it is not the {@link AbstractActor} parent class.
* @param clazz Actor class.
* @return Parent class or null if it is {@link AbstractActor}.
*/
public static Class getNonActorParentClass(Class clazz) {
if (clazz == null) {
return null;
}
Class[] items = Arrays.stream(clazz.getInterfaces()).filter(t -> !t.equals(Actor.class)).toArray(Class[]::new);
if (items.length == 0) {
return clazz;
}
for (Class c : items) {
Class nonActorParent = getNonActorParentClass(c);
if (nonActorParent != null) {
return nonActorParent;
}
}
return null;
}
}

View File

@ -0,0 +1,12 @@
/*
* Copyright (c) Microsoft Corporation.
* Licensed under the MIT License.
*/
package io.dapr.actors.runtime;
/**
* TODO
*/
public interface Remindable {
}

View File

@ -0,0 +1,94 @@
/*
* Copyright (c) Microsoft Corporation.
* Licensed under the MIT License.
*/
package io.dapr.actors.runtime;
import org.junit.Assert;
import org.junit.Test;
/**
* Unit tests for ActorTypeInformation.
*/
public class ActorTypeInformationTest {
/**
* Actor interfaced used in this test only.
*/
private interface MyActor extends Actor {
}
/**
* Checks information for a non-remindable actor.
*/
@Test
public void notRemindable() {
class A extends AbstractActor implements MyActor {
}
ActorTypeInformation info = ActorTypeInformation.create(A.class);
Assert.assertNotNull(info);
Assert.assertEquals("A", info.getName());
Assert.assertEquals(A.class, info.getImplementationClass());
Assert.assertFalse(info.isAbstractClass());
Assert.assertFalse(info.isRemindable());
Assert.assertEquals(1, info.getInterfaces().size());
Assert.assertTrue(info.getInterfaces().contains(MyActor.class));
}
/**
* Checks information for a remindable actor.
*/
@Test
public void remindable() {
class A extends AbstractActor implements MyActor, Remindable {
}
ActorTypeInformation info = ActorTypeInformation.create(A.class);
Assert.assertNotNull(info);
Assert.assertEquals("A", info.getName());
Assert.assertEquals(A.class, info.getImplementationClass());
Assert.assertFalse(info.isAbstractClass());
Assert.assertTrue(info.isRemindable());
Assert.assertEquals(2, info.getInterfaces().size());
Assert.assertTrue(info.getInterfaces().contains(Remindable.class));
Assert.assertTrue(info.getInterfaces().contains(MyActor.class));
}
/**
* Checks information for an actor renamed via annotation.
*/
@Test
public void renamedWithAnnotation() {
@ActorType(Name = "B")
class A extends AbstractActor implements MyActor {
}
ActorTypeInformation info = ActorTypeInformation.create(A.class);
Assert.assertNotNull(info);
Assert.assertEquals("B", info.getName());
Assert.assertEquals(A.class, info.getImplementationClass());
Assert.assertFalse(info.isAbstractClass());
Assert.assertFalse(info.isRemindable());
Assert.assertEquals(1, info.getInterfaces().size());
Assert.assertTrue(info.getInterfaces().contains(MyActor.class));
}
/**
* Checks information for an actor is invalid due to an non-actor parent.
*/
@Test
public void nonActorParentClass() {
abstract class MyAbstractClass extends AbstractActor implements MyActor {
}
class A extends MyAbstractClass {
}
ActorTypeInformation info = ActorTypeInformation.tryCreate(A.class);
Assert.assertNull(info);
}
}