cz.cas.lib.proarc.webapp.client.ErrorHandler.java Source code

Java tutorial

Introduction

Here is the source code for cz.cas.lib.proarc.webapp.client.ErrorHandler.java

Source

/*
 * Copyright (C) 2012 Jan Pokorsky
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */
package cz.cas.lib.proarc.webapp.client;

import com.google.gwt.core.client.GWT;
import com.google.gwt.json.client.JSONObject;
import com.google.gwt.json.client.JSONParser;
import com.google.gwt.json.client.JSONString;
import com.google.gwt.json.client.JSONValue;
import com.google.gwt.safehtml.shared.SafeHtmlUtils;
import com.smartgwt.client.data.DSRequest;
import com.smartgwt.client.data.DSResponse;
import com.smartgwt.client.data.DataSource;
import com.smartgwt.client.i18n.SmartGwtMessages;
import com.smartgwt.client.rpc.HandleErrorCallback;
import com.smartgwt.client.rpc.HandleTransportErrorCallback;
import com.smartgwt.client.rpc.RPCManager;
import com.smartgwt.client.rpc.RPCResponse;
import com.smartgwt.client.types.Overflow;
import com.smartgwt.client.util.SC;
import com.smartgwt.client.widgets.Button;
import com.smartgwt.client.widgets.Canvas;
import com.smartgwt.client.widgets.Dialog;
import com.smartgwt.client.widgets.layout.VLayout;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * Global Smart GWT RPC error handler.
 *
 * <p>Replaces default {@link HandleTransportErrorCallback} and {@link HandleErrorCallback}
 * to provide more detailed notifications of RPC errors and to make available
 * {@link #getTransportError() HTTP response text} in case of HTTP error.
 *
 * <p>Each {@link DataSource} can override global handler with custom one with {@link DataSource#addHandleErrorHandler }.
 * Then it is still necessary to cancel the {@link com.smartgwt.client.data.events.ErrorEvent event}.
 *
 * @author Jan Pokorsky
 */
public final class ErrorHandler {

    private static final Logger LOG = Logger.getLogger(ErrorHandler.class.getName());
    private TransportError transportError;
    private final ClientMessages i18n;

    public ErrorHandler() {
        this(GWT.<ClientMessages>create(ClientMessages.class));
    }

    public ErrorHandler(ClientMessages i18n) {
        this.i18n = i18n;
    }

    /**
     * Gets last transport error or {@code null}.
     */
    public TransportError getTransportError() {
        return transportError;
    }

    /**
     * Registers custom error handler.
     */
    void initTransportErrorHandler() {
        RPCManager.setHandleTransportErrorCallback(new HandleTransportErrorCallback() {

            @Override
            public void handleTransportError(int transactionNum, int status, int httpResponseCode,
                    String httpResponseText) {
                transportError = new TransportError(transactionNum, status, httpResponseCode, httpResponseText);
            }
        });

        RPCManager.setHandleErrorCallback(new HandleErrorCallback() {

            @Override
            public void handleError(DSResponse response, DSRequest request) {
                TransportError te = transportError;
                ErrorHandler.this.handleError(te, response, request);

            }
        });
    }

    /**
     * Gets simple message from error response for user.
     */
    private String getClientMessage(TransportError te, DSResponse response, DSRequest request) {
        String contentType = String.valueOf(response.getHttpHeaders().get("Content-Type"));
        String result;
        if (response.getStatus() == RPCResponse.STATUS_FAILURE) {
            return response.getDataAsString();
        }
        if (contentType.contains("text/plain")) {
            result = te == null ? null : te.getHttpResponseText();
        } else {
            result = i18n.ErrorHandler_UnexpectedError_Msg();
        }
        if (result == null || result.isEmpty() || result.length() > 1000) {
            // message from original error handler; contains URL
            return response.getDataAsString();
        } else {
            return result;
        }
    }

    private void handleError(TransportError te, DSResponse response, DSRequest request) {
        String requestDump = ClientUtils.dump(request.getJsObj());
        // message from original error handler; contains URL
        Object smartGwtMsg = response.getDataAsString();
        if (te == null || response.getTransactionNum() != te.getTransactionNum()) {
            String debugInfo = ClientUtils.format(
                    "Invalid transaction numbers %s != %s" + "\n%s\nStatus: %s\nQuery: %s",
                    response.getTransactionNum(), te == null ? null : te.getTransactionNum(), smartGwtMsg,
                    response.getStatus(), requestDump);
            SC.logWarn(debugInfo);
        }
        //            boolean clientError = te.getHttpResponseCode() >= 400 && te.getHttpResponseCode() < 500;
        String clientMsg = getClientMessage(te, response, request);
        String debugInfo = ClientUtils.format("%s\nStatus: %s\nQuery: %s", smartGwtMsg,
                te == null ? response.getStatus() : te.getStatus(), requestDump);
        String htmlDebugInfo = ClientUtils.format("%s<br/>Status: %s<br/>Query: %s", smartGwtMsg,
                te == null ? response.getStatus() : te.getStatus(), SafeHtmlUtils.htmlEscape(requestDump));
        warn(clientMsg, te == null ? response.getDataAsString() : te.getHttpResponseText(), htmlDebugInfo);
        SC.logWarn(debugInfo);
    }

    /**
     * The default notification about an error.
     */
    public static void warn(DSResponse response, DSRequest request) {
        new ErrorHandler().handleError(new TransportError(response.getTransactionNum(), response.getStatus(),
                response.getHttpResponseCode(), response.getHttpResponseText()), response, request);
    }

    /**
     * The default notification about an error using a text message.
     */
    public static void warn(String error) {
        new ErrorHandler().warn(null, error, null);
    }

    /**
     * Notifies user about error.
     * @param msg simple client message
     * @param detailMsg detail response message; can be HTML
     * @param debugInfo request details, URL, ...
     */
    private void warn(String msg, String detailMsg, String debugInfo) {
        if (msg == null) {
            msg = i18n.ErrorHandler_UnexpectedError_Msg();
        }
        SmartGwtMessages sgi18n = ClientUtils.createSmartGwtMessages();
        boolean allowDetail = !msg.equals(detailMsg);
        final Dialog d = new Dialog();
        d.setTitle(sgi18n.dialog_WarnTitle());
        d.setIsModal(true);
        d.setAutoSize(Boolean.FALSE);
        d.setMessage(msg);
        d.setIcon("[SKIN]warn.png");
        d.setCanDragResize(true);
        d.setCanDragReposition(Boolean.TRUE);
        d.setKeepInParentRect(Boolean.TRUE);
        //        d.setShowMaximizeButton(Boolean.TRUE);
        d.setMinMemberSize(50);
        Button details = new Button(i18n.ErrorHandler_ButtonDetalis_Title());
        details.setVisible(allowDetail);
        d.setButtons(Dialog.OK, details);
        if (allowDetail) {
            Canvas errorPane = new Canvas();
            errorPane.setOverflow(Overflow.AUTO);
            errorPane.setWidth100();
            errorPane.setHeight100();
            errorPane.setContents(detailMsg);
            errorPane.setCanSelectText(true);
            Canvas debugInfoPane = new Canvas();
            debugInfoPane.setWidth100();
            debugInfoPane.setAutoHeight();
            debugInfoPane.setContents(debugInfo);
            debugInfoPane.setCanSelectText(true);
            final VLayout detailPane = new VLayout(4);
            detailPane.setLayoutMargin(4);
            detailPane.setGroupTitle(i18n.ErrorHandler_ButtonDetalis_Title());
            detailPane.setIsGroup(true);
            detailPane.setVisible(false);
            detailPane.addMember(errorPane);
            detailPane.addMember(debugInfoPane);
            detailPane.setWidth100();
            detailPane.setHeight100();
            d.addItem(detailPane);
            details.addClickHandler(new com.smartgwt.client.widgets.events.ClickHandler() {
                @Override
                public void onClick(com.smartgwt.client.widgets.events.ClickEvent event) {
                    if (detailPane.isVisible()) {
                        d.restore();
                    } else {
                        d.maximize();
                    }
                    detailPane.setVisible(!detailPane.isVisible());
                }
            });
        }
        d.show();
    }

    /**
     * Creates response object from JSON response string in
     * {@link com.smartgwt.client.data.RestDataSource SmartGWT format}.
     * @param response JSON response string
     * @return response object
     */
    public static DSResponse getDsResponse(String response) {
        DSResponse dsResponse;
        //        ClientUtils.info(LOG, "response: %s", response);
        if (response == null || response.isEmpty()) {
            // not JSON response
            LOG.log(Level.WARNING, null, new IllegalStateException("Empty response!"));
            dsResponse = new DSResponse();
            dsResponse.setStatus(RPCResponse.STATUS_SUCCESS);
        } else {
            JSONValue rVal = JSONParser.parseStrict(response);
            if (rVal.isObject() != null) {
                rVal = rVal.isObject().get("response");
            }
            if (rVal != null && rVal.isObject() != null) {
                JSONObject rObj = rVal.isObject();
                dsResponse = DSResponse.getOrCreateRef(rObj.getJavaScriptObject());
            } else {
                // not JSON response in expected format
                JSONObject jsonObject = new JSONObject();
                jsonObject.put("data", new JSONString(response));
                dsResponse = new DSResponse(jsonObject.getJavaScriptObject());
                dsResponse.setStatus(RPCResponse.STATUS_FAILURE);
            }
        }
        return dsResponse;
    }

    /**
     * Holds parameters from last {@link HandleTransportErrorCallback#handleTransportError
     * transport error} that are not accessible from {@link DSResponse}.
     * Particularly {@code httpResponseText}.
     */
    public static final class TransportError {

        private int status;
        private int httpResponseCode;
        private int transactionNum;
        private String httpResponseText;

        public TransportError(int transactionNum, int status, int httpResponseCode, String httpResponseText) {
            this.transactionNum = transactionNum;
            this.status = status;
            this.httpResponseCode = httpResponseCode;
            this.httpResponseText = httpResponseText;
        }

        public int getStatus() {
            return status;
        }

        public int getHttpResponseCode() {
            return httpResponseCode;
        }

        public int getTransactionNum() {
            return transactionNum;
        }

        public String getHttpResponseText() {
            return httpResponseText;
        }

        @Override
        public String toString() {
            return "TransportError{" + "status=" + status + ", httpResponseCode=" + httpResponseCode
                    + ", transactionNum=" + transactionNum + ", httpResponseText=" + httpResponseText + '}';
        }

    }

}