org.opensingular.internal.form.wicket.util.WicketSerializationDebugUtil.java Source code

Java tutorial

Introduction

Here is the source code for org.opensingular.internal.form.wicket.util.WicketSerializationDebugUtil.java

Source

/*
 * Copyright (C) 2016 Singular Studios (a.k.a Atom Tecnologia) - www.opensingular.com
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.opensingular.internal.form.wicket.util;

import com.google.common.base.Throwables;
import org.apache.wicket.Application;
import org.apache.wicket.Component;
import org.apache.wicket.Page;
import org.apache.wicket.application.IComponentOnAfterRenderListener;
import org.apache.wicket.request.cycle.AbstractRequestCycleListener;
import org.apache.wicket.request.cycle.RequestCycle;
import org.opensingular.form.SingularFormException;
import org.opensingular.internal.lib.commons.util.SingularIOUtils;
import org.opensingular.lib.commons.base.SingularException;
import org.opensingular.lib.commons.base.SingularProperties;

import java.io.ByteArrayInputStream;
import java.io.EOFException;
import java.io.ObjectInputStream;
import java.util.logging.Logger;

/**
 * Mtodo para apoio ao debug em tempo de desenvolvimento da correta capacidade de serializao das pginas Wicket.
 * <p>ATENO: ESSE DEBUG  UMA OPERAO CUSTOSA. ATIVE-O APENAS EM DESENVOLVIMENTO.</p>
 * Deve ser usado da seguinte forma para ativar o uso dentro da classe que faz a configurao do application do wicket:
 * <pre>
 *    WicketSerializationDebugUtil.configurePageSerializationDebug(wicketApplication, this.getClass());
 * </pre>
 * Veja mais em {@link #configurePageSerializationDebug(Application, Class)}
 *
 * @author Daniel Bordin on 12/02/2017.
 */
public class WicketSerializationDebugUtil {

    private WicketSerializationDebugUtil() {
    }

    /**
     * Adiciona o verificado de pgina serializveis na aplicao se estiver no modo de desenvolvimento. Veja mais em
     * {@link #configurePageSerializationDebug(Application, Class)}.
     */
    public static void configurePageSerializationDebugIfInDevelopmentMode(Application application,
            Class<?> targetClassLog) {
        if (SingularProperties.get().isTrue(SingularProperties.SINGULAR_DEV_MODE)) {
            configurePageSerializationDebug(application, targetClassLog);
        }
    }

    /**
     * Adiciona listener na aplicao para fazer o teste se ao final de cada renderizao de pgina, a pgina 
     * serializvel. Passa a gera uma informao de tamanho e tempo da serializao no log da aplicao. Gera um log de
     * exception se a pgina no for serializvel.
     *
     * @param application    A ser adicionada a verificao
     * @param targetClassLog Classe para o qual ser gerado os logs da verificao
     */
    public static void configurePageSerializationDebug(Application application, Class<?> targetClassLog) {
        //Configura os listeners para verificao da serializao
        DebugSerializationListener debugger = new DebugSerializationListener(targetClassLog);
        application.getComponentOnAfterRenderListeners().add(debugger);
        application.getRequestCycleListeners().add(new DebugSerializationRequestCycleListeners(debugger));
        //Ativa a pilha de serializao detalhada
        if (!"true".equals(System.getProperty("sun.io.serialization.extendedDebugInfo"))) {
            System.setProperty("sun.io.serialization.extendedDebugInfo", "true");
        }
    }

    /** Apenas para implementao de JUnit. */
    final static String getLastVerificationResult(Application application) {
        for (IComponentOnAfterRenderListener listener : application.getComponentOnAfterRenderListeners()) {
            if (listener instanceof DebugSerializationListener) {
                return ((DebugSerializationListener) listener).lastVerification;
            }
        }
        return null;
    }

    /** Listener da execuo do ciclo de vida da chamada para verifica necessidades de teste de serializao. */
    private static class DebugSerializationRequestCycleListeners extends AbstractRequestCycleListener {
        private final DebugSerializationListener debugger;

        public DebugSerializationRequestCycleListeners(DebugSerializationListener debugger) {
            this.debugger = debugger;
        }

        @Override
        public void onBeginRequest(RequestCycle cycle) {
            super.onBeginRequest(cycle);
            debugger.clearCurrentThread();
        }

        @Override
        public void onEndRequest(RequestCycle cycle) {
            debugger.runSerializationIfNecessary();
            super.onEndRequest(cycle);
        }
    }

    /** Listener Wicket para executar a verificao da pgina como serializvel. */
    private static class DebugSerializationListener implements IComponentOnAfterRenderListener {

        private final ThreadLocal<Component> componentThreadLocal = new ThreadLocal<>();
        private final Logger logger;
        private String lastVerification;

        public DebugSerializationListener(Class<?> targetClassLog) {
            logger = Logger.getLogger(targetClassLog.getName());
        }

        /** Limpa a requesio antes de comear um novo ciclo. */
        public void clearCurrentThread() {
            componentThreadLocal.remove();
        }

        /**
         * Mtodo chamado pelo listener da aplicao wicket para verificar se a pgina que acabou de ser redenderizada 
         * serializvel.
         */
        @Override
        public void onAfterRender(Component c) {
            Component current = componentThreadLocal.get();
            if (c instanceof Page) {
                componentThreadLocal.set(c);
                tryComponentSerialization(c);
                return;
            } else if (current == null) {
                componentThreadLocal.set(c);
                return;
            } else if (current instanceof Page) {
                return;
            }
            for (Component parent = c.getParent(); parent != null; parent = parent.getParent()) {
                if (parent == current) {
                    return; //The new component is inside of the current one
                }
            }
            componentThreadLocal.set(c);
        }

        /** Verifica se ficou alguma execuo de serializao pendente para testar. */
        public void runSerializationIfNecessary() {
            Component c = componentThreadLocal.get();
            if (c != null) {
                componentThreadLocal.remove();
                if (!(c instanceof Page)) {
                    //No foi chamada de pgina, mas uma chamada Ajax, ento chamara a serializao agora
                    tryComponentSerialization(c);
                }
            }
        }

        private void tryComponentSerialization(Component c) {
            //Serialization
            long time = System.currentTimeMillis();
            byte[] result = c.getApplication().getFrameworkSettings().getSerializer().serialize(c);
            time = System.currentTimeMillis() - time;

            String msg = "Serialization: target=" + c.getClass().getName() + " size="
                    + (result == null ? "EXCEPTION" : SingularIOUtils.humanReadableByteCount(result.length))
                    + " serialization=" + SingularIOUtils.humanReadableMiliSeconds(time);
            try {
                if (result == null) {
                    throw new SingularFormException("Erro serializando a pgina " + c.getClass().getName()
                            + ". Verifique o log para obter a pilha de erro da serializao.");
                }

                //Deserialization
                time = System.currentTimeMillis();
                Object last = readAllObjects(result);
                time = System.currentTimeMillis() - time;

                msg += " deserialization=" + SingularIOUtils.humanReadableMiliSeconds(time);
                Class<?> classLast = (last == null ? null : last.getClass());
                if (c.getClass() != classLast) {
                    msg += " !!!! DESERIALIZATED CLASS NOT OF EXPECTED TYPE result=" + classLast;
                }
            } finally {
                lastVerification = msg;
                logger.info(msg);
            }
        }

        /** L todos os objetos serialziados, retornando o ltimo. */
        private Object readAllObjects(byte[] content) {
            Object last = null;
            try {
                ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(content));
                Object o;
                do {
                    o = in.readObject();
                    if (o != null) {
                        last = o;
                    }
                } while (o != null);
            } catch (Exception e) {
                if (!(e instanceof EOFException)) {
                    Throwables.throwIfUnchecked(e);
                    throw SingularException.rethrow(e);
                }
            }
            return last;
        }
    }

}