Commits

Yasir Arsanukaev  committed 9621526

Initial commit

  • Participants

Comments (0)

Files changed (28)

+db/
+���� ������, ���� �������������, ���� � ��������� ��� ������������ � ����������� �����
+
+lib/
+���������� Java, ����������� ��� ���������� � ������ ����������
+
+src/
+����� � �������� ����� ����������
+
+build.xml
+���������� ��� ������� ��������� ������ ant
+
+cert.xslt
+XSLT. ���� ��������� ��, ��� ������ ��������� ������������ � ������� Apache FOP ��������
+
+certbg.jpg
+��������.
+
+message.html
+����� ��������� ������������ ������
+
+mine.xconf
+��������� ��� Apache FOP
+���������� ��������� ������������ ����������� � PDF � ���������� �� �� ����������� ������� �� ���� ������.
+
+� ������ ������ ��� �������� � �������� ���� ������������ SQLite ������ 3.
+
+���� ������ ������������ ����������� ������ �������� �� CSV-����� � ������� ������� SQLite-���������� .import. �������� ������:
+
+sqlite> .separator "^"
+sqlite> .import data.csv imp
+
+��� imp -- �������� ������� � ��������������� ������� �������������. (��� ����� ���� ����� ����������, �������� ������� .schema � ��������� ������ SQLite.)
+
+��� ����� �� �������, ����� �������� ���������� �� ��������� ������������� ���� � ��-CSV-������� �������� CSV-����, ��� ���� ��������� �������� ^. ��� ����� ����� ������� � ������� �������� � OpenOffice � ��������� ^ � �������� �����������.
+
+��� ������ ������ ������ � ���� ������ SQLite, ���������� �� �������������. ��� ����� ����� ������������� ��� ������ � ������� imp � ������� ��������, ������� ������� � �������, ������� ������, ��� � ������� ������ ����������� ������ @.
+
+��� ��������� ������ ������ ����� �������� ����������. ��� ����� ������ ������ ����� ��������� ������������ begin/commit. ��� ����� ����� �������� ��������� ���������, ����� ��� ������������ DML �� �������� �� �����.
+
+��� ����� ������ ���� queries.sql, ����� ��� ������ �������� �� ���������� �������, ������������� ����. ����� ���� ������� �� �������� ����� � �������������� ��������, ��������.
+
+��������� ������������� ��� � ��-CSV-����� ���������� �� ����, ��� ������������ � ���� ������, �� ���������� ������������� ��������������� �������� � ������� ���� � ������� imp. ��� ����� ����� ��������� ������� ant convert-dates (����� ���� ������� � ����� build.xml).
+
+����� ���� ��� ������������ ���� �����������, ���������� ����������� ��� ������ �� ������������� impnotx � ������� users. ������ users � ����� �������� ��� ��������, ������ ���������� ������� ����������, ����������� ��� ��������. ����������� ������ ����� �� ���� � ����� queries.sql.
+
+����� ����������� ����� ������� � ������� users, �������� ������� certsent � ��� ����� ����� 0; ��� ���������� �������� ��������� ����, ��� �� ��� ������ �������� ��� �� �������������.
+
+��������� ���������� � �������� �������� ����������� � ����� SendEmail.java.
+
+���� ���������� ����� �������������� � ������� ������� ant jar. ����� ��� ���������� ������� ��������� ant clean, ����� ������� ��� ���������� ���������������� �������. �� ���� ���������� ����� ������ ��� ��������� �������� ���������� � �������� ��������. ��������, ��� ���������� ������������ � ���������� ��������� �������, ��� ��������� �������� �� �����.
+
+/usr/share/fonts -- ���������� ����� �������, ��� ����� ��������� ��������� English Rose, � ������� ������� ���������� ��� ����������� ���������� �����������.
+
+����� ���� ���������������� �������� ��������� �������� ����� � ������� ������� ant run.
+<?xml version="1.0"?>
+<project default="default" basedir="." name="rpcmp.cert">
+	<property name="build.dir" location="build"/>
+	<property name="classes.dir" value="${build.dir}/classes"/>
+	<property name="src.dir" location="src"/>
+	<property name="lib.dir"     value="lib"/>
+	<property name="jar.dir"     value="${build.dir}/jar"/>
+
+	<property name="main-class"  value="rpcmp.cert.Main"/>
+
+	<path id="classpath">
+		<fileset dir="${lib.dir}" includes="**/*.jar"/>
+	</path>
+
+	<target name="default" depends="jar"></target>
+
+	<target name="clean">
+		<delete dir="${build.dir}"/>
+	</target>
+
+	<target name="compile" depends="clean">
+		<delete dir="${classes.dir}" />
+		<mkdir dir="${classes.dir}" />
+		<!-- Yasir: We've specified encoding here
+		     otherwise some text might have been garbled. -->
+		<javac srcdir="${src.dir}" destdir="${classes.dir}" classpathref="classpath" encoding="utf-8">
+		</javac>
+	</target>
+
+	<target name="jar" depends="compile">
+		<mkdir dir="${jar.dir}"/>
+		<jar destfile="${jar.dir}/${ant.project.name}.jar" basedir="${classes.dir}">
+			<manifest>
+				<attribute name="Main-Class" value="${main-class}"/>
+			</manifest>
+		</jar>
+	</target>
+
+	<target name="run">
+		<java fork="true" classname="${main-class}">
+			<classpath>
+				<path refid="classpath"/>
+				<path location="${jar.dir}/${ant.project.name}.jar"/>
+			</classpath>
+		</java>
+	</target>
+
+	<target name="convert-dates">
+		<java fork="true" classname="rpcmp.cert.ConvertDates">
+			<classpath>
+				<path refid="classpath"/>
+				<path location="${jar.dir}/${ant.project.name}.jar"/>
+			</classpath>
+		</java>
+	</target>
+</project>
+<?xml version="1.0" encoding="UTF-8"?>
+<root>
+<id>42</id>
+<firstname>Ясир</firstname>
+<lastname>Арсанукаев</lastname>
+<added_date>03.09.2013</added_date>
+<added_year>2013</added_year>
+</root>
+<?xml version="1.0" encoding="utf-8"?>
+<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:xs="http://www.w3.org/2001/XMLSchema">
+	<xsl:output method="xml" indent="yes"/>
+	<xsl:template match="/">
+		<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
+			<fo:layout-master-set>
+				<fo:simple-page-master master-name="simpleA4" page-width="29.7cm" page-height="21cm" margin-top="0cm" margin-bottom="0cm" margin-left="0cm" margin-right="0cm">
+					<fo:region-body/>
+				</fo:simple-page-master>
+			</fo:layout-master-set>
+			<fo:page-sequence master-reference="simpleA4">
+				<fo:flow flow-name="xsl-region-body">
+					<fo:block-container position="absolute" left="0" width="29.7cm" top="0" height="21cm"
+						background-image="certbg.jpg">
+						<fo:block />
+					</fo:block-container>
+
+					<fo:block-container position="absolute" top="32%" height="3cm">
+						<fo:block font-family="Monotype Corsiva"
+							     font-size="16pt" 
+							     text-align="center">
+						     www.rpcmp.ru
+						</fo:block>
+					</fo:block-container>
+
+					<fo:block-container position="absolute" top="38%" height="3cm">
+						<fo:block font-family="Monotype Corsiva"
+							     font-size="16pt" 
+							     text-align="center">
+						     В соответствии с властью, возложенной на него, подписант сего сертификата, Пастриарх Кама Паста I,
+						</fo:block>
+						<fo:block font-family="Monotype Corsiva"
+							     font-size="16pt" 
+							     text-align="center">
+						     подтверждает, что отныне
+						</fo:block>
+					</fo:block-container>
+
+					<fo:block-container position="absolute" top="48%" height="3cm">
+						<fo:block font-family="English Rose"
+							     font-size="36pt" 
+							     text-align="center">
+							     <xsl:value-of select="concat(/root/firstname, '      ', /root/lastname)"/>
+						</fo:block>
+					</fo:block-container>
+
+					<fo:block-container position="absolute" top="58%" height="3cm">
+						<fo:block font-family="Monotype Corsiva"
+							     font-size="16pt" 
+							     text-align="center">
+							является членом
+						</fo:block>
+					</fo:block-container>
+
+					<fo:block-container position="absolute" top="64%" left="10%" height="3cm">
+						<fo:block font-family="Monotype Corsiva"
+							     font-size="18pt" 
+							     text-align="left">
+							Русской Пастафарианской Церкви Макаронного Пастриархата,
+						</fo:block>
+						<fo:block font-family="Monotype Corsiva"
+							     font-size="14pt" 
+							     text-align="left">
+							о чём была произведена запись в Монструозном Регистре № М-<xsl:value-of select="/root/id"/>-<xsl:value-of select="/root/added_year"/> от <xsl:value-of select="/root/added_date"/>.
+						</fo:block>
+					</fo:block-container>
+
+					<fo:block-container position="absolute" top="81%" left="23%" height="3cm" width="10cm">
+						<fo:block font-family="Monotype Corsiva"
+							     font-size="18pt" 
+							     text-align="center">
+							     _______________________________
+						</fo:block>
+						<fo:block font-family="Monotype Corsiva"
+							     font-size="14pt" 
+							     text-align="center">
+							     Пастриарх Кама Паста I
+						</fo:block>
+					</fo:block-container>
+				</fo:flow>
+			</fo:page-sequence>
+		</fo:root>
+	</xsl:template>
+</xsl:stylesheet>

File certbg.jpg

Added
New image
+CREATE TABLE 'users' (
+      'id' INTEGER PRIMARY KEY AUTOINCREMENT
+    , 'firstname' TEXT NOT NULL
+    , 'lastname' TEXT NOT NULL
+    , 'email' TEXT NOT NULL UNIQUE
+    , 'added' DATE NOT NULL DEFAULT (datetime('now'))
+    , 'age' TEXT
+    , 'city' TEXT
+    , 'district' TEXT
+    , 'certsent' INTEGER NOT NULL DEFAULT 0
+);
+
+CREATE TABLE 'imp' (
+      'added' TEXT NOT NULL
+    , 'firstname' TEXT NOT NULL
+    , 'lastname' TEXT NOT NULL
+    , 'age' TEXT
+    , 'city' TEXT
+    , 'district' TEXT
+    , 'email' TEXT NOT NULL
+);
+
+insert into users(firstname,lastname,email,added) values('Yasir','Arsanukaev','yasir.arsanukaev@example.com','2011-04-03 10:17:32');
+insert into users(firstname,lastname,email) values('John','Doe','yasir.arsanukaev@example.net');
+insert into users(firstname,lastname,email) values('John','Smith','yasir.arsanukaev@example.org');
+insert into users(firstname,lastname,email) values('Invalid','Email','pfff');
+
+select * from users;

File db/queries.sql

+> DONT FORGET TO CONVERT DATES with ant convert-dates !!!
+sqlite> select count(*) from imp;
+14049
+
+sqlite> select email,max(rowid) from imp group by email having count(*) > 1 order by count(*);
+
+sqlite> select count(*) from (select max(rowid) from imp group by email having count(*) > 1);
+943
+
+sqlite> update imp set firstname=trim(firstname), lastname=trim(lastname), email=trim(email);
+sqlite> update imp set email=replace(email, ' ', '');
+
+sqlite> select rowid,* from imp where firstname like '% %';
+
+sqlite> begin;
+sqlite> delete from imp where rowid not in(select max(rowid) from imp group by email having count(*) > 1) and email in (select email from imp group by email having count(*) > 1);
+sqlite> select count(*) from imp;                                                               12798
+sqlite> commit;
+
+select count(*) from (select firstname,lastname,count(*) from imp group by firstname,lastname having count(*) > 1);
+648
+
+> DONT FORGET TO CONVERT DATES with ant convert-dates !!!
+
+sqlite> delete from imp where email not like '%@%';
+sqlite> update users set certsent = 0 where rowid in (select u.rowid from users u inner join imp i on u.email = i.email);
+
+sqlite> delete from imp where rowid in (select i.rowid from users u inner join imp i on u.email = i.email);
+
+> DONT FORGET TO CONVERT DATES with ant convert-dates !!!
+
+sqlite> insert into users (added,firstname,lastname,age,city,district,email) select added,firstname,lastname,age,city,district,email from impnotx;

File db/users.sqlite3

Binary file added.

File lib/avalon-framework-4.2.0.jar

Binary file added.

File lib/batik-all-1.7.jar

Binary file added.

File lib/commons-io-1.3.1.jar

Binary file added.

File lib/commons-logging-1.0.4.jar

Binary file added.

File lib/fop.jar

Binary file added.

File lib/javax.mail.jar

Binary file added.

File lib/serializer-2.7.0.jar

Binary file added.

File lib/sqlite-jdbc-3.7.2.jar

Binary file added.

File lib/xalan-2.7.0.jar

Binary file added.

File lib/xercesImpl-2.7.1.jar

Binary file added.

File lib/xml-apis-1.3.04.jar

Binary file added.

File lib/xml-apis-ext-1.3.04.jar

Binary file added.

File lib/xmlgraphics-commons-1.5.jar

Binary file added.

File message.html

+<html><head>
+		<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+		<title>Сертификат пастафарианина</title>
+		<style>
+			body, p, li { font-family: Helvetica, Verdana, serif; font-size: 12pt; }
+
+			.small {
+				font-family: Helvetica, Verdana;
+				font-size: 6pt;
+				font-style: italic;
+			}
+		</style>
+	</head>
+
+	<body>
+		<span class="small">Вы получили это письмо, потому что заполнили заявление о вступлении в Русскую Пастафарианскую Церковь на сайте <a href="http://rpcmp.ru/">rpcmp.ru</a>. Пожалуйста, не отвечайте на него. Если у вас есть вопросы, пишите нам по адресу <a href="mailto:info@rpcmp.ru?subject=Сертификат пастафарианина: обратная связь">info@rpcmp.ru</a>.</span>
+		<br />
+		<br />
+		<p>Уважаемый вновьобращенный и новорукоположенный пастафарианин!</p>
+
+		<p>От имени Русской Пастафарианской Церкви Макаронного Паcтриархата поздравляю тебя с самым важным событием в твоей прежде никчемной жизни! Теперь тебя будет окормлять РПЦ МП именем Его Великой Макаронины!</p>
+
+		<p>Гордись этим и гордо распространяй Его слово и Его заповеди среди людей, открывай Истину своим друзьям и близким. Отныне будет много Святой Лапши на твоём столе!</p> 
+
+		<p>Строй свою жизнь в соответствии с Его Евангелием и Восемью заповедями «Лучше бы ты этого действительно не делал»!</p>
+
+		<p>Размести этот Сертификат на своем сайте, в блогах, распечатай и повесь его на самом видном месте на работе и дома!</p>
+
+		<p>При желании ты можешь стать архиеерем нашей церкви! Для этого нужно следующее. 
+		</p><ol>
+		<li>Прислать фотографию в духе и в стиле Летающего Макаронного Монстра. (Примеры ты найдешь на <a href="http://www.rpcmp.ru/#%21eparchy/cixg">странице сайта</a>, твоя фотография будет размещена там же.)</li>
+		<li>Быть готовым окормлять мирян, которые живут по месту нахождения Епархии, и нести им Монструозное Слово.</li>
+		<li>Участвовать в Макаронных Архиерейский собраниях, быть инициатором и организатором пастаславных мероприятий на своей территории, поддерживать группы в социальных сетях, сообщать Пастриарху о событиях своей Епархии.</li>
+		<li>После катехизации успешной сдать экзамен на знание Евангелия. (В разработке.)</li>
+	</ol>
+
+	<p>Владыка Пастриарх сожалеет, что сей сертификат заставил тебя долго ждать! Но уверен, что Вера твоя только укрепилась от ожидания, ибо не в сертификате она заложена, но в силе духа пастафарианского!</p>
+
+	<p>И да коснется тебя Его Наимакароннейшая Десница!</p>
+
+	<p>РАминь.</p>
+
+	<span class="small">P.S.: Пожалуйста, не отвечай на это письмо, оно было сформировано и отправлено роботом.</span>
+
+	<p>+7 495 669 5205 (послушать молитву и оставить сообщение ЛММ).</p>
+
+	<p>Сайт Церкви в интернете, а также страницы в соцсетях:<br />
+	<a href="http://rpcmp.ru/">http://rpcmp.ru</a><br />
+	Facebook: <a href="https://www.facebook.com/rpcmp">https://www.facebook.com/rpcmp</a><br />
+	Twitter: <a href="http://twitter.com/RPCMP">http://twitter.com/RPCMP</a><br />
+	ВКонтакте: <a href="https://vk.com/rpcmp">https://vk.com/rpcmp</a><br />
+	YouTube: <a href="http://www.youtube.com/RPCMPdotRU">http://www.youtube.com/RPCMPdotRU</a>
+	</p>
+	<a href="http://rpcmp.ru/" target="_blank"><img src="http://i.imgur.com/DSdD6UV.jpg" height="137px" width="225px"></a>
+</body></html>
+<?xml version="1.0"?>
+
+<!-- NOTE: This is the version of the configuration -->
+<fop version="1.0">
+
+  <!-- Base URL for resolving relative URLs -->
+  <base>.</base>
+
+  <!-- Source resolution in dpi (dots/pixels per inch) for determining the size
+       of pixels in SVG and bitmap images, default: 72dpi -->
+  <source-resolution>72</source-resolution>
+  <!-- Target resolution in dpi (dots/pixels per inch) for specifying
+       the target resolution for generated bitmaps, default: 72dpi -->
+  <target-resolution>72</target-resolution>
+
+  <!-- Default page-height and page-width, in case
+       value is specified as auto -->
+  <default-page-settings height="11in" width="8.26in"/>
+
+  <!-- Information for specific renderers -->
+  <!-- Uses renderer mime type for renderers -->
+  <renderers>
+    <renderer mime="application/pdf">
+      <filterList>
+      </filterList>
+      <fonts>
+	      <directory recursive="true">/usr/share/fonts</directory>
+	      <!-- <directory recursive="true">C:\Windows\Fonts</directory> -->
+        <auto-detect/>
+      </fonts>
+    </renderer>
+  </renderers>
+</fop>

File src/rpcmp/cert/ConvertDates.java

+package rpcmp.cert;
+
+// Java
+import java.text.SimpleDateFormat;
+
+import java.sql.*;
+
+public class ConvertDates {
+	public static void main(String[] args) {
+		Connection conn = null;
+		try {
+			// Initialize database engine
+			Class.forName("org.sqlite.JDBC");
+			conn = DriverManager.getConnection("jdbc:sqlite:db/users.sqlite3");
+			conn.setAutoCommit(false);
+
+			// Statement to select all users which
+			// haven't been sent email yet.
+			Statement stmtQuery = conn.createStatement();
+			// sqlite format, extracts date as dd-mm-yyyy
+			//String stmtQueryString = "select id,added from users_import;";
+			String stmtQueryString = "select rowid,added from imp;";
+			
+			// Statement to update email sent status of the user.
+			String stmtUpdateString = 
+				"update imp set added = ? where rowid = ?";
+			PreparedStatement stmtUpdate = 
+				conn.prepareStatement(stmtUpdateString);
+			
+
+			// Iterate through the user rows 
+			ResultSet rs = stmtQuery.executeQuery( stmtQueryString );
+			while ( rs.next() ) {
+				// retrieve user details
+				int    id        = rs.getInt("rowid");
+				String added	 = rs.getString("added");
+
+				// output details to console
+				/*System.out.println( "id = "        + id );
+				System.out.println( "added = " 	   + added );
+				System.out.println(); */
+
+				// Date format conversion
+				String convertedDate = null;
+				String sourceDate = added;
+				try {
+					SimpleDateFormat fromFmt = 
+						new SimpleDateFormat("MM/dd/yyyy HH:mm:ss");
+					SimpleDateFormat toFmt   = 
+						new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+					convertedDate = 
+						toFmt.format(fromFmt.parse(sourceDate));
+				} catch (java.text.ParseException pe) {
+					pe.printStackTrace();
+					System.exit(-1);
+				}
+
+				// Write converted date value back to db
+				stmtUpdate.setString(1, convertedDate);
+				stmtUpdate.setInt(2, id);
+				int rowsupd = stmtUpdate.executeUpdate();
+				if (rowsupd != 1) {
+					System.out.println(
+						"Update statement should've updated" 
+						+ "exactly 1 (one) row, "
+						+ "while actually it updated this "
+					       	+ "number of rows: " + rowsupd );
+					System.exit(-1);
+				}
+			}
+			rs.close();
+			conn.commit();
+			stmtQuery.close();
+			stmtUpdate.close();
+			conn.close();
+		} catch (Exception e) {
+			try {
+				conn.rollback();
+			} catch(SQLException excep) {
+				e.printStackTrace(System.err); 
+			}
+			e.printStackTrace(System.err);
+			System.exit(-1);
+		}
+	}
+
+}

File src/rpcmp/cert/GenCert.java

+package rpcmp.cert;
+
+// Java
+import java.io.ByteArrayOutputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.StringReader;
+import java.io.IOException;
+import java.io.OutputStream;
+
+//JAXP
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.Source;
+import javax.xml.transform.Result;
+import javax.xml.transform.stream.StreamSource;
+import javax.xml.transform.sax.SAXResult;
+
+
+// FOP
+import org.apache.fop.apps.FOUserAgent;
+import org.apache.fop.apps.Fop;
+import org.apache.fop.apps.FOPException;
+import org.apache.fop.apps.FopFactory;
+import org.apache.fop.apps.FormattingResults;
+import org.apache.fop.apps.MimeConstants;
+import org.apache.fop.apps.PageSequenceResults;
+
+
+
+public class GenCert {
+
+    private FopFactory fopFactory = FopFactory.newInstance();
+    File xslt = new File("cert.xslt");
+
+    public void init() {
+	    try {
+		    // Configure FopFactory
+		    fopFactory.setUserConfig(new File("mine.xconf"));
+	    } catch (Exception e) {
+		    e.printStackTrace(System.err);
+		    System.exit(-1);
+	    }
+    }
+
+    public void convertFO2PDF(OutputStream out, String xmlstring) 
+	    throws IOException, FOPException {
+        try {
+   	    FOUserAgent foUserAgent = fopFactory.newFOUserAgent();
+
+            // Setup output stream.  Note: Using BufferedOutputStream
+            // for performance reasons (helpful with FileOutputStreams).
+            out = new BufferedOutputStream(out);
+	    
+            // Setup input stream
+	    StringReader xml = new StringReader(xmlstring);
+            Source srcXml = new StreamSource(xml);
+	    Source srcXslt = new StreamSource(xslt);
+
+            // Construct fop with desired output format
+            Fop fop = fopFactory.newFop(MimeConstants.MIME_PDF, foUserAgent, out);
+
+            // Setup JAXP using identity transformer
+            TransformerFactory factory = TransformerFactory.newInstance();
+            Transformer transformer = factory.newTransformer(srcXslt);
+	    transformer.setParameter("versionParam", "2.0");
+
+            // Resulting SAX events (the generated FO) must be piped through to FOP
+            Result res = new SAXResult(fop.getDefaultHandler());
+
+            // Start XSLT transformation and FOP processing
+            transformer.transform(srcXml, res);
+        } catch (Exception e) {
+            e.printStackTrace(System.err);
+            System.exit(-1);
+        } finally {
+            out.close();
+        }
+    }
+}

File src/rpcmp/cert/Main.java

+package rpcmp.cert;
+
+// Java
+import java.io.ByteArrayOutputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+//JAXP
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.Source;
+import javax.xml.transform.Result;
+import javax.xml.transform.stream.StreamSource;
+import javax.xml.transform.sax.SAXResult;
+
+
+// FOP
+import org.apache.fop.apps.FOUserAgent;
+import org.apache.fop.apps.Fop;
+import org.apache.fop.apps.FOPException;
+import org.apache.fop.apps.FopFactory;
+import org.apache.fop.apps.FormattingResults;
+import org.apache.fop.apps.MimeConstants;
+import org.apache.fop.apps.PageSequenceResults;
+
+import java.sql.*;
+
+public class Main {
+	public static void main(String[] args) {
+		String subject = "Сертификат пастафарианина";
+		Connection conn = null;
+		ByteArrayOutputStream outputStream = null;
+
+		SendEmail eml = new SendEmail();
+		GenCert cert = new GenCert();
+		cert.init();
+		eml.init();
+
+		try {
+			String xsltfile = new String("cert.xslt");
+
+			// Initialize database engine
+			Class.forName("org.sqlite.JDBC");
+			conn = DriverManager.getConnection("jdbc:sqlite:db/users.sqlite3");
+			conn.setAutoCommit(true);
+
+			// Statement to select all users which
+			// haven't been sent email yet.
+			Statement stmtQuery = conn.createStatement();
+			// sqlite format, extracts date as dd-mm-yyyy
+			String dateStr = 
+				" strftime('%d.%m.%Y', added) as added_date ";
+			String yearStr = " strftime('%Y', added) as added_year ";
+			String stmtQueryString = 
+				"select id, firstname, lastname, email"
+			       + ", " + dateStr
+			       + ", " + yearStr
+			       + " from users where certsent = 0;";
+			
+			// Statement to update email sent status of the user.
+			String stmtUpdateSentString = 
+				"update users set certsent = 1 where id = ?";
+			PreparedStatement stmtUpdateSent = 
+				conn.prepareStatement(stmtUpdateSentString);
+
+			// Iterate through the user rows 
+			// sending email to them along the way.
+			ResultSet rs = stmtQuery.executeQuery( stmtQueryString );
+			while ( rs.next() ) {
+				// retrieve user details
+				int    id        = rs.getInt("id");
+				String firstname = rs.getString("firstname");
+				String lastname  = rs.getString("lastname");
+				String email     = rs.getString("email");
+				String added_date= rs.getString("added_date");
+				String added_year= rs.getString("added_year");
+
+				// output details to console
+				System.out.println( "id = "        + id );
+				System.out.println( "firstname = " + firstname );
+				System.out.println( "lastname = "  + lastname );
+				System.out.println( "email = "     + email );
+				System.out.println( "addeddate = " + added_date );
+				System.out.println( "addedyear = " + added_year );
+				System.out.println();
+
+				// generate pdf from user data in database
+				outputStream = new ByteArrayOutputStream();
+				cert.convertFO2PDF(outputStream,
+					"<?xml version='1.0' encoding='UTF-8'?><root>"
+					+ "<id>"        + id        + "</id>"
+					+ "<firstname>" + firstname + "</firstname>"
+					+ "<lastname>"  + lastname  + "</lastname>"
+					+ "<added_date>"+ added_date+ "</added_date>"
+					+ "<added_year>"+ added_year+ "</added_year>"
+					+ "</root>"
+				);
+
+				/* Send off email with pdf attachment.
+				 * Email message is sent as an HTML part
+				 * (see appropriate module) */
+				boolean result  = false;
+				result 		= eml.sendeml(
+							outputStream
+							, email
+							, subject); 
+				if (result == true) {
+
+					// Update "email sent" status,
+					// exit if less than one row is updated
+					stmtUpdateSent.setInt(1, id);
+					int rowsupd = stmtUpdateSent.executeUpdate();
+					if (rowsupd != 1) {
+						System.out.println(
+							"The app was unable to update" 
+							+ "exactly 1 (one) row. "
+							+ "It actually updated this "
+							+ "number of rows: " + rowsupd );
+						System.exit(-1);
+					}
+				} else {
+						System.out.println(
+							"failed to send email to "
+							+ email
+							+ ". Not updating cert sent status.");
+				}
+			}
+			rs.close();
+			stmtQuery.close();
+			stmtUpdateSent.close();
+			conn.close();
+
+			System.out.println("main app finished successfully");
+		} catch (Exception e) {
+			e.printStackTrace(System.err);
+			System.exit(-1);
+		}
+	}
+}

File src/rpcmp/cert/SendEmail.java

+package rpcmp.cert;
+
+// Java
+import java.io.*;
+import java.io.ByteArrayOutputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+// Java Mail
+import java.util.*;
+import javax.mail.*;
+import javax.mail.util.*;
+import javax.mail.internet.*;
+import javax.activation.*;
+
+public class SendEmail {
+	DataSource htmlSource = null;
+
+	final String username = new String("example@gmail.com");
+	final String password = new String("hackme");
+
+	Properties props = new Properties();
+	Session session = null;
+	Transport transport = null;
+
+	public void init() {
+		// Settings for email server requiring authentication
+		props.put("mail.smtp.auth", "true");
+		props.put("mail.smtp.starttls.enable", "true");
+		props.put("mail.smtp.host", "smtp.gmail.com");
+		props.put("mail.smtp.port", "587");
+		
+		// Settings for local mail server which doesn't require authn
+		/*
+		   props.put("mail.smtp.auth", "false");
+		   props.put("mail.smtp.starttls.enable", "true");
+		   props.put("mail.smtp.host", "localhost");
+		*/
+		
+		session = Session.getInstance(props);
+
+		try {
+			transport = session.getTransport("smtp");
+			transport.connect(username, password);
+		} catch (Exception e) {
+			e.printStackTrace(System.err);
+			System.exit(-1);
+		}
+
+		htmlSource = new FileDataSource("message.html");
+	}
+
+	public boolean sendeml(ByteArrayOutputStream outputStream
+			, String sendTo
+			, String subject) 
+	{
+		boolean result = true;
+
+		// Who will receive an email
+		InternetAddress addressTo = null;
+		try {
+			addressTo = new InternetAddress(sendTo);
+		} catch (AddressException ex) {
+			ex.printStackTrace();
+			return false;
+		}
+
+		try {
+			// Construct the HTML body part
+			BodyPart htmlBodyPart = new MimeBodyPart();
+			htmlBodyPart.setDataHandler(new DataHandler(htmlSource));
+			htmlBodyPart.setHeader(
+					"Content-Type"
+					, "text/html; charset=utf-8;");
+			
+			// Construct the PDF body part
+			byte[] bytes = outputStream.toByteArray();
+			DataSource source = new ByteArrayDataSource(
+					bytes, "application/pdf");
+			BodyPart pdfBodyPart = new MimeBodyPart();
+			pdfBodyPart.setDataHandler(new DataHandler(source));
+			pdfBodyPart.setFileName("RPCMP_cert.pdf");
+
+			// Construct the multipart
+			Multipart multipart = new MimeMultipart();
+			multipart.addBodyPart(htmlBodyPart);
+			multipart.addBodyPart(pdfBodyPart);
+
+			// Construct the mime message
+			Message message = new MimeMessage(session);
+			// Set From: header field of the header.
+			message.setFrom(
+				new InternetAddress(username));
+			// Set To: header field of the header.
+			message.addRecipient(
+					  Message.RecipientType.TO
+					, addressTo);
+			// Set Subject: header field
+			message.setSubject(subject);
+			
+			// Send the complete message parts
+			message.setContent(multipart);
+
+			// Send off the email
+			message.saveChanges();
+			transport.sendMessage(
+				message, 
+				new InternetAddress[] {addressTo});
+			return true;
+		} catch (MessagingException mex) {
+			mex.printStackTrace();
+			return false;
+		}
+	}
+}