View Javadoc
1   /*
2    * *************************************************************************************************************************************************************
3    *
4    * TheseFoolishThings: Miscellaneous utilities
5    * http://tidalwave.it/projects/thesefoolishthings
6    *
7    * Copyright (C) 2009 - 2024 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;
27  
28  import javax.annotation.Nonnegative;
29  import javax.annotation.Nonnull;
30  import javax.inject.Provider;
31  import java.util.Optional;
32  import java.util.Timer;
33  import java.util.TimerTask;
34  import java.util.concurrent.TimeUnit;
35  import java.io.Serializable;
36  import it.tidalwave.actor.impl.DefaultCollaboration;
37  import it.tidalwave.actor.impl.Locator;
38  import it.tidalwave.actor.spi.CollaborationAwareMessageBus;
39  import it.tidalwave.util.As;
40  import lombok.EqualsAndHashCode;
41  import lombok.experimental.Delegate;
42  import lombok.extern.slf4j.Slf4j;
43  import static it.tidalwave.actor.MessageDecorator._MessageDecorator_;
44  
45  /***************************************************************************************************************************************************************
46   *
47   * A support class for implementing messages.
48   *
49   * @stereotype Message
50   *
51   * @author  Fabrizio Giudici
52   *
53   **************************************************************************************************************************************************************/
54  @Slf4j @EqualsAndHashCode(of = "collaboration")
55  public abstract class MessageSupport implements Collaboration.Provider, As, Serializable
56    {
57      // TODO: @Inject
58      private final Provider<CollaborationAwareMessageBus> messageBus =
59              Locator.createProviderFor(CollaborationAwareMessageBus.class);
60  
61      @Nonnull
62      protected final DefaultCollaboration collaboration;
63  
64      private final MessageDecorator sameMessageDecorator = new MessageDecorator.Same<>(this);
65  
66      interface Exclusions
67        {
68          public <T> T maybeAs (Class<? extends T> type);
69        }
70  
71      @Delegate(excludes = Exclusions.class)
72      private final As as = As.forObject(this);
73  
74      /***********************************************************************************************************************************************************
75       *
76       **********************************************************************************************************************************************************/
77      protected MessageSupport()
78        {
79          this.collaboration = DefaultCollaboration.getOrCreateCollaboration(this);
80        }
81  
82      /***********************************************************************************************************************************************************
83       * @param   collaboration   the collaboration
84       **********************************************************************************************************************************************************/
85      protected MessageSupport (@Nonnull final Collaboration collaboration)
86        {
87          this.collaboration = (DefaultCollaboration)collaboration;
88        }
89  
90      /***********************************************************************************************************************************************************
91       * Returns the {@link Collaboration} that this message is part of.
92       *
93       * @return  the {@code Collaboration}
94       **********************************************************************************************************************************************************/
95      @Override @Nonnull
96      public Collaboration getCollaboration()
97        {
98          return collaboration;
99        }
100 
101     /***********************************************************************************************************************************************************
102      * Sends this message, eventually performing a replacement (see {@link MessageDecorator} for further info).
103      *
104      * @return  the {@code Collaboration} that this message is part of
105      **********************************************************************************************************************************************************/
106     @Nonnull @SuppressWarnings("UnusedReturnValue")
107     public Collaboration send()
108       {
109         log.debug("send() - {}", this);
110         return findDecoratedMessage().sendDirectly();
111       }
112 
113     /***********************************************************************************************************************************************************
114      * Sends this message directly, not performing any replacement (see {@link MessageDecorator} for further info).
115      *
116      * @return  the {@code Collaboration} that this message is part of
117      **********************************************************************************************************************************************************/
118     @Nonnull
119     public Collaboration sendDirectly()
120       {
121         log.debug("sendDirectly() - {}", this);
122         collaboration.registerDeliveringMessage(this);
123         messageBus.get().publish(this);
124         return collaboration;
125       }
126 
127     /***********************************************************************************************************************************************************
128      * Sends this message after a delay, eventually performing a replacement (see {@link MessageDecorator} for
129      * further info).
130      *
131      * @param   delay     the delay
132      * @param   timeUnit  the {@link TimeUnit} for the delay
133      * @return            the {@code Collaboration} that this message is part of
134      **********************************************************************************************************************************************************/
135     @Nonnull
136     public Collaboration sendLater (@Nonnegative final int delay, @Nonnull final TimeUnit timeUnit)
137       {
138         log.debug("sendLater({}, {}) - {}", delay, timeUnit, this);
139         final var message = findDecoratedMessage();
140         collaboration.registerDeliveringMessage(message);
141 
142         new Timer().schedule(new TimerTask()
143           {
144             @Override
145             public void run()
146               {
147                 messageBus.get().publish(message);
148               }
149           }, TimeUnit.MILLISECONDS.convert(delay, timeUnit));
150 
151         return collaboration;
152       }
153 
154     /***********************************************************************************************************************************************************
155      * {@inheritDoc}
156      **********************************************************************************************************************************************************/
157     @Override @Nonnull
158     public <T> Optional<T> maybeAs (@Nonnull final Class<? extends T> type)
159       {
160         final Optional<T> t = as.maybeAs(type);
161 
162         return t.isPresent()
163                ? t
164                : type.equals(MessageDecorator.class) ? Optional.of(type.cast(sameMessageDecorator)) : Optional.empty();
165       }
166 
167     /***********************************************************************************************************************************************************
168      *
169      **********************************************************************************************************************************************************/
170     @Nonnull
171     private MessageSupport findDecoratedMessage()
172       {
173         final var decoratedMessage = this.as(_MessageDecorator_).getDecoratedMessage();
174         return (decoratedMessage == this) ? this : decoratedMessage.findDecoratedMessage();
175 //        MessageSupport decoratedMessage = this.as(_MessageDecorator_).getDecoratedMessage();
176 //
177 //        if (decoratedMessage != this)
178 //          {
179 //            log.info("MESSAGE HAS BEEN DECORATED: {} -> {}", this, decoratedMessage);
180 //            decoratedMessage = decoratedMessage.findDecoratedMessage();
181 //          }
182 //
183 //        return decoratedMessage;
184       }
185  }