1
2
3
4
5
6
7
8
9
10
11
12
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
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;
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;
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
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 }