View Javadoc

1   /**
2    * Logback: the reliable, generic, fast and flexible logging framework.
3    * Copyright (C) 1999-2011, QOS.ch. All rights reserved.
4    *
5    * This program and the accompanying materials are dual-licensed under
6    * either the terms of the Eclipse Public License v1.0 as published by
7    * the Eclipse Foundation
8    *
9    *   or (per the licensee's choosing)
10   *
11   * under the terms of the GNU Lesser General Public License version 2.1
12   * as published by the Free Software Foundation.
13   */
14  package ch.qos.logback.core.recovery;
15  
16  import java.io.IOException;
17  import java.io.OutputStream;
18  
19  import ch.qos.logback.core.Context;
20  import ch.qos.logback.core.status.ErrorStatus;
21  import ch.qos.logback.core.status.InfoStatus;
22  import ch.qos.logback.core.status.Status;
23  import ch.qos.logback.core.status.StatusManager;
24  
25  abstract public class ResilientOutputStreamBase extends OutputStream {
26  
27    final static int STATUS_COUNT_LIMIT = 2 * 4;
28  
29    private int noContextWarning = 0;
30    private int statusCount = 0;
31  
32    private Context context;
33    private RecoveryCoordinator recoveryCoordinator;
34  
35    protected OutputStream os;
36    protected boolean presumedClean = true;
37  
38    private boolean isPresumedInError() {
39      // existence of recoveryCoordinator indicates failed state
40      return (recoveryCoordinator != null && !presumedClean);
41    }
42  
43    public void write(byte b[], int off, int len) {
44      if (isPresumedInError()) {
45        if (!recoveryCoordinator.isTooSoon()) {
46          attemptRecovery();
47        }
48        return; // return regardless of the success of the recovery attempt
49      }
50  
51      try {
52        os.write(b, off, len);
53        postSuccessfulWrite();
54      } catch (IOException e) {
55        postIOFailure(e);
56      }
57    }
58  
59    @Override
60    public void write(int b) {
61      if (isPresumedInError()) {
62        if (!recoveryCoordinator.isTooSoon()) {
63          attemptRecovery();
64        }
65        return; // return regardless of the success of the recovery attempt
66      }
67      try {
68        os.write(b);
69        postSuccessfulWrite();
70      } catch (IOException e) {
71        postIOFailure(e);
72      }
73    }
74  
75    @Override
76    public void flush() {
77      if (os != null) {
78        try {
79          os.flush();
80          postSuccessfulWrite();
81        } catch (IOException e) {
82          postIOFailure(e);
83        }
84      }
85    }
86  
87    abstract String getDescription();
88  
89    abstract OutputStream openNewOutputStream() throws IOException;
90  
91    private void postSuccessfulWrite() {
92      if (recoveryCoordinator != null) {
93        recoveryCoordinator = null;
94        statusCount = 0;
95        addStatus(new InfoStatus("Recovered from IO failure on "
96            + getDescription(), this));
97      }
98    }
99  
100   void postIOFailure(IOException e) {
101     addStatusIfCountNotOverLimit(new ErrorStatus("IO failure while writing to "
102         + getDescription(), this, e));
103     presumedClean = false;
104     if (recoveryCoordinator == null) {
105       recoveryCoordinator = new RecoveryCoordinator();
106     }
107   }
108 
109   @Override
110   public void close() throws IOException {
111     if (os != null) {
112       os.close();
113     }
114   }
115 
116   void attemptRecovery() {
117     try {
118       close();
119     } catch (IOException e) {
120     }
121 
122     addStatusIfCountNotOverLimit(new InfoStatus(
123         "Attempting to recover from IO failure on " + getDescription(), this));
124 
125     // subsequent writes must always be in append mode
126     try {
127       os = openNewOutputStream();
128       presumedClean = true;
129     } catch (IOException e) {
130       addStatusIfCountNotOverLimit(new ErrorStatus("Failed to open "
131           + getDescription(), this, e));
132     }
133   }
134 
135   void addStatusIfCountNotOverLimit(Status s) {
136     ++statusCount;
137     if (statusCount < STATUS_COUNT_LIMIT) {
138       addStatus(s);
139     }
140 
141     if (statusCount == STATUS_COUNT_LIMIT) {
142       addStatus(s);
143       addStatus(new InfoStatus("Will supress future messages regarding "
144           + getDescription(), this));
145     }
146   }
147 
148   public void addStatus(Status status) {
149     if (context == null) {
150       if (noContextWarning++ == 0) {
151         System.out.println("LOGBACK: No context given for " + this);
152       }
153       return;
154     }
155     StatusManager sm = context.getStatusManager();
156     if (sm != null) {
157       sm.add(status);
158     }
159   }
160 
161   public Context getContext() {
162     return context;
163   }
164 
165   public void setContext(Context context) {
166     this.context = context;
167   }
168 }