001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.wicket.response.filter;
018
019import java.util.HashMap;
020import java.util.Map;
021
022import org.apache.wicket.Application;
023import org.apache.wicket.model.Model;
024import org.apache.wicket.request.cycle.RequestCycle;
025import org.apache.wicket.util.string.AppendingStringBuffer;
026import org.apache.wicket.core.util.string.JavaScriptUtils;
027import org.slf4j.Logger;
028import org.slf4j.LoggerFactory;
029
030
031/**
032 * This is a filter that injects javascript code to the top head portion and after the body so that
033 * the time can me measured what the client parse time was for this page. It also reports the total
034 * server parse/response time in the client and logs the server response time and response size it
035 * took for a specific response in the server log.
036 * 
037 * You can specify what the status text should be like this: ServerAndClientTimeFilter.statustext=My
038 * Application, Server parsetime: ${servertime}, Client parsetime: ${clienttime}
039 * 
040 * <p>
041 * Usage: in YourApplication.java:
042 * 
043 * <pre>
044 * &#064;Override
045 * public init()
046 * {
047 *      super.init();
048 *      getRequestCycleSettings().addResponseFilter(new ServerAndClientTimeFilter());
049 * }
050 * </pre>
051 * 
052 * @author jcompagner
053 * @deprecated This class has been deprecated for several reasons. The way it tries to measure
054 *             server and client times is very inaccurate. Modern browsers provide much better tools
055 *             to measure Javascript execution times. The measurements were written in a property
056 *             that has been deprecated for years and removed in modern browsers. Finally, rendering
057 *             the Javascript directly into the response makes it hard to support a strict CSP with
058 *             nonces. There is no real replacement for this class. Use the tools provided by the
059 *             browser. See {@code WicketExampleApplication} for a simple example of passing
060 *             rendering times to the browser via the {@code Server-Timing} header.
061 */
062@Deprecated
063public class ServerAndClientTimeFilter implements IResponseFilter
064{
065        private static final Logger log = LoggerFactory.getLogger(ServerAndClientTimeFilter.class);
066
067        /**
068         * @see IResponseFilter#filter(AppendingStringBuffer)
069         */
070        @Override
071        public AppendingStringBuffer filter(AppendingStringBuffer responseBuffer)
072        {
073                int headIndex = responseBuffer.indexOf("<head>");
074                int bodyIndex = responseBuffer.indexOf("</body>");
075                long timeTaken = System.currentTimeMillis() - RequestCycle.get().getStartTime();
076                if (headIndex != -1 && bodyIndex != -1)
077                {
078                        Map<String, String> map = new HashMap<>(4);
079                        map.put("clienttime", "' + (new Date().getTime() - clientTimeVariable)/1000 +  's");
080                        map.put("servertime", ((double)timeTaken) / 1000 + "s");
081
082                        AppendingStringBuffer defaultValue = new AppendingStringBuffer(128);
083                        defaultValue.append("Server parsetime: ");
084                        defaultValue.append(((double)timeTaken) / 1000);
085                        defaultValue.append("s, Client parsetime: ' + (new Date().getTime() - clientTimeVariable)/1000 +  's");
086
087                        String txt = Application.get()
088                                .getResourceSettings()
089                                .getLocalizer()
090                                .getString("ServerAndClientTimeFilter.statustext", null, Model.ofMap(map),
091                                        defaultValue.toString());
092                        AppendingStringBuffer endScript = new AppendingStringBuffer(150);
093                        endScript.append("\n").append(JavaScriptUtils.SCRIPT_OPEN_TAG);
094                        endScript.append("\nwindow.defaultStatus='");
095                        endScript.append(txt);
096                        endScript.append("';\n").append(JavaScriptUtils.SCRIPT_CLOSE_TAG).append("\n");
097                        responseBuffer.insert(bodyIndex - 1, endScript);
098                        responseBuffer.insert(headIndex + 6, "\n" + JavaScriptUtils.SCRIPT_OPEN_TAG +
099                                "\nvar clientTimeVariable = new Date().getTime();\n" +
100                                JavaScriptUtils.SCRIPT_CLOSE_TAG + "\n");
101                }
102                log.info(timeTaken + "ms server time taken for request " +
103                        RequestCycle.get().getRequest().getUrl() + " response size: " + responseBuffer.length());
104                return responseBuffer;
105        }
106}