Tag: Java

How to Indent XML String in Java (Pretty)

Hello Guys,

This is a cool way to prettify your XML (String format) in Java Language:

public static String format(String xml, Boolean ommitXmlDeclaration) throws IOException, SAXException, ParserConfigurationException {

DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder();
Document doc = db.parse(new InputSource(new StringReader(xml)));

OutputFormat format = new OutputFormat(doc);
format.setIndenting(true);
format.setIndent(2);
format.setOmitXMLDeclaration(ommitXmlDeclaration);
format.setLineWidth(Integer.MAX_VALUE);
Writer outxml = new StringWriter();
XMLSerializer serializer = new XMLSerializer(outxml, format);
serializer.serialize(doc);

return outxml.toString();

}
Advertisements

Spring Batch Partitioner – Case Study with SourceCode – Best Practices

I’m writing this post because i report a bug at Spring Community Jira, this is the link:

https://jira.spring.io/browse/BATCH-2309

I started a sample project which could reproduce the problem to show the community what I was experiencing, but to my surprise I was using the partitioner feature incorrectly. I am writing this post to share what I learned throughout this experience to help those who are going through the same questions.

My Goal: I wanted to use the resource partitioner for parallel processing but was worried to use the primary key of the table (column ID) because my table has gaps (id column is not incremental) and for this reason the partitioner would distribute number of different records for each thread, thus being inefficient in their distribution.

For example:

This is the good example partitioner:

https://github.com/spring-projects/spring-batch/blob/master/spring-batch-samples/src/main/java/org/springframework/batch/sample/common/ColumnRangePartitioner.java

Suppose that my table has the following records: Ids 1, 8, 9,10 11, 12, 13, 14, 15.

min: 1

max: 15

gridSize = number of threads = 2 in this example

target size calculation: int targetSize = (max min) / gridSize + 1;

(15 – 1) / 2 + 1 = 8

In this example:

Thread number 1 will receive to process: 1 to 8

Thread number 2 will receive to process: 9 to 16

The Problem: Thread 1 receives only two records to process (The Id’s 1 and 8) and the thread 2 will receive 7 records to process. At this case the partitioner to split incorrectly number of records between threads.

My Goal: I want to split the number of records equally between all threads.

Where I was going wrong: To achieve my goal I tried to use a query that makes use of rownum and / or ntile oracle feature, the goal was to use the split an id that is sequential, with no gaps in the id column table, so the load would be uniform among the threads. The JdbcPagingItemReader class can not be used with multithreaded characteristics using Oracle ROWNUM because the query is partially executed multiple times in the database and there is no guarantee that all records are processed because a confusion of Ids between threads occurs.

 The correct way: You can use JdbcPagingItemReader using the Primary Key column (may be single or multiple columns) or JdbcCursorItemReader can use both the PK column or  Rownum / NTILE to do division.

Why use JdbcCursorItemReader not cause problems of mistaken IDs or lost records ?

This class executes the query once the database and will use chunk mode to fetch the records as needed. If you use a rownum column in this case will not cause data loss because every query is processed only once in the database.

To illustrate and facilitate understanding, I created a design example set with various possible configurations available here:

springbatchpoc

GitHub Example Project:

https://github.com/victorjabur/PartitionSpringBatch_DataLose_Poc_BATCH-2309

Here are the sql scripts to create the database tables used in this poc:

https://github.com/victorjabur/PartitionSpringBatch_DataLose_Poc_BATCH-2309/tree/master/src/main/resources/sql

  1. JdbcCursorItemReader-OracleNtile – It works
  2. JdbcCursorItemReader-OracleRownum – It works
  3. JdbcPagingItemReader-OracleNtile – It not works, don’t use this. PagingReader does not work with NTile
  4. JdbcPagingItemReader-OracleRownum – It not works, don’t use this. PagingReader does not work with Rownum
  5. JdbcPagingItemReader-TablePrimaryKey – It works, but the records aren’t distributed in an uniform way (same quantity for each thread)

What is Oracle NTile ?
This feature of Oracle Database can create a desired number of containers so that each thread can consume one. For example: I have 1000 records in the database to be divided among 10 threads:

SELECT ID, DESCRIPTION, FLAG_PROCESSED, NTILE(10) OVER (ORDER BY ID)
AS CONTAINER_COLUMN FROM TABLE_SOURCE
WHERE FLAG_PROCESSED = 'N';

With this query, you can use the column “CONTAINER_COLUMN”, values are already pre split into buckets ready to be divided among the various threads.

This is the documentation with more clarified explanation:

https://docs.oracle.com/cd/B19306_01/server.102/b14200/functions101.htm

That’s it.

Any question or suggestion is very welcome.

Credits to this post:

http://alexandreesl.wordpress.com/2014/09/21/spring-batch-construindo-processamento-massivo-de-dados-em-java/
http://www.mkyong.com/spring-batch/spring-batch-partitioning-example/
https://github.com/spring-projects/spring-batch/tree/master/spring-batch-samples

Cheers,
Victor Jabur

How to propagate FacesMessage between two jsf pages

Hello,

The JSF don’t propagate a FacesMessage between two jsf pages, i discovery this when i trying to make something like this:

1 –
User click in a Jsf Button

2 – Call the action method in the Managed Bean, something like this:

	public String someMethod(){
		// Execute some code
		FacesMessage msg = new FacesMessage(FacesMessage.SEVERITY_INFO, "", "Something message");
		FacesContext.getCurrentInstance().addMessage(null, msg);
		return "otherPage";
	}

3 – Suppose that the string “otherPage” cause a normal redirect to other Jsf page, through faces-config.xml, in this exact point the FacesMessage is losted.

Resolution: For resolve this problem, follow this steps, basically this will affect all lifecycle of your jsf application:

1 – In your faces-config.xml, put this code:

  <lifecycle>
    <phase-listener>com.somepackage.MultiPageMessagesSupport</phase-listener>  
  </lifecycle>

2 – Put the below java class in the package specified above:

import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import javax.faces.application.FacesMessage;
import javax.faces.context.FacesContext;
import javax.faces.event.PhaseEvent;
import javax.faces.event.PhaseId;
import javax.faces.event.PhaseListener;

/**
 * Enables messages to be rendered on different pages from which they were set.
 * To produce this behaviour, this class acts as a <code>PhaseListener</code>.
 * 
 * This is performed by moving the FacesMessage objects:
 * <li>After each phase where messages may be added, this moves the messages
 * from the page-scoped FacesContext to the session-scoped session map.
 * <li>Before messages are rendered, this moves the messages from the
 * session-scoped session map back to the page-scoped FacesContext.
 * 
 * Only messages that are not associated with a particular component are ever
 * moved. These are the only messages that can be rendered on a page that is
 * different from where they originated. * To enable this behaviour, add a
 * <code>lifecycle</code> block to your faces-config.xml file. That block
 * should contain a single <code>phase-listener</code> block containing the
 * fully-qualified classname of this file.
 * 
 * @author <a href="mailto:jesse@odel.on.ca">Jesse Wilson</a>
 * 
 * @version This version have the bug corrected: The bug was:
 * In the same page, the FacesMessage was displayed many times,
 * with this patch, only different messages is added into new context.
 */
public class MultiPageMessagesSupport implements PhaseListener {

	private static final long serialVersionUID = 3328743500652081238L;

	/** a name to save messages in the session under */
	private static final String sessionToken = "MULTI_PAGE_MESSAGES_SUPPORT";

	/**
	 * Return the identifier of the request processing phase during which this
	 * listener is interested in processing PhaseEvent events.
	 */
	public PhaseId getPhaseId() {
		return PhaseId.ANY_PHASE;
	}

	/**
	 * Handle a notification that the processing for a particular phase of the
	 * request processing lifecycle is about to begin.
	 */
	public void beforePhase(PhaseEvent event) {
		if (event.getPhaseId() == PhaseId.RENDER_RESPONSE) {
			FacesContext facesContext = event.getFacesContext();
			restoreMessages(facesContext);
		}
	}

	/**
	 * Handle a notification that the processing for a particular phase has just
	 * been completed.
	 */
	public void afterPhase(PhaseEvent event) {
		if (event.getPhaseId() == PhaseId.APPLY_REQUEST_VALUES
				|| event.getPhaseId() == PhaseId.PROCESS_VALIDATIONS
				|| event.getPhaseId() == PhaseId.INVOKE_APPLICATION) {
			FacesContext facesContext = event.getFacesContext();
			saveMessages(facesContext);
		}
	}

	/**
	 * Remove the messages that are not associated with any particular component
	 * from the faces context and store them to the user's session.
	 * 
	 * @return the number of removed messages.
	 */
	@SuppressWarnings("unchecked")
	private int saveMessages(FacesContext facesContext) {
		// remove messages from the context
		Set<FacesMessage> messages = new HashSet<FacesMessage>();
		for (Iterator i = facesContext.getMessages(null); i.hasNext();) {
			FacesMessage msg = (FacesMessage) i.next();
			messages.add(msg);
			i.remove();
		}
		// store them in the session
		if (messages.size() == 0)
			return 0;
		Map sessionMap = facesContext.getExternalContext().getSessionMap();
		// if there already are messages
		Set<FacesMessage> existingMessages = (Set<FacesMessage>) sessionMap
				.get(sessionToken);
		if (existingMessages != null) {
			existingMessages.addAll(messages);
			// if these are the first messages
		} else {
			sessionMap.put(sessionToken, messages);
		}
		return messages.size();
	}

	/**
	 * Remove the messages that are not associated with any particular component
	 * from the user's session and add them to the faces context.
	 * 
	 * @return the number of removed messages.
	 */
	@SuppressWarnings("unchecked")
	private int restoreMessages(FacesContext facesContext) {
		// remove messages from the session
		Map sessionMap = facesContext.getExternalContext().getSessionMap();
		Set<FacesMessage> messages = (Set<FacesMessage>) sessionMap
				.remove(sessionToken);
		// store them in the context
		if (messages == null)
			return 0;
		int restoredCount = messages.size();

		// set that contains wich messages already exists in the FacesContext
		Set<FacesMessage> facesContextMessages = new HashSet<FacesMessage>();
		for (Iterator i = facesContext.getMessages(null); i.hasNext();) {
			FacesMessage msg = (FacesMessage) i.next();
			facesContextMessages.add(msg);
			i.remove();
		}

		// add the messages that aren't in the FacesContext 
		for (FacesMessage facesMessage : messages) {
			if (!facesContextMessages.contains(facesMessage))
				facesContext.addMessage(null, facesMessage);
		}
		return restoredCount;
	}
}

Validador e Gerador de Renavam (Veículos) em Java – Novo Padrão 11 Digitos – A Partir de Abril 2013

Olá Pessoal,

Seguem abaixo 2 classes java para validação e geração de código renavam para veículos:

ValidadorRenavam.java

public class ValidadorRenavam {

 public boolean validarRenavam(String renavam){
 // Pegando como exemplo o renavam = 639884962

 // Completa com zeros a esquerda se for no padrao antigo de 9 digitos
 // renavam = 00639884962
 if(renavam.matches("^([0-9]{9})$")){
 renavam = "00" + renavam;
 }

 // Valida se possui 11 digitos pos formatacao
 if(!renavam.matches("[0-9]{11}")){
 return false;
 }

 // Remove o digito (11 posicao)
 // renavamSemDigito = 0063988496
 String renavamSemDigito = renavam.substring(0, 10);

 // Inverte os caracteres (reverso)
 // renavamReversoSemDigito = 69488936
 String renavamReversoSemDigito = new StringBuffer(renavamSemDigito).reverse().toString();

 int soma = 0;

 // Multiplica as strings reversas do renavam pelos numeros multiplicadores
 // para apenas os primeiros 8 digitos de um total de 10
 // Exemplo: renavam reverso sem digito = 69488936
 // 6, 9, 4, 8, 8, 9, 3, 6
 // * * * * * * * *
 // 2, 3, 4, 5, 6, 7, 8, 9 (numeros multiplicadores - sempre os mesmos [fixo])
 // 12 + 27 + 16 + 40 + 48 + 63 + 24 + 54
 // soma = 284
 for (int i = 0; i < 8; i++) {
 Integer algarismo = Integer.parseInt(renavamReversoSemDigito.substring(i, i + 1));
 Integer multiplicador = i + 2;
 soma += algarismo * multiplicador;
 }
 
 // Multiplica os dois ultimos digitos e soma
 soma += Character.getNumericValue(renavamReversoSemDigito.charAt(8)) * 2;
 soma += Character.getNumericValue(renavamReversoSemDigito.charAt(9)) * 3;

 // mod11 = 284 % 11 = 9 (resto da divisao por 11)
 int mod11 = soma % 11;

 // Faz-se a conta 11 (valor fixo) - mod11 = 11 - 9 = 2
 int ultimoDigitoCalculado = 11 - mod11;

 // ultimoDigito = Caso o valor calculado anteriormente seja 10 ou 11, transformo ele em 0
 // caso contrario, eh o proprio numero
 ultimoDigitoCalculado = (ultimoDigitoCalculado >= 10 ? 0 : ultimoDigitoCalculado);

 // Pego o ultimo digito do renavam original (para confrontar com o calculado)
 int digitoRealInformado = Integer.valueOf(renavam.substring(renavam.length()-1, renavam.length()));

 // Comparo os digitos calculado e informado
 if(ultimoDigitoCalculado == digitoRealInformado){
 return true;
 }
 return false;
 }
}

GeradorRenavam.java

import java.util.Random;

public class GeradorRenavam {

	public static void main(String[] args) throws Exception {
		int maximo = 0;
		try {
			maximo = Integer.valueOf(args[0]);
		} catch (Exception e) {
			throw new Exception("Especifique um valor de entrada válido");
		}
		for (int i = 0; i < maximo; i++) {
			String renavam = GeradorRenavam.geraNumeroRenavamValido();
			System.out.println(renavam);
		}
	}

 public static String geraNumeroRenavamValido() {
 Random randomizador = new Random();
 String renavamGeradoAleatoriamente = "";
 for (int i = 0; i < 10; i++) {
 renavamGeradoAleatoriamente += Math.abs(randomizador.nextInt(8));
 }

 String renavamSemDigito = renavamGeradoAleatoriamente.substring(0, 10);

 String renavamReversoSemDigito = new StringBuffer(renavamSemDigito).reverse().toString();

 int soma = 0;
 for (int i = 0; i < 8; i++) {
 Integer algarismo = Integer.parseInt(renavamReversoSemDigito.substring(i, i + 1));
 Integer multiplicador = i + 2;
 soma += algarismo * multiplicador;
 }

 soma += Character.getNumericValue(renavamReversoSemDigito.charAt(8)) * 2;
 soma += Character.getNumericValue(renavamReversoSemDigito.charAt(9)) * 3;

 int mod11 = soma % 11;
 int ultimoDigitoCalculado = 11 - mod11;
 ultimoDigitoCalculado = (ultimoDigitoCalculado >= 10 ? 0 : ultimoDigitoCalculado);
 return renavamGeradoAleatoriamente + ultimoDigitoCalculado;
 }
}

Créditos deste Post para ViniGodoy: Link : http://www.guj.com.br/posts/list/149379.java

Espero que ajude alguém, …
Abraços.

Victor Jabur.