source: OAIProvider/trunk/src/main/java/eu/clarin/oai/provider/impl/VerbContextImpl.java @ 1911

Last change on this file since 1911 was 1911, checked in by oschonef, 12 years ago
  • first round of major refactoring to simply the provider
    • result sets converted to iterator/cursor-mode (better streaming support)
    • delegate serializing of records to Result and ResultList?
    • prepare getting rid of over-engineered and complicated MetadataFormats? classes (not done, yet)

HEADS UP: breaks existing API

  • Property svn:eol-style set to native
File size: 10.6 KB
Line 
1package eu.clarin.oai.provider.impl;
2
3import java.io.IOException;
4import java.io.OutputStream;
5import java.util.ArrayList;
6import java.util.Collections;
7import java.util.Enumeration;
8import java.util.HashMap;
9import java.util.HashSet;
10import java.util.Iterator;
11import java.util.List;
12import java.util.Map;
13import java.util.Set;
14import java.util.StringTokenizer;
15import java.util.zip.Deflater;
16import java.util.zip.DeflaterOutputStream;
17import java.util.zip.GZIPOutputStream;
18
19import javax.servlet.http.HttpServletRequest;
20import javax.servlet.http.HttpServletResponse;
21import javax.xml.stream.XMLStreamException;
22
23import eu.clarin.oai.provider.Repository;
24import eu.clarin.oai.provider.ext.Argument;
25import eu.clarin.oai.provider.ext.OAIErrorCode;
26import eu.clarin.oai.provider.ext.OAIOutputStream;
27import eu.clarin.oai.provider.ext.RepositoryAdapter;
28import eu.clarin.oai.provider.ext.VerbContext;
29
30final class VerbContextImpl implements VerbContext {
31    private static final int ENCODING_NONE    = 0x00;
32    private static final int ENCODING_IDENITY = 0x01;
33    private static final int ENCODING_DEFLATE = 0x02;
34    private static final int ENCODING_GZIP    = 0x03;
35    private static final String HEADER_ACCEPT_ENCODING = "Accept-Encoding";
36    private static final String HEADER_CONTENT_ENCODING = "Content-Encoding";
37    private static final String ENC_NAME_GZIP = "gzip";
38    private static final String ENC_NAME_DEFLATE = "deflate";
39    private static final String ENC_NAME_IDENTITY = "identity";
40    private static final String ENC_NAME_WILDCARD = "*";
41
42    private final static class ErrorImpl implements Error {
43        private OAIErrorCode code;
44        private String message;
45
46        public ErrorImpl(OAIErrorCode code, String message) {
47            super();
48            this.code = code;
49            this.message = message;
50        }
51
52        @Override
53        public OAIErrorCode getCode() {
54            return code;
55        }
56
57        @Override
58        public String getMessage() {
59            return message;
60        }
61    } // class ErrorImpl
62
63    private final HttpServletRequest request;
64    private final HttpServletResponse response;
65    private final RepositoryAdapter repository;
66    private String verb;
67    private Map<String, Object> arguments;
68    private List<Error> errors;
69
70    VerbContextImpl(HttpServletRequest request, HttpServletResponse response,
71            RepositoryAdapter repository) {
72        this.request = request;
73        this.response = response;
74        this.repository = repository;
75    }
76
77    public String getParameter(String name) {
78        String value = request.getParameter(name);
79        if (value != null) {
80            value = value.trim();
81            if (!value.isEmpty()) {
82                return value;
83            }
84        }
85        return null;
86    }
87
88    public boolean isRepeatedParameter(String name) {
89        String[] params = request.getParameterValues(name);
90        if (params != null) {
91            return params.length > 1;
92        }
93        return false;
94    }
95
96    public Set<String> getParameterNames() {
97        Set<String> names = new HashSet<String>();
98        for (Iterator<?> i = request.getParameterMap().keySet().iterator();
99             i.hasNext();) {
100            String s = (String) i.next();
101            if (s.equalsIgnoreCase("verb")) {
102                continue;
103            }
104            names.add(s);
105        }
106        return names;
107    }
108
109    public void setVerb(String verb) {
110        this.verb = verb;
111    }
112
113    public void setArgument(Argument arg, Object value) {
114        if (arguments == null) {
115            arguments = new HashMap<String, Object>();
116        }
117        arguments.put(arg.getName(), value);
118    }
119
120    @Override
121    public String getVerb() {
122        return verb;
123    }
124
125    @Override
126    public RepositoryAdapter getRepository() {
127        return repository;
128    }
129
130    @Override
131    public boolean hasArgument(String name) {
132        boolean result = false;
133        if (arguments != null) {
134            result = arguments.containsKey(name);
135        }
136        return result;
137    }
138
139    @Override
140    public Object getArgument(String name) {
141        Object value = null;
142        if (arguments != null) {
143            value = arguments.get(name);
144        }
145        return value;
146    }
147
148    @Override
149    public Map<String, String> getUnparsedArguments() {
150        if (arguments == null) {
151            return Collections.emptyMap();
152        }
153        Map<String, String> result =
154            new HashMap<String, String>(arguments.size());
155        for (String name : arguments.keySet()) {
156            result.put(name, getParameter(name));
157        }
158        return result;
159    }
160
161    @Override
162    public void addError(OAIErrorCode code, String message) {
163        if (errors == null) {
164            errors = new ArrayList<Error>();
165        }
166        errors.add(new ErrorImpl(code, message));
167    }
168
169    @Override
170    public String getContextPath() {
171        return request.getContextPath();
172    }
173
174    @Override
175    public String getRequestURI() {
176        return request.getRequestURL().toString();
177    }
178
179    @Override
180    public boolean hasErrors() {
181        return (errors != null);
182    }
183
184    @Override
185    public List<Error> getErrors() {
186        if (errors != null) {
187            return errors;
188        }
189        return Collections.emptyList();
190    }
191
192    @Override
193    public OAIOutputStream getOutputStream() throws IOException,
194            XMLStreamException {
195        int bestEnc = ENCODING_NONE;
196        float bestQvalue = 0.0f;
197        boolean identityPresent = false;
198
199        @SuppressWarnings("unchecked")
200        Enumeration<String> headers =
201            request.getHeaders(HEADER_ACCEPT_ENCODING);
202        if (headers.hasMoreElements()) {
203            while (headers.hasMoreElements()) {
204                StringTokenizer tok =
205                    new StringTokenizer(headers.nextElement(), ",");
206                while (tok.hasMoreTokens()) {
207                    String item = tok.nextToken();
208   
209                    String enc = null;
210                    float qvalue = -1.0f;
211   
212                    int pos = item.indexOf(';');
213                    if (pos != -1) {
214                        enc = item.substring(0, pos).trim();
215                        int pos2 = item.indexOf('=', pos + 1);
216                        if (pos2 != -1) {
217                            String tmp = item.substring(pos2 + 1).trim();
218                            try {
219                                float value = Float.parseFloat(tmp);
220                                if (value >= 0.0f && value <= 1.0f) {
221                                    qvalue = value;
222                                }
223                            } catch (NumberFormatException e) {
224                                /* IGNORE */
225                            }
226                        }
227                    } else {
228                        enc = item.trim();
229                        qvalue = 1.0f;
230                    }
231
232                    // check, if encoding/qvalue pair is well-formed
233                    if (checkEncoding(enc) && (qvalue >= 0.0f)) {
234                        // special handling for identity encoding flag
235                        if (ENC_NAME_IDENTITY.equalsIgnoreCase(enc) &&
236                                (qvalue > 0.0f)) {
237                            identityPresent = true;
238                        }
239   
240                        // rate current best encoding versus parsed encoding
241                        if (qvalue > bestQvalue) {
242                            if (ENC_NAME_IDENTITY.equalsIgnoreCase(enc) ||
243                                    ENC_NAME_WILDCARD.equals(enc)) {
244                                bestEnc = ENCODING_IDENITY;
245                            } else if (ENC_NAME_DEFLATE.equalsIgnoreCase(enc)
246                                    && repository.supportsCompressionMethod(
247                                            Repository.COMPRESSION_DEFLATE)) {
248                                bestEnc = ENCODING_DEFLATE;
249                            } else if (ENC_NAME_GZIP.equalsIgnoreCase(enc)
250                                    && repository.supportsCompressionMethod(
251                                            Repository.COMPRESSION_GZIP)) {
252                                bestEnc = ENCODING_GZIP;
253                            } else {
254                                /* skip unsupported encoding */
255                                continue;
256                            }
257                            bestQvalue = qvalue;
258                        }
259                    }
260                } // while (tokens)
261            } // while (headers)
262
263            /*
264             * OAI Specification mandates that identity encoding is included
265             * in Accept-Encoding header with a non-zero qvalue.
266             * (see Section 3.1.3)
267             */
268            if (!identityPresent) {
269                /*
270                 * XXX: if we where being pedantic, we would signal an error
271                 * here. However, for now, we just assume, that identity
272                 * encoding can be understood by any harvester.
273                 */
274                // throw new OAIException("Accept-Encoding header must " +
275                // "include \"identity\" encoding with non-zero " +
276                // "qvalue (see OAI Specification section 3.1.3)");
277                if (bestEnc == ENCODING_NONE) {
278                    bestEnc = ENCODING_IDENITY;
279                }
280            }
281        } else {
282            /* no accept header, use identity encoding */
283            bestEnc = ENCODING_IDENITY;
284        }
285
286        response.setStatus(HttpServletResponse.SC_OK);
287        response.setContentType("text/xml");
288        response.setCharacterEncoding("utf-8");
289
290        OutputStream out = null;
291        switch (bestEnc) {
292        default:
293            /* FALL-TROUGH */
294        case ENCODING_IDENITY:
295            out = response.getOutputStream();
296            break;
297        case ENCODING_DEFLATE:
298            response.addHeader(HEADER_CONTENT_ENCODING, ENC_NAME_DEFLATE);
299            out = new DeflaterOutputStream(response.getOutputStream(),
300                    new Deflater(Deflater.DEFAULT_COMPRESSION, true));
301            break;
302        case ENCODING_GZIP:
303            response.addHeader(HEADER_CONTENT_ENCODING, ENC_NAME_GZIP);
304            out = new GZIPOutputStream(response.getOutputStream(),
305                    response.getBufferSize());
306            break;
307        }
308        return new OAIOutputStreamImpl(this, out);
309    }
310
311    private boolean checkEncoding(String encoding) {
312        if (ENC_NAME_WILDCARD.equals(encoding)) {
313            return true;
314        }
315        for (int i = 0; i < encoding.length(); i++) {
316            if (!Character.isLetter(encoding.charAt(i))) {
317                return false;
318            }
319        }
320        return true;
321    }
322
323} // class VerbContextImpl
Note: See TracBrowser for help on using the repository browser.