Guava
In Verify.verify
’s error message, only %s
is supported.
Logging
Log4j
In 1.2, %d{ISO8601}
means the format yyyy-MM-dd HH:mm:ss,SSS
which is wrong.
The correct format is yyyy-MM-ddTHH:mm:ss,SSS
as specified by
wikipedia ISO 8601 page. 2.x is in consistent with wikipedia.
Without configuration file, lo4j 1.x emits the following message to stdout and no log4j log messages are printed:
log4j:WARN No appenders could be found for logger (language.Log4jLogger).
log4j:WARN Please initialize the log4j system properly.
log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.
Without configuration file, log4j 1.x with slf4j emit the following message to stdout and no log4j log messages are printed:
log4j:WARN No appenders could be found for logger (language.Slf4jLogger).
log4j:WARN Please initialize the log4j system properly.
log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.
Without configuration file, log4j 2.x emits the following message to stdout and log4j log messages are printed to stdout:
ERROR StatusLogger No log4j2 configuration file found. Using default configuration: logging only errors to the console.
19:50:47.889 [main] ERROR com.rainbow.LoggerTest - info message
Setup
Gradle:
compile "org.apache.logging.log4j:log4j-core:2.11.1"
compile "org.apache.logging.log4j:log4j-api:2.11.1"
log42.xml:
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="info">
<Properties>
<Property name="log-dir">./logs</Property>
</Properties>
<Properties>
<Property name="project-name">rainbow</Property>
</Properties>
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{ISO8601} %-5p [%t] %c{2}: %m%n"/>
</Console>
<RollingFile name="RollingFile" fileName="${log-dir}/${project-name}.log"
filePattern="${log-dir}/${project-name}-%d{yyyy-MM-dd}-%i.log">
<PatternLayout>
<pattern>%d{ISO8601} %-5p [%t] %c{2}: %m%n</pattern>
</PatternLayout>
<Policies>
<OnStartupTriggeringPolicy/>
<SizeBasedTriggeringPolicy size="250 MB"/>
</Policies>
<DefaultRolloverStrategy max="4"/>
</RollingFile>
</Appenders>
<Loggers>
<Root level="info">
<AppenderRef ref="Console"/>
<!-- <AppenderRef ref="RollingFile" /> -->
</Root>
</Loggers>
</Configuration>
Logback with slf4j
Without configuration file, logback log message are printed to stdout. For details, refer to Chapter 3: Logback configuration.
Setup
Gradle:
compile 'ch.qos.logback:logback-core:1.2.3'
compile 'ch.qos.logback:logback-classic:1.2.3'
compile 'org.slf4j:slf4j-api:1.7.25'
logback.xml:
<?xml version="1.0" encoding="UTF-8" ?>
<configuration debug="false">
<!-- Stop the annoying output INFO at start, -->
<!-- see https://www.mkyong.com/logging/how-to-stop-logback-status-info-at-the-start-of-every-log/ -->
<statusListener class="ch.qos.logback.core.status.NopStatusListener" />
<property name="log-dir" value="logs"/>
<property name="project-name" value="message-producer"/>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<layout class="ch.qos.logback.classic.PatternLayout">
<pattern>%d{ISO8601} %-5p [%t] %c{2}: %m%n</pattern>
</layout>
</appender>
<appender name="RollingFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log-dir}/${project-name}.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
<fileNamePattern>${log-dir}/${project-name}-%i.log</fileNamePattern>
<minIndex>1</minIndex>
<maxIndex>20</maxIndex>
</rollingPolicy>
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<maxFileSize>2MB</maxFileSize>
</triggeringPolicy>
<encoder>
<pattern>%d{ISO8601} %-5p [%t] %c{2}: %m%n</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="STDOUT" />
<!--<appender-ref ref="RollingFile" />-->
</root>
</configuration>
JSON
- Fasterxml
- Jackson Documentation Hub
- Jackson Annotations
- Jackson-databind 2.7.0 API
- Jackson-annotation 2.7.0
- Jackson-core 2.7.0 API
Netty
Colorful Terminal
Build
Maven
To download a package without a POM file:
mvn dependency:get -DgroupId=org.apache.kafka -DartifactId=kafka-clients -Dversion=0.9.0.0
Note that the above command can’t be used to download Javadoc jar or source
jar for a package.
Download Javadoc Jar:
mvn dependency:resolve -Dclassifier=javadoc
Download source jar:
mvn dependency:sources
For details, refer to:
Gradle
Gretty:
In different environments, Gradle may execute a set of test cases in different
orders. For example, there are test cases A and B. In environment 1, the
execution order is A -> B
. In environment 2, the execution order is B -> A
.
Test case failures related to such kind of execution order are hard to be fixed.
Gradle eclipse
task downloads source jars by default.
Configuration for Gradle eclipse
task:
eclipse.classpath {
defaultOutputDir = file("build-eclipse")
}
eclipseJdt.doLast {
File f1 = file(".settings/org.eclipse.core.resources.prefs")
String firstLine = "eclipse.preferences.version=1\n"
f1.write(firstLine)
f1.append("encoding/<project>=UTF-8\n")
File f2 = file(".settings/org.eclipse.core.runtime.prefs")
f2.write(firstLine)
f2.append("line.separator=\\n\n")
}
eclipseClasspath.dependsOn cleanEclipse
gradle run
accepts arguments with --args
. For exmaple,
gradle run --args="-b localhost:9092"
. gradle bootRun
also dose the same
thing. For example, gradle bootRun --args="--server.port=8888"
.
JVM
Scala
- Download Scala documentation from http://www.scala-lang.org/files/archive/
Web App
RequestDispatcher is confusing. But Introduction to Request Dispatcher explains it well.
classes places in src/test/java
are not loaded by gradle jettyRun
.
Error Handling in Web App
- If no customized error handling is provided, the default error page provided by the container such as Tomcat is returned. The error page usually contains Java exception stack trace. HTTP response is a error page with a HTTP status 500.
- Customized error handling usually return a HTTP response with status code 302.
The browser will then go to the URL contained in
Location
header. So there are two HTTP responses: one with status code 302 and one with status code 200.
One way to do customized error handling is to use a Filter
to catch the
exception and does the redirect with HttpServletResponse.sendRedirect
. Such a
filter catches the exceptions thrown by the application code and the exceptions
thrown by Web framework code. If Spring framework is used, a @ControllerAdvice
which has some methods annotated with @ExceptionHandler
can also be used. But
such a controller advice can only catch exceptions thrown by a web controller.
404 Not Found Handling in Web App
Approaches:
- Web container returns a 404 page by default. Only one HTTP response is produced.
- Web container returns a customized 404 page. Only one HTTP response is produced. github.com uses this approach.
- Web container returns a HTTP 302 response. Browser redirects to the
Location
header in the response. Two HTTP responses are produced: one HTTP 302 and one HTTP 200.
Servlet
The following conclusion is verified with gradle jettyRun
.
Filters are initialized before servlets. Unlike servlets which can be lazily
initialized, filters are always initialized when the servlet contains starts up.
ORM
Tomcat
Spring
- [Spring 3 MVC accessing HttpRequest from controller] (http://stackoverflow.com/questions/8504258/spring-3-mvc-accessing-httprequest-from-controller)
- [how to get getServletContext() in spring mvc Controller] (http://stackoverflow.com/questions/26923907/how-to-get-getservletcontext-in-spring-mvc-controller)
- how to modify tomcat8 acceptCount in spring boot
- TomcatEmbeddedServletContainerFactory
- Asciiart Banner Generation: Use
slant
Banner Font.
RequstMapping
If RequstMapping
s produces
field is not specified, the content-type
header of HTTP response is application/json;charset=UTF-8
.
If RequstMapping
s consumes
field is not specified, the behaviour is accept requests with any content-type
header.
Qualifier
Create the following three classes in a package which is component scaned.
@Service
public class Wife {
public Wife() {
System.out.println("construct a wife");
}
}
@Service
public class Husband {
public Husband() {
System.out.println("construct a husband");
}
}
@Service
public class Couple {
@Autowired(required = true)
public Couple(@Qualifier("husband") Husband husband, @Qualifier("wife") Wife wife) {
System.out.println("construct a couple");
}
}
A test case which knows how to load application context:
public class CoupleTest extends AbstractContextTest {
@Autowired
private Couple couple;
@Test
public void testConstruct() {
System.out.println(couple);
}
}
Running the test case produces:
construct a wife
construct a husband
construct a couple
com.rainbow.service.Couple@733c423e
MVC
If an interceptor’s preHandle
method returns false
for a HTTP GET, a
response with status code 200 and an empty body may be returned.
Magic
spittleList
forList<Spittle>
for model name- In
web.xml
, a serlvet named asappServlet
’s context isWEB-INF/appServlet-context.xml
. - In a application context xml file, the
/
in<import resource="classpath:/service-config.xml" />
is optional. The reason is thatClassPathResource.ClassPathResource(String path)
ignores it. supportedMediaTypes
inHttpMessageConverter
can be used to set HTTP response’sContent-Type
header.produces = MediaType.APPLICATION_JSON_VALUE
overrides it.
REST
- Improve Your Spring REST API, Part I
- Improve Your Spring REST API, Part II
- Improve Your Spring REST API, Part III
- Improve Your Spring REST API, Part IV
Allow CORS
Add the following text to web.xml:
<!-- CORS -->
<filter>
<filter-name>CorsFilter</filter-name>
<filter-class>com.rainbow.CorsFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>CorsFilter</filter-name>
<url-pattern>/rest/*</url-pattern>
</filter-mapping>
CorsFilter:
package com.rainbow;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;
/**
* Filter to allow CORS rest API calls.
*/
public class CorsFilter implements Filter {
@Override
public void doFilter(ServletRequest req, ServletResponse resp,
FilterChain chain) throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) resp;
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods",
"POST, GET, OPTIONS, DELETE");
response.setHeader("Access-Control-Max-Age", "3600"); // seconds
response.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
chain.doFilter(req, resp);
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {}
@Override
public void destroy() {}
}
JDBC
Before Java 7:
Connection conn = ...;
try {
// SQL statements
conn.commit();
} catch (Throwable t) {
if (conn != null) conn.rollback();
} finally {
if (conn != null) conn.close();
}
}
Java 7 and above:
try (Connection conn = ...;) {
conn.commit();
}
The above code works only if Connection.close
method does rollback
if
conn.commit()
is not executed. Java 8 Connection.close
method’s Javadoc
discourages this way.
And remember to always invoke Connection.close()
to release database resources.
If a thrown exception causes Java process to terminate, the OS will terminate
the Java process’s TCP connection to the remote database server. The database
server can release related resources. The related database resources will not be
released for the following cases:
- The exception is swallowed or the
- Network cable is unplugged suddenly. In this case, the OS hosting Java process does not have a change to send more TCP packages to do a graceful TCP close with the remote database server.
Be careful with JDBC url in Java code. Some unrecognized options are ignored
silently. If the JDBC url is given literally in XML, &
needs to be escaped. If
the JDBC url is given in a properties file, don’t escape it.
Performance Tuning
Web Application Development
Not need to close input stream for HTTP request. See Is is necessary to close the input stream returned from HttpServletRequest?. Not need to close output stream for HTTP response. See Should one call .close() on HttpServletResponse.getOutputStream()/.getWriter()?.
Caliper
Invoke Caliper
public class Entry {
public static void main(String[] args) {
CaliperMain.main(Tutorial.Benchmark1.class, new String[0]);
}
}
Benchmark:
@VmOptions("-XX:-TieredCompilation")
public static class Benchmark1 {
@Benchmark
void timeNanoTime(int reps) {
for (int i = 0; i < reps; i++) {
System.nanoTime();
}
}
}
Caliper 1.0-beta-2
is used. And guava 19.0
is required.
Concurrency
AtomicInteger.getAndIncrement:
public final int getAndIncrement() {
return unsafe.getAndAddInt(this, valueOffset, 1);
}
Unsafe.getAndAddInt:
public final int getAndAddInt(Object o, long offset, int delta) {
int v;
do {
v = getIntVolatile(o, offset);
} while (!compareAndSwapInt(o, offset, v, v + delta));
return v;
}
Java Concurrency in Practice’s Code Listings