ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/i-scream/experimental/server/Logger/FileLog.java
Revision: 1.1
Committed: Mon Nov 6 21:28:53 2000 UTC (24 years, 1 month ago) by tdb
Branch: MAIN
CVS Tags: PROJECT_COMPLETION, HEAD
Log Message:
File Logging implementation of the logging mechanism interface.

File Contents

# User Rev Content
1 tdb 1.1 import java.util.Date;
2     import java.text.DateFormat;
3     import java.util.Locale;
4     import java.io.BufferedWriter;
5     import java.io.File;
6     import java.io.FileWriter;
7     import java.io.IOException;
8    
9     /**
10     * FileLog <br> <br>
11     *
12     * The FileLog class is an implementation of the Log interface, and as the
13     * name suggest writes all output to a file. The filename is specified
14     * during construction, and there is no default for a no-arg constructor.
15     * Various checks are carried out to check that writing will be possible and
16     * an IOException is thrown if for some reason it fails. If the file exists
17     * then the write() method will append to it. <br> <br>
18     *
19     * Revision History: <br>
20     * 1.8 - Added a fix to the write() method to ensure log is synchronised correctly [28/04/00] <br>
21     * 1.7 - Added suspend() and resume() methods [26/04/00] <br>
22     * 1.6 - Added open() method so that a new log file can be opened after a close() [26/04/00] <br>
23     * 1.5 - Made sure writing cannot be attempted when logfile not open [26/04/00] <br>
24     * 1.4 - Method of clearing the logfile improved [26/04/00] <br>
25     * 1.3 - Improved javadoc comments and tidied up [26/04/00] <br>
26     * 1.2 - Used the .newLine() method to write a new line, rather than \n [14/04/00] <br>
27     * 1.1 - Used an interface to create seperate File & Screen logs [15/03/00] <br>
28     * 1.0 - Original File-only version <br>
29     *
30     * @author T.D.Bishop [tdb1@ukc.ac.uk]
31     * @version 1.8, 28/04/2000
32     */
33     public class FileLog implements Log {
34    
35     /**
36     * This attribute is used by the Server class to print out versions
37     * of all class files that form the server on startup.
38     */
39     public static String version = "1.8, 28/04/2000";
40    
41     /**
42     * The constructor checks that writing will be ok, then sets up the
43     * writer. It makes use of the fileCheck() method found later on to
44     * ensure writing will be ok, and throws an IOException if there is
45     * a problem. Errors at this stage cannot be logged because the
46     * logging mechanism has not be setup completely, so any problems
47     * are printed to the screen in the same format used for logging.
48     *
49     * @param filename The filename to which logging should be written.
50     * @throws IOException if there is a problem with the file check.
51     */
52     public FileLog(String filename) throws IOException{
53     this.filename = filename;
54     // Perform file check to make sure writing is ok
55     if(!fileCheck()){
56     // Have to system.out.println errors because logging mechanism failed !
57     System.out.println(formatLogLine(this, "File check failed, construction terminated"));
58     throw new IOException("File check failed, unable to create FileLog");
59     }
60     try{
61     // Setup the writer
62     writer = new BufferedWriter(new FileWriter(filename, true));
63     // File is now open
64     open = true;
65     }
66     catch(IOException e){
67     System.out.println(formatLogLine(this, "Attemp to setup writer failed: "+e.getMessage()));
68     }
69     // Put an initial line into the log
70     write(this, "Logging initialised");
71     }
72    
73     /**
74     * The write() method writes a line of log to the file, prepending
75     * it with some information about where to line came from and the
76     * date. This formatting is handled by the formatLogLine() method.
77     * There is some synchronisation here due to problems with the logfiles
78     * having multiple lines written on the same line, followed by dotted
79     * newlines in the wrong places. This is due to this method being
80     * called by multiple threads.
81     *
82     * @param source A reference to the source of the message.
83     * @param input The message to be logged.
84     */
85     public void write(Object source, String input){
86     // Check to make sure file is open
87     if(open){
88     // Produce a nicely formatted line for the logfile
89     String line = formatLogLine(source, input);
90     try{
91     // We have to synchronize here due to problems with two write()'s being called before a newLine()
92     synchronized(writer){
93     // Attempt to write the line
94     writer.write(line);
95     // Best to use newLine() as it will use the correct platform encoding
96     writer.newLine();
97     }
98     // Make sure the line is written immeidiately
99     writer.flush();
100     }
101     catch(IOException e){
102     // We'd best log the error
103     System.out.println(formatLogLine(this, "Writing to logfile failed: "+e.getMessage()));
104     // As it's an IOException we should suspend logging
105     open = false;
106     }
107     }
108     else{
109     // If file is not open we should print this to the screen
110     System.out.println(formatLogLine(this, "Write failed: file not open"));
111     }
112     }
113    
114     /**
115     * This method generates a nicely formatted line for the logfile,
116     * including the date/time and the source of the message. The date
117     * and time are formatted using the DateFormat class, and the source
118     * class is formatted using the toString() method found in every
119     * source file for the server. This is then prepended to the message
120     * and returned.
121     *
122     * @param source A reference to the source of the message.
123     * @param input The message to be logged.
124     * @return The string to be written to the log.
125     */
126     private String formatLogLine(Object source, String input){
127     // Construct the date string using the DateFormat class
128     String date = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.MEDIUM).format(new Date());
129     // Return the date, the toString() of the source and the input
130     return "[" + date + "] " + source.toString() + ": " + input;
131     }
132    
133     /**
134     * The toString() method returns the name of the Class for formatting
135     * when it writes to the logfile. <br>
136     *
137     * @return The formatted string identifying this Class.
138     */
139     public String toString(){
140     return this.getClass().getName();
141     }
142    
143     /**
144     * The clear() method erases the contents of the logfile. It does
145     * this in a very clumsy way - by closing the writer and opening a
146     * new one which is set not to append. This results in the logfile
147     * being overwritten, which effectively erases it.
148     */
149     public void clear(){
150     // Check file is open
151     if(open){
152     write(this, "Attempting to clear logfile");
153     try{
154     // Close appending writer
155     open = false;
156     writer.close();
157     // Open a non-appending writer
158     writer = new BufferedWriter(new FileWriter(filename));
159     open = true;
160     }
161     catch(IOException e){
162     // We'd best log the error
163     write(this, "Attempt to clear logfile failed: "+e.getMessage());
164     // As it's an IOException we should suspend logging
165     open = false;
166     }
167     }
168     else{
169     // If file is not open we should print this to the screen
170     System.out.println(formatLogLine(this, "Clearing failed: file not open"));
171     }
172     }
173    
174     /**
175     * The close() method closes the logfile and no more data can be written.
176     * If writing is attempted after close() has been called an error will be
177     * generated. The open() method can be used to reopen or open a new file.
178     */
179     public void close(){
180     // Check to see if a file is open
181     if(open){
182     write(this, "Attempting to terminate logging");
183     try{
184     open = false;
185     writer.close();
186     }
187     catch(IOException e){
188     write(this, "Attempt to close logfile failed: "+e.getMessage());
189     // As it's an IOException we should suspend logging
190     open = false;
191     }
192     }
193     else{
194     // If file is not open we should print this to the screen
195     System.out.println(formatLogLine(this, "Close failed: file not open"));
196     }
197     }
198    
199     /**
200     * The open() method opens a new file after the close() method has
201     * been called on the FileLog. This enables the user to easily change
202     * the file to which the log is being written. An error is generated
203     * if a file is already open.
204     *
205     * @param filename The new file to write to.
206     * @throws IOException if the file cannot be written to.
207     */
208     public void open(String filename) throws IOException{
209     // Check to see if a file is open
210     if(open){
211     // If file is already open then we should log that this went wrong
212     write(this, "Open failed: a file is already open");
213     }
214     else{
215     this.filename = filename;
216     if(!fileCheck()) {
217     // Have to system.out.println errors because logging mechanism failed !
218     System.out.println(formatLogLine(this, "File check failed, construction terminated"));
219     throw new IOException("File check failed, unable to create FileLog");
220     }
221     try{
222     // Setup the writer
223     writer = new BufferedWriter(new FileWriter(filename, true));
224     // File is now open
225     open = true;
226     }
227     catch(IOException e){
228     System.out.println(formatLogLine(this, "Attempt to open writer failed: "+e.getMessage()));
229     }
230     }
231     }
232    
233     /**
234     * The suspend() method halts writing to the file by setting the open
235     * boolean to false, and thus blocking the write() method. A line is
236     * written to the logfile beforehand to note that writing has been
237     * suspended.
238     */
239     public void suspend(){
240     // Check to see if a file is open
241     if(open){
242     // Make a note of the fact that writing has been suspended
243     write(this, "Writing suspended");
244     // Make sure writing not permitted
245     open=false;
246     }
247     else{
248     // If file is not open we should print this to the screen
249     System.out.println(formatLogLine(this, "Suspend failed: file not open"));
250     }
251     }
252    
253     /**
254     * The resume() method resumes writing to the file by setting the open
255     * boolean to true. A line is written to the logfile to signify this
256     * event occuring.
257     */
258     public void resume(){
259     // Check to see if a file is open
260     if(open){
261     // If file is open we should print this to the screen
262     System.out.println(formatLogLine(this, "Resume failed: file open"));
263     }
264     else{
265     // Permit writing again
266     open=true;
267     // Make a note of the fact that writing has been resumed
268     write(this, "Writing resumed");
269     }
270     }
271    
272     /**
273     * The fileCheck() method is used to ensure that writing is ok. It
274     * performs so basic checks to make sure that if the file exists
275     * it can be written to, and if not a new file is created. A boolean
276     * result is returned to signify success or failure.
277     *
278     * @return A boolean value signify whether the test was sucessful.
279     */
280     private boolean fileCheck(){
281     boolean fileOK = false;
282     try{
283     File file = new File(filename);
284    
285     // File already exists
286     if(file.exists()) {
287     /* Only want success if it is a file (not a directory)
288     * and can be written to.
289     */
290     if(file.isFile() && file.canWrite()) {
291     fileOK = true;
292     }
293     }
294     // Create a new file and suceed
295     else{
296     file.createNewFile();
297     fileOK = true;
298     }
299     }
300     catch(IOException e){
301     System.out.println(formatLogLine(this, "File check failed: "+e.getMessage()));
302     }
303    
304     return fileOK;
305     }
306    
307     /**
308     * The filename of the currently open, or last open, file.
309     */
310     private String filename;
311    
312     /**
313     * A reference to the writer being used.
314     */
315     private BufferedWriter writer;
316    
317     /**
318     * A boolean signifying whether a file is open or not.
319     */
320     private boolean open = false;
321    
322     }