⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 hybridurlcodingstrategy.java

📁 Wicket一个开发Java Web应用程序框架。它使得开发web应用程序变得容易而轻松。 Wicket利用一个POJO data beans组件使得它可以与任何持久层技术相结合。
💻 JAVA
📖 第 1 页 / 共 2 页
字号:
/*
 * 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.request.target.coding;

import java.lang.ref.WeakReference;

import org.apache.wicket.Application;
import org.apache.wicket.IRedirectListener;
import org.apache.wicket.IRequestTarget;
import org.apache.wicket.MetaDataKey;
import org.apache.wicket.Page;
import org.apache.wicket.PageParameters;
import org.apache.wicket.RequestCycle;
import org.apache.wicket.Session;
import org.apache.wicket.protocol.http.PageExpiredException;
import org.apache.wicket.protocol.http.WebRequest;
import org.apache.wicket.protocol.http.request.WebRequestCodingStrategy;
import org.apache.wicket.request.RequestParameters;
import org.apache.wicket.request.target.component.BookmarkableListenerInterfaceRequestTarget;
import org.apache.wicket.request.target.component.BookmarkablePageRequestTarget;
import org.apache.wicket.request.target.component.PageRequestTarget;
import org.apache.wicket.request.target.component.listener.ListenerInterfaceRequestTarget;
import org.apache.wicket.util.string.AppendingStringBuffer;
import org.apache.wicket.util.string.Strings;


/**
 * An URL coding strategy that encodes the mount point, page parameters and page instance
 * information into the URL. The benefits compared to mounting page with
 * {@link BookmarkablePageRequestTargetUrlCodingStrategy} are that the mount point is preserved even
 * after invoking listener interfaces (thus you don't lose bookmarkability after clicking links) and
 * that for ajax only pages the state is preserved on refresh.
 * <p>
 * The url with {@link HybridUrlCodingStrategy} looks like /mount/path/param1/value1.3. or
 * /mount/path/param1/value1.3.2 where 3 is page Id and 2 is version number.
 * <p>
 * Also to preserve state on refresh with ajax-only pages the {@link HybridUrlCodingStrategy} does
 * an immediate redirect after hitting bookmarkable URL, e.g. it immediately redirects from
 * /mount/path to /mount/path.3 where 3 is the next page id. This preserves the page instance on
 * subsequent page refresh.
 * 
 * @author Matej Knopp
 */
public class HybridUrlCodingStrategy extends AbstractRequestTargetUrlCodingStrategy
{
	/** bookmarkable page class. */
	protected final WeakReference/* <Class> */pageClassRef;

	private final boolean redirectOnBookmarkableRequest;

	/**
	 * Construct.
	 * 
	 * @param mountPath
	 * @param pageClass
	 * @param redirectOnBookmarkableRequest
	 *            whether after hitting the page with URL in bookmarkable form it should be
	 *            redirected to hybrid URL - needed for ajax to work properly after page refresh
	 */
	public HybridUrlCodingStrategy(String mountPath, Class pageClass,
		boolean redirectOnBookmarkableRequest)
	{
		super(mountPath);
		pageClassRef = new WeakReference(pageClass);
		this.redirectOnBookmarkableRequest = redirectOnBookmarkableRequest;
	}

	/**
	 * Construct.
	 * 
	 * @param mountPath
	 * @param pageClass
	 */
	public HybridUrlCodingStrategy(String mountPath, Class pageClass)
	{
		this(mountPath, pageClass, true);
	}

	/**
	 * Returns the amount of trailing slashes in the given string
	 * 
	 * @param seq
	 * @return
	 */
	private int getTrailingSlashesCount(CharSequence seq)
	{
		int count = 0;
		for (int i = seq.length() - 1; i >= 0; --i)
		{
			if (seq.charAt(i) == '/')
			{
				++count;
			}
			else
			{
				break;
			}
		}
		return count;
	}

	/**
	 * Returns whether after hitting bookmarkable url the request should be redirected to a hybrid
	 * URL. This is recommended for pages with Ajax.
	 * 
	 * @return
	 */
	protected boolean isRedirectOnBookmarkableRequest()
	{
		return redirectOnBookmarkableRequest;
	}

	/**
	 * Returns whether to redirect when there is pageMap specified in bookmarkable URL
	 * 
	 * @return
	 */
	protected boolean alwaysRedirectWhenPageMapIsSpecified()
	{
		// returns true if the pageId is unique, so we can get rid of the
		// pageMap name in the url this way
		return Application.exists() &&
			Application.get().getSessionSettings().isPageIdUniquePerSession();
	}

	/**
	 * @see org.apache.wicket.request.target.coding.IRequestTargetUrlCodingStrategy#decode(org.apache.wicket.request.RequestParameters)
	 */
	public IRequestTarget decode(RequestParameters requestParameters)
	{
		String parametersFragment = requestParameters.getPath().substring(getMountPath().length());

		// try to extract page info
		PageInfoExtraction extraction = extractPageInfo(parametersFragment);

		PageInfo pageInfo = extraction.getPageInfo();
		String pageMapName = pageInfo != null ? pageInfo.getPageMapName() : null;
		Integer pageVersion = pageInfo != null ? pageInfo.getVersionNumber() : null;
		Integer pageId = pageInfo != null ? pageInfo.getPageId() : null;

		// decode parameters
		PageParameters parameters = new PageParameters(decodeParameters(
			extraction.getUrlAfterExtraction(), requestParameters.getParameters()));

		if (requestParameters.getPageMapName() == null)
		{
			requestParameters.setPageMapName(pageMapName);
		}
		else
		{
			pageMapName = requestParameters.getPageMapName();
		}

		// do some extra work for checking whether this is a normal request to a
		// bookmarkable page, or a request to a stateless page (in which case a
		// wicket:interface parameter should be available
		final String interfaceParameter = (String)parameters.remove(WebRequestCodingStrategy.INTERFACE_PARAMETER_NAME);

		// we need to remember the amount of trailing slashes after the redirect
		// (otherwise we'll break relative urls)
		int originalUrlTrailingSlashesCount = getTrailingSlashesCount(extraction.getUrlAfterExtraction());

		boolean redirect = isRedirectOnBookmarkableRequest();
		if (Strings.isEmpty(pageMapName) != true && alwaysRedirectWhenPageMapIsSpecified())
		{
			redirect = true;
		}

		if (interfaceParameter != null)
		{
			// stateless listener interface
			WebRequestCodingStrategy.addInterfaceParameters(interfaceParameter, requestParameters);
			return new BookmarkableListenerInterfaceRequestTarget(pageMapName,
				(Class)pageClassRef.get(), parameters, requestParameters.getComponentPath(),
				requestParameters.getInterfaceName(), requestParameters.getVersionNumber());
		}
		else if (pageId == null)
		{
			// bookmarkable page request
			return new HybridBookmarkablePageRequestTarget(pageMapName, (Class)pageClassRef.get(),
				parameters, originalUrlTrailingSlashesCount, redirect);
		}
		else
		// hybrid url
		{
			Page page;

			if (Strings.isEmpty(pageMapName) && Application.exists() &&
				Application.get().getSessionSettings().isPageIdUniquePerSession())
			{
				page = Session.get().getPage(pageId.intValue(),
					pageVersion != null ? pageVersion.intValue() : 0);
			}
			else
			{
				page = Session.get().getPage(pageMapName, "" + pageId,
					pageVersion != null ? pageVersion.intValue() : 0);
			}

			// check if the found page match the required class
			if (page != null && page.getClass().equals(pageClassRef.get()))
			{
				requestParameters.setInterfaceName(IRedirectListener.INTERFACE.getName());
				RequestCycle.get().getRequest().setPage(page);
				return new PageRequestTarget(page);
			}
			else
			{
				// we didn't find the page, act as bookmarkable page request -
				// create new instance, but only if there is no callback to a non-existing page
				if (requestParameters.getInterface() != null)
				{
					handleExpiredPage(pageMapName, (Class)pageClassRef.get(),
						originalUrlTrailingSlashesCount, redirect);
				}
				return new HybridBookmarkablePageRequestTarget(pageMapName,
					(Class)pageClassRef.get(), parameters, originalUrlTrailingSlashesCount,
					redirect);
			}
		}

	}

	/**
	 * Handles the case where a non-bookmarkable url with a hybrid base refers to a page that is no
	 * longer in session. eg <code>/context/hybrid-mount.0.23?wicket:interface=...</code>. The
	 * default behavior is to throw a <code>PageExpiredException</code>.
	 * 
	 * This method can be overwritten to, for example, return the user to a new instance of the
	 * bookmarkable page that was mounted using hybrid strategy - this, however, should only be used
	 * in cases where the page expects no page parameters because they are no longer available.
	 * 
	 * @param pageMapName
	 *            page map name this page is mounted in
	 * @param pageClass
	 *            class of mounted page
	 * @param trailingSlashesCount
	 *            count of trailing slsahes in the url
	 * @param redirect
	 *            whether or not a redirect should be issued
	 * @return request target used to handle this situation
	 */
	protected IRequestTarget handleExpiredPage(String pageMapName, Class pageClass,
		int trailingSlashesCount, boolean redirect)
	{
		throw new PageExpiredException(
			"Request cannot be processed. The target page does not exist anymore.");
	}

	/**
	 * Returns the number of trailing slashes in the url when the page in request target was created
	 * or null if the number can't be determined.
	 * 
	 * @param requestTarget
	 * @return
	 */
	private Integer getOriginalOriginalTrailingSlashesCount(IRequestTarget requestTarget)
	{
		if (requestTarget instanceof ListenerInterfaceRequestTarget)
		{
			ListenerInterfaceRequestTarget target = (ListenerInterfaceRequestTarget)requestTarget;
			Page page = target.getPage();
			return (Integer)page.getMetaData(ORIGINAL_TRAILING_SLASHES_COUNT_METADATA_KEY);
		}
		return null;
	}

	/**
	 * Extracts the PageParameters from given request target
	 * 
	 * @param requestTarget
	 * @return
	 */
	private PageParameters getPageParameters(IRequestTarget requestTarget)
	{
		if (requestTarget instanceof BookmarkablePageRequestTarget)
		{
			BookmarkablePageRequestTarget target = (BookmarkablePageRequestTarget)requestTarget;
			return target.getPageParameters();
		}
		else if (requestTarget instanceof ListenerInterfaceRequestTarget)
		{
			ListenerInterfaceRequestTarget target = (ListenerInterfaceRequestTarget)requestTarget;
			Page page = target.getPage();
			return getInitialPagePageParameters(page);
		}
		else
		{
			return null;
		}
	}

	/**
	 * Extracts the PageInfo from given request target
	 * 
	 * @param requestTarget
	 * @return
	 */
	private PageInfo getPageInfo(IRequestTarget requestTarget)
	{
		if (requestTarget instanceof BookmarkablePageRequestTarget)
		{
			BookmarkablePageRequestTarget target = (BookmarkablePageRequestTarget)requestTarget;
			if (target.getPageMapName() != null)
			{
				return new PageInfo(null, null, target.getPageMapName());
			}
			else
			{
				return null;
			}
		}
		else if (requestTarget instanceof ListenerInterfaceRequestTarget)
		{
			ListenerInterfaceRequestTarget target = (ListenerInterfaceRequestTarget)requestTarget;
			Page page = target.getPage();
			return new PageInfo(new Integer(page.getNumericId()), new Integer(
				page.getCurrentVersionNumber()), page.getPageMapName());
		}
		else
		{
			return null;
		}
	}

	/**
	 * Sets the initial page parameters for page instance. Use this only if you know what you are
	 * doing.
	 * 
	 * @param page
	 * @param pageParameters
	 */
	public static void setInitialPageParameters(Page page, PageParameters pageParameters)
	{
		page.setMetaData(PAGE_PARAMETERS_META_DATA_KEY, pageParameters);
	}

	/**
	 * @param page
	 * @return
	 */
	public static PageParameters getInitialPagePageParameters(Page page)
	{
		return (PageParameters)page.getMetaData(PAGE_PARAMETERS_META_DATA_KEY);
	}

	/**
	 * Meta data key to store PageParameters in page instance. This is used to save the
	 * PageParameters that were used to create the page instance so that later they can be used when
	 * generating page URL
	 */
	public static final PageParametersMetaDataKey PAGE_PARAMETERS_META_DATA_KEY = new PageParametersMetaDataKey();

	private static class PageParametersMetaDataKey extends MetaDataKey
	{
		private static final long serialVersionUID = 1L;

		/**
		 * Construct.
		 */
		public PageParametersMetaDataKey()
		{
			super(PageParameters.class);
		}
	};

	// used to store number of trailing slashes in page url (prior the PageInfo)
	// part. This is necessary to maintain
	// the exact number of slashes after page redirect, so that we don't break
	// things that rely on URL depth
	// (other mounted URLs)
	private static final OriginalUrlTrailingSlashesCountMetaDataKey ORIGINAL_TRAILING_SLASHES_COUNT_METADATA_KEY = new OriginalUrlTrailingSlashesCountMetaDataKey();

	private static class OriginalUrlTrailingSlashesCountMetaDataKey extends MetaDataKey
	{
		private static final long serialVersionUID = 1L;

		/**
		 * Construct.
		 */
		public OriginalUrlTrailingSlashesCountMetaDataKey()
		{
			super(Integer.class);
		}

	}

	/**
	 * Fix the amount of trailing slashes in the specified buffer.
	 * 
	 * @param buffer
	 * @param desiredCount
	 */
	private void fixTrailingSlashes(AppendingStringBuffer buffer, int desiredCount)
	{
		int current = getTrailingSlashesCount(buffer);
		if (current > desiredCount)
		{
			buffer.setLength(buffer.length() - (current - desiredCount));
		}
		else if (desiredCount > current)
		{
			int toAdd = desiredCount - current;
			while (toAdd > 0)
			{
				buffer.append("/");
				--toAdd;
			}
		}
	}

	/**
	 * @see org.apache.wicket.request.target.coding.IRequestTargetUrlCodingStrategy#encode(org.apache.wicket.IRequestTarget)
	 */
	public CharSequence encode(IRequestTarget requestTarget)
	{
		if (matches(requestTarget) == false)
		{
			throw new IllegalArgumentException("Unsupported request target type.");
		}

		PageParameters parameters = getPageParameters(requestTarget);
		PageInfo pageInfo = getPageInfo(requestTarget);

		final AppendingStringBuffer url = new AppendingStringBuffer(40);
		url.append(getMountPath());

		// there are cases where the parameters are null
		if (parameters != null)
		{
			appendParameters(url, parameters);
		}

		// check whether we know if the initial URL ended with slash
		Integer trailingSlashesCount = getOriginalOriginalTrailingSlashesCount(requestTarget);
		if (trailingSlashesCount != null)
		{
			fixTrailingSlashes(url, trailingSlashesCount.intValue());
		}

		return addPageInfo(url.toString(), pageInfo);
	}

	/**
	 * @see org.apache.wicket.request.target.coding.IRequestTargetUrlCodingStrategy#matches(org.apache.wicket.IRequestTarget)
	 */
	public boolean matches(IRequestTarget requestTarget)
	{
		if (requestTarget instanceof BookmarkablePageRequestTarget)
		{
			BookmarkablePageRequestTarget target = (BookmarkablePageRequestTarget)requestTarget;
			return target.getPageClass().equals(pageClassRef.get());
		}
		else if (requestTarget instanceof ListenerInterfaceRequestTarget)
		{
			ListenerInterfaceRequestTarget target = (ListenerInterfaceRequestTarget)requestTarget;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -