View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
4   //  ------------------------------------------------------------------------
5   //  All rights reserved. This program and the accompanying materials
6   //  are made available under the terms of the Eclipse Public License v1.0
7   //  and Apache License v2.0 which accompanies this distribution.
8   //
9   //      The Eclipse Public License is available at
10  //      http://www.eclipse.org/legal/epl-v10.html
11  //
12  //      The Apache License v2.0 is available at
13  //      http://www.opensource.org/licenses/apache2.0.php
14  //
15  //  You may elect to redistribute this code under either of these licenses.
16  //  ========================================================================
17  //
18  
19  package org.eclipse.jetty.util.thread;
20  
21  import java.util.List;
22  import java.util.concurrent.CopyOnWriteArrayList;
23  import java.util.concurrent.TimeUnit;
24  import java.util.concurrent.atomic.AtomicReference;
25  
26  import org.eclipse.jetty.util.component.AbstractLifeCycle;
27  import org.eclipse.jetty.util.log.Log;
28  import org.eclipse.jetty.util.log.Logger;
29  
30  /**
31   * <p>A utility class to perform periodic sweeping of resources.</p>
32   * <p>{@link Sweepable} resources may be added to or removed from a
33   * {@link Sweeper} and the resource implementation decides whether
34   * it should be swept or not.</p>
35   * <p>If a {@link Sweepable} resources is itself a container of
36   * other sweepable resources, it will forward the sweep operation
37   * to children resources, and so on recursively.</p>
38   * <p>Typical usage is to add {@link Sweeper} as a bean to an existing
39   * container:</p>
40   * <pre>
41   * Server server = new Server();
42   * server.addBean(new Sweeper(), true);
43   * server.start();
44   * </pre>
45   * Code that knows it has sweepable resources can then lookup the
46   * {@link Sweeper} and offer the sweepable resources to it:
47   * <pre>
48   * class MyComponent implements Sweeper.Sweepable
49   * {
50   *     private final long creation;
51   *     private volatile destroyed;
52   *
53   *     MyComponent(Server server)
54   *     {
55   *         this.creation = System.nanoTime();
56   *         Sweeper sweeper = server.getBean(Sweeper.class);
57   *         sweeper.offer(this);
58   *     }
59   *
60   *     void destroy()
61   *     {
62   *         destroyed = true;
63   *     }
64   *
65   *     &#64;Override
66   *     public boolean sweep()
67   *     {
68   *         return destroyed;
69   *     }
70   * }
71   * </pre>
72   */
73  public class Sweeper extends AbstractLifeCycle implements Runnable
74  {
75      private static final Logger LOG = Log.getLogger(Sweeper.class);
76  
77      private final AtomicReference<List<Sweepable>> items = new AtomicReference<>();
78      private final AtomicReference<Scheduler.Task> task = new AtomicReference<>();
79      private final Scheduler scheduler;
80      private final long period;
81  
82      public Sweeper(Scheduler scheduler, long period)
83      {
84          this.scheduler = scheduler;
85          this.period = period;
86      }
87  
88      @Override
89      protected void doStart() throws Exception
90      {
91          super.doStart();
92          items.set(new CopyOnWriteArrayList<Sweepable>());
93          activate();
94      }
95  
96      @Override
97      protected void doStop() throws Exception
98      {
99          deactivate();
100         items.set(null);
101         super.doStop();
102     }
103 
104     public int getSize()
105     {
106         List<Sweepable> refs = items.get();
107         return refs == null ? 0 : refs.size();
108     }
109 
110     public boolean offer(Sweepable sweepable)
111     {
112         List<Sweepable> refs = items.get();
113         if (refs == null)
114             return false;
115         refs.add(sweepable);
116         if (LOG.isDebugEnabled())
117             LOG.debug("Resource offered {}", sweepable);
118         return true;
119     }
120 
121     public boolean remove(Sweepable sweepable)
122     {
123         List<Sweepable> refs = items.get();
124         return refs != null && refs.remove(sweepable);
125     }
126 
127     @Override
128     public void run()
129     {
130         List<Sweepable> refs = items.get();
131         if (refs == null)
132             return;
133         for (Sweepable sweepable : refs)
134         {
135             try
136             {
137                 if (sweepable.sweep())
138                 {
139                     refs.remove(sweepable);
140                     if (LOG.isDebugEnabled())
141                         LOG.debug("Resource swept {}", sweepable);
142                 }
143             }
144             catch (Throwable x)
145             {
146                 LOG.info("Exception while sweeping " + sweepable, x);
147             }
148         }
149         activate();
150     }
151 
152     private void activate()
153     {
154         if (isRunning())
155         {
156             Scheduler.Task t = scheduler.schedule(this, period, TimeUnit.MILLISECONDS);
157             if (LOG.isDebugEnabled())
158                 LOG.debug("Scheduled in {} ms sweep task {}", period, t);
159             task.set(t);
160         }
161         else
162         {
163             if (LOG.isDebugEnabled())
164                 LOG.debug("Skipping sweep task scheduling");
165         }
166     }
167 
168     private void deactivate()
169     {
170         Scheduler.Task t = task.getAndSet(null);
171         if (t != null)
172         {
173             boolean cancelled = t.cancel();
174             if (LOG.isDebugEnabled())
175                 LOG.debug("Cancelled ({}) sweep task {}", cancelled, t);
176         }
177     }
178 
179     /**
180      * <p>A {@link Sweepable} resource implements this interface to
181      * signal to a {@link Sweeper} or to a parent container if it
182      * needs to be swept or not.</p>
183      * <p>Typical implementations will check their own internal state
184      * and return true or false from {@link #sweep()} to indicate
185      * whether they should be swept.</p>
186      */
187     public interface Sweepable
188     {
189         /**
190          * @return whether this resource should be swept
191          */
192         public boolean sweep();
193     }
194 }