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.pageStore; 018 019import java.util.ArrayList; 020import java.util.LinkedList; 021 022import org.apache.wicket.MetaDataKey; 023import org.apache.wicket.page.IManageablePage; 024import org.apache.wicket.request.cycle.RequestCycle; 025import org.slf4j.Logger; 026import org.slf4j.LoggerFactory; 027 028/** 029 * Buffers storage of added pages until the end of the request, when they are delegated to the next store in 030 * the identical order they where added. 031 */ 032public class RequestPageStore extends DelegatingPageStore 033{ 034 035 private static final Logger log = LoggerFactory.getLogger(RequestPageStore.class); 036 037 private static final MetaDataKey<RequestData> KEY = new MetaDataKey<>() 038 { 039 private static final long serialVersionUID = 1L; 040 }; 041 042 public RequestPageStore(IPageStore delegate) 043 { 044 super(delegate); 045 } 046 047 @Override 048 public IManageablePage getPage(IPageContext context, int id) 049 { 050 IManageablePage page = getRequestData(context).get(id); 051 if (page != null) 052 { 053 return page; 054 } 055 056 return getDelegate().getPage(context, id); 057 } 058 059 @Override 060 public void addPage(IPageContext context, IManageablePage page) 061 { 062 getRequestData(context).add(page); 063 } 064 065 @Override 066 public void removePage(IPageContext context, IManageablePage page) 067 { 068 getRequestData(context).remove(page); 069 070 getDelegate().removePage(context, page); 071 } 072 073 @Override 074 public void removeAllPages(IPageContext context) 075 { 076 getRequestData(context).removeAll(); 077 078 getDelegate().removeAllPages(context); 079 } 080 081 @Override 082 public void revertPage(IPageContext context, IManageablePage page) 083 { 084 getRequestData(context).remove(page); 085 086 getDelegate().revertPage(context, page); 087 } 088 089 @Override 090 public void end(IPageContext context) 091 { 092 getDelegate().end(context); 093 094 RequestData requestData = getRequestData(context); 095 for (IManageablePage page : requestData.pages()) 096 { 097 if (isPageStateless(page) == false) 098 { 099 // last opportunity to create a session 100 context.getSessionId(true); 101 break; 102 } 103 } 104 } 105 106 @Override 107 public void detach(IPageContext context) 108 { 109 RequestData requestData = getRequestData(context); 110 for (IManageablePage page : requestData.pages()) 111 { 112 if (isPageStateless(page) == false) 113 { 114 getDelegate().addPage(context, page); 115 } 116 } 117 requestData.removeAll(); 118 119 getDelegate().detach(context); 120 } 121 122 private boolean isPageStateless(final IManageablePage page) { 123 boolean isPageStateless; 124 try 125 { 126 isPageStateless = page.isPageStateless(); 127 } 128 catch (Exception x) 129 { 130 log.warn("An error occurred while checking whether a page is stateless. Assuming it is stateful.", x); 131 isPageStateless = false; 132 } 133 return isPageStateless; 134 } 135 136 private RequestData getRequestData(IPageContext context) 137 { 138 return context.getRequestData(KEY, RequestData::new); 139 } 140 141 /** 142 * Data kept in the {@link RequestCycle}. 143 */ 144 static class RequestData 145 { 146 private final LinkedList<IManageablePage> pages = new LinkedList<>(); 147 148 public void add(IManageablePage page) 149 { 150 // add as last 151 pages.remove(page); 152 pages.addLast(page); 153 } 154 155 public Iterable<IManageablePage> pages() 156 { 157 // must work on copy to prevent concurrent modification when page is re-added during detaching 158 return new ArrayList<>(pages); 159 } 160 161 public IManageablePage get(int id) 162 { 163 for (IManageablePage page : pages) 164 { 165 if (page.getPageId() == id) 166 { 167 return page; 168 } 169 } 170 return null; 171 } 172 173 public void remove(IManageablePage page) 174 { 175 pages.remove(page); 176 } 177 178 public void removeAll() 179 { 180 pages.clear(); 181 } 182 } 183}