001/*
002 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003 *
004 * Copyright (c) 1997-2013 Oracle and/or its affiliates. All rights reserved.
005 *
006 * The contents of this file are subject to the terms of either the GNU
007 * General Public License Version 2 only ("GPL") or the Common Development
008 * and Distribution License("CDDL") (collectively, the "License").  You
009 * may not use this file except in compliance with the License.  You can
010 * obtain a copy of the License at
011 * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
012 * or packager/legal/LICENSE.txt.  See the License for the specific
013 * language governing permissions and limitations under the License.
014 *
015 * When distributing the software, include this License Header Notice in each
016 * file and include the License file at packager/legal/LICENSE.txt.
017 *
018 * GPL Classpath Exception:
019 * Oracle designates this particular file as subject to the "Classpath"
020 * exception as provided by Oracle in the GPL Version 2 section of the License
021 * file that accompanied this code.
022 *
023 * Modifications:
024 * If applicable, add the following below the License Header, with the fields
025 * enclosed by brackets [] replaced by your own identifying information:
026 * "Portions Copyright [year] [name of copyright owner]"
027 *
028 * Contributor(s):
029 * If you wish your version of this file to be governed by only the CDDL or
030 * only the GPL Version 2, indicate your decision by adding "[Contributor]
031 * elects to include this software in this distribution under the [CDDL or GPL
032 * Version 2] license."  If you don't indicate a single choice of license, a
033 * recipient has the option to distribute your version of this file under
034 * either the CDDL, the GPL Version 2 or to extend the choice of license to
035 * its licensees as provided above.  However, if you add GPL Version 2 code
036 * and therefore, elected the GPL Version 2 license, then the option applies
037 * only if the new code is made subject to such option by the copyright
038 * holder.
039 */
040
041package org.nuxeo.ecm.platform.ui.web.application;
042
043import static com.sun.faces.config.WebConfiguration.WebContextInitParameter.StateSavingMethod;
044
045import java.io.IOException;
046
047import javax.faces.FacesException;
048import javax.faces.application.StateManager;
049import javax.faces.context.FacesContext;
050import javax.faces.render.ResponseStateManager;
051import javax.servlet.ServletException;
052import javax.servlet.http.HttpServletRequest;
053
054import com.sun.faces.config.WebConfiguration;
055import com.sun.faces.renderkit.ClientSideStateHelper;
056import com.sun.faces.renderkit.ResponseStateManagerImpl;
057import com.sun.faces.renderkit.StateHelper;
058import com.sun.faces.util.RequestStateManager;
059
060/**
061 * @since 6.0
062 */
063public class NuxeoResponseStateManagerImpl extends ResponseStateManagerImpl {
064
065    public static final String MULTIPART_SIZE_ERROR_FLAG = "NX_MULTIPART_SIZE_ERROR";
066
067    public static final Object MULTIPART_SIZE_ERROR_COMPONENT_ID = "NX_MULTIPART_SIZE_COMPONENTID";
068
069    private final StateHelper helper;
070
071    public NuxeoResponseStateManagerImpl() {
072
073        WebConfiguration webConfig = WebConfiguration.getInstance();
074        String stateMode = webConfig.getOptionValue(StateSavingMethod);
075        helper = ((StateManager.STATE_SAVING_METHOD_CLIENT.equalsIgnoreCase(stateMode) ? new ClientSideStateHelper()
076                : new NuxeoServerSideStateHelper()));
077
078    }
079
080    // --------------------------------------- Methods from ResponseStateManager
081
082    @Override
083    public String getCryptographicallyStrongTokenFromSession(FacesContext context) {
084        return helper.getCryptographicallyStrongTokenFromSession(context);
085    }
086
087    /**
088     * @see ResponseStateManager#getState(javax.faces.context.FacesContext, java.lang.String)
089     */
090    @Override
091    public Object getState(FacesContext context, String viewId) {
092
093        Object state = RequestStateManager.get(context, RequestStateManager.FACES_VIEW_STATE);
094        if (state == null) {
095            try {
096                state = helper.getState(context, viewId);
097                if (state != null) {
098                    RequestStateManager.set(context, RequestStateManager.FACES_VIEW_STATE, state);
099                }
100            } catch (IOException e) {
101                throw new FacesException(e);
102            }
103        }
104        return state;
105
106    }
107
108    /**
109     * @see ResponseStateManager#writeState(javax.faces.context.FacesContext, java.lang.Object)
110     */
111    @Override
112    public void writeState(FacesContext context, Object state) throws IOException {
113
114        helper.writeState(context, state, null);
115
116    }
117
118    /**
119     * @see ResponseStateManager#getViewState(javax.faces.context.FacesContext, java.lang.Object)
120     */
121    @Override
122    public String getViewState(FacesContext context, Object state) {
123
124        StringBuilder sb = new StringBuilder(32);
125        try {
126            helper.writeState(context, state, sb);
127        } catch (IOException e) {
128            throw new FacesException(e);
129        }
130        return sb.toString();
131
132    }
133
134    /**
135     * @param facesContext the Faces context.
136     * @param viewId the view id.
137     * @return true if "stateless" was found, false otherwise.
138     * @throws IllegalStateException when the request is not a postback.
139     */
140    @Override
141    public boolean isStateless(FacesContext facesContext, String viewId) {
142        return helper.isStateless(facesContext, viewId);
143    }
144
145    /**
146     * @since 7.1
147     */
148    @Override
149    public boolean isPostback(FacesContext context) {
150        boolean result = super.isPostback(context);
151        if (!result) {
152            HttpServletRequest req = (HttpServletRequest) context.getExternalContext().getRequest();
153            final String contentType = req.getContentType();
154            if (contentType != null && contentType.contains("multipart/form-data")) {
155                try {
156                    req.getParts();
157                } catch (IllegalStateException e) {
158                    context.getAttributes().put(MULTIPART_SIZE_ERROR_FLAG, true);
159                    if (e.getCause() != null) {
160                        final String componentId = getComponentId(e.getCause().getMessage());
161                        if (componentId != null) {
162                            context.getAttributes().put(MULTIPART_SIZE_ERROR_COMPONENT_ID, componentId);
163                        }
164                    }
165                } catch (IOException e) {
166                    // Do nothing
167                } catch (ServletException e) {
168                    // Do nothing
169                }
170            }
171        }
172        return result;
173    }
174
175    /**
176     * @since 7.1
177     */
178    private static String getComponentId(final String excetionMessage) {
179        String sep = ":";
180        if (excetionMessage.indexOf(sep) > 0) {
181            String[] split = excetionMessage.split(sep);
182            String result = "";
183            if (split != null && split.length > 0) {
184                result += split[0].substring(split[0].lastIndexOf(" ") + 1);
185                for (int i = 1; i < split.length - 1; ++i) {
186                    result += sep + split[i];
187                }
188                result += sep + split[split.length - 1].substring(0, split[split.length - 1].indexOf(' '));
189            }
190            return result;
191        }
192        return null;
193    }
194}