first commit

This commit is contained in:
Boris
2024-01-15 20:14:10 +00:00
commit 8c81ee28b7
3106 changed files with 474415 additions and 0 deletions

View File

@@ -0,0 +1,14 @@
#BlueJ class context
comment0.params=
comment0.target=LogAnalyzer()
comment0.text=\n\ Create\ an\ object\ to\ analyze\ hourly\ web\ accesses.\n
comment1.params=
comment1.target=void\ analyzeHourlyData()
comment1.text=\n\ Analyze\ the\ hourly\ access\ data\ from\ the\ log\ file.\n
comment2.params=
comment2.target=void\ printHourlyCounts()
comment2.text=\n\ Print\ the\ hourly\ counts.\n\ These\ should\ have\ been\ set\ with\ a\ prior\n\ call\ to\ analyzeHourlyData.\n
comment3.params=
comment3.target=void\ printData()
comment3.text=\n\ Print\ the\ lines\ of\ data\ read\ by\ the\ LogfileReader\n
numComments=4

View File

@@ -0,0 +1,59 @@
/**
* Read web server data and analyse
* hourly access patterns.
*
* @author David J. Barnes and Michael Kölling.
* @version 2016.02.29
*/
public class LogAnalyzer
{
// Where to calculate the hourly access counts.
private int[] hourCounts;
// Use a LogfileReader to access the data.
private LogfileReader reader;
/**
* Create an object to analyze hourly web accesses.
*/
public LogAnalyzer()
{
// Create the array object to hold the hourly
// access counts.
hourCounts = new int[24];
// Create the reader to obtain the data.
reader = new LogfileReader();
}
/**
* Analyze the hourly access data from the log file.
*/
public void analyzeHourlyData()
{
while(reader.hasNext()) {
LogEntry entry = reader.next();
int hour = entry.getHour();
hourCounts[hour]++;
}
}
/**
* Print the hourly counts.
* These should have been set with a prior
* call to analyzeHourlyData.
*/
public void printHourlyCounts()
{
System.out.println("Hr: Count");
for(int hour = 0; hour < hourCounts.length; hour++) {
System.out.println(hour + ": " + hourCounts[hour]);
}
}
/**
* Print the lines of data read by the LogfileReader
*/
public void printData()
{
reader.printData();
}
}

View File

@@ -0,0 +1,26 @@
#BlueJ class context
comment0.params=logline
comment0.target=LogEntry(java.lang.String)
comment0.text=\n\ Decompose\ a\ log\ line\ so\ that\ the\ individual\ fields\n\ are\ available.\n\ @param\ logline\ A\ single\ line\ from\ the\ log.\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ This\ should\ be\ in\ the\ format\:\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ year\ month\ day\ hour\ minute\ etc.\n
comment1.params=year\ month\ day\ hour\ minute
comment1.target=LogEntry(int,\ int,\ int,\ int,\ int)
comment1.text=\n\ Create\ a\ LogEntry\ from\ the\ individual\ components.\n\ @param\ year\ The\ year\n\ @param\ month\ The\ month\ (1-12)\n\ @param\ day\ The\ day\ (1-31)\n\ @param\ hour\ The\ hour\ (0-23)\n\ @param\ minute\ The\ minute\ (0-59)\n
comment2.params=
comment2.target=int\ getHour()
comment2.text=\n\ Return\ the\ hour.\n\ @return\ The\ hour\ field\ from\ the\ log\ line.\n
comment3.params=
comment3.target=int\ getMinute()
comment3.text=\n\ Return\ the\ minute.\n\ @return\ The\ minute\ field\ from\ the\ log\ line.\n
comment4.params=
comment4.target=java.lang.String\ toString()
comment4.text=\n\ Create\ a\ string\ representation\ of\ the\ data.\n\ This\ is\ not\ necessarily\ identical\ with\ the\n\ text\ of\ the\ original\ log\ line.\n\ @return\ A\ string\ representing\ the\ data\ of\ this\ entry.\n
comment5.params=otherEntry
comment5.target=int\ compareTo(LogEntry)
comment5.text=\n\ Compare\ the\ date/time\ combination\ of\ this\ log\ entry\n\ with\ another.\n\ @param\ otherEntry\ The\ other\ entry\ to\ compare\ against.\n\ @return\ A\ negative\ value\ if\ this\ entry\ comes\ before\ the\ other.\n\ \ \ \ \ \ \ \ \ A\ positive\ value\ if\ this\ entry\ comes\ after\ the\ other.\n\ \ \ \ \ \ \ \ \ Zero\ if\ the\ entries\ are\ the\ same.\n
comment6.params=
comment6.target=java.util.Calendar\ getWhen()
comment6.text=\n\ Return\ the\ Calendar\ object\ representing\ this\ event.\n\ @return\ The\ Calendar\ for\ this\ event.\n
comment7.params=
comment7.target=void\ setWhen()
comment7.text=\n\ Create\ an\ equivalent\ Calendar\ object\ from\ the\ data\ values.\n
numComments=8

View File

@@ -0,0 +1,140 @@
import java.util.Calendar;
/**
* Store the data from a single line of a
* web-server log file.
* Individual fields are made available via
* accessors such as getHour() and getMinute().
*
* @author David J. Barnes and Michael Kölling.
* @version 2016.02.29
*/
public class LogEntry implements Comparable<LogEntry>
{
// Where the data values extracted from a single
// log line are stored.
private int[] dataValues;
// The equivalent Calendar object for the log time.
private Calendar when;
// At which index in dataValues the different fields
// from a log line are stored.
private static final int YEAR = 0, MONTH = 1, DAY = 2,
HOUR = 3, MINUTE = 4;
// The number of fields. If more fields are added, e.g. for
// seconds or a status code, then this value must be increased
// to match.
private static final int NUMBER_OF_FIELDS = 5;
/**
* Decompose a log line so that the individual fields
* are available.
* @param logline A single line from the log.
* This should be in the format:
* year month day hour minute etc.
*/
public LogEntry(String logline)
{
// The array to store the data for a single line.
dataValues = new int[NUMBER_OF_FIELDS];
// Break up the log line.
LoglineTokenizer tokenizer = new LoglineTokenizer();
tokenizer.tokenize(logline,dataValues);
setWhen();
}
/**
* Create a LogEntry from the individual components.
* @param year The year
* @param month The month (1-12)
* @param day The day (1-31)
* @param hour The hour (0-23)
* @param minute The minute (0-59)
*/
public LogEntry(int year, int month, int day, int hour, int minute)
{
// The array to store the data for a single line.
dataValues = new int[NUMBER_OF_FIELDS];
dataValues[YEAR] = year;
dataValues[MONTH] = month;
dataValues[DAY] = day;
dataValues[HOUR] = hour;
dataValues[MINUTE] = minute;
setWhen();
}
/**
* Return the hour.
* @return The hour field from the log line.
*/
public int getHour()
{
return dataValues[HOUR];
}
/**
* Return the minute.
* @return The minute field from the log line.
*/
public int getMinute()
{
return dataValues[MINUTE];
}
/**
* Create a string representation of the data.
* This is not necessarily identical with the
* text of the original log line.
* @return A string representing the data of this entry.
*/
public String toString()
{
StringBuffer buffer = new StringBuffer();
for(int value : dataValues) {
// Prefix a leading zero on single digit numbers.
if(value < 10) {
buffer.append('0');
}
buffer.append(value);
buffer.append(' ');
}
// Drop any trailing space.
return buffer.toString().trim();
}
/**
* Compare the date/time combination of this log entry
* with another.
* @param otherEntry The other entry to compare against.
* @return A negative value if this entry comes before the other.
* A positive value if this entry comes after the other.
* Zero if the entries are the same.
*/
public int compareTo(LogEntry otherEntry)
{
// Use the equivalent Calendars comparison method.
return when.compareTo(otherEntry.getWhen());
}
/**
* Return the Calendar object representing this event.
* @return The Calendar for this event.
*/
private Calendar getWhen()
{
return when;
}
/**
* Create an equivalent Calendar object from the data values.
*/
private void setWhen()
{
when = Calendar.getInstance();
// Adjust from 1-based month and day to 0-based.
when.set(dataValues[YEAR],
dataValues[MONTH] - 1, dataValues[DAY] - 1,
dataValues[HOUR], dataValues[MINUTE]);
}
}

View File

@@ -0,0 +1,11 @@
#BlueJ class context
comment0.params=
comment0.target=LogfileCreator()
comment0.text=\n\ Create\ log\ files.\n
comment1.params=filename\ numEntries
comment1.target=boolean\ createFile(java.lang.String,\ int)
comment1.text=\n\ Create\ a\ file\ of\ random\ log\ entries.\n\ @param\ filename\ The\ file\ to\ write.\n\ @param\ numEntries\ How\ many\ entries.\n\ @return\ true\ if\ successful,\ false\ otherwise.\n
comment2.params=
comment2.target=LogEntry\ createEntry()
comment2.text=\n\ Create\ a\ single\ (random)\ entry\ for\ a\ log\ file.\n\ @return\ A\ log\ entry\ containing\ random\ data.\n
numComments=3

View File

@@ -0,0 +1,69 @@
import java.io.*;
import java.util.*;
/**
* A class for creating log files of random data.
*
* @author David J. Barnes and Michael Kölling
* @version 2016.02.29
*/
public class LogfileCreator
{
private Random rand;
/**
* Create log files.
*/
public LogfileCreator()
{
rand = new Random();
}
/**
* Create a file of random log entries.
* @param filename The file to write.
* @param numEntries How many entries.
* @return true if successful, false otherwise.
*/
public boolean createFile(String filename, int numEntries)
{
boolean success = false;
if(numEntries > 0) {
try (FileWriter writer = new FileWriter(filename)) {
LogEntry[] entries = new LogEntry[numEntries];
for(int i = 0; i < numEntries; i++) {
entries[i] = createEntry();
}
Arrays.sort(entries);
for(int i = 0; i < numEntries; i++) {
writer.write(entries[i].toString());
writer.write('\n');
}
success = true;
}
catch(IOException e) {
System.err.println("There was a problem writing to " + filename);
}
}
return success;
}
/**
* Create a single (random) entry for a log file.
* @return A log entry containing random data.
*/
public LogEntry createEntry()
{
int year = 2016;
int month = 1 + rand.nextInt(12);
// Avoid the complexities of days-per-month.
int day = 1 + rand.nextInt(28);
int hour = rand.nextInt(24);
int minute = rand.nextInt(60);
return new LogEntry(year, month, day, hour, minute);
}
}

View File

@@ -0,0 +1,29 @@
#BlueJ class context
comment0.params=
comment0.target=LogfileReader()
comment0.text=\n\ Create\ a\ LogfileReader\ to\ supply\ data\ from\ a\ default\ file.\n
comment1.params=filename
comment1.target=LogfileReader(java.lang.String)
comment1.text=\n\ Create\ a\ LogfileReader\ that\ will\ supply\ data\n\ from\ a\ particular\ log\ file.\ \n\ @param\ filename\ The\ file\ of\ log\ data.\n
comment2.params=
comment2.target=boolean\ hasNext()
comment2.text=\n\ Does\ the\ reader\ have\ more\ data\ to\ supply?\n\ @return\ true\ if\ there\ is\ more\ data\ available,\n\ \ \ \ \ \ \ \ \ false\ otherwise.\n
comment3.params=
comment3.target=LogEntry\ next()
comment3.text=\n\ Analyze\ the\ next\ line\ from\ the\ log\ file\ and\n\ make\ it\ available\ via\ a\ LogEntry\ object.\n\ \n\ @return\ A\ LogEntry\ containing\ the\ data\ from\ the\n\ \ \ \ \ \ \ \ \ next\ log\ line.\n
comment4.params=
comment4.target=void\ remove()
comment4.text=\n\ Remove\ an\ entry.\n\ This\ operation\ is\ not\ permitted.\n
comment5.params=
comment5.target=java.lang.String\ getFormat()
comment5.text=\n\ @return\ A\ string\ explaining\ the\ format\ of\ the\ data\n\ \ \ \ \ \ \ \ \ in\ the\ log\ file.\n
comment6.params=
comment6.target=void\ reset()
comment6.text=\n\ Set\ up\ a\ fresh\ iterator\ to\ provide\ access\ to\ the\ data.\n\ This\ allows\ a\ single\ file\ of\ data\ to\ be\ processed\n\ more\ than\ once.\n
comment7.params=
comment7.target=void\ printData()
comment7.text=\n\ Print\ the\ data.\n
comment8.params=data
comment8.target=void\ createSimulatedData(java.util.ArrayList)
comment8.text=\n\ Provide\ a\ sample\ of\ simulated\ data.\n\ NB\:\ To\ simplify\ the\ creation\ of\ this\ data,\ no\n\ days\ after\ the\ 28th\ of\ a\ month\ are\ ever\ generated.\n\ @param\ data\ Where\ to\ store\ the\ simulated\ LogEntry\ objects.\n
numComments=9

View File

@@ -0,0 +1,166 @@
import java.io.File;
import java.io.FileNotFoundException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.Random;
import java.util.Scanner;
/**
* A class to read information from a file of web server accesses.
* Currently, the log file is assumed to contain simply
* date and time information in the format:
*
* year month day hour minute
* Log entries are sorted into ascending order of date.
*
* @author David J. Barnes and Michael Kölling.
* @version 2016.02.29
*/
public class LogfileReader implements Iterator<LogEntry>
{
// The data format in the log file.
private String format;
// Where the file's contents are stored in the form
// of LogEntry objects.
private ArrayList<LogEntry> entries;
// An iterator over entries.
private Iterator<LogEntry> dataIterator;
/**
* Create a LogfileReader to supply data from a default file.
*/
public LogfileReader()
{
this("weblog.txt");
}
/**
* Create a LogfileReader that will supply data
* from a particular log file.
* @param filename The file of log data.
*/
public LogfileReader(String filename)
{
// The format for the data.
format = "Year Month(1-12) Day Hour Minute";
// Where to store the data.
entries = new ArrayList<>();
// Attempt to read the complete set of data from file.
boolean dataRead;
try{
// Locate the file with respect to the current environment.
URL fileURL = getClass().getClassLoader().getResource(filename);
if(fileURL == null) {
throw new FileNotFoundException(filename);
}
Scanner logfile = new Scanner(new File(fileURL.toURI()));
// Read the data lines until the end of file.
while(logfile.hasNextLine()) {
String logline = logfile.nextLine();
// Break up the line and add it to the list of entries.
LogEntry entry = new LogEntry(logline);
entries.add(entry);
}
logfile.close();
dataRead = true;
}
catch(FileNotFoundException e) {
System.out.println("Problem encountered: " + e);
dataRead = false;
}
catch(URISyntaxException e) {
System.out.println("Problem encountered: " + e);
dataRead = false;
}
// If we couldn't read the log file, use simulated data.
if(!dataRead) {
System.out.println("Failed to read the data file: " +
filename);
System.out.println("Using simulated data instead.");
createSimulatedData(entries);
}
// Sort the entries into ascending order.
Collections.sort(entries);
reset();
}
/**
* Does the reader have more data to supply?
* @return true if there is more data available,
* false otherwise.
*/
public boolean hasNext()
{
return dataIterator.hasNext();
}
/**
* Analyze the next line from the log file and
* make it available via a LogEntry object.
*
* @return A LogEntry containing the data from the
* next log line.
*/
public LogEntry next()
{
return dataIterator.next();
}
/**
* Remove an entry.
* This operation is not permitted.
*/
public void remove()
{
System.err.println("It is not permitted to remove entries.");
}
/**
* @return A string explaining the format of the data
* in the log file.
*/
public String getFormat()
{
return format;
}
/**
* Set up a fresh iterator to provide access to the data.
* This allows a single file of data to be processed
* more than once.
*/
public void reset()
{
dataIterator = entries.iterator();
}
/**
* Print the data.
*/
public void printData()
{
for(LogEntry entry : entries) {
System.out.println(entry);
}
}
/**
* Provide a sample of simulated data.
* NB: To simplify the creation of this data, no
* days after the 28th of a month are ever generated.
* @param data Where to store the simulated LogEntry objects.
*/
private void createSimulatedData(ArrayList<LogEntry> data)
{
LogfileCreator creator = new LogfileCreator();
// How many simulated entries we want.
int numEntries = 100;
for(int i = 0; i < numEntries; i++) {
data.add(creator.createEntry());
}
}
}

View File

@@ -0,0 +1,8 @@
#BlueJ class context
comment0.params=
comment0.target=LoglineTokenizer()
comment0.text=\n\ Construct\ a\ LogLineAnalyzer\n
comment1.params=logline\ dataLine
comment1.target=void\ tokenize(java.lang.String,\ int[])
comment1.text=\n\ Tokenize\ a\ log\ line.\ Place\ the\ integer\ values\ from\n\ it\ into\ an\ array.\ The\ number\ of\ tokens\ on\ the\ line\n\ must\ be\ sufficient\ to\ fill\ the\ array.\n\n\ @param\ logline\ The\ line\ to\ be\ tokenized.\n\ @param\ dataLine\ Where\ to\ store\ the\ values.\n
numComments=2

View File

@@ -0,0 +1,43 @@
import java.util.Scanner;
/**
* Break up line from a web server log file into
* its separate fields.
* Currently, the log file is assumed to contain simply
* integer date and time information.
*
* @author David J. Barnes and Michael Kolling.
* @version 2016.02.29
*/
public class LoglineTokenizer
{
/**
* Construct a LogLineAnalyzer
*/
public LoglineTokenizer()
{
}
/**
* Tokenize a log line. Place the integer values from
* it into an array. The number of tokens on the line
* must be sufficient to fill the array.
*
* @param logline The line to be tokenized.
* @param dataLine Where to store the values.
*/
public void tokenize(String logline, int[] dataLine)
{
try {
// Scan the logline for integers.
Scanner tokenizer = new Scanner(logline);
for(int i = 0; i < dataLine.length; i++) {
dataLine[i] = tokenizer.nextInt();
}
}
catch(java.util.NoSuchElementException e) {
System.out.println("Insuffient data items on log line: " + logline);
throw e;
}
}
}

View File

@@ -0,0 +1,24 @@
Project: weblog-analyzer
Aothors: David J. Barnes and Michael Kölling
This project is part of the material for chapter 4 of the book
Objects First with Java - A Practical Introduction using BlueJ
Sixth edition
David J. Barnes and Michael Kölling
Pearson Education, 2016
Purpose of project: To provide an illustration of the use of arrays.
How to start this project: Create a LogAnalyzer object.
The LogfileReader expects to read a file, weblog.txt,
containing lines of data in the format:
year month day hour minute
month values are in the range 1-12 and day values in the range 1-31.
If the sample file cannot be found, the reader will create some simulated
data. Alternatively, use the LogfileCreator to create some random data.
Use its createFile method to give a file name and the number of entries
to create.

View File

@@ -0,0 +1,99 @@
#BlueJ package file
dependency1.from=LogAnalyzer
dependency1.to=LogfileReader
dependency1.type=UsesDependency
dependency2.from=LogAnalyzer
dependency2.to=LogEntry
dependency2.type=UsesDependency
dependency3.from=LogfileReader
dependency3.to=LogEntry
dependency3.type=UsesDependency
dependency4.from=LogfileCreator
dependency4.to=LogEntry
dependency4.type=UsesDependency
dependency5.from=LogEntry
dependency5.to=LoglineTokenizer
dependency5.type=UsesDependency
dependency6.from=LogfileReader
dependency6.to=LogfileCreator
dependency6.type=UsesDependency
objectbench.height=76
objectbench.width=859
package.editor.height=479
package.editor.width=751
package.editor.x=70
package.editor.y=80
package.numDependencies=6
package.numTargets=5
package.showExtends=true
package.showUses=true
project.charset=UTF-8
readme.editor.height=707
readme.editor.width=830
readme.editor.x=151
readme.editor.y=60
target1.editor.height=733
target1.editor.width=831
target1.editor.x=869
target1.editor.y=51
target1.height=60
target1.name=LogfileCreator
target1.naviview.expanded=true
target1.showInterface=false
target1.type=ClassTarget
target1.typeParameters=
target1.width=120
target1.x=500
target1.y=60
target2.editor.height=777
target2.editor.width=986
target2.editor.x=70
target2.editor.y=23
target2.height=60
target2.name=LogfileReader
target2.naviview.expanded=true
target2.showInterface=false
target2.type=ClassTarget
target2.typeParameters=
target2.width=110
target2.x=230
target2.y=140
target3.editor.height=700
target3.editor.width=900
target3.editor.x=53
target3.editor.y=23
target3.height=60
target3.name=LoglineTokenizer
target3.naviview.expanded=true
target3.showInterface=false
target3.type=ClassTarget
target3.typeParameters=
target3.width=130
target3.x=490
target3.y=320
target4.editor.height=734
target4.editor.width=816
target4.editor.x=67
target4.editor.y=23
target4.height=60
target4.name=LogAnalyzer
target4.naviview.expanded=true
target4.showInterface=false
target4.type=ClassTarget
target4.typeParameters=
target4.width=120
target4.x=80
target4.y=60
target5.editor.height=777
target5.editor.width=989
target5.editor.x=50
target5.editor.y=23
target5.height=60
target5.name=LogEntry
target5.naviview.expanded=true
target5.showInterface=false
target5.type=ClassTarget
target5.typeParameters=
target5.width=110
target5.x=370
target5.y=230