Tag Archives: Java

Bysutradi

JMeter – Erro de SSL “Certificates does not conform to algorithm constraints”

O JMeter é uma ferramenta muito útil no processo de teste de software. Permite que façamos testes de stress/carga em aplicações.

Quando criamos nossos casos de teste, é comum atualmente que os testes sejam realizados em aplicações que rodam sobre SSL. Neste caso, um certificado criado corretamente faz certa diferença quando usado o JMeter.

O caso de teste pode ser criado manualmente, ou através da captura de navegação através de uma configuração de “Servidor HTTP Proxy” e um “Recording Controller” (acho que vale um artigo sobre como fazer isso).

Quando se usa este recurso, o JMeter cria alguns arquivos no diretório “bin”:

  • ApacheJMeterTemporaryRootCA.crt
  • ApacheJMeterTemporaryRootCA.usr
  • proxyserver.jks

Sendo assim, quando o desenvolvedor executa o teste localmente, não ocasionam em erros de SSL. Porém, quando o caso de teste é executado em outro equipamento, possivelmente ocorrerão erros sobre “Certificates does not conform to algorithm constraints”.

O erro de SSL “Certificates does not conform to algorithm constraints” pode ocorrer quando o certificado não está em conformidade com alguns padrões esperados pela JVM, ocasionando o erro abaixo.

javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException: Certificates does not conform to algorithm constraints
at …
at java.lang.Thread.run(Unknown Source)
Caused by: java.security.cert.CertificateException: Certificates does not conform to algorithm constraints
at sun.security.ssl.AbstractTrustManagerWrapper.checkAlgorithmConstraints(Unknown Source)
at sun.security.ssl.AbstractTrustManagerWrapper.checkAdditionalTrust(Unknown Source)
at sun.security.ssl.AbstractTrustManagerWrapper.checkServerTrusted(Unknown Source)
… 24 more

​Solução

Há algumas maneiras de resolver este problema. Uma delas, talvez a mais simples/rápida, é alterar propriedades no java.security da JVM onde o JMeter está executando.

Edite o arquivo <JRE>\lib\security\java.security e alte​re as propriedades abaixo:

#jdk.certpath.disabledAlgorithms=MD2, MD5, RSA keySize < 1024
jdk.certpath.disabledAlgorithms=
#jdk.tls.disabledAlgorithms=SSLv3, RC4, MD5withRSA, DH keySize < 768
jdk.tls.disabledAlgorithms=​

Estas configurações fazem com que a JVM aceite qualquer certificado.​

Referências:

Apache JMeter

 

Bysutradi

Android: Usando um layout customizado para ajustar altura de uma RecyclerView

O componente RecyclerView (clique neste link para ver detalhes de sua implementação, que não é o foco deste artigo) é uma widget introduzida no Android como o novo padrão para exibição de listas em substituição ao ListView.

Até aqui, OK.

Porém, este componente deve ser adicionado em um RelativeLayout que seja a identificação principal do seu layout, ou seja, você não pode ter um ScrollView > LinearLayout > seu componente RecyclerView.

Até aqui, OK, pois você pode trabalhar com RelativeLayout e adequar sua interface visual para encaixar o componente.

Outro comportamento do componente é que, normalmente, ele preenche a altura até completar a altura do dispositivo.

Não investi tempo para procurar por justificativas, mas por soluções. E uma das soluções encontradas e que funcionou logo de cara foi a criação de uma classe de Layout customizada que faz estes ajustes.

Compartilho ela com vocês logo abaixo:

 

import android.content.Context;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.view.ViewGroup;

public class RecyclerViewLinearLayout extends LinearLayoutManager {

    public RecyclerViewLinearLayout(Context context, int orientation, boolean reverseLayout) {
        super(context, orientation, reverseLayout);
        this.c = context;
    }

    private Context c;
    private int[] mMeasuredDimension = new int[2];

    @Override
    public void onMeasure(RecyclerView.Recycler recycler, RecyclerView.State state,
                          int widthSpec, int heightSpec) {
        final int widthMode = View.MeasureSpec.getMode(widthSpec);
        final int heightMode = View.MeasureSpec.getMode(heightSpec);
        final int widthSize = View.MeasureSpec.getSize(widthSpec);
        final int heightSize = View.MeasureSpec.getSize(heightSpec);

        int width = 0;
        int height = 0;
        for (int i = 0; i < getItemCount(); i++) {
            measureScrapChild(recycler, i,
                    View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED),
                    View.MeasureSpec.makeMeasureSpec(i, View.MeasureSpec.UNSPECIFIED),
                    mMeasuredDimension);

            if (getOrientation() == HORIZONTAL) {
                width = width + mMeasuredDimension[0];
                if (i == 0) {
                    height = mMeasuredDimension[1];
                }
            } else {
                height = height + mMeasuredDimension[1];
                if (i == 0) {
                    width = mMeasuredDimension[0];
                }
            }
        }

        switch (widthMode) {
            case View.MeasureSpec.EXACTLY:
                width = widthSize;
            case View.MeasureSpec.AT_MOST:
            case View.MeasureSpec.UNSPECIFIED:
        }

        switch (heightMode) {
            case View.MeasureSpec.EXACTLY:
                height = heightSize;
            case View.MeasureSpec.AT_MOST:
            case View.MeasureSpec.UNSPECIFIED:
        }

        int widthDesired = Math.min(widthSize, width);
        setMeasuredDimension(widthDesired, height);
    }

    private void measureScrapChild(RecyclerView.Recycler recycler, int position, int widthSpec,
                                   int heightSpec, int[] measuredDimension) {
        View view = recycler.getViewForPosition(position);
        if (view != null) {
            RecyclerView.LayoutParams p = (RecyclerView.LayoutParams) view.getLayoutParams();
            int childWidthSpec = ViewGroup.getChildMeasureSpec(widthSpec,
                    getPaddingLeft() + getPaddingRight(), p.width);
            int childHeightSpec = ViewGroup.getChildMeasureSpec(heightSpec,
                    getPaddingTop() + getPaddingBottom(), p.height);
            view.measure(childWidthSpec, childHeightSpec);
            measuredDimension[0] = view.getMeasuredWidth() + p.leftMargin + p.rightMargin;
            measuredDimension[1] = view.getMeasuredHeight() + p.bottomMargin + p.topMargin;
            recycler.recycleView(view);
        }
    }
}

Você usa esta implementação no momento de definir o Layout para o RecyclerView.

acoesCompradasLista = (RecyclerView) view.findViewById(R.id.cardList);
acoesCompradasLista.setHasFixedSize(true);
RecyclerViewLinearLayout llm = new RecyclerViewLinearLayout(getActivity(), LinearLayoutManager.VERTICAL, false);

Com isso, o RecyclerView cria a lista com todos os elementos, ajusta a altura automaticamente (portrait ou landscape) e você consegue adicionar outros elementos na sua interface abaixo do RecyclerView.

Bysutradi

Criando um Spinner customizado com valores preenchidos por enum

O uso de Spinners em aplicativos Android é muito comum e isso pode ser feito de muitas maneiras, seguinte a rotina: crie o spinner, passe a lista, crie o adapter, crie o layout, etc.

Em alguns casos, tais Spinners são preenchidos com informações de domínio fechado.

Diante desta necessidade, é possível usar classes do tipo enum para preencher os valores em um Spinner customizado.

Neste tutorial, mostrarei como fazer isso criando algumas classes.

Vamos começar pela interface, para padronizar as classes enum.

public interface SpinnerEnum {

   public long getId();
   public String getNome();
   public Object[] listar();
}

Usaremos esta interface em todas as nossas futuras classes de enum.

O que precisamos agora é de nosso Adapter.

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.TextView;

import com.sutradi.lib.drawer.DatabaseModel;

import java.util.List;

public class SpinnerAdapter extends ArrayAdapter<DatabaseModel> {

	private final Context contexto;
	private final List<DatabaseModel> lista;
	DatabaseModel tempValues = null;
	int textViewID;
	int textViewNome;
	int spinnerLayoutID;

	/**
	 * Construtor.
	 * @param contexto
	 * @param listaObjetos
	 * @param spinnerLayout Layout que contenha 02 objetos TextView para ID e Nome
	 * @param _textViewID TextView para armazenar o ID
	 * @param _textViewNome TextView para armazenar o Nome
	 */
	public SpinnerAdapter(Context contexto, List<DatabaseModel> listaObjetos,
			int spinnerLayout, int _textViewID, int _textViewNome) {

		super(contexto, spinnerLayout, listaObjetos);
		this.contexto = contexto;
		this.lista = listaObjetos;
		this.textViewID = _textViewID;
		this.textViewNome = _textViewNome;
		this.spinnerLayoutID = spinnerLayout;
	}

	@Override
	public View getDropDownView(int position, View convertView, ViewGroup parent) {
		return getCustomView(position, convertView, parent);
	}

	@Override
	public View getView(int position, View convertView, ViewGroup parent) {
		return getCustomView(position, convertView, parent);
	}

	public View getCustomView(int position, View convertView, ViewGroup parent) {
		LayoutInflater inflater = (LayoutInflater) contexto
				.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
		View rowView = inflater.inflate(spinnerLayoutID, parent, false);
		tempValues = null;
		tempValues = (DatabaseModel) lista.get(position);

		TextView viewID = (TextView) rowView.findViewById(textViewID);
		TextView viewNome = (TextView) rowView.findViewById(textViewNome);
		viewID.setText(tempValues.getId());
		viewNome.setText(tempValues.getNome());

		return rowView;
	}
}

Em nosso Adapter, informaremos o ID para o layout e os IDs para os campos que representação o ID e Nome que serão usados no Spinner.

public SpinnerAdapter(Context contexto, List<DatabaseModel> listaObjetos,
			int spinnerLayout, int _textViewID, int _textViewNome) {

Essa implementação permitirá que você crie layouts diferentes para o Spinner. Caso queira padronizar o Spinner, basta remover estas informações do construtor e definir as informações corretas na implementação em getCustomView.

A classe DatabaseModel deve conter uma implementação POJO que contém ID e Nome. Bem simples.

Agora que temos o Adapter definido, vamos criar nosso Spinner customizado.

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.widget.Spinner;

import com.sutradi.lib.drawer.DatabaseModel;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * Classe de apoio para uso de Spinners que apresentam somente Texto, carregando
 * dados a partir de um Enum (que deve extender à SpinnerEnum) O layout do
 * spinner deve ter somente um EditText para ID (hide) e outro para Nome.
 *
 * @author SUTRADI
 */
public class SpinnerUI extends Spinner {
    ArrayList<DatabaseModel> lista = null;

    /**
     * Construtor.
     *
     * @param _context
     */
    public SpinnerUI(Context _context) {
        super(_context);
    }

    /**
     * Construtor.
     *
     * @param _context
     * @param _attrs
     * @param _defStyle
     * @param _mode
     */
    public SpinnerUI(Context _context, AttributeSet _attrs, int _defStyle,
                     int _mode) {
        super(_context, _attrs, _defStyle, _mode);
    }

    /**
     * Construtor.
     *
     * @param _context
     * @param _attrs
     * @param _defStyle
     */
    public SpinnerUI(Context _context, AttributeSet _attrs, int _defStyle) {
        super(_context, _attrs, _defStyle);
    }

    /**
     * Construtor.
     *
     * @param _context
     * @param _attrs
     */
    public SpinnerUI(Context _context, AttributeSet _attrs) {
        super(_context, _attrs);
    }

    /**
     * Construtor.
     *
     * @param _context
     * @param _mode
     */
    public SpinnerUI(Context _context, int _mode) {
        super(_context, _mode);
    }

    /**
     * @param context
     * @param spinnerText
     * @param listaEnum
     * @param spinnerLayout
     * @param spinnerItemId
     * @param spinnerItemNome
     */
    public void loadSpinner(Context context, String spinnerText,
                            SpinnerEnum[] listaEnum, int spinnerLayout, int spinnerItemId,
                            int spinnerItemNome, SpinnerEnum... enumIgnorar) {

        ArrayList<DatabaseModel> listaDatabaseModel = montaLista(spinnerText,
                listaEnum, enumIgnorar);

        SpinnerAdapter msa = new SpinnerAdapter(context, listaDatabaseModel,
                spinnerLayout, spinnerItemId, spinnerItemNome);

        this.setAdapter(msa);

    }

    /**
     * @param labelPrimeiro
     * @param listaEnum
     * @return
     */
    private ArrayList<DatabaseModel> montaLista(final String labelPrimeiro,
                                                SpinnerEnum[] listaEnum, SpinnerEnum... enumIgnorar) {
        List<SpinnerEnum> temp = new ArrayList<SpinnerEnum>(Arrays.asList(listaEnum));

        for(SpinnerEnum e : enumIgnorar) {
            temp.remove(e);
        }
        lista = new ArrayList<DatabaseModel>();
        lista.add(new DatabaseModel("0", labelPrimeiro));
        for (SpinnerEnum obj : temp) {
            DatabaseModel dm = new DatabaseModel(String.valueOf(obj.getId()),
                    obj.getNome());
            lista.add(dm);
        }

        return lista;
    }

}

A implementação pública loadSpinner será a responsável por receber as informações necessárias para o preenchimento do Spinner, com base nas necessidades do SpinnerAdapter. Notem que além de informar a lista de nossa interface SpinnerEnum, também é necessário informar os IDs do layout, ID e Nome. De quebra, é possível também passarmos uma lista de itens do SpinnerEnum que não queremos que seja adicionado.

public void loadSpinner(Context context, String spinnerText,
                            SpinnerEnum[] listaEnum, int spinnerLayout, int spinnerItemId,
                            int spinnerItemNome, SpinnerEnum... enumIgnorar) {

A implementação privada montaLista conterá toda a lógica para o preenchimento do Spinner. Note que o primeiro elemento terá o valor “0” sempre.

E como podemos usar este SpinnerUI ?

SpinnerUI spinner = (SpinnerUI) view.findViewById(R.id.spinnerUI_despesas);
        spinner.loadSpinner(getActivity(), getString(R.string.despesaSelecionar), DespesaEnum.getSortedVaules(), R.layout.spinner_ui, R.id.spinnerUI_id, R.id.spinnerUI_nome, DespesaEnum.ABASTECIMENTO, DespesaEnum.TROCA_OLEO, DespesaEnum.REVISAO);

E como implementamos nossas classes do tipo enum? Veja uma implementação simples abaixo.

import com.sutradi.lib.ui.spinner.SpinnerEnum;

import java.util.Arrays;
import java.util.Comparator;

public enum DespesaEnum implements SpinnerEnum {

	REVISAO(1, "Revisão"), 
	ABASTECIMENTO(2, "Abastecimento"), 
	TROCA_OLEO(3,"Troca de Óleo"), 
	MANUTENCAO(4, "Manutenção / Mecânica"), 
	OUTRA(99, "Outras");

	private long id;
	private String nome;

	DespesaEnum(long _id, String _nome) {
		this.id = _id;
		this.nome = _nome;
	}

	public static DespesaEnum getById(long id) {
		for (DespesaEnum item : DespesaEnum.values()) {
			if (item.getId() == id) {
				return item;
			}
		}
		return DespesaEnum.OUTRA;
	}

	/**
	 * @return the id
	 */
	public long getId() {
		return id;
	}

	/**
	 * @param id
	 *            the id to set
	 */
	public void setId(long id) {
		this.id = id;
	}

	/**
	 * @return the nome
	 */
	public String getNome() {
		return nome;
	}

	/**
	 * @param nome
	 *            the nome to set
	 */
	public void setNome(String nome) {
		this.nome = nome;
	}

	@Override
	public Object[] listar() {
		return DespesaEnum.values();
	}
	
	public static DespesaEnum[] getSortedVaules(DespesaEnum... ignore) {
		DespesaEnum[] statures = values();
		Arrays.sort(statures, EnumByNameComparator.INSTANCE);
		return statures;
	}

	private static class EnumByNameComparator implements Comparator<Enum<?>> {
		public static final Comparator<Enum<?>> INSTANCE = new EnumByNameComparator();
		public int compare(Enum<?> enum1, Enum<?> enum2) {
			return enum1.name().compareTo(enum2.name());
		}
	}

}

Você será obrigado a implementar os métodos getId e getNome, que farão a ligação com os elementos no layout do Spinner.

Você pode adicionar mais atributos na interface SpinnerEnum e enriquecer sua implementação.

Bysutradi

Como usar Log em aplicações

O uso de Logs em aplicações é uma boa prática para apoiar a equipe de desenvolvimento a encontrar ocorrências (erros) apontadas pelos usuários. Neste ponto não abordarei a prática do TDD, ok?

Pensando no uso do Log4J, um dos principais frameworks de Log, você sabe usar os tipos mensagens que podem ser apresentadas e usá-las de uma forma a ajudá-lo?

Vejam abaixo alguns pontos sobre os principais usos.

INFO

O nível INFO é utilizado para informar eventos significativos do ciclo de vida normal da aplicação. Exemplo:

LOG.info("Conta para o CPF " + cpf + " foi cadastrada com sucesso pelo usuário " + usuario);
LOG.info("SMS enviado com sucesso para o cliente " + cpf);

DEBUG

O nível DEBUG é normalmente utilizado como um complemento ao nível INFO como, por exemplo, parâmetros de um determinado método ou resultado de uma operação que pode ser importante para o rastreamento de problemas na aplicação. Exemplo:

// no início do método
LOG.debug("salvarConta(" + cpf + ", " + usuario + ")");

// no fim do método
LOG.debug("salvarConta finalizado em " + tempo + " milisegundos");

ERROR

O nível ERROR é utilizado mais freqüentemente, normalmente seguindo uma Java Exception. Essas condições de erro, não necessariamente causam o término anormal da aplicação, sendo que a aplicação pode continuar atendendo às próximas requisições normalmente. Exemplo:

// Dentro de um catch
LOG.error("Erro na tentativa de salvar a conta para o CPF " + cpf + ". Erro: " + exception);

WARN

O nível WARN indica problemas de menor gravidade, normalmente causados por fatores externos, como falta ou inconsistência de parâmetros passados pelo usuário. Exemplo:

LOG.warn("O parâmetro CPF não foi preenchido");

LOG.warn("A Mensagem " + nomeMensagem + " deveria ter 120 caracteres, mas veio com 118");

Exemplo prático

Suponhamos que temos a regra “Ao cadastrar a conta, enviar SMS para cliente.”, a implementação seria algo como:

// Início do método salvarConta
LOG.debug("salvarConta(" + cpf + ", " + usuario + ")");
try {
// Cadastrar Conta
LOG.info("Conta para o CPF " + cpf + " foi cadastrada com sucesso pelo usuário " + usuario);
// Enviar SMS
LOG.info("SMS enviado com sucesso para o cliente " + cpf);
} catch (ExceptionNegocio en) {
// Tratar aqui também com WARN, DEBUG ou ERROR
} catch (Exception exception) {
LOG.error("Erro na tentativa de salvar a conta para o CPF " + cpf + ". Erro: " + exception);
}
LOG.debug("salvarConta finalizado em " + tempo + " milisegundos");
// Fim do método salvarConta

Claro, a forma de uso pode ser mais refinada para cada projeto.