We all know the Golden Object Oriented rule Don't talk to strangers
We all know that you will never find the API/frameworks defects or magic or limitations at the beginning of the development.
Imagine yourself using API in all your projects for years. Everybody else is using it as well, it is famous and mature. Suddenly you have a huge production issue. Changing that API is too hard and costly at this stage!
What now?
Or you decided to change any API/framework for new requirements. Lets take logging as an example, now we have log4j, logback, JDK logging. At the beginning of the project you decide to go with the logback since it's so famous and faster than log4j and it's bundled with Spring, etc.
After 1 year of the project you got a new requirements for the logging which are:
- All logs should be localized
- FATAL errors should be sent to a specific email
Disaster, right?!
So how we can solve this issue , simply we need to follow three Design Patterns:
- Factory
- Wrapper
- Facade
And use this flow:
Below is an exmple class diagram from the logger used in JavaFF
Memory Leak
- (POI) OOM caused by Memory Leak in FileBackedDataSource
- (MySql JDBC connector) Memory leak in ResultSet
- (Gson) Memory Leak in web application
Performance
- (LOG4J) log4j performance tuning in production config
- (Guava) performance problem in LinkedHashMultimap
Deadlocks
So, we should always protect our project and noy use a framework or API directly and this is the main idea here
Requires JDK 1.7 or higher.
Gson VS Jakson: Example shows how to contol the json implementation through the classpath
Exception Handling: Example shows how to control the exceptions handling by the type
Log4j Vs Logback Vs JavaUtillogger: : Example shows how to control the logging through the classpath
This project offers a standard/clear API for the most used API's in the Java Applications like: Exceptions, Locale, Beans, Formatter's, Json Handlers, Loggers, ReflectionHelpers, etc.
You can control the implementations through the class path without changing line of code
The below example shows how the implementation will be changed without changing the code: Now I have the below dependencies in my pom.xml
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-jdk14</artifactId>
<version>${slf4.version}</version>
<scope>provided</scope>
</dependency>So when I call
LogFactory.getLogger(LogFactory.class).info("LOCALIZED_MSG", "Kiswani"); Output:
Nov22, 20166:54:07PMio.github.rhkiswani.javaff.log.Slf4jLoginfoINFO: thisislocalizedmsgfrommessages_en.propertiesthanksforMrKiswaniWhen I remove the dependencies from the pom.xml and run the same code I will get
LogFactory.getLogger(LogFactory.class).info("LOCALIZED_MSG", "Kiswani");
Output:
Nov22, 20167:00:15PMio.github.rhkiswani.javaff.log.DefaultLoginfoINFO: thisislocalizedmsgfrommessages_en.propertiesthanksforMrKiswaniTransparent localization for logs, strings, exceptions
Centralized and Configurable Exception Handling by the class type. See below a full example:
packageio.github.rhkiswani.javaff;
importio.github.rhkiswani.javaff.exceptions.ExceptionHandler;
importio.github.rhkiswani.javaff.exceptions.ExceptionHandlersFactory;
importio.github.rhkiswani.javaff.exceptions.ExceptionUtil;
publicclassTestMain {
publicstaticvoidmain(String[] args) {
//Any instance of ConsoleException will be handled hereExceptionHandlersFactory.instance().add(ConsoleException.class, newExceptionHandler() {
@Overridepublicvoidhandle(Throwablet) {
System.out.println("ConsoleException handler");
}
});
//Any instance of MailException will be handled hereExceptionHandlersFactory.instance().add(MailException.class, newExceptionHandler() {
@Overridepublicvoidhandle(Throwablet) {
System.out.println("MailException handler");
}
});
ExceptionUtil.handle(newConsoleException());
ExceptionUtil.handle(newSubConsoleException());
ExceptionUtil.handle(newMailException());
ExceptionUtil.handle(newSubMailException());
//Null is not related to any class from the previous class's, it will be handled by the default handler for Throwable.class//which is printing the stack trace by defaultExceptionUtil.handle(newNullPointerException());
//We decided to override the default implantation for Throwable.classExceptionHandlersFactory.instance().overrideImp(Throwable.class, newExceptionHandler() {
@Overridepublicvoidhandle(Throwablet) {
System.out.println("Overridden handler");
}
});
ExceptionUtil.handle(newNullPointerException());
}
privatestaticclassConsoleExceptionextendsRuntimeException {
}
privatestaticclassSubConsoleExceptionextendsConsoleException {
}
privatestaticclassMailExceptionextendsRuntimeException {
}
privatestaticclassSubMailExceptionextendsMailException {
}
}- Logging with localization out of the box
LogFactory.getLogger(LogFactory.class).info("LOCALIZED_MSG", "Kiswani");
Output : INFO: thisislocalizedmsgfrommessages_en.propertiesthanksforMrKiswaniLogFactory.getLogger(LogFactory.class).info("normal msg num {0} date {1}", Integer.MAX_VALUE, newDate());
Output:
INFO: normalmsgnum2,147,483,647date11/22/166:06PMMany Utilities, see below just a few examples:
- Formatter's
System.out.println(FormatUtil.format("Mr {0} {1}", "Mohamed", "Kiswani")); System.out.println(FormatUtil.format(newDate())); System.out.println(FormatUtil.format(newDate(), "yyyy-MM-dd")); System.out.println(FormatUtil.format(Integer.MAX_VALUE)); Output: MrMohamedKiswani11/22/166:16PM2016-11-222,147,483,647
- Json Handlers
packagejavaff.samples.beans; importio.github.rhkiswani.javaff.json.JsonHandler; importio.github.rhkiswani.javaff.json.JsonHandlerFactory; publicclassMain { publicstaticvoidmain(String[] args) { Employeeemp = newEmployee(); emp.setId(100); emp.setName("Kiswani"); emp.setEmpId(1000); // comment jakson in pom.xmlSystem.out.println("\n======================= Default based on class path ========================"); // default json handler gsonSystem.out.println(JsonHandlerFactory.getJsonHandler(Employee.class).toJson(emp)); System.out.println(JsonHandlerFactory.getJsonHandler(Employee.class).getImplementation()); // when you uncomment jakson from the pom.xmlSystem.out.println(JsonHandlerFactory.getJsonHandler(Employee.class).toJson(emp)); System.out.println(JsonHandlerFactory.getJsonHandler(Employee.class).getImplementation()); System.out.println("\n======================= Force to use Gson By annotation ========================"); // you want to have jakson for all the classes except the student , student should use Gson// you need to add @GsonBeanStudentstd = newStudent(); std.setId(100); std.setName("Kiswani"); std.setStdId(1000); System.out.println(JsonHandlerFactory.getJsonHandler(Student.class).toJson(emp)); System.out.println(JsonHandlerFactory.getJsonHandler(Student.class).getImplementation()); System.out.println("\n======================= Custom Json Handler ========================"); // you want to add your custom implantation// Note : if you set your custom it will be used everywhereJsonHandlerFactory.instance().setDefault(newMyJsonHandler()); System.out.println(JsonHandlerFactory.getJsonHandler(Employee.class).toJson(emp)); System.out.println(JsonHandlerFactory.getJsonHandler(Employee.class).getImplementation()); System.out.println(JsonHandlerFactory.getJsonHandler(Student.class).toJson(emp)); System.out.println(JsonHandlerFactory.getJsonHandler(Student.class).getImplementation()); } privatestaticclassMyJsonHandlerimplementsJsonHandler { public <T> TfromJson(Strings, ClassaClass) { returnnull; } publicStringtoJson(Objecto) { returno.toString(); } publicObjectgetImplementation() { returnMyJsonHandler.class; } } packagejavaff.samples.beans; importio.github.rhkiswani.javaff.json.annotations.GsonBean; @GsonBeanpublicclassStudentextendsPerson{ privateintstdId; publicintgetStdId() { returnstdId; } publicvoidsetStdId(intstdId) { this.stdId = stdId; } } }

