Let's say I have an abstract class, called Logger:
public abstract class AbstractLogger {
public enum Levels {
DEBUG, INFO, WARNING, ERROR
}
public void debug(String message) {
Levels level = Levels.DEBUG;
log(level, message);
}
public void info(String message) {
Levels level = Levels.INFO;
log(level, message);
}
public void warning(String message) {
Levels level = Levels.WARNING;
log(level, message); }
public void error(String message) {
Levels level = Levels.ERROR;
log(level, message); }
public void log(Levels level, String message) {}
}
And I also have classes that inherit this class, such as FileAppenderLogger:
public class FileAppenderLogger extends AbstractLogger {
private final Path logPath;
public FileAppender(Path logPath) {
this.logPath = logPath;
createLogFile();
}
private void createLogFile() {
try {
File logFile = new File(logPath.toString());
if (logFile.createNewFile()) {
System.out.println("File created: " + logFile.getName());
} else {
System.out.println("File already exists.");
}
} catch (IOException e) {
System.out.println("An error occurred.");
e.printStackTrace();
}
}
#Override
public void log(Levels level, String message) {
try {
FileWriter myWriter = new FileWriter(this.logPath.toString());
myWriter.write(message+"\n");
myWriter.close();
System.out.println("Successfully wrote to the file.");
} catch (IOException e) {
System.out.println("An error occurred.");
e.printStackTrace();
}
}
#Override
public void debug(String message) {
super.info(message);
}
#Override
public void info(String message) {
super.info(message);
}
#Override
public void warning(String message) {
super.warning(message);
}
#Override
public void error(String message) {
super.error(message);
}
}
Now, let's say I need to extend Logger to support new Log level, such as "FATAL", and also extend its children, such as FileAppenderLogger to support it, without modify any of those classes, only extend them.
what could be the best practice for that (if I still want to preserve non generic methods such as ".info(String s)" or ".debug(String s))?
What design pattern may I use here?
I'm open for changes regard this problem.
Thank you!
Simply add it to AbstractLogger:
public abstract class AbstractLogger {
public enum Levels {
DEBUG, INFO, WARNING, ERROR, /* added */ FATAL,
}
public void fatal(String message) {
log(Levels.FATAL, message);
}
}
Given that the types that extend AbstractLogger all already implement the log method, then 'things will just work' - possibly some of the implementations cannot deal with the fact that a new log level has now appeared. Assuming they were appropriately programmed, they'll throw. Your FileAppenderLogger class, for example, would just continue to work without requiring any change or even recompilation.
The key design pattern to make this work is that all those non-generic methods such as .error(x) are light wrappers that all send the data to a single method that does the real work - log. But, you already do that.
NB: Reinventing the wheel is a bad idea. Logging frameworks already exist, use an existing one instead.
NB2: Idiomatic java dictates you call your enum types the singular - it should be Level, not Levels. The type name describes, well, a type name. It's called String, not Strings, because an instance of java.lang.String represents one string. The class itself represents all strings, but that doesn't mean it should be called Strings. Similarly, an instance of the Levels enum represents a single level. Hence, it should be named Level, not Levels.
Instead of using enum for level, you can make class LogLevel and make classes that extend it, for example LogLevelError, LogLevelFatal, then in log method: this.logLevel.log(message);. Of course, it look strange, but this is the way I see to add new log levels. Also, as said by #rzwitserloot :"NB: Reinventing the wheel is a bad idea. Logging frameworks already exist, use an existing one instead". They are much faster, optimized, and 'time-tested'.
You can't add more values to your enum, that's not possible in java. I would suggest to either use a String for levels, or declare your own Level class, so you can add more levels.
public class Level {
private final String levelName;
//getter, etc.
}
To extend the functionality of your AbstractLogger, without modifying it, you can wrap it in another class and declare the additional methods, fatal() in this case.
public class ExtendedLogger extends AbstractLogger {
private final AbstractLogger abstractLogger;
public ExtendedLogger(AbstractLogger abstractLogger) {
this.abstractLogger = abstractLogger;
}
#Override
public void debug(String message) {
abstractLogger.debug(message);
}
//info, warning and rest of methods
#Override
public void log(Levels level, String message) {
abstractLogger.log(level, message);
}
public void fatal(String message) {
//implement
}
}
First: logger libraries are numerous, and the first reform was the introduction of java.util.Logger to unify things a bit. Still not the dead of the other logging libraries.
Then came - especially for libraries - the underestimated System.Logger: a Logger façade that can be discover logging implementations. This allows publishing a library, use Logging, but leave the actual logging library choice to the library user.
So in that context meddling in class hierarchies and enum constants is counter-productive to say the least.
What you can do is a specific configuration, say for some packages, implement a specific file handler (FileHandler, Handler), and reserve ERROR for your own FATALISH when using *Exception classes or such.
Though seemingly simple, using FileWriter in the Logger child was not intended to be done as such. You should leave it to configuring the usage to your own Handler class.
Unfortunately the solution does not exist. You'll better write a prototype to test your specific configuration.
Related
I'm trying to get into exception handling via custom exceptions.
I'm creating the class CustomExceptions and extending Exception as follows:
public class CustomExceptions extends Exception{
public CustomExceptions (String s) {
super(s);
}
However, rather than having to create multiple files for every custom exception I want, or bloating my main class file, I'd like to put all of my custom exceptions in this class and invoke them via a method
So let's say that I want to handle 2 situations: When the user tries to input a seat reservation, but the seat is already taken, and when the user tries to provide a ticket for someone outside of the age range.
Can I create 2 methods inside of the CustomExceptions class which invoke the constructor passing a custom message to it?
public void seatTaken(String s) {
String s = "The seat is taken, please choose a new one";
CustomExceptions(s);
}
public void notOldEnough(String s) {
String s = "User is not old enough for this movie.";
CustomExceptions(s)
}
}
Would this work? Or am I forced to create multiple custom exception files?
Generally custom exceptions should be defined in the top level. Because, almost universally, these exceptions are part of the interface of the package or module.
If the user cannot see them, then how are they going to catch them separately? And if you don't want to catch them separately, then why would you need separate classes?
However, if you must, you can include them into a class for which they are required:
public class SeatReservationSystem {
public static class ReservationFailedException {
... constructors taking a message ...
}
public static class SeatTakenException extends ReservationFailedException {
... constructors taking a message ...
}
public static class OutsideAgeException extends ReservationFailedException {
... constructors taking a message ...
}
....
}
After that you can create any method that returns them as required. Don't create methods that throw them as the compiler won't see those as exit points of the block you're in, and you'll get strange situations.
Here is some code to show what I mean:
// wrong
public static void throwRuntimeException() throws RuntimeException {
throw new RuntimeException();
}
// correct, but dangerous
public static RuntimeException createRuntimeException() {
return new RuntimeException();
}
public static void main(String[] args) {
String initializeMeOrThrowException;
if (new Random().nextBoolean()) {
// compiler doesn't recognize that the method always throws an exception
throwRuntimeException();
// this the compiler can understand, there is an explicit throw here:
// throw createRuntimeException();
// but this is the pitfall, it doesn't do anything:
// createRuntimeException();
} else {
initializeMeOrThrowException = "Initialized!";
}
// Compiler error for throwRuntimeException and createRuntimeException without throws:
// "The local variable initializeMeOrThrowException may not have been initialized"
System.out.println(initializeMeOrThrowException);
}
However, experience learns me that I forget the throws statement for the throw createException(...); method, and the stupid compiler doesn't warn me about that (even though the statement is utterly useless without it). So I try and not use either.
Note that I'm not certain if you should use exceptions for this. If your system is a reservation system, then refusing tickets is not that exceptional. Returning a ReservationResult makes more sense.
I have the following class:
public class DataService {
static <T> void load(Structure structure, String path, DataServiceType dataService) {
//do smth
}
private interface DataServiceType<T> {
//do smth
}
private static class DataServiceInteger implements DataServiceType<Integer> {
//do smth
}
private static class DataServiceString implements DataServiceType<String> {
//do smth
}
}
I want to add there two following methods:
public static void load(Structure<Integer> structure,String path) throws IOException {
load(structure,path,new DataServiceInteger());
}
public static void load(Structure<String> structure,String path) throws IOException {
load(structure,path,new DataServiceString());
}
but both methods have same erasure. How can I achive it without changing methods names?
EDIT
I wasn't accurate. Classes implement DataServiceType have mathod :
void getDataFromString(String in, T out);
(they are paresers)
Reading from file is held in mehod static <T> void load(Structure structure, String path, DataServiceType dataService) from DataService, so M. le Rutte's solution wouldn't be good for me, because I would have to repeat myself. Is it possible to implement berry's soulution for my problem?
As you already found out, due to type erasure the runtime would not be able to distinguish between the different methods. Either the name must be different, or the arguments must be different.
However, you use a static method. My personal choice would to be to use specific instances of a DataService:
public interface DataService<T> {
Structure<T> load(Path path);
}
public StringDataService implements DataService<String> {
public Structure<String> load(Path path) {
...
}
}
public IntDataService implements DataService<Integer> {
public Structure<Integer> load(Path path) {
...
}
}
You cannot. The way type erasure works in Java, is that a 'hidden' (synthetic) method is created by the compiler during compilation which casts the object from some superclass (usually Object) to the correct type. As there are two different types in your example, the Java compiler does not know which to cast since both name and the rest of the parameters match completely.
It might be good practice to name the methods differently either way, as loading a String and loading an integer may not necessarily be handled exactly the same way. For example, you might need to load into memory a list of user input strings: in this case, the string might need to be sanitized first.
As said already, you can't do it exactly as described. However, you could do it by adding generic parameters to the load() method itself, and then making a generic DataServiceClazz type (as oppose to separate DataServiceInteger, DataServiceString classes) that implements your DataServiceType interface:
private static class DataServiceClazz<T> implements DataServiceType<T> { //Replaces DataServiceInteger, DataServiceString, etc.
//do smth
}
public static <T> void load(Structure<T> structure, String path) throws IOException {
load(structure, path, new DataServiceClazz<>());
}
This may not work, depending on your use case, since you won't be able to use different logic based on the type of T - but it's the closest pattern to what you have currently.
I need to extend Exception class in my application and i ran into a situation.
My Parent Exception class with a httpCode setter/getter to translate the exception to http error code:
abstract class ParentException
{
private int httpCode;
protected ParentException()
{
this("");
}
public ParentException(String message)
{
super(message);
}
protected ParentException(Exception e)
{
super(e);
}
public int getHttpCode()
{
return httpCode;
}
public void setHttpCode(int httpCode)
{
this.httpCode = httpCode;
}
}
Sub class:
public class AccessDeniedException extends ParentException
{
public AccessDeniedException()
{
this("");
}
public AccessDeniedException(String message)
{
super(message);
setHttpCode(HttpCodes.HTTP_CODE_403_FORBIDDEN_UNAUTHORIZED);
}
public AccessDeniedException(Exception e)
{
super(message);
setHttpCode(HttpCodes.HTTP_CODE_403_FORBIDDEN_UNAUTHORIZED);
}
}
Similarly i have bunch of other exception implementations for various relevant http codes. What I do not like is that setHttpCode() method is in two places. I want this to be called just from one constructor.
I could have just one constructor in all classes as following (ofcourse I need to fix parent class similarly):
public class AccessDeniedException extends ParentException
{
public AccessDeniedException()
{
this("");
}
public AccessDeniedException(String message)
{
this(message, null);
}
public AccessDeniedException(Exception e)
{
this("", e);
}
public AccessDeniedException(String message,Exception e)
{
super(message, e);
setHttpCode(HttpCodes.HTTP_CODE_403_FORBIDDEN_UNAUTHORIZED);
}
}
But I am worried that if a constructor with just message or exception is called, root Exception or Throwable classes may not get instantiated properly. Do you see any issue with this approach? If not, what is the better way of doing this.
One way would be to have in the base class:
protected abstract int getHttpCode();
And then every child class would need a:
#Override
protected getHttpCode() {
return HttpCodes.HTTP_CODE_403_FORBIDDEN_UNAUTHORIZED;
}
If you do it the second way you should probably pass in null as the message for the empty and Exception constructors.
The second approach will work OK, but you might see slightly different output compared to the first, as some Throwable constructors fill the detailMessage differently.
The same issue as described here was is seen in Exception's extension of Throwable. They solve it in the same way as your first example.
Of the two, I'd recommended using your first example as it follows the established convention and is more robust design if the superclass behavior changed.
Edit - M. Prokhorov noted that there should be an abstract getter method, this approach would work and is better design. Further along those lines I would suggest including the httpCode as part of the constructors of Parent.
e.g.
protected ParentException(int httpCode)
{
super();
this.httpCode = httpCode;
}
public AccessDeniedException()
{
super(HttpCodes.HTTP_CODE_403_FORBIDDEN_UNAUTHORIZED);
}
You could also extract the code to a constant (static final) variable.
Today I was thinking about a nice way to write less code for a common functionality that is required for different objects.
Inheritance can do the job but then the classes won't be able to inherit from anyone else, so I chose Interfaces.
So I have my interface with the functionality I will need for some objects:
public interface Test {
String message = "Hello from Interface!";
default void printMessage() {
System.out.println(message);
}
}
And then I can use it in any object without having to override/write any code more than just simply calling the method when needed:
public class TestingTest implements Test {
public String message = "Hello from Class!";
public TestingTest() {
printMessage();
}
public static void main(String[] args) {
new TestingTest();
}
}
It works like a charm! But... Then I thought, what if I want some of those objects to specify a different message without being required (optional), well first thing I thought was to shadow the interface variable, but it doesn't work, the default method keeps using the variable from the interface instead of the class variable (which shadowed it).
A solution of course would be to overload the printMessage method in the interface so it recieves the message as a parameter for when the user requires to specify the message, but is there any more elegant way? Something like simply just declaring a new message in the class?
The String message in the interface is static (AFAIK). So that scheme does not work.
You might do something (ugly) as:
default void printMessage(String... messages) {
if (messages.length == 0) {
messages = new String[] { "arrgg" };
}
System.out.println(messages[0]);
}
Fields have no inheritance, so the value can only stem from an overridable method like
public String message() { return "..."; }
What you want is a functionality in n classes that should also be modifiable, if needed.
To be honest, your example is a little bit abstract and thus my answer will be abstract, too.
public interface Test {
void printMessage();
default void printMessage(String message) {
System.out.println(message);
}
}
public class TestingTest {
private final test;
public TestingTest(Test test) {
this.test = test;
}
public void someMethod() {
test.printMessage("Hello from class");
}
}
Additionally, you would have a class that implements the interface and offers the message. This way you could group your objects, change the message, make more complex logging and you would actually see the dependency from outside.
In my opinion, you are misusing the interface. An interface offers public methods to call it from outside, but you want to use them inside like they were private functionalities for the class.
Just use objects instead.
I was wondering, if it is possible to extend the standard java logger (java.util.logging.Logger;) for another logger level.
The goal is, that there should show up "ERROR" instead of "SEVERE" in the log files.
Is that possible?
Or do I have to use a different logger instead (e.g. Log4j)?
Thanks a lot!
If you just want to print something different than the standard you could set your own formatter, see http://tutorials.jenkov.com/java-logging/formatters.html
If you want to add an additional log level you can do so by subclassing java.util.logging.Level:
public class MyErrorLevel extends java.util.logging.Level {
public MyErrorLevel() {
super("ERROR", 1000);
}
}
Ok, Andreas Vogler's version works. Create this class:
public class MyErrorLevel extends java.util.logging.Level
{
public MyErrorLevel()
{
super("ERROR", 1000);
}
}
To use it in your running program code, you have to do it that way:
logger.log(new MyErrorLevel(),"My Error number one");
If you need more than one errorLevel you can do it that way:
public class MyErrorLevel extends Level
{
public static MyErrorLevel ERROR = new MyErrorLevel ("ERROR", 950);
public static MyErrorLevel SERIOUS_ERROR = new MyErrorLevel("SERIOUS_ERROR", 980);
//...and so on...
private MyErrorLevel(String name, int value)
{
super (name, value);
}
}
In your program code, you can use it like this:
logger.log(MyErrorLevel.ERROR, "my other error");
logger.log(MyErrorLevel.SERIOUS_ERROR, "my significant Error");
Now, if you don't want to specify your own classname (MyErrorLevel.SERIOUS_ERROR) everytime and instead you want to use 'standard-methods' (e. g. like the already existing method logger.info("my information")) you may think about extending the logger itself with new methods. This should (as far as my understanding goes) basically work like that:
public class MyLogger extends Logger
{
public MyLogger(String name, String resourceBundleName)
{
super(name, resourceBundleName);
}
public void error(String msg)
{
super.log(MyErrorLevel.ERROR, msg);
}
public void error(String msg)
{
super.log(MyErrorLevel.SERIOUS_ERROR, msg);
}
}
Now you should be able to call these methods in your code like that:
myLogger.error("my error")
myLogger.seriousError("my serious error")
But I wasnt able to do it:
I couldn't initialize my own logger with:
MyLogger myLogger = MyLogger.getLogger("MyModifiedLogger");
This doesn't compile because of type mismatch (Cannont convert from logger to MyLogger).
I also tried:
MyLogger myLogger = (MyLogger)Logger.getLogger("MyModifiedLogger");
This results in an error message while running:
java.util.logging.Logger cannot be cast to utility.MyLogger
So somehow my extension failed. Any ideas what I am missing?