Package org.jboss.resteasy.core.registry

Source Code of org.jboss.resteasy.core.registry.SegmentNode$SortEntry

package org.jboss.resteasy.core.registry;

import org.jboss.resteasy.core.ResourceInvoker;
import org.jboss.resteasy.core.ResourceLocatorInvoker;
import org.jboss.resteasy.core.ResourceMethodInvoker;
import org.jboss.resteasy.spi.DefaultOptionsMethodException;
import org.jboss.resteasy.spi.HttpRequest;
import org.jboss.resteasy.spi.ResteasyUriInfo;
import org.jboss.resteasy.util.HttpHeaderNames;
import org.jboss.resteasy.util.HttpResponseCodes;
import org.jboss.resteasy.util.WeightedMediaType;

import javax.ws.rs.NotAcceptableException;
import javax.ws.rs.NotAllowedException;
import javax.ws.rs.NotFoundException;
import javax.ws.rs.NotSupportedException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class SegmentNode
{
   public static final String RESTEASY_CHOSEN_ACCEPT = "RESTEASY_CHOSEN_ACCEPT";
   public static final MediaType[] WILDCARD_ARRAY = {MediaType.WILDCARD_TYPE};
   public static final List<MediaType> DEFAULT_ACCEPTS = new ArrayList<MediaType>();

   static
   {
      DEFAULT_ACCEPTS.add(MediaType.WILDCARD_TYPE);
   }
   protected String segment;
   protected Map<String, SegmentNode> children = new HashMap<String, SegmentNode>();
   protected List<MethodExpression> targets = new ArrayList<MethodExpression>();

   public SegmentNode(String segment)
   {
      this.segment = segment;
   }

   protected static class Match
   {
      MethodExpression expression;
      Matcher matcher;

      public Match(MethodExpression expression, Matcher matcher)
      {
         this.expression = expression;
         this.matcher = matcher;
      }
   }

   public ResourceInvoker match(HttpRequest request, int start)
   {
      String path = request.getUri().getMatchingPath();
      if (start < path.length() && path.charAt(start) == '/') start++;
      List<MethodExpression> potentials = new ArrayList<MethodExpression>();
      potentials(path, start, potentials);
      Collections.sort(potentials);

      boolean expressionMatched = false;
      List<Match> matches = new ArrayList<Match>();
      for (MethodExpression expression : potentials)
      {
         // We ignore locators if the first match was a resource method as per the spec Section 3, Step 2(h)
         if (expressionMatched && expression.isLocator()) continue;

         Pattern pattern = expression.getPattern();
         Matcher matcher = pattern.matcher(path);
         matcher.region(start, path.length());

         if (matcher.matches())
         {
            expressionMatched = true;
            ResourceInvoker invoker = expression.getInvoker();
            if (invoker instanceof ResourceLocatorInvoker)
            {
               ResteasyUriInfo uriInfo = (ResteasyUriInfo) request.getUri();
               int length = matcher.start(expression.getNumGroups() + 1);
               if (length == -1)
               {
                  uriInfo.pushMatchedPath(path);
                  uriInfo.pushMatchedURI(path);
               }
               else
               {
                  String substring = path.substring(0, length);
                  uriInfo.pushMatchedPath(substring);
                  uriInfo.pushMatchedURI(substring);
               }
               expression.populatePathParams(request, matcher, path);
               return invoker;
            }
            else
            {
               matches.add(new Match(expression, matcher));
            }
         }
      }
      if (matches.size() == 0)
      {
         throw new NotFoundException("Could not find resource for full path: " + request.getUri().getRequestUri());
      }
      Match match = match(matches, request.getHttpMethod(), request);
      match.expression.populatePathParams(request, match.matcher, path);
      return match.expression.getInvoker();

   }

   public void potentials(String path, int start, List<MethodExpression> matches)
   {
      if (start == path.length()) // we've reached end of string
      {
         matches.addAll(targets);
         return;
      }

      if (start < path.length())
      {
         String simpleSegment = null;
         int endOfSegmentIndex = path.indexOf('/', start);
         if (endOfSegmentIndex > -1) simpleSegment = path.substring(start, endOfSegmentIndex);
         else simpleSegment = path.substring(start);
         SegmentNode child = children.get(simpleSegment);
         if (child != null)
         {
            int next = start + simpleSegment.length();
            if (endOfSegmentIndex > -1) next++; // go past '/'
            child.potentials(path, next, matches);
         }
      }
      for (MethodExpression exp : targets)
      {
         // skip any static matches as they will not match anyways
         if (exp.getNumGroups() > 0 || exp.getInvoker() instanceof ResourceLocatorInvoker)
         {
            matches.add(exp);
         }
      }
   }

   public static class SortFactor
   {
      public float q = 1.0f;
      public float qs = 1.0f;
      public int d;
      public int dm;
      public String type;
      public String subtype;
      public Map<String, String> params;

      public boolean isWildcardType()
      {
         return type.equals(MediaType.MEDIA_TYPE_WILDCARD);
      }
      public boolean isWildcardSubtype()
      {
         return subtype.equals(MediaType.MEDIA_TYPE_WILDCARD);
      }

   }

   public static SortFactor createSortFactor(MediaType client, MediaType server)
   {
      SortFactor sortFactor = new SortFactor();
      if (client.isWildcardType() != server.isWildcardType())
      {
         sortFactor.type = (client.isWildcardType()) ? server.getType() : client.getType();
         sortFactor.d++;
      }
      else
      {
         sortFactor.type = client.getType();
      }
      if (client.isWildcardSubtype() != server.isWildcardSubtype())
      {
         sortFactor.subtype = (client.isWildcardSubtype()) ? server.getSubtype() : client.getSubtype();
         sortFactor.d++;
      }
      else
      {
         sortFactor.subtype = client.getSubtype();
      }
      String q = client.getParameters().get("q");
      if (q != null) sortFactor.q = Float.parseFloat(q);
      String qs = server.getParameters().get("qs");
      if (qs != null) sortFactor.qs = Float.parseFloat(qs);

      sortFactor.dm = 0;
      for (Map.Entry<String, String> entry : client.getParameters().entrySet())
      {
         String name = entry.getKey();
         if ("q".equals(name)
                 || "qs".equals(name)) continue;
         String val = server.getParameters().get(name);
         if (val == null)
         {
            sortFactor.dm++;
            continue;
         }
         if (!val.equals(entry.getValue()))
         {
            sortFactor.dm++;
            continue;
         }
      }

      for (Map.Entry<String, String> entry : server.getParameters().entrySet())
      {
         String name = entry.getKey();
         if ("q".equals(name)
                 || "qs".equals(name)) continue;
         String val = client.getParameters().get(name);
         if (val == null)
         {
            sortFactor.dm++;
            continue;
         }
         if (!val.equals(entry.getValue()))
         {
            sortFactor.dm++;
            continue;
         }
      }
      return sortFactor;
   }

   protected class SortEntry implements Comparable<SortEntry>
   {
      Match match;
      MediaType serverProduce;
      SortFactor consumes;
      SortFactor produces;

      public SortEntry(Match match, SortFactor consumes, SortFactor produces, MediaType serverProduce)
      {
         this.serverProduce = serverProduce;
         this.match = match;
         this.consumes = consumes;
         this.produces = produces;
      }

      public MediaType getAcceptType()
      {
         // take params from produce and type and subtype from sort factor
         // to define the returned media type
         Map<String, String> params = new HashMap<String, String>();
         for (Map.Entry<String, String> entry : serverProduce.getParameters().entrySet())
         {
            String name = entry.getKey();
            if ("q".equals(name)
                    || "qs".equals(name)) continue;
            params.put(name, entry.getValue());
         }
         return new MediaType(produces.type, produces.subtype, params);
      }


      @Override
      public int compareTo(SortEntry o)
      {
         if (consumes.isWildcardType() && !o.consumes.isWildcardType()) return 1;
         if (!consumes.isWildcardType() && o.consumes.isWildcardType()) return -1;
         if (consumes.isWildcardSubtype() && !o.consumes.isWildcardSubtype()) return 1;
         if (!consumes.isWildcardSubtype() && o.consumes.isWildcardSubtype()) return -1;

         if (consumes.q > o.consumes.q) return -1;
         if (consumes.q < o.consumes.q) return 1;

         if (consumes.qs > o.consumes.qs) return -1;
         if (consumes.qs < o.consumes.qs) return 1;

         if (consumes.d < o.consumes.d) return -1;
         if (consumes.d > o.consumes.d) return 1;

         if (consumes.dm < o.consumes.dm) return -1;
         if (consumes.dm > o.consumes.dm) return 1;

         if (produces.isWildcardType() && !o.produces.isWildcardType()) return 1;
         if (!produces.isWildcardType() && o.produces.isWildcardType()) return -1;
         if (produces.isWildcardSubtype() && !o.produces.isWildcardSubtype()) return 1;
         if (!produces.isWildcardSubtype() && o.produces.isWildcardSubtype()) return -1;

         if (produces.q > o.produces.q) return -1;
         if (produces.q < o.produces.q) return 1;

         if (produces.qs > o.produces.qs) return -1;
         if (produces.qs < o.produces.qs) return 1;

         if (produces.d < o.produces.d) return -1;
         if (produces.d > o.produces.d) return 1;

         if (produces.dm < o.produces.dm) return -1;
         if (produces.dm > o.produces.dm) return 1;

         return match.expression.compareTo(o.match.expression);
      }
   }

   public Match match(List<Match> matches, String httpMethod, HttpRequest request)
   {
      MediaType contentType = request.getHttpHeaders().getMediaType();

      List<MediaType> requestAccepts = request.getHttpHeaders().getAcceptableMediaTypes();
      List<WeightedMediaType> weightedAccepts = new ArrayList<WeightedMediaType>();
      for (MediaType accept : requestAccepts) weightedAccepts.add(WeightedMediaType.parse(accept));

      List<Match> list = new ArrayList<Match>();

      boolean methodMatch = false;
      boolean consumeMatch = false;

      // make a list of all compatible ResourceMethods
      for (Match match : matches)
      {

         ResourceMethodInvoker invoker = (ResourceMethodInvoker) match.expression.getInvoker();
         if (invoker.getHttpMethods().contains(httpMethod.toUpperCase()))
         {
            methodMatch = true;
            if (invoker.doesConsume(contentType))
            {
               consumeMatch = true;
               if (invoker.doesProduce(weightedAccepts))
               {
                  list.add(match);
               }
            }

         }
      }

      if (list.size() == 0)
      {
         if (!methodMatch)
         {
            HashSet<String> allowed = new HashSet<String>();
            for (Match match : matches)
               allowed.addAll(((ResourceMethodInvoker) match.expression.getInvoker()).getHttpMethods());

            if (httpMethod.equalsIgnoreCase("HEAD") && allowed.contains("GET"))
            {
               return match(matches, "GET", request);
            }

            if (allowed.contains("GET")) allowed.add("HEAD");
            allowed.add("OPTIONS");
            String allowHeaderValue = "";
            boolean first = true;
            for (String allow : allowed)
            {
               if (first) first = false;
               else allowHeaderValue += ", ";
               allowHeaderValue += allow;
            }

            if (httpMethod.equals("OPTIONS"))
            {
               Response res = Response.ok(allowHeaderValue,  MediaType.TEXT_PLAIN_TYPE).header(HttpHeaderNames.ALLOW, allowHeaderValue).build();
               throw new DefaultOptionsMethodException("No resource method found for options, return OK with Allow header", res);
            }
            else
            {
               Response res = Response.status(HttpResponseCodes.SC_METHOD_NOT_ALLOWED).header(HttpHeaderNames.ALLOW, allowHeaderValue).build();
               throw new NotAllowedException("No resource method found for " + httpMethod + ", return 405 with Allow header", res);
            }
         }
         else if (!consumeMatch)
         {
            throw new NotSupportedException("Cannot consume content type");
         }
         throw new NotAcceptableException("No match for accept header");
      }
      //if (list.size() == 1) return list.get(0); //don't do this optimization as we need to set chosen accept
      List<SortEntry> sortList = new ArrayList<SortEntry>();
      for (Match match : list)
      {
         ResourceMethodInvoker invoker = (ResourceMethodInvoker) match.expression.getInvoker();
         if (contentType == null) contentType = MediaType.WILDCARD_TYPE;

         MediaType[] consumes = invoker.getConsumes();
         if (consumes.length == 0)
         {
            consumes = WILDCARD_ARRAY;
         }
         MediaType[] produces = invoker.getProduces();
         if (produces.length == 0)
         {
            produces = WILDCARD_ARRAY;
         }
         List<SortFactor> consumeCombo = new ArrayList<SortFactor>();
         for (MediaType consume : consumes)
         {
            consumeCombo.add(createSortFactor(contentType, consume));
         }
         for (MediaType produce : produces)
         {
            List<MediaType> acceptableMediaTypes = requestAccepts;
            if (acceptableMediaTypes.size() == 0)
            {
               acceptableMediaTypes = DEFAULT_ACCEPTS;
            }
            for (MediaType accept : acceptableMediaTypes)
            {
               if (accept.isCompatible(produce))
               {
                  SortFactor sortFactor = createSortFactor(accept, produce);

                  for (SortFactor consume : consumeCombo)
                  {
                     sortList.add(new SortEntry(match, consume, sortFactor, produce));
                  }
               }

            }
         }
      }
      Collections.sort(sortList);
      SortEntry sortEntry = sortList.get(0);
      request.setAttribute(RESTEASY_CHOSEN_ACCEPT, sortEntry.getAcceptType());
      return sortEntry.match;
   }

   protected void addExpression(MethodExpression expression)
   {
      targets.add(expression);
      Collections.sort(targets);

   }


}
TOP

Related Classes of org.jboss.resteasy.core.registry.SegmentNode$SortEntry

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.