View Javadoc
1   /*
2    * This file is part of Waarp Project (named also Waarp or GG).
3    *
4    *  Copyright (c) 2019, Waarp SAS, and individual contributors by the @author
5    *  tags. See the COPYRIGHT.txt in the distribution for a full listing of
6    * individual contributors.
7    *
8    *  All Waarp Project is free software: you can redistribute it and/or
9    * modify it under the terms of the GNU General Public License as published by
10   * the Free Software Foundation, either version 3 of the License, or (at your
11   * option) any later version.
12   *
13   * Waarp is distributed in the hope that it will be useful, but WITHOUT ANY
14   * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
15   * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
16   *
17   *  You should have received a copy of the GNU General Public License along with
18   * Waarp . If not, see <http://www.gnu.org/licenses/>.
19   */
20  package org.waarp.openr66.protocol.networkhandler;
21  
22  import io.netty.channel.Channel;
23  import org.waarp.common.future.WaarpLock;
24  import org.waarp.common.logging.SysErrLogger;
25  import org.waarp.common.logging.WaarpLogger;
26  import org.waarp.common.logging.WaarpLoggerFactory;
27  import org.waarp.common.lru.ConcurrentUtility;
28  import org.waarp.openr66.context.ErrorCode;
29  import org.waarp.openr66.context.R66Result;
30  import org.waarp.openr66.context.R66Session;
31  import org.waarp.openr66.context.task.exception.OpenR66RunnerErrorException;
32  import org.waarp.openr66.database.data.DbTaskRunner;
33  import org.waarp.openr66.database.data.DbTaskRunner.TASKSTEP;
34  import org.waarp.openr66.protocol.configuration.Configuration;
35  import org.waarp.openr66.protocol.exception.OpenR66ProtocolRemoteShutdownException;
36  import org.waarp.openr66.protocol.exception.OpenR66ProtocolSystemException;
37  import org.waarp.openr66.protocol.localhandler.LocalChannelReference;
38  
39  import java.net.InetSocketAddress;
40  import java.net.SocketAddress;
41  import java.util.ArrayList;
42  import java.util.Set;
43  import java.util.concurrent.TimeUnit;
44  
45  import static org.waarp.openr66.protocol.configuration.Configuration.*;
46  
47  /**
48   * NetworkChannelReference object to keep Network channel open while some local
49   * channels are attached to it.
50   */
51  public class NetworkChannelReference {
52    /**
53     * Internal Logger
54     */
55    private static final WaarpLogger logger =
56        WaarpLoggerFactory.getLogger(NetworkChannelReference.class);
57  
58    private static final LocalChannelReference[] LCR_0_LENGTH =
59        new LocalChannelReference[0];
60  
61    /**
62     * Does this Network Channel is in shutdown
63     */
64    protected boolean isShuttingDown;
65    /**
66     * Associated LocalChannelReference
67     */
68    private final Set<LocalChannelReference> localChannelReferences =
69        ConcurrentUtility.newConcurrentSet();
70    /**
71     * Network Channel
72     */
73    protected final Channel channel;
74    /**
75     * Remote network address (when valid)
76     */
77    protected final SocketAddress networkAddress;
78    /**
79     * Remote IP address
80     */
81    private final String hostAddress;
82    /**
83     * Remote Host Id
84     */
85    private String hostId;
86    /**
87     * ClientNetworkChannels object that contains this NetworkChannelReference
88     */
89    protected ClientNetworkChannels clientNetworkChannels;
90    /**
91     * Associated lock
92     */
93    protected final WaarpLock lock;
94    /**
95     * Last Time in ms this channel was used by a LocalChannel
96     */
97    private long lastTimeUsed = System.currentTimeMillis();
98    /**
99     * Is this channel multiplexed using Ssl
100    */
101   private final boolean isSSL;
102 
103   public NetworkChannelReference(final Channel networkChannel,
104                                  final WaarpLock lock, final boolean isSSL) {
105     channel = networkChannel;
106     networkAddress = channel.remoteAddress();
107     hostAddress =
108         ((InetSocketAddress) networkAddress).getAddress().getHostAddress();
109     this.lock = lock;
110     this.isSSL = isSSL;
111   }
112 
113   public NetworkChannelReference(final SocketAddress address,
114                                  final WaarpLock lock, final boolean isSSL) {
115     channel = null;
116     networkAddress = address;
117     hostAddress =
118         ((InetSocketAddress) networkAddress).getAddress().getHostAddress();
119     this.lock = lock;
120     this.isSSL = isSSL;
121   }
122 
123   public final boolean isSSL() {
124     return isSSL;
125   }
126 
127   public final void add(final LocalChannelReference localChannel)
128       throws OpenR66ProtocolRemoteShutdownException {
129     // lock is of no use since caller is itself in locked situation for the very same lock
130     if (isShuttingDown) {
131       throw new OpenR66ProtocolRemoteShutdownException(
132           "Current NetworkChannelReference is closed");
133     }
134     use();
135     localChannelReferences.add(localChannel);
136   }
137 
138   /**
139    * To set the last time used
140    */
141   public final void use() {
142     if (!isShuttingDown) {
143       lastTimeUsed = System.currentTimeMillis();
144     }
145   }
146 
147   /**
148    * To set the last time used when correct
149    *
150    * @return True if last time used is set
151    */
152   public final boolean useIfUsed() {
153     if (!isShuttingDown && !localChannelReferences.isEmpty()) {
154       lastTimeUsed = System.currentTimeMillis();
155       return true;
156     }
157     return false;
158   }
159 
160   /**
161    * Remove one LocalChanelReference, closing it if necessary.
162    *
163    * @param localChannel
164    */
165   public final void closeAndRemove(final LocalChannelReference localChannel) {
166     if (!localChannel.getFutureRequest().isDone()) {
167       localChannel.close();
168     }
169     remove(localChannel);
170   }
171 
172   /**
173    * Remove one LocalChanelReference
174    *
175    * @param localChannel
176    */
177   public final void remove(final LocalChannelReference localChannel) {
178     localChannelReferences.remove(localChannel);
179     // Do not since it prevents shutdown: lastTimeUsed = System.currentTimeMillis()
180   }
181 
182   /**
183    * Shutdown All Local Channels associated with this NCR
184    */
185   public final void shutdownAllLocalChannels() {
186     lock.lock(Configuration.WAITFORNETOP, TimeUnit.MILLISECONDS);
187     try {
188       logger.info("Will shutdown all local channels");
189       isShuttingDown = true;
190       final LocalChannelReference[] localChannelReferenceArray =
191           localChannelReferences.toArray(LCR_0_LENGTH);
192       final ArrayList<LocalChannelReference> toCloseLater =
193           new ArrayList<LocalChannelReference>();
194       for (final LocalChannelReference localChannelReference : localChannelReferenceArray) {
195         localChannelReference.getFutureRequest().awaitOrInterruptible(
196             Configuration.configuration.getTimeoutCon() / 3);
197         if (!localChannelReference.getFutureRequest().isDone()) {
198           localChannelReference.getFutureValidRequest().awaitOrInterruptible(
199               Configuration.configuration.getTimeoutCon() / 3);
200           if (localChannelReference.getFutureValidRequest().isDone() &&
201               localChannelReference.getFutureValidRequest().isFailed()) {
202             toCloseLater.add(localChannelReference);
203             continue;
204           } else {
205             final R66Result finalValue =
206                 new R66Result(localChannelReference.getSession(), true,
207                               ErrorCode.Shutdown, null);
208             if (localChannelReference.getSession() != null) {
209               try {
210                 localChannelReference.getSession()
211                                      .tryFinalizeRequest(finalValue);
212               } catch (final OpenR66RunnerErrorException ignored) {
213                 // nothing
214               } catch (final OpenR66ProtocolSystemException ignored) {
215                 // nothing
216               }
217             }
218           }
219         }
220         localChannelReference.close();
221       }
222       try {
223         Thread.sleep(Configuration.WAITFORNETOP);
224       } catch (final InterruptedException e) {//NOSONAR
225         SysErrLogger.FAKE_LOGGER.ignoreLog(e);
226       }
227       for (final LocalChannelReference localChannelReference : toCloseLater) {
228         localChannelReference.getFutureRequest().awaitOrInterruptible(
229             Configuration.configuration.getTimeoutCon() / 3);
230         localChannelReference.close();
231       }
232       toCloseLater.clear();
233     } finally {
234       lock.unlock();
235     }
236   }
237 
238   /**
239    * @param localChannelReference the localChannelReference to be closed
240    *
241    * @return True if the localChannelReference is the only one still active or there is no more LCR
242    */
243   public final boolean isLastLocalChannelActive(
244       final LocalChannelReference localChannelReference) {
245     final boolean someActive = isSomeLocalChannelsActive();
246     return
247         (someActive && localChannelReferences.contains(localChannelReference) &&
248          localChannelReferences.size() == 1) ||
249         localChannelReferences.isEmpty();
250   }
251 
252   /**
253    * @return -1 if not allowed, 0 if allowed, else time in ms before ready to recheck
254    */
255   public final long shutdownAllowed() {
256     lock.lock(Configuration.WAITFORNETOP, TimeUnit.MILLISECONDS);
257     try {
258       logger.debug("NC count: {}", this);
259       if (nbLocalChannels() <= 0) {
260         boolean reallyShutdownNetwork = true;
261         for (int i = 0; i < RETRYNB; i++) {
262           try {
263             Thread.sleep(RETRYINMS);
264           } catch (final InterruptedException e) { // NOSONAR
265             SysErrLogger.FAKE_LOGGER.ignoreLog(e);
266           }
267           if (nbLocalChannels() != 0) {
268             reallyShutdownNetwork = false;
269             break;
270           }
271         }
272         if (reallyShutdownNetwork) {
273           long time =
274               checkLastTime(Configuration.configuration.getTimeoutCon() * 2);
275           if (time > Configuration.RETRYINMS &&
276               Configuration.configuration.isTimerCloseReady()) {
277             logger.debug("NC reschedule at {} : {}", time, this);
278             // will re execute this request later on
279             time = (time / 10) * 10 + 100; // round to 10
280             return time;
281           }
282           logger.info("Closing NETWORK channel {}", this);
283           return 0;
284         } else {
285           use();
286           logger.debug("Ignore closing Network channel");
287           return -1;
288         }
289       }
290       return -1;
291     } finally {
292       lock.unlock();
293     }
294   }
295 
296   public final void lockNetwork() {
297     lock.lock(Configuration.WAITFORNETOP, TimeUnit.MILLISECONDS);
298   }
299 
300   public final void unlockNetwork() {
301     lock.unlock();
302   }
303 
304   public final int nbLocalChannels() {
305     return localChannelReferences.size();
306   }
307 
308   /**
309    * @return True if at least one LocalChannel is not yet finished (OK or Error)
310    */
311   public final boolean isSomeLocalChannelsActive() {
312     lock.lock(Configuration.WAITFORNETOP, TimeUnit.MILLISECONDS);
313     try {
314       for (final LocalChannelReference localChannelReference : localChannelReferences) {
315         final R66Session session = localChannelReference.getSession();
316         if (session != null) {
317           final DbTaskRunner runner = session.getRunner();
318           if (runner != null && !runner.isFinished() &&
319               runner.getGlobalStep() != TASKSTEP.NOTASK) {
320             return true;
321           }
322         }
323       }
324       return false;
325     } finally {
326       lock.unlock();
327     }
328   }
329 
330   @Override
331   public final String toString() {
332     return "NC: " + hostId + ':' + (channel != null && channel.isActive()) +
333            ' ' + networkAddress + " Count: " + localChannelReferences.size();
334   }
335 
336   @Override
337   public final boolean equals(final Object obj) {
338     if (obj instanceof NetworkChannelReference) {
339       final NetworkChannelReference obj2 = (NetworkChannelReference) obj;
340       if (obj2.channel == null || channel == null) {
341         return false;
342       }
343       return obj2.channel.id().compareTo(channel.id()) == 0;
344     }
345     return false;
346   }
347 
348   @Override
349   public final int hashCode() {
350     if (channel == null) {
351       return Integer.MIN_VALUE;
352     }
353     return channel.id().hashCode();
354   }
355 
356   /**
357    * @return the hashcode for the global remote networkaddress
358    */
359   public final int getSocketHashCode() {
360     return networkAddress.hashCode();
361   }
362 
363   /**
364    * Used for BlackList
365    *
366    * @return the hashcode for the address
367    */
368   public final int getAddressHashCode() {
369     return hostAddress.hashCode();
370   }
371 
372   /**
373    * Check if the last time used is ok with a delay applied to the current
374    * time
375    * (timeout)
376    *
377    * @param delay
378    *
379    * @return <= 0 if OK, else > 0 (should send a KeepAlive or wait that time
380    *     in ms)
381    */
382   public final long checkLastTime(final long delay) {
383     return lastTimeUsed + delay - System.currentTimeMillis();
384   }
385 
386   /**
387    * @return the isShuttingDown
388    */
389   public final boolean isShuttingDown() {
390     return isShuttingDown;
391   }
392 
393   /**
394    * @return the channel
395    */
396   public final Channel channel() {
397     return channel;
398   }
399 
400   /**
401    * @return the hostId
402    */
403   public final String getHostId() {
404     return hostId;
405   }
406 
407   /**
408    * @param hostId the hostId to set
409    */
410   public final void setHostId(final String hostId) {
411     this.hostId = hostId;
412   }
413 
414   /**
415    * @return the lastTimeUsed
416    */
417   public final long getLastTimeUsed() {
418     return lastTimeUsed;
419   }
420 
421 }