1 /*
2 * *************************************************************************************************************************************************************
3 *
4 * TheseFoolishThings: Miscellaneous utilities
5 * http://tidalwave.it/projects/thesefoolishthings
6 *
7 * Copyright (C) 2009 - 2026 by Tidalwave s.a.s. (http://tidalwave.it)
8 *
9 * *************************************************************************************************************************************************************
10 *
11 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
13 *
14 * http://www.apache.org/licenses/LICENSE-2.0
15 *
16 * 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
17 * CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
18 *
19 * *************************************************************************************************************************************************************
20 *
21 * git clone https://bitbucket.org/tidalwave/thesefoolishthings-src
22 * git clone https://github.com/tidalwave-it/thesefoolishthings-src
23 *
24 * *************************************************************************************************************************************************************
25 */
26 package it.tidalwave.actor.spi;
27
28 import java.lang.reflect.InvocationTargetException;
29 // import javax.annotation.Nonnegative;
30 import jakarta.annotation.Nonnull;
31 import jakarta.annotation.Nullable;
32 import it.tidalwave.actor.annotation.Actor;
33 import it.tidalwave.actor.impl.CollaborationAwareMessageBusAdapter;
34 import it.tidalwave.actor.impl.ExecutorWithPriority;
35 import it.tidalwave.actor.impl.MBeansManager;
36 import it.tidalwave.actor.impl.PostConstructInvoker;
37 import it.tidalwave.actor.impl.PreDestroyInvoker;
38 import lombok.Getter;
39 import lombok.extern.slf4j.Slf4j;
40 import static it.tidalwave.messagebus.spi.ReflectionUtils.*;
41
42 /***************************************************************************************************************************************************************
43 *
44 * This class is used to activate and deactivate an actor.
45 *
46 * @author Fabrizio Giudici
47 *
48 **************************************************************************************************************************************************************/
49 @Slf4j
50 public class ActorActivator
51 {
52 @Nonnull
53 private final Class<?> actorClass;
54
55 /* @Nonnegative */ @Getter
56 private final int poolSize;
57
58 @Getter
59 private Object actorObject;
60
61 private ExecutorWithPriority executor;
62
63 private CollaborationAwareMessageBusAdapter messageBusAdapter;
64
65 private MBeansManager mBeansManager;
66
67 /***********************************************************************************************************************************************************
68 * Creates an instance for the given actor class.
69 *
70 * @param actorClass the actor class
71 * @return the instance
72 **********************************************************************************************************************************************************/
73 public static ActorActivator activatorFor (@Nonnull final Class<?> actorClass)
74 {
75 return new ActorActivator(actorClass, 1);
76 }
77
78 /***********************************************************************************************************************************************************
79 * Specifies the pool size for this activator.
80 *
81 * @param poolSize the pool size
82 * @return the activator
83 **********************************************************************************************************************************************************/
84 @Nonnull
85 public ActorActivator withPoolSize (/* @Nonnegative */ final int poolSize)
86 {
87 return new ActorActivator(actorClass, poolSize);
88 }
89
90 /***********************************************************************************************************************************************************
91 *
92 **********************************************************************************************************************************************************/
93 private ActorActivator (@Nonnull final Class<?> actorClass, /* @Nonnegative */ final int poolSize)
94 {
95 this.actorClass = actorClass;
96 this.poolSize = poolSize;
97 }
98
99 /***********************************************************************************************************************************************************
100 * Activates the managed actor.
101 **********************************************************************************************************************************************************/
102 public void initialize()
103 {
104 try
105 {
106 final var actor = validated(actorClass.getAnnotation(Actor.class));
107 actorObject = actorClass.getDeclaredConstructor().newInstance();
108 executor = new ExecutorWithPriority(poolSize, actorClass.getSimpleName(), actor.initialPriority());
109 mBeansManager = new MBeansManager(actorObject, poolSize);
110 messageBusAdapter = new CollaborationAwareMessageBusAdapter(actorObject, executor, mBeansManager.getStats());
111 }
112 catch (InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e)
113 {
114 throw new RuntimeException(e);
115 }
116
117 forEachMethodInTopDownHierarchy(actorObject, messageBusAdapter);
118 forEachMethodInTopDownHierarchy(actorObject, new PostConstructInvoker(actorObject));
119 mBeansManager.register();
120 }
121
122 /***********************************************************************************************************************************************************
123 * Deactivates the managed actor and releases resources.
124 **********************************************************************************************************************************************************/
125 public void dispose()
126 {
127 mBeansManager.unregister();
128 forEachMethodInBottomUpHierarchy(actorObject, new PreDestroyInvoker(actorObject));
129 messageBusAdapter.unsubscribe();
130 }
131
132 /***********************************************************************************************************************************************************
133 *
134 **********************************************************************************************************************************************************/
135 @Nonnull
136 private Actor validated (@Nullable final Actor actor)
137 {
138 //noinspection ConstantConditions
139 if (actor == null)
140 {
141 throw new IllegalArgumentException("Actor class must be annotated with @Actor: " + actorClass);
142 }
143
144 if (!actor.threadSafe() && (poolSize != 1))
145 {
146 throw new IllegalArgumentException("Actors that aren't thread safe can't have pool size > 1");
147 }
148
149 return actor;
150 }
151 }