Package org.brixcms.web

Source Code of org.brixcms.web.BrixRequestMapper

/**
* Licensed 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.brixcms.web;

import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.List;
import java.util.Set;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;

import org.apache.wicket.Application;
import org.apache.wicket.Component;
import org.apache.wicket.MetaDataKey;
import org.apache.wicket.RequestListenerInterface;
import org.apache.wicket.Session;
import org.apache.wicket.markup.html.WebPage;
import org.apache.wicket.request.IRequestHandler;
import org.apache.wicket.request.IRequestMapper;
import org.apache.wicket.request.Request;
import org.apache.wicket.request.Url;
import org.apache.wicket.request.Url.QueryParameter;
import org.apache.wicket.request.component.IRequestablePage;
import org.apache.wicket.request.cycle.RequestCycle;
import org.apache.wicket.request.handler.BookmarkableListenerInterfaceRequestHandler;
import org.apache.wicket.request.handler.BookmarkablePageRequestHandler;
import org.apache.wicket.request.handler.IPageProvider;
import org.apache.wicket.request.handler.ListenerInterfaceRequestHandler;
import org.apache.wicket.request.handler.PageAndComponentProvider;
import org.apache.wicket.request.handler.PageProvider;
import org.apache.wicket.request.handler.RenderPageRequestHandler;
import org.apache.wicket.request.http.WebRequest;
import org.apache.wicket.request.http.WebResponse;
import org.apache.wicket.request.mapper.IMapperContext;
import org.apache.wicket.request.mapper.IPageSource;
import org.apache.wicket.request.mapper.info.ComponentInfo;
import org.apache.wicket.request.mapper.info.PageComponentInfo;
import org.apache.wicket.request.mapper.info.PageInfo;
import org.apache.wicket.request.mapper.parameter.PageParameters;
import org.apache.wicket.request.mapper.parameter.PageParameters.NamedPair;
import org.apache.wicket.util.lang.Args;
import org.apache.wicket.util.string.StringValue;
import org.apache.wicket.util.string.Strings;
import org.apache.wicket.util.visit.IVisit;
import org.apache.wicket.util.visit.IVisitor;
import org.brixcms.Brix;
import org.brixcms.BrixNodeModel;
import org.brixcms.Path;
import org.brixcms.config.BrixConfig;
import org.brixcms.jcr.api.JcrSession;
import org.brixcms.jcr.exception.JcrException;
import org.brixcms.jcr.wrapper.BrixNode;
import org.brixcms.plugin.site.SiteNodePlugin;
import org.brixcms.plugin.site.SitePlugin;
import org.brixcms.plugin.site.page.AbstractSitePagePlugin;
import org.brixcms.web.nodepage.BrixNodePageRequestHandler;
import org.brixcms.web.nodepage.BrixNodePageUrlMapper;
import org.brixcms.web.nodepage.BrixNodeRequestHandler;
import org.brixcms.web.nodepage.BrixNodeWebPage;
import org.brixcms.web.nodepage.BrixPageParameters;
import org.brixcms.web.nodepage.PageParametersAware;
import org.brixcms.workspace.Workspace;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class BrixRequestMapper implements IRequestMapper {

    public static final String WORKSPACE_PARAM = Brix.NS_PREFIX + "workspace";

    private static final Logger logger = LoggerFactory.getLogger(BrixRequestMapper.class);

    private static final String COOKIE_NAME = "brix-revision";

    private static final MetaDataKey<String> WORKSPACE_METADATA = new MetaDataKey<String>() {
        private static final long serialVersionUID = 1L;
    };
    private static final Logger log = LoggerFactory.getLogger(BrixRequestMapper.class);
    final Brix brix;
    private boolean handleHomePage = true;

    public BrixRequestMapper(Brix brix) {
        this.brix = brix;
    }

    public String getWorkspace() {
        String workspace = getWorkspaceFromUrl();

        if (workspace != null) {
            return workspace;
        }

        RequestCycle rc = RequestCycle.get();
        workspace = rc.getMetaData(WORKSPACE_METADATA);
        if (workspace == null) {
            WebRequest req = (WebRequest) RequestCycle.get().getRequest();
            WebResponse resp = (WebResponse) RequestCycle.get().getResponse();
            Cookie cookie = req.getCookie(COOKIE_NAME);
            workspace = getDefaultWorkspaceName();
            if (cookie != null) {
                if (cookie.getValue() != null) {
                    workspace = cookie.getValue();
                }
            }
            if (!checkSession(workspace)) {
                workspace = getDefaultWorkspaceName();
            }
            if (workspace == null) {
                throw new IllegalStateException("Could not resolve jcr workspace to use for this request " + req.getUrl());
            }
            Cookie c = new Cookie(COOKIE_NAME, workspace);
            c.setPath("/");
            if (workspace.toString().equals(getDefaultWorkspaceName()) == false) {
                resp.addCookie(c);
            } else if (cookie != null) {
                resp.clearCookie(cookie);
            }
            rc.setMetaData(WORKSPACE_METADATA, workspace);
        }
        return workspace;
    }

    private String getWorkspaceFromUrl() {
        HttpServletRequest request = (HttpServletRequest) ((WebRequest) RequestCycle.get().getRequest()).getContainerRequest();

        if (request.getParameter(WORKSPACE_PARAM) != null) {
            return request.getParameter(WORKSPACE_PARAM);
        }

        String referer = request.getHeader("referer");

        if (!Strings.isEmpty(referer)) {
            return extractWorkspaceFromReferer(referer);
        } else {
            return null;
        }
    }

    private static String extractWorkspaceFromReferer(String refererURL) {
        int i = refererURL.indexOf('?');
        if (i != -1 && i != refererURL.length() - 1) {
            String param = refererURL.substring(i + 1);
            String params[] = Strings.split(param, '&');
            for (String s : params) {
                try {
                    s = URLDecoder.decode(s, "utf-8");
                } catch (UnsupportedEncodingException e) {
                    // rrright
                    throw new RuntimeException(e);
                }
                if (s.startsWith(WORKSPACE_PARAM + "=")) {
                    String value = s.substring(WORKSPACE_PARAM.length() + 1);
                    if (value.length() > 0) {
                        return value;
                    }
                }
            }
        }
        return null;
    }

    private boolean checkSession(String workspaceId) {
        return brix.getWorkspaceManager().workspaceExists(workspaceId);
    }

    private String getDefaultWorkspaceName() {
        final Workspace workspace = brix.getConfig().getMapper().getWorkspaceForRequest(RequestCycle.get(), brix);
        return (workspace != null) ? workspace.getId() : null;
    }

    @Override
    public IRequestHandler mapRequest(Request request) {
        final Url url = request.getClientUrl();

        if (isInternalWicket(request)) {
            return null;
        }

      // TODO: This is just a quick fix
      if (url.getSegments().size() > 0) {
        if (url.getSegments().get(0).equals("webdav") || url.getSegments().get(0).equals("jcrwebdav")) {
          return null;
        }
      }

      Path path = new Path("/" + url.getPath());

      // root path handling
      if (path.isRoot()) {
        if (handleHomePage) {
          final BrixNode node = getNodeForUriPath(path);
          return SitePlugin.get().getNodePluginForNode(node)
              .respond(new BrixNodeModel(node), new BrixPageParameters(request.getRequestParameters()));
        } else {
          return null;
        }
      }

      IRequestHandler handler = null;
      try {
        while (handler == null) {
          final BrixNode node = getNodeForUriPath(path);
          if (node != null) {
            SiteNodePlugin plugin = SitePlugin.get().getNodePluginForNode(node);
            if (plugin instanceof AbstractSitePagePlugin) {
              handler = SitePlugin.get().getNodePluginForNode(node)
                  .respond(new BrixNodeModel(node), createBrixPageParams(request.getUrl(), path));
            } else {
              handler = SitePlugin.get().getNodePluginForNode(node)
                  .respond(new BrixNodeModel(node), new BrixPageParameters(request.getRequestParameters()));
            }
          }
          if (handler != null || path.toString().equals(".")) {
            break;
          }
          path = path.parent();
          if (path.isRoot()) {
            break;
          }
        }
      } catch (JcrException e) {
        logger.warn("JcrException caught due to incorrect url", e);
      }
     
        final PageComponentInfo info = getPageComponentInfo(request.getUrl());
        if (info != null && info.getPageInfo().getPageId() != null) {
            Integer renderCount = info.getComponentInfo() != null ? info.getComponentInfo().getRenderCount() : null;

            if (info.getComponentInfo() == null) {
              PageProvider provider;
              if (handler instanceof BrixNodePageRequestHandler)
              {
                provider = new PageProvider(info.getPageInfo().getPageId(), BrixNodeWebPage.class, renderCount);
                BrixNodePageRequestHandler brixNodePageRequestHandler = (BrixNodePageRequestHandler) handler;
                final IPageProvider pageProviderAdapter = brixNodePageRequestHandler.getPageProvider();
                provider.setPageSource(new IPageSource()
                {
                  @Override
                  public IRequestablePage getPageInstance(int pageId)
                  {
                    IRequestablePage page = null;
                    Integer existingPageId = pageProviderAdapter.getPageId();
                    if (existingPageId != null && pageId == existingPageId) {
                page = pageProviderAdapter.getPageInstance();
                    }
                    return page;
                  }

                  @Override
                  public IRequestablePage newPageInstance(Class<? extends IRequestablePage> pageClass, PageParameters pageParameters)
                  {
                    IRequestablePage page = pageProviderAdapter.getPageInstance();
                    page.getPageParameters().set(info.toString(), "");
                    return page;
                  }
                });
              }
              else
              {
                provider = new PageProvider(info.getPageInfo().getPageId(), renderCount);
                provider.setPageSource(getContext())
              }
               
                // render page
                return new RenderPageRequestHandler(provider);
            } else {
                ComponentInfo componentInfo = info.getComponentInfo();
                PageAndComponentProvider provider = new PageAndComponentProvider(info.getPageInfo().getPageId(), renderCount,
                        componentInfo.getComponentPath());

                provider.setPageSource(getContext());
                // listener interface
                RequestListenerInterface listenerInterface = requestListenerInterfaceFromString(componentInfo.getListenerInterface());

                return new ListenerInterfaceRequestHandler(provider, listenerInterface, componentInfo.getBehaviorId());
            }
        }

        return handler;
    }

    private BrixPageParameters createBrixPageParams(Url url, Path path) {
        BrixPageParameters parameters = new BrixPageParameters();
        Path nodePath = path;
        Path requestPath = new Path("/" + url.getPath());

        if (nodePath.isAncestorOf(requestPath)) {
            Path remaining = new Path(requestPath.toString(), false).toRelative(nodePath);
            int i = 0;
            for (String s : remaining) {
                parameters.set(i, BrixNodePageUrlMapper.urlDecode(s));
                ++i;
            }
        }
        for (QueryParameter parameter : url.getQueryParameters()) {
          String parameterName = parameter.getName();
          if (PageComponentInfo.parse(parameterName) == null)
          {
              parameters.add(parameterName, parameter.getValue());
          }
        }
        return parameters;
    }

    @Override
    public int getCompatibilityScore(Request request) {
        if (isInternalWicket(request)) {
            return 0;
        }
        // bluff we can parse all segments - makes sure we run first
        return request.getUrl().getSegments().size();
    }

    private boolean isInternalWicket(Request request) {
        Url url = request.getUrl();
        if (url.getSegments().size() > 0) {
            if (url.getSegments().get(0).equals((Application.get().getMapperContext().getNamespace()))) {
                // starts with wicket namespace - is an internal wicket url
                return true;
            }
        }
        return false;
    }

    @Override
    public Url mapHandler(IRequestHandler requestHandler) {
        if (requestHandler instanceof BrixNodeRequestHandler) {
            BrixNodeRequestHandler handler = (BrixNodeRequestHandler) requestHandler;
            String nodeURL = handler.getNodeURL();
            return encode(nodeURL, handler.getPageParameters(), null);
        } else if (requestHandler instanceof ListenerInterfaceRequestHandler) {
            ListenerInterfaceRequestHandler handler = (ListenerInterfaceRequestHandler) requestHandler;
            if (handler.getPage() instanceof BrixNodeWebPage) {
                BrixNodeWebPage page = (BrixNodeWebPage) handler.getPage();
                String componentPath = handler.getComponentPath();
                RequestListenerInterface listenerInterface = handler.getListenerInterface();
                Integer renderCount = null;
                if (listenerInterface.isIncludeRenderCount()) {
                    renderCount = page.getRenderCount();
                }
                PageInfo pageInfo = new PageInfo(page.getPageId());
                ComponentInfo componentInfo = new ComponentInfo(renderCount, requestListenerInterfaceToString(listenerInterface),
                        componentPath, handler.getBehaviorIndex());
                PageComponentInfo info = new PageComponentInfo(pageInfo, componentInfo);
                Url url = encode(page);
                encodePageComponentInfo(url, info);
                return url;
            } else {
                return null;
            }
        } else if (requestHandler instanceof RenderPageRequestHandler) {
            RenderPageRequestHandler handler = (RenderPageRequestHandler) requestHandler;
            if (handler.getPage() instanceof BrixNodeWebPage) {
                BrixNodeWebPage page = (BrixNodeWebPage) handler.getPage();
                PageInfo i = new PageInfo(page.getPageId());
                PageComponentInfo info = new PageComponentInfo(i, null);
                Url url = encode(page);
                encodePageComponentInfo(url, info);
                return url;
            } else {
                return null;
            }
        } else if (requestHandler instanceof BookmarkableListenerInterfaceRequestHandler) {
            BookmarkableListenerInterfaceRequestHandler target = (BookmarkableListenerInterfaceRequestHandler) requestHandler;
            BrixNodeWebPage page = (BrixNodeWebPage) target.getPage();
            BrixNode node = page.getModelObject();
            PageInfo info = new PageInfo(page.getPageId());
            return encode(node, page.getBrixPageParameters(), info);
        } else if (requestHandler instanceof BookmarkablePageRequestHandler
                && ((BookmarkablePageRequestHandler) requestHandler).getPageClass().equals(HomePage.class)) {
            BrixNode node = ((BrixRequestCycleProcessor) RequestCycle.get().getActiveRequestHandler()).getNodeForUriPath(Path.ROOT);
            return mapHandler(new BrixNodeRequestHandler(new BrixNodeModel(node)));
        } else {
            return null;
        }
    }

    /**
     * Converts the specified listener interface to String.
     *
     * @param listenerInterface
     * @return listenerInterface name as string
     */
    protected String requestListenerInterfaceToString(RequestListenerInterface listenerInterface) {
        Args.notNull(listenerInterface, "listenerInterface");

        return getContext().requestListenerInterfaceToString(listenerInterface);
    }

    /**
     * Extracts the {@link PageComponentInfo} from the URL. The
     * {@link PageComponentInfo} is encoded as the very first query parameter
     * and the parameter consists of name only (no value).
     *
     * @param url
     *
     * @return PageComponentInfo instance if one was encoded in URL,
     *         <code>null</code> otherwise.
     */
    protected PageComponentInfo getPageComponentInfo(final Url url) {
        if (url == null) {
            throw new IllegalStateException("Argument 'url' may not be null.");
        } else {
            for (QueryParameter queryParameter : url.getQueryParameters()) {
                if (Strings.isEmpty(queryParameter.getValue())) {
                    PageComponentInfo pageComponentInfo = PageComponentInfo.parse(queryParameter.getName());
                    if (pageComponentInfo != null) {
                        return pageComponentInfo;
                    }
                }
            }
        }
        return null;
    }

    /**
     * Creates listener interface from the specified string
     *
     * @param interfaceName
     * @return listener interface
     */
    protected RequestListenerInterface requestListenerInterfaceFromString(String interfaceName) {
        Args.notEmpty(interfaceName, "interfaceName");

        return getContext().requestListenerInterfaceFromString(interfaceName);
    }

    protected IMapperContext getContext() {
        return Application.get().getMapperContext();
    }

    protected void encodePageComponentInfo(Url url, PageComponentInfo info) {
        Args.notNull(url, "url");
        if (info != null && Session.exists() && !Session.get().isSessionInvalidated()) {
            String s = info.toString();
            if (!Strings.isEmpty(s)) {
                QueryParameter parameter = new QueryParameter(s, "");
                url.getQueryParameters().add(parameter);
            }
        }
    }

    private Url encode(BrixNodeWebPage page) {
        BrixNode node = page.getModelObject();
        // This is a URL for redirect. Allow components to contribute state to
        // URL if they want to
        final BrixPageParameters parameters = page.getBrixPageParameters();
        for (NamedPair namedPair : parameters.getAllNamed()) {
            if (isNumber(namedPair.getKey())) {
                parameters.remove(namedPair.getKey());
            }
        }
        page.visitChildren(PageParametersAware.class, new IVisitor<Component, PageParametersAware>() {
            @Override
            public void component(Component component, IVisit iVisit) {
                ((PageParametersAware) component).contributeToPageParameters(parameters);
            }
        });
        return encode(node, parameters, null);
    }

    private Url encode(BrixNode node, PageParameters parameters, PageInfo info) {
        return encode(getUriPathForNode(node).toString(), parameters, info);
    }

    private Url encode(String nodeURL, PageParameters parameters, PageInfo info) {
        StringBuilder builder = new StringBuilder();

        if (nodeURL.startsWith("/")) {
            nodeURL = nodeURL.substring(1);
        }

        builder.append(urlEncodePath(new Path(nodeURL, false)));

        boolean skipFirstSlash = builder.charAt(builder.length() - 1) == '/';

        for (int i = 0; i < parameters.getIndexedCount(); ++i) {
            if (!skipFirstSlash) {
                builder.append('/');
            } else {
                skipFirstSlash = false;
            }

            final StringValue value = parameters.get(i);
            final String s = value.toString();

            if (s != null) {
                builder.append(urlEncode(s));
            }
        }

        Set<String> keys = parameters.getNamedKeys();
        if (info != null || !keys.isEmpty()) {
            builder.append("?");
        }

        if (info != null) {
            builder.append(info.toString());
        }

        boolean first = (info == null);

        for (String key : keys) {
            List<StringValue> values = parameters.getValues(key);
            for (StringValue value : values) {
                if (first) {
                    first = false;
                } else {
                    builder.append("&");
                }
                builder.append(urlEncode(key));
                builder.append("=");
                builder.append(urlEncode(value.toString()));
            }
        }

        return Url.parse(builder.toString());
    }

    private String urlEncodePath(Path path) {
        StringBuilder res = new StringBuilder(path.size());
        boolean first = true;
        for (String s : path) {
            if (first) {
                first = false;
            } else {
                res.append("/");
            }
            res.append(urlEncode(s));
        }
        return res.toString();
    }

    /**
     * Url encodes a string
     *
     * @param string
     *            string to be encoded
     * @return encoded string
     */
    public static String urlEncode(String string) {
        try {
            return URLEncoder.encode(string, Application.get().getRequestCycleSettings().getResponseRequestEncoding());
        } catch (UnsupportedEncodingException e) {
            log.error(e.getMessage(), e);
            return string;
        }
    }

    /**
     * Resolves uri path to a {@link BrixNode}. By default this method uses
     * {@link BrixConfig#getMapper()} to map the uri to a node path.
     *
     * @param uriPath
     *            uri path
     * @return node that maps to the <code>uriPath</code> or <code>null</code>
     *         if none
     */
    public BrixNode getNodeForUriPath(final Path uriPath) {
        BrixNode node = null;

        // create desired nodepath
        final Path nodePath = brix.getConfig().getMapper().getNodePathForUriPath(uriPath.toAbsolute(), brix);

        if (nodePath != null) {
            // allow site plugin to translate the node path into an actual jcr
            // path
            final String jcrPath = SitePlugin.get().toRealWebNodePath(nodePath.toString());

            // retrieve jcr session
            final String workspace = getWorkspace();
            final JcrSession session = brix.getCurrentSession(workspace);

            if (session.itemExists(jcrPath)) {
                // node exists, return it
                node = (BrixNode) session.getItem(jcrPath);
            }
        }

        return node;
    }

    /**
     * Creates a uri path for the specified <code>node</code> By default this
     * method uses {@link BrixConfig#getMapper()} to map node path to a uri
     * path.
     *
     * @param node
     *            node to create uri path for
     * @return uri path that represents the node
     */
    public Path getUriPathForNode(final BrixNode node) {
        // allow site plugin to translate jcr path into node path
        final String jcrPath = SitePlugin.get().fromRealWebNodePath(node.getPath());
        final Path nodePath = new Path(jcrPath);

        // use urimapper to create the uri
        return brix.getConfig().getMapper().getUriPathForNode(nodePath, brix);
    }

    /**
     * Method that rigidly checks if the string consists of digits only.
     *
     * @param string
     * @return
     */
    private static boolean isNumber(String string) {
        if (string == null || string.length() == 0) {
            return false;
        }
        for (int i = 0; i < string.length(); ++i) {
            if (Character.isDigit(string.charAt(i)) == false) {
                return false;
            }
        }
        return true;
    }

    // -------------------------- INNER CLASSES --------------------------

    public static final class HomePage extends WebPage {
    }
}
TOP

Related Classes of org.brixcms.web.BrixRequestMapper

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.