| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.wicket.pageStore; |
| |
| import java.util.ArrayList; |
| import java.util.LinkedList; |
| |
| import org.apache.wicket.MetaDataKey; |
| import org.apache.wicket.page.IManageablePage; |
| import org.apache.wicket.request.IRequestCycle; |
| import org.apache.wicket.request.cycle.RequestCycle; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| /** |
| * Buffers storage of added pages until the end of the request, when they are delegated to the next store in |
| * the identical order they where added. |
| */ |
| public class RequestPageStore extends DelegatingPageStore |
| { |
| |
| private static final Logger log = LoggerFactory.getLogger(RequestPageStore.class); |
| |
| private static final MetaDataKey<RequestData> KEY = new MetaDataKey<>() |
| { |
| private static final long serialVersionUID = 1L; |
| }; |
| |
| public RequestPageStore(IPageStore delegate) |
| { |
| super(delegate); |
| } |
| |
| @Override |
| public IManageablePage getPage(IPageContext context, int id) |
| { |
| IManageablePage page = getRequestData(context).get(id); |
| if (page != null) |
| { |
| return page; |
| } |
| |
| return getDelegate().getPage(context, id); |
| } |
| |
| @Override |
| public void addPage(IPageContext context, IManageablePage page) |
| { |
| getRequestData(context).add(page); |
| } |
| |
| @Override |
| public void removePage(IPageContext context, IManageablePage page) |
| { |
| getRequestData(context).remove(page); |
| |
| getDelegate().removePage(context, page); |
| } |
| |
| @Override |
| public void removeAllPages(IPageContext context) |
| { |
| getRequestData(context).removeAll(); |
| |
| getDelegate().removeAllPages(context); |
| } |
| |
| @Override |
| public void revertPage(IPageContext context, IManageablePage page) |
| { |
| getRequestData(context).remove(page); |
| |
| getDelegate().revertPage(context, page); |
| } |
| |
| @Override |
| public void end(IPageContext context) |
| { |
| getDelegate().end(context); |
| |
| RequestData requestData = getRequestData(context); |
| for (IManageablePage page : requestData.pages()) |
| { |
| if (isPageStateless(page) == false) |
| { |
| // last opportunity to create a session |
| context.getSessionId(true); |
| break; |
| } |
| } |
| } |
| |
| @Override |
| public void detach(IPageContext context) |
| { |
| RequestData requestData = getRequestData(context); |
| IRequestCycle requestCycle = RequestCycle.get(); |
| for (IManageablePage page : requestData.pages()) |
| { |
| if (isPageStateless(page) == false && shouldSerializePage(requestCycle, page)) |
| { |
| getDelegate().addPage(context, page); |
| } |
| } |
| requestData.removeAll(); |
| |
| getDelegate().detach(context); |
| } |
| |
| /** |
| * Give the opportunity to skip some serializations. E.g. we have some AJAX behavior that is sending some |
| * info from client to page but page structure didn't change at all and nothing is repainted via AJAX. |
| * But this will trigger a serialization. Returning false here would prevent that request from doing a |
| * page serialization. For heavy pages this can really make a difference. |
| * |
| * @param requestCycle The request |
| * @param page The {@link IManageablePage} |
| * @return <code>true</code> if page should be serialized for this request. The default is true. |
| */ |
| protected boolean shouldSerializePage(IRequestCycle requestCycle, IManageablePage page) |
| { |
| return true; |
| } |
| |
| private boolean isPageStateless(final IManageablePage page) { |
| boolean isPageStateless; |
| try |
| { |
| isPageStateless = page.isPageStateless(); |
| } |
| catch (Exception x) |
| { |
| log.warn("An error occurred while checking whether a page is stateless. Assuming it is stateful.", x); |
| isPageStateless = false; |
| } |
| return isPageStateless; |
| } |
| |
| private RequestData getRequestData(IPageContext context) |
| { |
| return context.getRequestData(KEY, RequestData::new); |
| } |
| |
| /** |
| * Data kept in the {@link RequestCycle}. |
| */ |
| static class RequestData |
| { |
| private final LinkedList<IManageablePage> pages = new LinkedList<>(); |
| |
| public void add(IManageablePage page) |
| { |
| // add as last |
| pages.remove(page); |
| pages.addLast(page); |
| } |
| |
| public Iterable<IManageablePage> pages() |
| { |
| // must work on copy to prevent concurrent modification when page is re-added during detaching |
| return new ArrayList<>(pages); |
| } |
| |
| public IManageablePage get(int id) |
| { |
| for (IManageablePage page : pages) |
| { |
| if (page.getPageId() == id) |
| { |
| return page; |
| } |
| } |
| return null; |
| } |
| |
| public void remove(IManageablePage page) |
| { |
| pages.remove(page); |
| } |
| |
| public void removeAll() |
| { |
| pages.clear(); |
| } |
| } |
| } |