Category Archives: Java

Sending html emails with images inside

If the images are external image links that can be accessed by http url, then nothing speical needs to be done.

If the images are external image files, then you have 2 choices.

Embed the images as base64 strings. . This is simple. However email clients such as gmail will refuse to render them

Using cid. This is the most robust way. An example can be found here . In addition, you should set the content type of the image body part as "image/png", otherwise the email receipients will see there are attachments

     
              MimeBodyPart imagePart = new MimeBodyPart();
                DataSource fds = new FileDataSource(imageFile);
                imagePart.setDataHandler(new DataHandler(fds));
                imagePart.setHeader("Content-ID", "<" + imageFile.getName() + ">");
                imagePart.setHeader("Content-Type", "image/png");

Things to do for a serious Java website when using Elastic Beanstalk

Before you start

Get a domain

If you haven’t got one, you can buy one. You can get it from AWS Route 53

Get a SSL certificate for your domain

You can get it from AWS Certificate Manager.

Create an IAM User

You will need this user’s access key and access secret to run eb-cli

Create a keypair

You need this to ssh login to your EC2 instance

Some limitations to your code

  • You application should listen to port 5000. Beanstalks’ included nginx will map it to 80
  • Don’t let your app read from System.getProperty(), but from System.getEnv(), if you don’t want to add any other file other than your *.war/*.shaded-jar to upload to Beanstalk. Tnat’s because Beanstalk can take enviroment properties only by default
  • Let all your log4j loggers write to console instead of any file. Console outptu will be automatically collected by Beanstalk
  • Let your webapp have a health check url, which can be configured with Beanstalk’s load balancer

Create your beanstalk application

You can do this with the web console or eb-cli

After application created

  • Add a CNAME to your own domain. So www.yoursite.com will point to xxx.xxx-beanstalk.com
  • In Beanstalk’s load balancer, disable http 80 and enable https 443, use the SSL certificate your got from AWS Certificate Manager
  • Try ssh log-in using eb-cli’s “eb ssh”.
  • Set up the security group of your RDS, so that you can access it from your local machine

Quick log4j set up

<!-- pom.xml --> 


		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-api</artifactId>
			<version>1.7.7</version>
		</dependency>

		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-log4j12</artifactId>
			<version>1.7.7</version>
		</dependency>


<!-- src/main/resources/log4j.xml --> 
  
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">

	<appender name="my-file-appender" class="org.apache.log4j.FileAppender">
		<param name="file" value="some.file" />
		<param name="append" value="true" />
		<param name="encoding" value="UTF-8" />
		<layout class="org.apache.log4j.PatternLayout">
			<param name="ConversionPattern" value="%d %t %-5p %c - %m%n" />
		</layout>
	</appender>

	<appender name="console" class="org.apache.log4j.ConsoleAppender">
	    <layout class="org.apache.log4j.PatternLayout">
		<param name="ConversionPattern" value="%d %t %-5p %c - %m%n" />
	    </layout>
	</appender>

	<logger name="com.my.package">
		<level value="info" />  
		<appender-ref ref="my-file-appender" />
	</logger>	

	<root>
		<level value="warn" />
		<appender-ref ref="console" />
	</root>

</log4j:configuration>

If you don’t get expected result, add "-Dlog4j.debug" to your VM options and see the diagnotisc information

Dynamic file name in log4j’s file appender

In your log4j.xml, set the file name as a variable

 	<appender name="my-file-appender" class="org.apache.log4j.FileAppender">
		<param name="file" value="${myFilePath}" />
		<!-- ... --> 
	</appender> 

Then in your java code, make sure the following is called before the first getLogger() call

 	System.setProperty("myFilePath", someFilePath); 

Quartz concepts

Basic concepts

  • Job: What to do
  • Trigger: When to do it
  • Scheduler: The director to associate triggers to jobs and to invoke the trigger

A job can be associated with several triggers, but a trigger should not be used to trigger more than one job

Go into details for some depth

  • JobKey = Job Name + Job Group, TriggerKey = Trigger Name + Trigger Group: The `name` and `namespace` of these things, i.e. the ID
  • Jod Data Map: The context for jobs or triggers

Somethings that may be confusing

  • Job detail: Running instance of a job (IMAO, It’s overdesign) . For a single HelloJob class, you can create a JobDetail based on it called `fooJobDetail` and another called `barJobDetail`, each has its own Job Data Map and its own JobKey
  • DisallowConcurrentExecution: If it is set up on HelloJob class, then only one process of "fooJobDetail" can be run at the same time; But you can run `barJobDetail` at the same time.

Persistence

  • JDBC-JobStore: Not only job, but schedulers/triggers/jdbcDataMap can be saved into database. You can use jdbc configurations as well as dataSource

Clustering

  • Single-node firing: With Jbdc-JobStore you can make sure only one node will fire the job for each firing. All you need to do is to set `org.quartz.jobStore.isClustered = true`
  • Fail-over: fail-over can also be done if `org.quartz.jobStore.isClustered = true` and the JobDetail`s `request recovery` flag is set to true).

Things that can be handy

  • Calendar: If you have trigger that fires everday but not during public holdays, you can use this
  • Job/Trigger/Scheduler Listeners: an interface to let you plugin some callbacks when something is done. Can be handy for monitoring or auditing purposes.

To make things more robust

  • RequestsRecovery: After you recover from a `hard shutdown`, when the scheduler starts again, the job that went away during the hard shutdown will be re-executed. This only applies to persistent jobs.
  • Misfire Instructions: A trigger may not be fired when it should. When a scheduler is started again, it will find out all misfired trigger to do something, such as MISFIRE_INSTRUCTION_FIRE_NOW or MISFIRE_INSTRUCTION_DO_NOTHING. This only applies to persistent triggers.

Customization

  • DirectSchedulerFactory: with this you don’t have to use quartz.properties but rather configure them in a programatic way

How to prevent Quartz from re-firing a job while it is still running

Answer: use @DisallowConcurrentExecution

Example

Without this annotation


import java.time.LocalTime;

import org.quartz.DisallowConcurrentExecution;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;

public class HelloJob implements Job {


    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {

        System.out.println(Thread.currentThread().getName() +  ":Job started at " + LocalTime.now());
        try {
            Thread.sleep(10 * 1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println(Thread.currentThread().getName() + ":Job done at " + LocalTime.now());

    }
}

import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.impl.StdSchedulerFactory;

import static org.quartz.SimpleScheduleBuilder.simpleSchedule;

public class DisallowConcurrentExecutionExample {

    public static void main(String[] args) throws Exception {

        Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
        scheduler.start();


        JobDetail job = JobBuilder.newJob(HelloJob.class).withIdentity("helloJob", "anyGroup").build();
        Trigger trigger = TriggerBuilder.newTrigger().withIdentity("helloTrigger", "anyGroup")
                                        .withSchedule(simpleSchedule()
                                        .withIntervalInSeconds(5) //re-fire every five minutes
                                        .withRepeatCount(2))  //so with the first invocation together there will be 3 times
                                        .startNow().build();

        scheduler.scheduleJob(job, trigger);
    }
}

And the output will be


LearnQuartz_Worker-1:Job started at 18:31:28.296
LearnQuartz_Worker-2:Job started at 18:31:33.275   #the first run hasn't been finished yet! 
LearnQuartz_Worker-3:Job started at 18:31:38.275
LearnQuartz_Worker-1:Job done at 18:31:38.353
LearnQuartz_Worker-2:Job done at 18:31:43.275
LearnQuartz_Worker-3:Job done at 18:31:48.275

Now if you put @DisallowConcurrentExecution to HelloJob class, the output will be like,

LearnQuartz_Worker-1:Job started at 18:34:44.665
LearnQuartz_Worker-1:Job done at 18:34:54.723
LearnQuartz_Worker-2:Job started at 18:34:54.726 #On hold until the first run has finished
LearnQuartz_Worker-2:Job done at 18:35:04.726
LearnQuartz_Worker-3:Job started at 18:35:04.727
LearnQuartz_Worker-3:Job done at 18:35:14.727

That’s what we want.

There are two Application Contexts in Spring MVC apps

The root context, normally seen as biz-layer context, is normally defined as "applicationContext.xml" and loaded by ContextLoaderListener. 

The other context, the web-layer one is normally defined as "mvc-servlet.xml" and loaded by DispatcherServlet.

The web-layer context  will inherit beans from the root one, Except  some processors such as PropertyConfigurator. As a result, you should define two PropertyConfigurators. One for the biz layer, the other for the web layer.   See https://stackoverflow.com/questions/11890544/spring-value-annotation-in-controller-class-not-evaluating-to-value-inside-pro 

You may argue that your system has only one PropertyConfigurator and things work fine.  In this case, it’s highly possible your web-layer context is scanning biz-layer classes,  in addition to the web-layer classes.  As a result, your biz-layer spring beans have been loaded twice, one by the  ContextLoaderListener, the other by DispatcherServlet.