JAXB jednoduše

JAXB (Java Architecture for XML Bindings) je Java standard, který určuje, jak mají být Java objekty převedeny do a z xml. JAXB představuje rozhraní pro čtení a zápis Java objektů do a z xml. Pro zápis Java objektů do xml se používá metoda marshall() a pro načtění z xml do Java objektů metoda unmarshall(). JAXB používá anotace pro mapování Java objektů na xml elementy a zpět. Dvě nejpoužívanější anotace jsou @XmlRootElement (xml dokument musí mít root element a tato anotace označuje třídu, která v xml bude představovat root element) a @XmlType (označuje xml element).

V příkladu si ukážeme fiktivní firmu (Company), která pronajímá kanceláře (Room) v různých office centrech (Center).

Nejdříve si vytvoříme třídu Room, která bude představovat místnost v office centru k pronájmu. Zároveň tuto třídu namapujeme na element room. Namapováním je myšleno to, že řekneme, že naše třída má být v xml dokumentu reprezentována elementem room. To provedem tak, že ji označíme anotací @XmlType.

Room.java

import javax.xml.bind.annotation.XmlType;

@XmlType
public class Room {

	private String code;
	private int numberOfSeats;
	
	// musí mít defaultní konstruktor
	public Room() {}
	
	public Room(String code, int numberOfSeats) {
		this.code = code;
		this.numberOfSeats = numberOfSeats;
	}

	public String getCode() {
		return code;
	}
	
	public void setCode(String code) {
		this.code = code;
	}

	public int getNumberOfSeats() {
		return numberOfSeats;
	}

	public void setNumberOfSeats(int numberOfSeats) {
		this.numberOfSeats = numberOfSeats;
	}
}

Všimněte si, že třída Room má dva konstruktory. Proto, aby JAXB fungovalo, je nutné, aby objekty mapované na elementy měly bezparametrický konstruktor.

Číst dál

Posílání jednoduchých emailů přes Google SMTP

Pokud máte účet na gmail.com, můžete použít Google SMTP pro posílání emailů z vaší aplikace. Pozor ale na to, že počet takto zaslaných emailů je omezen (maximálně 20 za hodinu). Pro zkoušení to ale většinou stačí.

Funguje to tak, že Google SMTP server přijme vaši zprávu a předá ji dalšímu serveru pomocí SMTP protokolu (Simple Mail Transfer Protocol). Tak to pokračuje do té doby, dokud se zpráva nedostane na server, kde je vaše poštovní schránka. Pro přístup k poštovní schránce se pak používají protokoly POP3 nebo IMAP.

Následující příklad ukazuje poslání jednoduchého textového emailu přes Google SMTP server. Jak poslat html email ukážu v nějakém další příspěvku.

import java.util.Properties;

import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;

public class GoogleMail {
	public static void main(String args[]) throws AddressException, MessagingException {
		System.out.println("Started");
		
		// Set up properties
		Properties mailServerProperties = System.getProperties();
		mailServerProperties.put("mail.smtp.port", "587");
		mailServerProperties.put("mail.smtp.auth", "true");
		mailServerProperties.put("mail.smtp.starttls.enable", "true");

		// Get session
		Session mailSession = Session.getDefaultInstance(mailServerProperties);

		// Create message
		MimeMessage message = new MimeMessage(mailSession);
		message.addRecipient(Message.RecipientType.TO, new InternetAddress("email@email.cz"));
		message.setSubject("Pokusný email", "UTF-8");
		message.setText("Ahoj, toto je pokusný email.", "UTF-8");

		// Send
		Transport transport = mailSession.getTransport("smtp");
		transport.connect("smtp.gmail.com", "user", "password");
		transport.sendMessage(message, message.getAllRecipients());
		transport.close();

		System.out.println("Finished");
	}
}

Pro zaslání emailu se musí nejdříve vytvořit session, které předáme properties. Dále se vytvoří email, který by měl minimálně obsahovat TO adresu, SUBJECT a TEXT. Zároveň se dá nastavit kódování. Já jsem použil UTF-8. Nakonec se provede samotné poslání emailu (připojení na server, odeslání, ukončení připojení). Pro připojení k serveru je třeba uvést platného uživatele (pokud máte email jmeno.prijmeni@gmail.com tak jako uživatele uveďte jmeno.prijmeni) a heslo.

Pokud dostanete chybu AuthenticationFailedException: 534-5.7.14, řešení naleznete v tomto příspěvku.

Pokud vše proběhne v pořádku a přesto vám nedojde email na emailovou adresu zadanou v TO (zde je to na email@email.cz), zkontrolujte si, zda neskončil ve spamu. Z vlastní zkušenosti mohu říci, že pokud máte email na seznam.cz, zprávy zprávy se zobrazí v emailové schránce téměř okamžitě. Pokud používáte email na centrum.cz, můžete čekat i několik desítek vteřin.


Zdroj:

BufferedReader, BufferedWriter a try with resources

BufferedReader načítatá textová data do paměti (buffer) a umožňuje efektivnější čtení po řádcích. Obdobně to platí pro BufferedWriter, který zase zapisuje po blocích a ne znak po znaku. Blok try-with-resources zjednodušuje práci se zdroji a zpracování výjimek a je vhodný například právě pro načítání ze a zápis do souborů. Zdroje (zde to představuje streamy pro čtení a zápis) definované v části try budou po skončení bloku (a nebo v případě vyhození výjimky) korektně uzavřeny.

V následujícím příkladu čtu ze souboru input.txt, který je uložen v kódování Windows-1250 a hned načtené řádky ukládám do souboru output.txt. Pro ukládání používám kódování UTF-8. Soubor input.txt se nachází v adresáři s projektem (v adresáři, kde je i složka src).

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;

public class App {

	public static void main(String[] args) throws UnsupportedEncodingException, FileNotFoundException, IOException {
		
		try (BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("input.txt"), "Cp1250"))) {
			try (BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("output.txt"), "UTF-8"))) {
				
				String line = null;
				while ((line = br.readLine()) != null) {
					System.out.println(line);
					bw.write(line);
					bw.newLine();
				}
			}
		}
	}
}

Pravděpodobně jste si všimli, pro někoho trochu divoké, konstrukce BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("input.txt"), "Cp1250")). Při práci se streamy se jednotlivé streamy zabalují do sebe a tím se výslednému streamu přidává funkcionalita. V tomto příkladu FileInputStream dokáže číst ze souboru ale pouze ve výchozím kódování. Pokud je kódování jiné, výstup vypadá třeba takto:

�lu�ou�k� k�� �p�l ��belsk� �dy.

Pokud ale tento stream zabalíme do InputStreamReader, může určit i kódování souboru. Stále ale načítáme znaky jednotlivě, což není příliš efektivní a určitě by bylo lepší je načítat po blocí (po řádcích). V tom případě použijeme BufferedReader, kterému předáme jako parametr InputStreamReader. Obdobné je i pro zapisování BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("output.txt"), "UTF-8")). Zde se ale zase používají streamy obsahující v názvu Writer nebo Output.

Samotné čtení a zápis se děje v této části:

String line = null;
while ((line = br.readLine()) != null) {
	System.out.println(line);
	bw.write(line);
	bw.newLine();
}

Do proměnné line postupně načítáme řádky. V případě že BufferedReader#readLine() vrátí null znamená to, že jsem na konci souboru. Pro zápis se používá metoda BufferedWriter#write(). Pokud chceme zapsat konec rádky, používá se metoda BufferedWriter#newLine(). Po skončení práce se streamy by se na nich měla zavolat metoda close(). To platí i v případě, že máme otevřený stream a dojde k výjimce. Používám ale blok try-with-resources takže to udělá za mě.


Zdroje:

Nastavení pozice kurzoru v PSPad

Ve výchozím nastavení je pozice kurzoru v programu PSPad v místě kliknutí myši a ne, jak je obvyklé, na konci textu.

cursor_in_pspad

Toto zobrazení je možné změnit v Nastavení -> Nastavení programu… -> Editor 1 -> odškrtnout volbu Vstup za konec řádku.

cursor_in_pspad_02

Spring Batch 1. díl

Spring Batch je open source framework pro dávkové zpracování (batch processing). Dávkové zpracování znamená vykonání série programů bez účasti uživatele. Většinou se jedná o akce, které se vykonávají pravidelně v určitý čas jako například vygenerování statistik ke konci měsíce, zjištění změn za poslední týden (např. nové příspěvky za toto období), zpracování výkazů ke konci dne, týdne, měsíce, …

Základní pojmy ve Spring Batch

  • JobLauncher: Vstupní místo, kde se spouští job.
  • Job: Definuje co se má provést (definuje dávku). Každý job může obsahovat několik kroků (Step).
  • Step: Část jobu, jeden z kroků při provádění (jeden z kroků dávky). Step obsahuje atribut next, který udává, jaký další step se má vykonat po tom aktuálním. Step obsahuje Tasklet.
  • Tasklet: Je částí kroku a definuje, co se má provést (reference na třídu implementující rozhraní org.springframework.batch.core.step.tasklet.Tasklet) a nebo může ještě obsahovat Chunk.
  • Chunk: Nejmenší část, ve které se určuje co se má načíst (Reader), jak to zpracovat (Processor) a co má být výstupem (Writer).

Jak vypadá typický job

  • načtení dat (z databáze, ze souboru, …)
  • zpracování dat (výpočty, validace, filtrování, …)
  • zápis dat (do databáze, do různých typů souborů – pdf, doc, txt, …)

Jednoduchý projekt

V následujícím jednoduchém projektu vytvoříme a spustíme job, který bude obsahovat jediný Step (a ten jediný Tasklet), který pouze vypíše text.

Zde je struktura projektu. Jedná se Maven projekt.

│   pom.xml
│
└───src
    ├───main
    │   └───java
    │       └───cz
    │           └───vitfo
    │                   ExecuteJob.java
    │                   MyTasklet.java
    │
    └───resources
            context.xml

pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<groupId>cz.vito</groupId>
	<artifactId>springbatch</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>

	<name>springbatch</name>
	<url>http://maven.apache.org</url>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<spring.framework.version>3.2.1.RELEASE</spring.framework.version>
		<spring.batch.version>3.0.2.RELEASE</spring.batch.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.batch</groupId>
			<artifactId>spring-batch-core</artifactId>
			<version>${spring.batch.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.batch</groupId>
			<artifactId>spring-batch-infrastructure</artifactId>
			<version>${spring.batch.version}</version>
		</dependency>
	</dependencies>
</project>

Do pom.xml stačí přidat pouze dvě závislosti a to na org.springframework.batch a spring-batch-infrastructure.

context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/batch"
	xmlns:beans="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans 
						http://www.springframework.org/schema/beans/spring-beans-3.0.xsd 
						http://www.springframework.org/schema/batch 
						http://www.springframework.org/schema/batch/spring-batch-3.0.xsd">
		
	<!-- Beans definition -->	
	<beans:bean id="jobRepository" class="org.springframework.batch.core.repository.support.MapJobRepositoryFactoryBean">
		<beans:property name="transactionManager" ref="transactionManager" />
	</beans:bean>
	
	<beans:bean id="jobLauncher" class="org.springframework.batch.core.launch.support.SimpleJobLauncher">
		<beans:property name="jobRepository" ref="jobRepository" />
	</beans:bean>
	
	<beans:bean id="transactionManager" class="org.springframework.batch.support.transaction.ResourcelessTransactionManager" />
	
	<beans:bean id="myTasklet" class=" cz.vitfo.MyTasklet" />
	
	
	<!-- Job definition -->
	<job id="myJob">
		<step id="myStep">
			<tasklet ref="myTasklet"></tasklet>
		</step>
	</job>
</beans:beans>

Job launcher potřebuje mít definovánu job repository, do které si ukládá údaje o jobech. Job repository potřebuje mít definován transaction manager. Job má definován Step a ten Tasklet. Tasklet odkazuje na třídu implementující rozhraní Tasklet.

MyTasklet.java

package cz.vitfo;

import org.springframework.batch.core.StepContribution;
import org.springframework.batch.core.scope.context.ChunkContext;
import org.springframework.batch.core.step.tasklet.Tasklet;
import org.springframework.batch.repeat.RepeatStatus;

public class MyTasklet implements Tasklet {

	public RepeatStatus execute(StepContribution arg0, ChunkContext arg1) throws Exception {
		System.out.println("Code in " + this.getClass().getSimpleName() + " is executed.");
		return null;
	}
}

Třída, která implementuje rozhraní Tasklet. Jediným jejím úkolem je vypsat text.

ExecuteJob.java

package cz.vitfo;

import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class ExecuteJob {

	public static void main(String[] args) {
		ApplicationContext context = new ClassPathXmlApplicationContext("context.xml");
		
		JobLauncher jobLauncher = (JobLauncher) context.getBean("jobLauncher");
		Job job = (Job) context.getBean("myJob");
		
		try {
			JobExecution execution = jobLauncher.run(job, new JobParameters());
			System.out.println("Exit Status: " + execution.getStatus());
			System.out.println("Start: " + execution.getStartTime());
			System.out.println("End: " + execution.getEndTime());
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (context != null) {
				context = null;
			}
		}
		System.out.println("Done");
	}
}

Nejdříve načteme kontext. Poté z něj získáme JobLauncher a Job. Tento Job spustíme pomocí JobLauncher a vypíšeme status jobu po spuštění.

Výsledek

Code in MyTasklet is executed.
Exit Status: COMPLETED
Start: Tue Nov 01 17:26:15 CET 2016
End: Tue Nov 01 17:26:15 CET 2016
Done

Posílání mailů přes Google SMTP: AuthenticationFailedException: 534-5.7.14

Pokud máte účet na gmailu, můžete používat pro zkušební posílání mailů ze vlastní aplikace, Google SMTP server. Při posílání můžete narazit na chybu AuthenticationFailedException: 534-5.7.14. Zároveň vám na váš email pravděpodobně dojde zpráva

Dobrý den,
Někdo se právě pokusil o přihlášení k vašemu účtu Google xxx.xxx@gmail.com z aplikace, která nesplňuje moderní bezpečnostní standardy…

Je potřeba zapnout přístup k účtu pro měně bezpečné aplikace: https://support.google.com/mail/answer/78754 a klikněte na „allow less secure apps to access your account“ link. Otevře se stránka https://support.google.com/accounts/answer/6010255. Zvolte Option 2 a klikněte na Go to the „Less secure apps“ section link. Nyní zvolte „Zapnout“ přístup pro méně bezpečné aplikace.

google_smtp_authenticationfailedexception

Méně zabezpečný přístup je povolen. Na váš email by měl přijít informativní email o této změně.

google_smtp_authenticationfailedexception_02

Zjištění expirace hesla v Oracle databázi

V případě, že se na Oracle databázi připojujete pomocí zadání uživatelského jména a hesla, můžete si jednoduchým dotazem zjistit datum expirace hesla.

SELECT username, expiry_date FROM user_users;

Pohled (View) user_users popisuje aktuálního uživatele. Struktura view je následující.

descr user_users;

Name                        Null     Type             
--------------------------- -------- ---------------- 
USERNAME                    NOT NULL VARCHAR2(128)    
USER_ID                     NOT NULL NUMBER           
ACCOUNT_STATUS              NOT NULL VARCHAR2(32)     
LOCK_DATE                            DATE             
EXPIRY_DATE                          DATE             
DEFAULT_TABLESPACE          NOT NULL VARCHAR2(30)     
TEMPORARY_TABLESPACE        NOT NULL VARCHAR2(30)     
CREATED                     NOT NULL DATE             
INITIAL_RSRC_CONSUMER_GROUP          VARCHAR2(128)    
EXTERNAL_NAME                        VARCHAR2(4000)   
PROXY_ONLY_CONNECT                   VARCHAR2(1 CHAR) 
COMMON                               VARCHAR2(3 CHAR) 
ORACLE_MAINTAINED                    VARCHAR2(1 CHAR)