001/* 002 * This library is part of OpenCms - 003 * the Open Source Content Management System 004 * 005 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com) 006 * 007 * This library is free software; you can redistribute it and/or 008 * modify it under the terms of the GNU Lesser General Public 009 * License as published by the Free Software Foundation; either 010 * version 2.1 of the License, or (at your option) any later version. 011 * 012 * This library is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 015 * Lesser General Public License for more details. 016 * 017 * For further information about Alkacon Software, please see the 018 * company website: http://www.alkacon.com 019 * 020 * For further information about OpenCms, please see the 021 * project website: http://www.opencms.org 022 * 023 * You should have received a copy of the GNU Lesser General Public 024 * License along with this library; if not, write to the Free Software 025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 026 */ 027 028package org.opencms.gwt.client.rpc; 029 030import org.opencms.gwt.client.Messages; 031import org.opencms.gwt.client.ui.CmsErrorDialog; 032import org.opencms.gwt.client.ui.CmsNotification; 033import org.opencms.gwt.client.ui.CmsNotificationMessage; 034 035import com.google.gwt.core.client.GWT; 036import com.google.gwt.event.shared.UmbrellaException; 037import com.google.gwt.user.client.Timer; 038import com.google.gwt.user.client.rpc.AsyncCallback; 039import com.google.gwt.user.client.rpc.StatusCodeException; 040 041/** 042 * Consistently manages RPCs errors and 'loading' state.<p> 043 * 044 * @param <T> The type of the expected return value 045 * 046 * @since 8.0 047 */ 048public abstract class CmsRpcAction<T> implements AsyncCallback<T> { 049 050 /** The sync token value, used to allow synchronous RPC calls within vaadin, see also com.google.gwt.http.client.RequestBuilder within the super source */ 051 public static final String SYNC_TOKEN = "this_is_a_synchronous_rpc_call"; 052 053 /** The message displayed when loading. */ 054 private String m_loadingMessage; 055 056 /** The current notification. */ 057 private CmsNotificationMessage m_notification; 058 059 /** The result, used only for synchronized request. */ 060 private T m_result; 061 062 /** The timer to control the display of the 'loading' state, if the action takes too long. */ 063 private Timer m_timer; 064 065 /** 066 * Executes the current RPC call.<p> 067 * 068 * Initializes client-server communication and will 069 */ 070 public abstract void execute(); 071 072 /** 073 * Executes a synchronized request.<p> 074 * 075 * @return the RPC result 076 * 077 * @see #execute() 078 */ 079 public T executeSync() { 080 081 execute(); 082 return m_result; 083 } 084 085 /** 086 * Handle errors.<p> 087 * 088 * @see com.google.gwt.user.client.rpc.AsyncCallback#onFailure(java.lang.Throwable) 089 */ 090 public void onFailure(Throwable t) { 091 092 if ((t instanceof StatusCodeException) && (((StatusCodeException)t).getStatusCode() == 0)) { 093 // a status code 0 indicates the client aborted the request, most likely when leaving the page, this should be ignored 094 return; 095 } else if ((t instanceof StatusCodeException) && (((StatusCodeException)t).getStatusCode() == 401)) { 096 // a server error 500 most likely indicates an expired session and there for insufficient user rights to access any GWT service 097 CmsErrorDialog dialog = new CmsErrorDialog(Messages.get().key(Messages.GUI_SESSION_EXPIRED_0), null); 098 dialog.center(); 099 } else { 100 CmsErrorDialog.handleException(t); 101 } 102 // remove the overlay 103 stop(false); 104 } 105 106 /** 107 * @see com.google.gwt.user.client.rpc.AsyncCallback#onSuccess(java.lang.Object) 108 */ 109 public void onSuccess(T value) { 110 111 try { 112 m_result = value; 113 onResponse(value); 114 } catch (UmbrellaException exception) { 115 Throwable wrappedException = exception.getCauses().iterator().next(); 116 onFailure(wrappedException); 117 if (!GWT.isProdMode()) { 118 throw exception; 119 } 120 } catch (RuntimeException error) { 121 onFailure(error); 122 if (!GWT.isProdMode()) { 123 throw error; 124 } 125 } 126 } 127 128 /** 129 * Sets the loading message.<p> 130 * 131 * @param loadingMessage the loading message to set 132 */ 133 public void setLoadingMessage(String loadingMessage) { 134 135 m_loadingMessage = loadingMessage; 136 } 137 138 /** 139 * Starts the timer for showing the 'loading' state.<p> 140 * 141 * Note: Has to be called manually before calling the RPC service.<p> 142 * 143 * @param delay the delay in milliseconds 144 * @param blocking shows an blocking overlay if <code>true</code> 145 */ 146 public void start(int delay, final boolean blocking) { 147 148 if (delay <= 0) { 149 show(blocking); 150 return; 151 } 152 m_timer = new Timer() { 153 154 /** 155 * @see com.google.gwt.user.client.Timer#run() 156 */ 157 @Override 158 public void run() { 159 160 show(blocking); 161 } 162 }; 163 m_timer.schedule(delay); 164 } 165 166 /** 167 * Stops the timer.<p> 168 * 169 * Note: Has to be called manually on success.<p> 170 * 171 * @param displayDone <code>true</code> if you want to tell the user that the operation was successful 172 */ 173 public void stop(boolean displayDone) { 174 175 if (m_timer != null) { 176 m_timer.cancel(); 177 m_timer = null; 178 } 179 if (m_notification != null) { 180 CmsNotification.get().removeMessage(m_notification); 181 m_notification = null; 182 } 183 if (displayDone) { 184 CmsNotification.get().send(CmsNotification.Type.NORMAL, Messages.get().key(Messages.GUI_DONE_0)); 185 } 186 } 187 188 /** 189 * Handles the result when received from server.<p> 190 * 191 * @param result the result from server 192 * 193 * @see AsyncCallback#onSuccess(Object) 194 */ 195 protected abstract void onResponse(T result); 196 197 /** 198 * Shows the 'loading message'.<p> 199 * 200 * Overwrite to customize the message.<p> 201 * 202 * @param blocking shows an blocking overlay if <code>true</code> 203 */ 204 protected void show(boolean blocking) { 205 206 if (blocking) { 207 m_notification = CmsNotification.get().sendBusy(CmsNotification.Type.NORMAL, m_loadingMessage); 208 } else { 209 m_notification = CmsNotification.get().sendSticky(CmsNotification.Type.NORMAL, m_loadingMessage); 210 } 211 } 212}