1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969 |
- /*
- @ITMillApache2LicenseForJavaFiles@
- */
-
- package com.vaadin.terminal.gwt.server;
-
- import java.io.BufferedWriter;
- import java.io.CharArrayWriter;
- import java.io.IOException;
- import java.io.InputStream;
- import java.io.InputStreamReader;
- import java.io.OutputStream;
- import java.io.OutputStreamWriter;
- import java.io.PrintWriter;
- import java.io.Serializable;
- import java.lang.reflect.InvocationTargetException;
- import java.lang.reflect.Method;
- import java.net.URL;
- import java.security.GeneralSecurityException;
- import java.text.DateFormat;
- import java.text.DateFormatSymbols;
- import java.text.SimpleDateFormat;
- import java.util.ArrayList;
- import java.util.Calendar;
- import java.util.Collection;
- import java.util.Collections;
- import java.util.Comparator;
- import java.util.GregorianCalendar;
- import java.util.HashMap;
- import java.util.HashSet;
- import java.util.Iterator;
- import java.util.List;
- import java.util.Locale;
- import java.util.Map;
- import java.util.Set;
- import java.util.StringTokenizer;
- import java.util.UUID;
-
- import javax.portlet.PortletRequest;
- import javax.portlet.PortletResponse;
- import javax.servlet.ServletRequest;
- import javax.servlet.ServletResponse;
-
- import com.vaadin.Application;
- import com.vaadin.Application.SystemMessages;
- import com.vaadin.external.org.apache.commons.fileupload.FileItemIterator;
- import com.vaadin.external.org.apache.commons.fileupload.FileItemStream;
- import com.vaadin.external.org.apache.commons.fileupload.FileUpload;
- import com.vaadin.external.org.apache.commons.fileupload.FileUploadException;
- import com.vaadin.external.org.apache.commons.fileupload.ProgressListener;
- import com.vaadin.terminal.ApplicationResource;
- import com.vaadin.terminal.DownloadStream;
- import com.vaadin.terminal.PaintException;
- import com.vaadin.terminal.PaintTarget;
- import com.vaadin.terminal.Paintable;
- import com.vaadin.terminal.Paintable.RepaintRequestEvent;
- import com.vaadin.terminal.Terminal.ErrorEvent;
- import com.vaadin.terminal.Terminal.ErrorListener;
- import com.vaadin.terminal.URIHandler;
- import com.vaadin.terminal.UploadStream;
- import com.vaadin.terminal.VariableOwner;
- import com.vaadin.terminal.gwt.client.ApplicationConnection;
- import com.vaadin.terminal.gwt.server.ComponentSizeValidator.InvalidLayout;
- import com.vaadin.ui.AbstractField;
- import com.vaadin.ui.Component;
- import com.vaadin.ui.DragAndDropWrapper;
- import com.vaadin.ui.Upload;
- import com.vaadin.ui.Upload.UploadException;
- import com.vaadin.ui.Window;
-
- /**
- * This is a common base class for the server-side implementations of the
- * communication system between the client code (compiled with GWT into
- * JavaScript) and the server side components. Its client side counterpart is
- * {@link ApplicationConnection}.
- *
- * A server side component sends its state to the client in a paint request (see
- * {@link Paintable} and {@link PaintTarget} on the server side). The client
- * widget receives these paint requests as calls to
- * {@link com.vaadin.terminal.gwt.client.Paintable#updateFromUIDL()}. The client
- * component communicates back to the server by sending a list of variable
- * changes (see {@link ApplicationConnection#updateVariable()} and
- * {@link VariableOwner#changeVariables(Object, Map)}).
- *
- * TODO Document better!
- */
- @SuppressWarnings("serial")
- public abstract class AbstractCommunicationManager implements
- Paintable.RepaintRequestListener, Serializable {
-
- /**
- * Generic interface of a (HTTP or Portlet) request to the application.
- *
- * This is a wrapper interface that allows
- * {@link AbstractCommunicationManager} to use a unified API.
- *
- * @see javax.servlet.ServletRequest
- * @see javax.portlet.PortletRequest
- *
- * @author peholmst
- */
- protected interface Request {
-
- /**
- * Gets a {@link Session} wrapper implementation representing the
- * session for which this request was sent.
- *
- * Multiple Vaadin applications can be associated with a single session.
- *
- * @return Session
- */
- public Session getSession();
-
- /**
- * Are the applications in this session running in a portlet or directly
- * as servlets.
- *
- * @return true if in a portlet
- */
- public boolean isRunningInPortlet();
-
- /**
- * Get the named HTTP or portlet request parameter.
- *
- * @see javax.servlet.ServletRequest#getParameter(String)
- * @see javax.portlet.PortletRequest#getParameter(String)
- *
- * @param name
- * @return
- */
- public String getParameter(String name);
-
- /**
- * Returns the length of the request content that can be read from the
- * input stream returned by {@link #getInputStream()}.
- *
- * @return content length in bytes
- */
- public int getContentLength();
-
- /**
- * Returns an input stream from which the request content can be read.
- * The request content length can be obtained with
- * {@link #getContentLength()} without reading the full stream contents.
- *
- * @return
- * @throws IOException
- */
- public InputStream getInputStream() throws IOException;
-
- /**
- * Returns the request identifier that identifies the target Vaadin
- * window for the request.
- *
- * @return String identifier for the request target window
- */
- public String getRequestID();
-
- /**
- * @see javax.servlet.ServletRequest#getAttribute(String)
- * @see javax.portlet.PortletRequest#getAttribute(String)
- */
- public Object getAttribute(String name);
-
- /**
- * @see javax.servlet.ServletRequest#setAttribute(String, Object)
- * @see javax.portlet.PortletRequest#setAttribute(String, Object)
- */
- public void setAttribute(String name, Object value);
-
- /**
- * Gets the underlying request object. The request is typically either a
- * {@link ServletRequest} or a {@link PortletRequest}.
- *
- * @return wrapped request object
- */
- public Object getWrappedRequest();
-
- }
-
- /**
- * Generic interface of a (HTTP or Portlet) response from the application.
- *
- * This is a wrapper interface that allows
- * {@link AbstractCommunicationManager} to use a unified API.
- *
- * @see javax.servlet.ServletResponse
- * @see javax.portlet.PortletResponse
- *
- * @author peholmst
- */
- protected interface Response {
-
- /**
- * Gets the output stream to which the response can be written.
- *
- * @return
- * @throws IOException
- */
- public OutputStream getOutputStream() throws IOException;
-
- /**
- * Sets the MIME content type for the response to be communicated to the
- * browser.
- *
- * @param type
- */
- public void setContentType(String type);
-
- /**
- * Gets the wrapped response object, usually a class implementing either
- * {@link ServletResponse} or {@link PortletResponse}.
- *
- * @return wrapped request object
- */
- public Object getWrappedResponse();
-
- }
-
- /**
- * Generic wrapper interface for a (HTTP or Portlet) session.
- *
- * Several applications can be associated with a single session.
- *
- * TODO Document me!
- *
- * @see javax.servlet.http.HttpSession
- * @see javax.portlet.PortletSession
- *
- * @author peholmst
- */
- protected interface Session {
-
- public boolean isNew();
-
- public Object getAttribute(String name);
-
- public void setAttribute(String name, Object o);
-
- public int getMaxInactiveInterval();
-
- public Object getWrappedSession();
-
- }
-
- /**
- * TODO Document me!
- *
- * @author peholmst
- */
- protected interface Callback {
-
- public void criticalNotification(Request request, Response response,
- String cap, String msg, String details, String outOfSyncURL)
- throws IOException;
-
- public String getRequestPathInfo(Request request);
-
- public InputStream getThemeResourceAsStream(String themeName,
- String resource) throws IOException;
-
- }
-
- private static String GET_PARAM_REPAINT_ALL = "repaintAll";
-
- // flag used in the request to indicate that the security token should be
- // written to the response
- private static final String WRITE_SECURITY_TOKEN_FLAG = "writeSecurityToken";
-
- /* Variable records indexes */
- private static final int VAR_PID = 1;
- private static final int VAR_NAME = 2;
- private static final int VAR_TYPE = 3;
- private static final int VAR_VALUE = 0;
-
- private static final char VTYPE_PAINTABLE = 'p';
- private static final char VTYPE_BOOLEAN = 'b';
- private static final char VTYPE_DOUBLE = 'd';
- private static final char VTYPE_FLOAT = 'f';
- private static final char VTYPE_LONG = 'l';
- private static final char VTYPE_INTEGER = 'i';
- private static final char VTYPE_STRING = 's';
- private static final char VTYPE_ARRAY = 'a';
- private static final char VTYPE_STRINGARRAY = 'c';
- private static final char VTYPE_MAP = 'm';
-
- private static final String VAR_RECORD_SEPARATOR = "\u001e";
-
- private static final String VAR_FIELD_SEPARATOR = "\u001f";
-
- public static final String VAR_BURST_SEPARATOR = "\u001d";
-
- public static final String VAR_ARRAYITEM_SEPARATOR = "\u001c";
-
- private final HashMap<String, OpenWindowCache> currentlyOpenWindowsInClient = new HashMap<String, OpenWindowCache>();
-
- private static final int MAX_BUFFER_SIZE = 64 * 1024;
-
- private static final String GET_PARAM_ANALYZE_LAYOUTS = "analyzeLayouts";
-
- private final ArrayList<Paintable> dirtyPaintables = new ArrayList<Paintable>();
-
- private final HashMap<Paintable, String> paintableIdMap = new HashMap<Paintable, String>();
-
- private final HashMap<String, Paintable> idPaintableMap = new HashMap<String, Paintable>();
-
- private int idSequence = 0;
-
- private final Application application;
-
- // Note that this is only accessed from synchronized block and
- // thus should be thread-safe.
- private String closingWindowName = null;
-
- private List<String> locales;
-
- private int pendingLocalesIndex;
-
- private int timeoutInterval = -1;
-
- private DragAndDropService dragAndDropService;
-
- private static int nextUnusedWindowSuffix = 1;
-
- /**
- * TODO New constructor - document me!
- *
- * @param application
- */
- public AbstractCommunicationManager(Application application) {
- this.application = application;
- requireLocale(application.getLocale().toString());
- }
-
- /**
- * Create an upload handler that is appropriate to the context in which the
- * application is being run (servlet or portlet).
- *
- * @return new {@link FileUpload} instance
- */
- protected abstract FileUpload createFileUpload();
-
- /**
- * TODO New method - document me!
- *
- * @param upload
- * @param request
- * @return
- * @throws IOException
- * @throws FileUploadException
- */
- protected abstract FileItemIterator getUploadItemIterator(
- FileUpload upload, Request request) throws IOException,
- FileUploadException;
-
- /**
- * TODO New method - document me!
- *
- * @param request
- * @param response
- * @throws IOException
- * @throws FileUploadException
- */
- protected void doHandleFileUpload(Request request, Response response)
- throws IOException, FileUploadException {
-
- // Create a new file upload handler
- final FileUpload upload = createFileUpload();
-
- final UploadProgressListener pl = new UploadProgressListener();
-
- upload.setProgressListener(pl);
-
- // Parse the request
- FileItemIterator iter;
-
- try {
- iter = getUploadItemIterator(upload, request);
- /*
- * ATM this loop is run only once as we are uploading one file per
- * request.
- */
- while (iter.hasNext()) {
- final FileItemStream item = iter.next();
- final String name = item.getFieldName();
- // Should report only the filename even if the browser sends the
- // path
- final String filename = removePath(item.getName());
- final String mimeType = item.getContentType();
- final InputStream stream = item.openStream();
- if (item.isFormField()) {
- // ignored, upload requests contains only files
- } else {
- final UploadStream upstream = new UploadStream() {
-
- public String getContentName() {
- return filename;
- }
-
- public String getContentType() {
- return mimeType;
- }
-
- public InputStream getStream() {
- return stream;
- }
-
- public String getStreamName() {
- return "stream";
- }
-
- };
-
- if (name.startsWith("XHRFILE")) {
- String[] split = item.getFieldName().substring(7)
- .split("\\.");
- DragAndDropWrapper ddw = (DragAndDropWrapper) idPaintableMap
- .get(split[0]);
-
- try {
- ddw.receiveFile(upstream, split[1]);
- } catch (UploadException e) {
- synchronized (application) {
- handleChangeVariablesError(application, ddw, e,
- new HashMap<String, Object>());
- }
- }
-
- } else {
-
- int separatorPos = name.lastIndexOf("_");
- final String pid = name.substring(0, separatorPos);
- final Upload uploadComponent = (Upload) idPaintableMap
- .get(pid);
- if (uploadComponent == null) {
- throw new FileUploadException(
- "Upload component not found");
- }
- if (uploadComponent.isReadOnly()) {
- throw new FileUploadException(
- "Warning: ignored file upload because upload component is set as read-only");
- }
- synchronized (application) {
- // put upload component into receiving state
- uploadComponent.startUpload();
- }
-
- // tell UploadProgressListener which component is
- // receiving
- // file
- pl.setUpload(uploadComponent);
-
- try {
- uploadComponent.receiveUpload(upstream);
- } catch (UploadException e) {
- // error happened while receiving file. Handle the
- // error in the same manner as it would have
- // happened in
- // variable change.
- synchronized (application) {
- handleChangeVariablesError(application,
- uploadComponent, e,
- new HashMap<String, Object>());
- }
- }
- }
-
- }
- }
- } catch (final FileUploadException e) {
- throw e;
- }
-
- sendUploadResponse(request, response);
- }
-
- /**
- * Removes any possible path information from the filename and returns the
- * filename. Separators / and \\ are used.
- *
- * @param name
- * @return
- */
- private static String removePath(String filename) {
- if (filename != null) {
- filename = filename.replaceAll("^.*[/\\\\]", "");
- }
-
- return filename;
- }
-
- /**
- * TODO document
- *
- * @param request
- * @param response
- * @throws IOException
- */
- protected void sendUploadResponse(Request request, Response response)
- throws IOException {
- response.setContentType("text/html");
- final OutputStream out = response.getOutputStream();
- final PrintWriter outWriter = new PrintWriter(new BufferedWriter(
- new OutputStreamWriter(out, "UTF-8")));
- outWriter.print("<html><body>download handled</body></html>");
- outWriter.flush();
- out.close();
- }
-
- /**
- * Internally process a UIDL request from the client.
- *
- * This method calls
- * {@link #handleVariables(Request, Response, Callback, Application, Window)}
- * to process any changes to variables by the client and then repaints
- * affected components using {@link #paintAfterVariableChanges()}.
- *
- * Also, some cleanup is done when a request arrives for an application that
- * has already been closed.
- *
- * The method handleUidlRequest(...) in subclasses should call this method.
- *
- * TODO better documentation
- *
- * @param request
- * @param response
- * @param callback
- * @param window
- * target window for the UIDL request, can be null if target not
- * found
- * @throws IOException
- * @throws InvalidUIDLSecurityKeyException
- */
- protected void doHandleUidlRequest(Request request, Response response,
- Callback callback, Window window) throws IOException,
- InvalidUIDLSecurityKeyException {
-
- // repaint requested or session has timed out and new one is created
- boolean repaintAll;
- final OutputStream out;
-
- repaintAll = (request.getParameter(GET_PARAM_REPAINT_ALL) != null);
- // || (request.getSession().isNew()); FIXME What the h*ll is this??
- out = response.getOutputStream();
-
- boolean analyzeLayouts = false;
- if (repaintAll) {
- // analyzing can be done only with repaintAll
- analyzeLayouts = (request.getParameter(GET_PARAM_ANALYZE_LAYOUTS) != null);
- }
-
- final PrintWriter outWriter = new PrintWriter(new BufferedWriter(
- new OutputStreamWriter(out, "UTF-8")));
-
- // The rest of the process is synchronized with the application
- // in order to guarantee that no parallel variable handling is
- // made
- synchronized (application) {
-
- // Finds the window within the application
- if (application.isRunning()) {
- // Returns if no window found
- if (window == null) {
- // This should not happen, no windows exists but
- // application is still open.
- System.err
- .println("Warning, could not get window for application with request ID "
- + request.getRequestID());
- return;
- }
- } else {
- // application has been closed
- endApplication(request, response, application);
- return;
- }
-
- // Change all variables based on request parameters
- if (!handleVariables(request, response, callback, application,
- window)) {
-
- // var inconsistency; the client is probably out-of-sync
- SystemMessages ci = null;
- try {
- Method m = application.getClass().getMethod(
- "getSystemMessages", (Class[]) null);
- ci = (Application.SystemMessages) m.invoke(null,
- (Object[]) null);
- } catch (Exception e2) {
- // FIXME: Handle exception
- // Not critical, but something is still wrong; print
- // stacktrace
- e2.printStackTrace();
- }
- if (ci != null) {
- String msg = ci.getOutOfSyncMessage();
- String cap = ci.getOutOfSyncCaption();
- if (msg != null || cap != null) {
- callback.criticalNotification(request, response, cap,
- msg, null, ci.getOutOfSyncURL());
- // will reload page after this
- return;
- }
- }
- // No message to show, let's just repaint all.
- repaintAll = true;
-
- }
-
- paintAfterVariableChanges(request, response, callback, repaintAll,
- outWriter, window, analyzeLayouts);
-
- if (closingWindowName != null) {
- currentlyOpenWindowsInClient.remove(closingWindowName);
- closingWindowName = null;
- }
- }
-
- outWriter.close();
- }
-
- /**
- * TODO document
- *
- * @param request
- * @param response
- * @param callback
- * @param repaintAll
- * @param outWriter
- * @param window
- * @param analyzeLayouts
- * @throws PaintException
- * @throws IOException
- */
- private void paintAfterVariableChanges(Request request, Response response,
- Callback callback, boolean repaintAll, final PrintWriter outWriter,
- Window window, boolean analyzeLayouts) throws PaintException,
- IOException {
-
- if (repaintAll) {
- // If repaint is requested, clean all ids in this root window
- for (final Iterator<String> it = idPaintableMap.keySet().iterator(); it
- .hasNext();) {
- final Component c = (Component) idPaintableMap.get(it.next());
- if (isChildOf(window, c)) {
- it.remove();
- paintableIdMap.remove(c);
- }
- }
- // clean WindowCache
- OpenWindowCache openWindowCache = currentlyOpenWindowsInClient
- .get(window.getName());
- if (openWindowCache != null) {
- openWindowCache.clear();
- }
- }
-
- // Removes application if it has stopped during variable changes
- if (!application.isRunning()) {
- endApplication(request, response, application);
- return;
- }
-
- // Sets the response type
- response.setContentType("application/json; charset=UTF-8");
- // some dirt to prevent cross site scripting
- outWriter.print("for(;;);[{");
-
- // security key
- Object writeSecurityTokenFlag = request
- .getAttribute(WRITE_SECURITY_TOKEN_FLAG);
-
- if (writeSecurityTokenFlag != null) {
- String seckey = (String) request.getSession().getAttribute(
- ApplicationConnection.UIDL_SECURITY_TOKEN_ID);
- if (seckey == null) {
- seckey = UUID.randomUUID().toString();
- request.getSession().setAttribute(
- ApplicationConnection.UIDL_SECURITY_TOKEN_ID, seckey);
- }
- outWriter.print("\"" + ApplicationConnection.UIDL_SECURITY_TOKEN_ID
- + "\":\"");
- outWriter.print(seckey);
- outWriter.print("\",");
- }
-
- outWriter.print("\"changes\":[");
-
- ArrayList<Paintable> paintables = null;
-
- // If the browser-window has been closed - we do not need to paint it at
- // all
- if (!window.getName().equals(closingWindowName)) {
-
- List<InvalidLayout> invalidComponentRelativeSizes = null;
-
- // re-get window - may have been changed
- Window newWindow = doGetApplicationWindow(request, callback,
- application, window);
- if (newWindow != window) {
- window = newWindow;
- repaintAll = true;
- }
-
- JsonPaintTarget paintTarget = new JsonPaintTarget(this, outWriter,
- !repaintAll);
- OpenWindowCache windowCache = currentlyOpenWindowsInClient
- .get(window.getName());
- if (windowCache == null) {
- windowCache = new OpenWindowCache();
- currentlyOpenWindowsInClient.put(window.getName(), windowCache);
- }
-
- // Paints components
- if (repaintAll) {
- paintables = new ArrayList<Paintable>();
- paintables.add(window);
-
- // Reset sent locales
- locales = null;
- requireLocale(application.getLocale().toString());
-
- } else {
- // remove detached components from paintableIdMap so they
- // can be GC'ed
- for (Iterator<Paintable> it = paintableIdMap.keySet()
- .iterator(); it.hasNext();) {
- Component p = (Component) it.next();
- if (p.getApplication() == null) {
- idPaintableMap.remove(paintableIdMap.get(p));
- it.remove();
- dirtyPaintables.remove(p);
- p.removeListener(this);
- }
- }
- paintables = getDirtyVisibleComponents(window);
- }
- if (paintables != null) {
-
- // We need to avoid painting children before parent.
- // This is ensured by ordering list by depth in component
- // tree
- Collections.sort(paintables, new Comparator<Paintable>() {
- public int compare(Paintable o1, Paintable o2) {
- Component c1 = (Component) o1;
- Component c2 = (Component) o2;
- int d1 = 0;
- while (c1.getParent() != null) {
- d1++;
- c1 = c1.getParent();
- }
- int d2 = 0;
- while (c2.getParent() != null) {
- d2++;
- c2 = c2.getParent();
- }
- if (d1 < d2) {
- return -1;
- }
- if (d1 > d2) {
- return 1;
- }
- return 0;
- }
- });
-
- for (final Iterator<Paintable> i = paintables.iterator(); i
- .hasNext();) {
- final Paintable p = i.next();
-
- // TODO CLEAN
- if (p instanceof Window) {
- final Window w = (Window) p;
- if (w.getTerminal() == null) {
- w.setTerminal(application.getMainWindow()
- .getTerminal());
- }
- }
- /*
- * This does not seem to happen in tk5, but remember this
- * case: else if (p instanceof Component) { if (((Component)
- * p).getParent() == null || ((Component)
- * p).getApplication() == null) { // Component requested
- * repaint, but is no // longer attached: skip
- * paintablePainted(p); continue; } }
- */
-
- // TODO we may still get changes that have been
- // rendered already (changes with only cached flag)
- if (paintTarget.needsToBePainted(p)) {
- paintTarget.startTag("change");
- paintTarget.addAttribute("format", "uidl");
- final String pid = getPaintableId(p);
- paintTarget.addAttribute("pid", pid);
-
- p.paint(paintTarget);
-
- paintTarget.endTag("change");
- }
- paintablePainted(p);
-
- if (analyzeLayouts) {
- Window w = (Window) p;
- invalidComponentRelativeSizes = ComponentSizeValidator
- .validateComponentRelativeSizes(w.getContent(),
- null, null);
-
- // Also check any existing subwindows
- if (w.getChildWindows() != null) {
- for (Window subWindow : w.getChildWindows()) {
- invalidComponentRelativeSizes = ComponentSizeValidator
- .validateComponentRelativeSizes(
- subWindow.getContent(),
- invalidComponentRelativeSizes,
- null);
- }
- }
- }
- }
- }
-
- paintTarget.close();
- outWriter.print("]"); // close changes
-
- outWriter.print(", \"meta\" : {");
- boolean metaOpen = false;
-
- if (repaintAll) {
- metaOpen = true;
- outWriter.write("\"repaintAll\":true");
- if (analyzeLayouts) {
- outWriter.write(", \"invalidLayouts\":");
- outWriter.write("[");
- if (invalidComponentRelativeSizes != null) {
- boolean first = true;
- for (InvalidLayout invalidLayout : invalidComponentRelativeSizes) {
- if (!first) {
- outWriter.write(",");
- } else {
- first = false;
- }
- invalidLayout.reportErrors(outWriter, this,
- System.err);
- }
- }
- outWriter.write("]");
- }
- }
-
- SystemMessages ci = null;
- try {
- Method m = application.getClass().getMethod(
- "getSystemMessages", (Class[]) null);
- ci = (Application.SystemMessages) m.invoke(null,
- (Object[]) null);
- } catch (NoSuchMethodException e1) {
- e1.printStackTrace();
- } catch (IllegalArgumentException e) {
- e.printStackTrace();
- } catch (IllegalAccessException e) {
- e.printStackTrace();
- } catch (InvocationTargetException e) {
- e.printStackTrace();
- }
-
- // meta instruction for client to enable auto-forward to
- // sessionExpiredURL after timer expires.
- if (ci != null && ci.getSessionExpiredMessage() == null
- && ci.getSessionExpiredCaption() == null
- && ci.isSessionExpiredNotificationEnabled()) {
- int newTimeoutInterval = request.getSession()
- .getMaxInactiveInterval();
- if (repaintAll || (timeoutInterval != newTimeoutInterval)) {
- String escapedURL = ci.getSessionExpiredURL() == null ? ""
- : ci.getSessionExpiredURL().replace("/", "\\/");
- if (metaOpen) {
- outWriter.write(",");
- }
- outWriter.write("\"timedRedirect\":{\"interval\":"
- + (newTimeoutInterval + 15) + ",\"url\":\""
- + escapedURL + "\"}");
- metaOpen = true;
- }
- timeoutInterval = newTimeoutInterval;
- }
-
- outWriter.print("}, \"resources\" : {");
-
- // Precache custom layouts
- String themeName = window.getTheme();
- String requestThemeName = request.getParameter("theme");
-
- if (requestThemeName != null) {
- themeName = requestThemeName;
- }
- if (themeName == null) {
- themeName = AbstractApplicationServlet.getDefaultTheme();
- }
-
- // TODO We should only precache the layouts that are not
- // cached already (plagiate from usedPaintableTypes)
- int resourceIndex = 0;
- for (final Iterator<Object> i = paintTarget.getUsedResources()
- .iterator(); i.hasNext();) {
- final String resource = (String) i.next();
- InputStream is = null;
- try {
- is = callback.getThemeResourceAsStream(themeName, resource);
- } catch (final Exception e) {
- // FIXME: Handle exception
- e.printStackTrace();
- }
- if (is != null) {
-
- outWriter.print((resourceIndex++ > 0 ? ", " : "") + "\""
- + resource + "\" : ");
- final StringBuffer layout = new StringBuffer();
-
- try {
- final InputStreamReader r = new InputStreamReader(is,
- "UTF-8");
- final char[] buffer = new char[20000];
- int charsRead = 0;
- while ((charsRead = r.read(buffer)) > 0) {
- layout.append(buffer, 0, charsRead);
- }
- r.close();
- } catch (final java.io.IOException e) {
- // FIXME: Handle exception
- System.err.println("Resource transfer failed: "
- + request.getRequestID() + ". ("
- + e.getMessage() + ")");
- }
- outWriter.print("\""
- + JsonPaintTarget.escapeJSON(layout.toString())
- + "\"");
- } else {
- // FIXME: Handle exception
- System.err.println("CustomLayout not found: " + resource);
- }
- }
- outWriter.print("}");
-
- Collection<Class<? extends Paintable>> usedPaintableTypes = paintTarget
- .getUsedPaintableTypes();
- boolean typeMappingsOpen = false;
- for (Class<? extends Paintable> class1 : usedPaintableTypes) {
- if (windowCache.cache(class1)) {
- // client does not know the mapping key for this type, send
- // mapping to client
- if (!typeMappingsOpen) {
- typeMappingsOpen = true;
- outWriter.print(", \"typeMappings\" : { ");
- } else {
- outWriter.print(" , ");
- }
- String canonicalName = class1.getCanonicalName();
- outWriter.print("\"");
- outWriter.print(canonicalName);
- outWriter.print("\" : ");
- outWriter.print(getTagForType(class1));
- }
- }
- if (typeMappingsOpen) {
- outWriter.print(" }");
- }
-
- // add any pending locale definitions requested by the client
- printLocaleDeclarations(outWriter);
-
- if (dragAndDropService != null) {
- dragAndDropService.printJSONResponse(outWriter);
- }
-
- outWriter.print("}]");
- }
- outWriter.close();
-
- }
-
- /**
- * TODO document
- *
- * If this method returns false, something was submitted that we did not
- * expect; this is probably due to the client being out-of-sync and sending
- * variable changes for non-existing pids
- *
- * @return true if successful, false if there was an inconsistency
- */
- private boolean handleVariables(Request request, Response response,
- Callback callback, Application application2, Window window)
- throws IOException, InvalidUIDLSecurityKeyException {
- boolean success = true;
- int contentLength = request.getContentLength();
-
- if (contentLength > 0) {
- String changes = readRequest(request);
-
- // Manage bursts one by one
- final String[] bursts = changes.split(VAR_BURST_SEPARATOR);
-
- // Security: double cookie submission pattern unless disabled by
- // property
- if (!"true"
- .equals(application2
- .getProperty(AbstractApplicationServlet.SERVLET_PARAMETER_DISABLE_XSRF_PROTECTION))) {
- if (bursts.length == 1 && "init".equals(bursts[0])) {
- // init request; don't handle any variables, key sent in
- // response.
- request.setAttribute(WRITE_SECURITY_TOKEN_FLAG, true);
- return true;
- } else {
- // ApplicationServlet has stored the security token in the
- // session; check that it matched the one sent in the UIDL
- String sessId = (String) request.getSession().getAttribute(
- ApplicationConnection.UIDL_SECURITY_TOKEN_ID);
-
- if (sessId == null || !sessId.equals(bursts[0])) {
- throw new InvalidUIDLSecurityKeyException(
- "Security key mismatch");
- }
- }
-
- }
-
- for (int bi = 1; bi < bursts.length; bi++) {
-
- // extract variables to two dim string array
- final String[] tmp = bursts[bi].split(VAR_RECORD_SEPARATOR);
- final String[][] variableRecords = new String[tmp.length][4];
- for (int i = 0; i < tmp.length; i++) {
- variableRecords[i] = tmp[i].split(VAR_FIELD_SEPARATOR);
- }
-
- for (int i = 0; i < variableRecords.length; i++) {
- String[] variable = variableRecords[i];
- String[] nextVariable = null;
- if (i + 1 < variableRecords.length) {
- nextVariable = variableRecords[i + 1];
- }
- final VariableOwner owner = getVariableOwner(variable[VAR_PID]);
- if (owner != null && owner.isEnabled()) {
- // TODO this should be Map<String, Object>, but the
- // VariableOwner API does not guarantee the key is a
- // string
- Map<String, Object> m;
- if (nextVariable != null
- && variable[VAR_PID]
- .equals(nextVariable[VAR_PID])) {
- // we have more than one value changes in row for
- // one variable owner, collect em in HashMap
- m = new HashMap<String, Object>();
- m.put(variable[VAR_NAME],
- convertVariableValue(
- variable[VAR_TYPE].charAt(0),
- variable[VAR_VALUE]));
- } else {
- // use optimized single value map
- m = Collections.singletonMap(
- variable[VAR_NAME],
- convertVariableValue(
- variable[VAR_TYPE].charAt(0),
- variable[VAR_VALUE]));
- }
-
- // collect following variable changes for this owner
- while (nextVariable != null
- && variable[VAR_PID]
- .equals(nextVariable[VAR_PID])) {
- i++;
- variable = nextVariable;
- if (i + 1 < variableRecords.length) {
- nextVariable = variableRecords[i + 1];
- } else {
- nextVariable = null;
- }
- m.put(variable[VAR_NAME],
- convertVariableValue(
- variable[VAR_TYPE].charAt(0),
- variable[VAR_VALUE]));
- }
- try {
- owner.changeVariables(request, m);
-
- // Special-case of closing browser-level windows:
- // track browser-windows currently open in client
- if (owner instanceof Window
- && ((Window) owner).getParent() == null) {
- final Boolean close = (Boolean) m.get("close");
- if (close != null && close.booleanValue()) {
- closingWindowName = ((Window) owner)
- .getName();
- }
- }
- } catch (Exception e) {
- if (owner instanceof Component) {
- handleChangeVariablesError(application2,
- (Component) owner, e, m);
- } else {
- // TODO DragDropService error handling
- throw new RuntimeException(e);
- }
- }
- } else {
-
- // Handle special case where window-close is called
- // after the window has been removed from the
- // application or the application has closed
- if ("close".equals(variable[VAR_NAME])
- && "true".equals(variable[VAR_VALUE])) {
- // Silently ignore this
- continue;
- }
-
- // Ignore variable change
- String msg = "Warning: Ignoring variable change for ";
- if (owner != null) {
- msg += "disabled component " + owner.getClass();
- String caption = ((Component) owner).getCaption();
- if (caption != null) {
- msg += ", caption=" + caption;
- }
- } else {
- msg += "non-existent component, VAR_PID="
- + variable[VAR_PID];
- success = false;
- }
- System.err.println(msg);
- continue;
- }
- }
-
- // In case that there were multiple bursts, we know that this is
- // a special synchronous case for closing window. Thus we are
- // not interested in sending any UIDL changes back to client.
- // Still we must clear component tree between bursts to ensure
- // that no removed components are updated. The painting after
- // the last burst is handled normally by the calling method.
- if (bi < bursts.length - 1) {
-
- // We will be discarding all changes
- final PrintWriter outWriter = new PrintWriter(
- new CharArrayWriter());
-
- paintAfterVariableChanges(request, response, callback,
- true, outWriter, window, false);
-
- }
-
- }
- }
- return success;
- }
-
- private VariableOwner getVariableOwner(String string) {
- VariableOwner owner = (VariableOwner) idPaintableMap.get(string);
- if (owner == null && string.startsWith("DD")) {
- return getDragAndDropService();
- }
- return owner;
- }
-
- private VariableOwner getDragAndDropService() {
- if (dragAndDropService == null) {
- dragAndDropService = new DragAndDropService(this);
- }
- return dragAndDropService;
- }
-
- /**
- * Reads the request data from the Request and returns it converted to an
- * UTF-8 string.
- *
- * @param request
- * @return
- * @throws IOException
- */
- private static String readRequest(Request request) throws IOException {
-
- int requestLength = request.getContentLength();
-
- byte[] buffer = new byte[requestLength];
- InputStream inputStream = request.getInputStream();
-
- int bytesRemaining = requestLength;
- while (bytesRemaining > 0) {
- int bytesToRead = Math.min(bytesRemaining, MAX_BUFFER_SIZE);
- int bytesRead = inputStream.read(buffer, requestLength
- - bytesRemaining, bytesToRead);
- if (bytesRead == -1) {
- break;
- }
-
- bytesRemaining -= bytesRead;
- }
-
- String result = new String(buffer, "utf-8");
-
- return result;
- }
-
- public class ErrorHandlerErrorEvent implements ErrorEvent, Serializable {
- private final Throwable throwable;
-
- public ErrorHandlerErrorEvent(Throwable throwable) {
- this.throwable = throwable;
- }
-
- public Throwable getThrowable() {
- return throwable;
- }
-
- }
-
- /**
- * Handles an error (exception) that occurred when processing variable
- * changes from the client or a failure of a file upload.
- *
- * For {@link AbstractField} components,
- * {@link AbstractField#handleError(com.vaadin.ui.AbstractComponent.ComponentErrorEvent)}
- * is called. In all other cases (or if the field does not handle the
- * error), {@link ErrorListener#terminalError(ErrorEvent)} for the
- * application error handler is called.
- *
- * @param application
- * @param owner
- * component that the error concerns
- * @param e
- * exception that occurred
- * @param m
- * map from variable names to values
- */
- private void handleChangeVariablesError(Application application,
- Component owner, Exception e, Map<? extends Object, Object> m) {
- boolean handled = false;
- ChangeVariablesErrorEvent errorEvent = new ChangeVariablesErrorEvent(
- owner, e, m);
-
- if (owner instanceof AbstractField) {
- try {
- handled = ((AbstractField) owner).handleError(errorEvent);
- } catch (Exception handlerException) {
- /*
- * If there is an error in the component error handler we pass
- * the that error to the application error handler and continue
- * processing the actual error
- */
- application.getErrorHandler().terminalError(
- new ErrorHandlerErrorEvent(handlerException));
- handled = false;
- }
- }
-
- if (!handled) {
- application.getErrorHandler().terminalError(errorEvent);
- }
-
- }
-
- private Object convertVariableValue(char variableType, String strValue) {
- Object val = null;
- switch (variableType) {
- case VTYPE_ARRAY:
- val = convertArray(strValue);
- break;
- case VTYPE_MAP:
- val = convertMap(strValue);
- break;
- case VTYPE_STRINGARRAY:
- val = convertStringArray(strValue);
- break;
- case VTYPE_STRING:
- val = strValue;
- break;
- case VTYPE_INTEGER:
- val = Integer.valueOf(strValue);
- break;
- case VTYPE_LONG:
- val = Long.valueOf(strValue);
- break;
- case VTYPE_FLOAT:
- val = Float.valueOf(strValue);
- break;
- case VTYPE_DOUBLE:
- val = Double.valueOf(strValue);
- break;
- case VTYPE_BOOLEAN:
- val = Boolean.valueOf(strValue);
- break;
- case VTYPE_PAINTABLE:
- val = idPaintableMap.get(strValue);
- break;
- }
-
- return val;
- }
-
- private Object convertMap(String strValue) {
- String[] parts = strValue.split(VAR_ARRAYITEM_SEPARATOR);
- HashMap<String, Object> map = new HashMap<String, Object>();
- for (int i = 0; i < parts.length; i += 2) {
- String key = parts[i];
- if (key.length() > 0) {
- char variabletype = key.charAt(0);
- Object value = convertVariableValue(variabletype, parts[i + 1]);
- map.put(key.substring(1), value);
- }
- }
- return map;
- }
-
- private String[] convertStringArray(String strValue) {
- // need to return delimiters and filter them out; otherwise empty
- // strings are lost
- // an extra empty delimiter at the end is automatically eliminated
- StringTokenizer tokenizer = new StringTokenizer(strValue,
- VAR_ARRAYITEM_SEPARATOR, true);
- List<String> tokens = new ArrayList<String>();
- String prevToken = VAR_ARRAYITEM_SEPARATOR;
- while (tokenizer.hasMoreTokens()) {
- String token = tokenizer.nextToken();
- if (!VAR_ARRAYITEM_SEPARATOR.equals(token)) {
- tokens.add(token);
- } else if (VAR_ARRAYITEM_SEPARATOR.equals(prevToken)) {
- tokens.add("");
- }
- prevToken = token;
- }
- return tokens.toArray(new String[tokens.size()]);
- }
-
- private Object convertArray(String strValue) {
- String[] val = strValue.split(VAR_ARRAYITEM_SEPARATOR);
- if (val.length == 0 || (val.length == 1 && val[0].length() == 0)) {
- return new Object[0];
- }
- Object[] values = new Object[val.length];
- for (int i = 0; i < values.length; i++) {
- String string = val[i];
- // first char of string is type
- char variableType = string.charAt(0);
- values[i] = convertVariableValue(variableType, string.substring(1));
- }
- return values;
- }
-
- /**
- * Prints the queued (pending) locale definitions to a {@link PrintWriter}
- * in a (UIDL) format that can be sent to the client and used there in
- * formatting dates, times etc.
- *
- * @param outWriter
- */
- private void printLocaleDeclarations(PrintWriter outWriter) {
- /*
- * ----------------------------- Sending Locale sensitive date
- * -----------------------------
- */
-
- // Send locale informations to client
- outWriter.print(", \"locales\":[");
- for (; pendingLocalesIndex < locales.size(); pendingLocalesIndex++) {
-
- final Locale l = generateLocale(locales.get(pendingLocalesIndex));
- // Locale name
- outWriter.print("{\"name\":\"" + l.toString() + "\",");
-
- /*
- * Month names (both short and full)
- */
- final DateFormatSymbols dfs = new DateFormatSymbols(l);
- final String[] short_months = dfs.getShortMonths();
- final String[] months = dfs.getMonths();
- outWriter.print("\"smn\":[\""
- + // ShortMonthNames
- short_months[0] + "\",\"" + short_months[1] + "\",\""
- + short_months[2] + "\",\"" + short_months[3] + "\",\""
- + short_months[4] + "\",\"" + short_months[5] + "\",\""
- + short_months[6] + "\",\"" + short_months[7] + "\",\""
- + short_months[8] + "\",\"" + short_months[9] + "\",\""
- + short_months[10] + "\",\"" + short_months[11] + "\""
- + "],");
- outWriter.print("\"mn\":[\""
- + // MonthNames
- months[0] + "\",\"" + months[1] + "\",\"" + months[2]
- + "\",\"" + months[3] + "\",\"" + months[4] + "\",\""
- + months[5] + "\",\"" + months[6] + "\",\"" + months[7]
- + "\",\"" + months[8] + "\",\"" + months[9] + "\",\""
- + months[10] + "\",\"" + months[11] + "\"" + "],");
-
- /*
- * Weekday names (both short and full)
- */
- final String[] short_days = dfs.getShortWeekdays();
- final String[] days = dfs.getWeekdays();
- outWriter.print("\"sdn\":[\""
- + // ShortDayNames
- short_days[1] + "\",\"" + short_days[2] + "\",\""
- + short_days[3] + "\",\"" + short_days[4] + "\",\""
- + short_days[5] + "\",\"" + short_days[6] + "\",\""
- + short_days[7] + "\"" + "],");
- outWriter.print("\"dn\":[\""
- + // DayNames
- days[1] + "\",\"" + days[2] + "\",\"" + days[3] + "\",\""
- + days[4] + "\",\"" + days[5] + "\",\"" + days[6] + "\",\""
- + days[7] + "\"" + "],");
-
- /*
- * First day of week (0 = sunday, 1 = monday)
- */
- final Calendar cal = new GregorianCalendar(l);
- outWriter.print("\"fdow\":" + (cal.getFirstDayOfWeek() - 1) + ",");
-
- /*
- * Date formatting (MM/DD/YYYY etc.)
- */
-
- DateFormat dateFormat = DateFormat.getDateTimeInstance(
- DateFormat.SHORT, DateFormat.SHORT, l);
- if (!(dateFormat instanceof SimpleDateFormat)) {
- System.err
- .println("Unable to get default date pattern for locale "
- + l.toString());
- dateFormat = new SimpleDateFormat();
- }
- final String df = ((SimpleDateFormat) dateFormat).toPattern();
-
- int timeStart = df.indexOf("H");
- if (timeStart < 0) {
- timeStart = df.indexOf("h");
- }
- final int ampm_first = df.indexOf("a");
- // E.g. in Korean locale AM/PM is before h:mm
- // TODO should take that into consideration on client-side as well,
- // now always h:mm a
- if (ampm_first > 0 && ampm_first < timeStart) {
- timeStart = ampm_first;
- }
- // Hebrew locale has time before the date
- final boolean timeFirst = timeStart == 0;
- String dateformat;
- if (timeFirst) {
- int dateStart = df.indexOf(' ');
- if (ampm_first > dateStart) {
- dateStart = df.indexOf(' ', ampm_first);
- }
- dateformat = df.substring(dateStart + 1);
- } else {
- dateformat = df.substring(0, timeStart - 1);
- }
-
- outWriter.print("\"df\":\"" + dateformat.trim() + "\",");
-
- /*
- * Time formatting (24 or 12 hour clock and AM/PM suffixes)
- */
- final String timeformat = df.substring(timeStart, df.length());
- /*
- * Doesn't return second or milliseconds.
- *
- * We use timeformat to determine 12/24-hour clock
- */
- final boolean twelve_hour_clock = timeformat.indexOf("a") > -1;
- // TODO there are other possibilities as well, like 'h' in french
- // (ignore them, too complicated)
- final String hour_min_delimiter = timeformat.indexOf(".") > -1 ? "."
- : ":";
- // outWriter.print("\"tf\":\"" + timeformat + "\",");
- outWriter.print("\"thc\":" + twelve_hour_clock + ",");
- outWriter.print("\"hmd\":\"" + hour_min_delimiter + "\"");
- if (twelve_hour_clock) {
- final String[] ampm = dfs.getAmPmStrings();
- outWriter.print(",\"ampm\":[\"" + ampm[0] + "\",\"" + ampm[1]
- + "\"]");
- }
- outWriter.print("}");
- if (pendingLocalesIndex < locales.size() - 1) {
- outWriter.print(",");
- }
- }
- outWriter.print("]"); // Close locales
- }
-
- /**
- * TODO New method - document me!
- *
- * @param request
- * @param callback
- * @param application
- * @param assumedWindow
- * @return
- */
- protected Window doGetApplicationWindow(Request request, Callback callback,
- Application application, Window assumedWindow) {
-
- Window window = null;
-
- // If the client knows which window to use, use it if possible
- String windowClientRequestedName = request.getParameter("windowName");
-
- if (assumedWindow != null
- && application.getWindows().contains(assumedWindow)) {
- windowClientRequestedName = assumedWindow.getName();
- }
- if (windowClientRequestedName != null) {
- window = application.getWindow(windowClientRequestedName);
- if (window != null) {
- return window;
- }
- }
-
- // If client does not know what window it wants
- if (window == null && !request.isRunningInPortlet()) {
- // This is only supported if the application is running inside a
- // servlet
-
- // Get the path from URL
- String path = callback.getRequestPathInfo(request);
- if (path != null && path.startsWith("/UIDL")) {
- path = path.substring("/UIDL".length());
- }
-
- // If the path is specified, create name from it
- if (path != null && path.length() > 0 && !path.equals("/")) {
- String windowUrlName = null;
- if (path.charAt(0) == '/') {
- path = path.substring(1);
- }
- final int index = path.indexOf('/');
- if (index < 0) {
- windowUrlName = path;
- path = "";
- } else {
- windowUrlName = path.substring(0, index);
- path = path.substring(index + 1);
- }
-
- window = application.getWindow(windowUrlName);
- }
- }
-
- // By default, use mainwindow
- if (window == null) {
- window = application.getMainWindow();
- // Return null if no main window was found
- if (window == null) {
- return null;
- }
- }
-
- // If the requested window is already open, resolve conflict
- if (currentlyOpenWindowsInClient.containsKey(window.getName())) {
- String newWindowName = window.getName();
-
- synchronized (AbstractCommunicationManager.class) {
- while (currentlyOpenWindowsInClient.containsKey(newWindowName)) {
- newWindowName = window.getName() + "_"
- + nextUnusedWindowSuffix++;
- }
- }
-
- window = application.getWindow(newWindowName);
-
- // If everything else fails, use main window even in case of
- // conflicts
- if (window == null) {
- window = application.getMainWindow();
- }
- }
-
- return window;
- }
-
- /**
- * Ends the Application.
- *
- * The browser is redirected to the Application logout URL set with
- * {@link Application#setLogoutURL(String)}, or to the application URL if no
- * logout URL is given.
- *
- * @param request
- * the request instance.
- * @param response
- * the response to write to.
- * @param application
- * the Application to end.
- * @throws IOException
- * if the writing failed due to input/output error.
- */
- private void endApplication(Request request, Response response,
- Application application) throws IOException {
-
- String logoutUrl = application.getLogoutURL();
- if (logoutUrl == null) {
- logoutUrl = application.getURL().toString();
- }
- // clients JS app is still running, send a special json file to tell
- // client that application has quit and where to point browser now
- // Set the response type
- final OutputStream out = response.getOutputStream();
- response.setContentType("application/json; charset=UTF-8");
- final PrintWriter outWriter = new PrintWriter(new BufferedWriter(
- new OutputStreamWriter(out, "UTF-8")));
- outWriter.print("for(;;);[{");
- outWriter.print("\"redirect\":{");
- outWriter.write("\"url\":\"" + logoutUrl + "\"}}]");
- outWriter.flush();
- outWriter.close();
- out.flush();
- }
-
- /**
- * Gets the Paintable Id. If Paintable has debug id set it will be used
- * prefixed with "PID_S". Otherwise a sequenced ID is created.
- *
- * @param paintable
- * @return the paintable Id.
- */
- public String getPaintableId(Paintable paintable) {
-
- String id = paintableIdMap.get(paintable);
- if (id == null) {
- // use testing identifier as id if set
- id = paintable.getDebugId();
- if (id == null) {
- id = "PID" + Integer.toString(idSequence++);
- } else {
- id = "PID_S" + id;
- }
- Paintable old = idPaintableMap.put(id, paintable);
- if (old != null && old != paintable) {
- /*
- * Two paintables have the same id. We still make sure the old
- * one is a component which is still attached to the
- * application. This is just a precaution and should not be
- * absolutely necessary.
- */
-
- if (old instanceof Component
- && ((Component) old).getApplication() != null) {
- throw new IllegalStateException("Two paintables ("
- + paintable.getClass().getSimpleName() + ","
- + old.getClass().getSimpleName()
- + ") have been assigned the same id: "
- + paintable.getDebugId());
- }
- }
- paintableIdMap.put(paintable, id);
- }
-
- return id;
- }
-
- public boolean hasPaintableId(Paintable paintable) {
- return paintableIdMap.containsKey(paintable);
- }
-
- /**
- * Returns dirty components which are in given window. Components in an
- * invisible subtrees are omitted.
- *
- * @param w
- * root window for which dirty components is to be fetched
- * @return
- */
- private ArrayList<Paintable> getDirtyVisibleComponents(Window w) {
- final ArrayList<Paintable> resultset = new ArrayList<Paintable>(
- dirtyPaintables);
-
- // The following algorithm removes any components that would be painted
- // as a direct descendant of other components from the dirty components
- // list. The result is that each component should be painted exactly
- // once and any unmodified components will be painted as "cached=true".
-
- for (final Iterator<Paintable> i = dirtyPaintables.iterator(); i
- .hasNext();) {
- final Paintable p = i.next();
- if (p instanceof Component) {
- final Component component = (Component) p;
- if (component.getApplication() == null) {
- // component is detached after requestRepaint is called
- resultset.remove(p);
- i.remove();
- } else {
- Window componentsRoot = component.getWindow();
- if (componentsRoot == null) {
- // This should not happen unless somebody has overriden
- // getApplication or getWindow in an illegal way.
- throw new IllegalStateException(
- "component.getWindow() returned null for a component attached to the application");
- }
- if (componentsRoot.getParent() != null) {
- // this is a subwindow
- componentsRoot = (Window) componentsRoot.getParent();
- }
- if (componentsRoot != w) {
- resultset.remove(p);
- } else if (component.getParent() != null
- && !component.getParent().isVisible()) {
- /*
- * Do not return components in an invisible subtree.
- *
- * Components that are invisible in visible subree, must
- * be rendered (to let client know that they need to be
- * hidden).
- */
- resultset.remove(p);
- }
- }
- }
- }
-
- return resultset;
- }
-
- /**
- * @see com.vaadin.terminal.Paintable.RepaintRequestListener#repaintRequested(com.vaadin.terminal.Paintable.RepaintRequestEvent)
- */
- public void repaintRequested(RepaintRequestEvent event) {
- final Paintable p = event.getPaintable();
- if (!dirtyPaintables.contains(p)) {
- dirtyPaintables.add(p);
- }
- }
-
- /**
- * Internally mark a {@link Paintable} as painted and start collecting new
- * repaint requests for it.
- *
- * @param paintable
- */
- private void paintablePainted(Paintable paintable) {
- dirtyPaintables.remove(paintable);
- paintable.requestRepaintRequests();
- }
-
- /**
- * Implementation of {@link URIHandler.ErrorEvent} interface.
- */
- public class URIHandlerErrorImpl implements URIHandler.ErrorEvent,
- Serializable {
-
- private final URIHandler owner;
-
- private final Throwable throwable;
-
- /**
- *
- * @param owner
- * @param throwable
- */
- private URIHandlerErrorImpl(URIHandler owner, Throwable throwable) {
- this.owner = owner;
- this.throwable = throwable;
- }
-
- /**
- * @see com.vaadin.terminal.Terminal.ErrorEvent#getThrowable()
- */
- public Throwable getThrowable() {
- return throwable;
- }
-
- /**
- * @see com.vaadin.terminal.URIHandler.ErrorEvent#getURIHandler()
- */
- public URIHandler getURIHandler() {
- return owner;
- }
- }
-
- /**
- * Queues a locale to be sent to the client (browser) for date and time
- * entry etc. All locale specific information is derived from server-side
- * {@link Locale} instances and sent to the client when needed, eliminating
- * the need to use the {@link Locale} class and all the framework behind it
- * on the client.
- *
- * @see Locale#toString()
- *
- * @param value
- */
- public void requireLocale(String value) {
- if (locales == null) {
- locales = new ArrayList<String>();
- locales.add(application.getLocale().toString());
- pendingLocalesIndex = 0;
- }
- if (!locales.contains(value)) {
- locales.add(value);
- }
- }
-
- /**
- * Constructs a {@link Locale} instance to be sent to the client based on a
- * short locale description string.
- *
- * @see #requireLocale(String)
- *
- * @param value
- * @return
- */
- private Locale generateLocale(String value) {
- final String[] temp = value.split("_");
- if (temp.length == 1) {
- return new Locale(temp[0]);
- } else if (temp.length == 2) {
- return new Locale(temp[0], temp[1]);
- } else {
- return new Locale(temp[0], temp[1], temp[2]);
- }
- }
-
- /*
- * Upload progress listener notifies upload component once when Jakarta
- * FileUpload can determine content length. Used to detect files total size,
- * uploads progress can be tracked inside upload.
- */
- private class UploadProgressListener implements ProgressListener,
- Serializable {
-
- Upload uploadComponent;
-
- boolean updated = false;
-
- public void setUpload(Upload u) {
- uploadComponent = u;
- }
-
- public void update(long bytesRead, long contentLength, int items) {
- if (!updated && uploadComponent != null) {
- uploadComponent.setUploadSize(contentLength);
- updated = true;
- }
- }
- }
-
- /**
- * Helper method to test if a component contains another
- *
- * @param parent
- * @param child
- */
- private static boolean isChildOf(Component parent, Component child) {
- Component p = child.getParent();
- while (p != null) {
- if (parent == p) {
- return true;
- }
- p = p.getParent();
- }
- return false;
- }
-
- protected class InvalidUIDLSecurityKeyException extends
- GeneralSecurityException {
-
- InvalidUIDLSecurityKeyException(String message) {
- super(message);
- }
-
- }
-
- /**
- * Calls the Window URI handler for a request and returns the
- * {@link DownloadStream} returned by the handler.
- *
- * If the window is the main window of an application, the (deprecated)
- * {@link Application#handleURI(java.net.URL, String)} is called first to
- * handle {@link ApplicationResource}s, and the window handler is only
- * called if it returns null.
- *
- * @param window
- * the target window of the request
- * @param request
- * the request instance
- * @param response
- * the response to write to
- * @return DownloadStream if the request was handled and further processing
- * should be suppressed, null otherwise.
- * @see com.vaadin.terminal.URIHandler
- */
- protected DownloadStream handleURI(Window window, Request request,
- Response response, Callback callback) {
-
- String uri = callback.getRequestPathInfo(request);
-
- // If no URI is available
- if (uri == null) {
- uri = "";
- } else {
- // Removes the leading /
- while (uri.startsWith("/") && uri.length() > 0) {
- uri = uri.substring(1);
- }
- }
-
- // Handles the uri
- try {
- URL context = application.getURL();
- if (window == application.getMainWindow()) {
- DownloadStream stream = null;
- /*
- * Application.handleURI run first. Handles possible
- * ApplicationResources.
- */
- stream = application.handleURI(context, uri);
- if (stream == null) {
- stream = window.handleURI(context, uri);
- }
- return stream;
- } else {
- // Resolve the prefix end index
- final int index = uri.indexOf('/');
- if (index > 0) {
- String prefix = uri.substring(0, index);
- URL windowContext;
- windowContext = new URL(context, prefix + "/");
- final String windowUri = (uri.length() > prefix.length() + 1) ? uri
- .substring(prefix.length() + 1) : "";
- return window.handleURI(windowContext, windowUri);
- } else {
- return null;
- }
- }
-
- } catch (final Throwable t) {
- application.getErrorHandler().terminalError(
- new URIHandlerErrorImpl(application, t));
- return null;
- }
- }
-
- private HashMap<Class<? extends Paintable>, Integer> typeToKey = new HashMap<Class<? extends Paintable>, Integer>();
- private int nextTypeKey = 0;
-
- String getTagForType(Class<? extends Paintable> class1) {
- Integer object = typeToKey.get(class1);
- if (object == null) {
- object = nextTypeKey++;
- typeToKey.put(class1, object);
- }
- return object.toString();
- }
-
- /**
- * Helper class for terminal to keep track of data that client is expected
- * to know.
- *
- * TODO make customlayout templates (from theme) to be cached here.
- */
- class OpenWindowCache implements Serializable {
-
- private Set<Object> res = new HashSet<Object>();
-
- /**
- *
- * @param paintable
- * @return true if the given class was added to cache
- */
- boolean cache(Object object) {
- return res.add(object);
- }
-
- public void clear() {
- res.clear();
- }
-
- }
- }
|