001    /**
002     * Licensed to the Apache Software Foundation (ASF) under one
003     * or more contributor license agreements.  See the NOTICE file
004     * distributed with this work for additional information
005     * regarding copyright ownership.  The ASF licenses this file
006     * to you under the Apache License, Version 2.0 (the
007     * "License"); you may not use this file except in compliance
008     * with the License.  You may obtain a copy of the License at
009     *
010     *     http://www.apache.org/licenses/LICENSE-2.0
011     *
012     * Unless required by applicable law or agreed to in writing, software
013     * distributed under the License is distributed on an "AS IS" BASIS,
014     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015     * See the License for the specific language governing permissions and
016     * limitations under the License.
017     */
018    
019    package org.apache.hadoop.lib.wsrs;
020    
021    import com.google.common.collect.Lists;
022    import com.sun.jersey.api.core.HttpContext;
023    import com.sun.jersey.core.spi.component.ComponentContext;
024    import com.sun.jersey.core.spi.component.ComponentScope;
025    import com.sun.jersey.server.impl.inject.AbstractHttpContextInjectable;
026    import com.sun.jersey.spi.inject.Injectable;
027    import com.sun.jersey.spi.inject.InjectableProvider;
028    import org.apache.hadoop.classification.InterfaceAudience;
029    
030    import javax.ws.rs.core.Context;
031    import javax.ws.rs.core.MultivaluedMap;
032    import java.lang.reflect.Type;
033    import java.text.MessageFormat;
034    import java.util.HashMap;
035    import java.util.List;
036    import java.util.Map;
037    
038    /**
039     * Jersey provider that parses the request parameters based on the
040     * given parameter definition. 
041     */
042    @InterfaceAudience.Private
043    public class ParametersProvider
044      extends AbstractHttpContextInjectable<Parameters>
045      implements InjectableProvider<Context, Type> {
046    
047      private String driverParam;
048      private Class<? extends Enum> enumClass;
049      private Map<Enum, Class<Param<?>>[]> paramsDef;
050    
051      public ParametersProvider(String driverParam, Class<? extends Enum> enumClass,
052                                Map<Enum, Class<Param<?>>[]> paramsDef) {
053        this.driverParam = driverParam;
054        this.enumClass = enumClass;
055        this.paramsDef = paramsDef;
056      }
057    
058      @Override
059      @SuppressWarnings("unchecked")
060      public Parameters getValue(HttpContext httpContext) {
061        Map<String, List<Param<?>>> map = new HashMap<String, List<Param<?>>>();
062        Map<String, List<String>> queryString =
063          httpContext.getRequest().getQueryParameters();
064        String str = ((MultivaluedMap<String, String>) queryString).
065            getFirst(driverParam);
066        if (str == null) {
067          throw new IllegalArgumentException(
068            MessageFormat.format("Missing Operation parameter [{0}]",
069                                 driverParam));
070        }
071        Enum op;
072        try {
073          op = Enum.valueOf(enumClass, str.toUpperCase());
074        } catch (IllegalArgumentException ex) {
075          throw new IllegalArgumentException(
076            MessageFormat.format("Invalid Operation [{0}]", str));
077        }
078        if (!paramsDef.containsKey(op)) {
079          throw new IllegalArgumentException(
080            MessageFormat.format("Unsupported Operation [{0}]", op));
081        }
082        for (Class<Param<?>> paramClass : paramsDef.get(op)) {
083          Param<?> param = newParam(paramClass);
084          List<Param<?>> paramList = Lists.newArrayList();
085          List<String> ps = queryString.get(param.getName());
086          if (ps != null) {
087            for (String p : ps) {
088              try {
089                param.parseParam(p);
090              }
091              catch (Exception ex) {
092                throw new IllegalArgumentException(ex.toString(), ex);
093              }
094              paramList.add(param);
095              param = newParam(paramClass);
096            }
097          } else {
098            paramList.add(param);
099          }
100    
101          map.put(param.getName(), paramList);
102        }
103        return new Parameters(map);
104      }
105    
106      private Param<?> newParam(Class<Param<?>> paramClass) {
107        try {
108          return paramClass.newInstance();
109        } catch (Exception ex) {
110          throw new UnsupportedOperationException(
111            MessageFormat.format(
112              "Param class [{0}] does not have default constructor",
113              paramClass.getName()));
114        }
115      }
116    
117      @Override
118      public ComponentScope getScope() {
119        return ComponentScope.PerRequest;
120      }
121    
122      @Override
123      public Injectable getInjectable(ComponentContext componentContext, Context context, Type type) {
124        return (type.equals(Parameters.class)) ? this : null;
125      }
126    }