Project

General

Profile

1 5211 jones
/**
2
 *  '$RCSfile$'
3 5805 berkley
 *  Copyright: 2011 Regents of the University of California and the
4 5211 jones
 *              National Center for Ecological Analysis and Synthesis
5
 *
6
 *   '$Author: Serhan AKIN $'
7
 *     '$Date: 2009-06-13 15:28:13 +0300  $'
8
 *
9
 * This program is free software; you can redistribute it and/or modify
10
 * it under the terms of the GNU General Public License as published by
11
 * the Free Software Foundation; either version 2 of the License, or
12
 * (at your option) any later version.
13
 *
14
 * This program is distributed in the hope that it will be useful,
15
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
 * GNU General Public License for more details.
18
 *
19
 * You should have received a copy of the GNU General Public License
20
 * along with this program; if not, write to the Free Software
21
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22
 */
23
package edu.ucsb.nceas.metacat.restservice;
24
25 6130 leinfelder
import java.io.File;
26 6269 leinfelder
import java.io.FileInputStream;
27
import java.io.FileNotFoundException;
28 6130 leinfelder
import java.io.IOException;
29
import java.io.InputStream;
30
import java.io.OutputStream;
31
import java.io.PrintWriter;
32
import java.text.DateFormat;
33
import java.text.ParseException;
34
import java.text.SimpleDateFormat;
35
import java.util.Date;
36
import java.util.Enumeration;
37
import java.util.Hashtable;
38 6269 leinfelder
import java.util.Iterator;
39
import java.util.List;
40
import java.util.Map;
41 6130 leinfelder
import java.util.TimeZone;
42
import java.util.Timer;
43 5211 jones
44
import javax.servlet.ServletContext;
45
import javax.servlet.http.HttpServletRequest;
46
import javax.servlet.http.HttpServletResponse;
47 6067 rnahf
48 6269 leinfelder
import org.apache.commons.fileupload.FileUploadException;
49 5299 jones
import org.apache.commons.io.IOUtils;
50 5211 jones
import org.apache.log4j.Logger;
51 6244 leinfelder
import org.dataone.client.auth.CertificateManager;
52 6269 leinfelder
import org.dataone.mimemultipart.MultipartRequest;
53
import org.dataone.mimemultipart.MultipartRequestResolver;
54 5299 jones
import org.dataone.service.exceptions.BaseException;
55 6269 leinfelder
import org.dataone.service.exceptions.InvalidRequest;
56
import org.dataone.service.exceptions.ServiceFailure;
57 6366 leinfelder
import org.dataone.service.types.v1.Session;
58
import org.dataone.service.types.v1.SystemMetadata;
59 6367 leinfelder
import org.dataone.service.util.TypeMarshaller;
60 5320 jones
import org.jibx.runtime.JiBXException;
61 5211 jones
62
import edu.ucsb.nceas.metacat.MetacatHandler;
63 5637 berkley
import edu.ucsb.nceas.metacat.properties.PropertyService;
64
import edu.ucsb.nceas.utilities.PropertyNotFoundException;
65 5211 jones
/**
66
 *
67 6268 leinfelder
 * Base class for handling D1 REST calls in Metacat
68 5211 jones
 *
69 6268 leinfelder
 * @author leinfelder
70 5211 jones
 */
71 6267 leinfelder
public class D1ResourceHandler {
72 5211 jones
73
    /**HTTP Verb GET*/
74
    public static final byte GET = 1;
75
    /**HTTP Verb POST*/
76
    public static final byte POST = 2;
77
    /**HTTP Verb PUT*/
78
    public static final byte PUT = 3;
79
    /**HTTP Verb DELETE*/
80
    public static final byte DELETE = 4;
81 5651 berkley
    /**HTTP Verb HEAD*/
82
    public static final byte HEAD = 5;
83 5211 jones
84
    /*
85
     * API Resources
86
     */
87 6271 leinfelder
    protected static final String RESOURCE_BASE_URL = "d1";
88
89 6247 leinfelder
    protected static final String RESOURCE_OBJECTS = "object";
90
    protected static final String RESOURCE_META = "meta";
91
    protected static final String RESOURCE_LOG = "log";
92
    protected static final String RESOURCE_CHECKSUM = "checksum";
93 6249 leinfelder
94
    protected static final String RESOURCE_IS_AUTHORIZED = "isAuthorized";
95
    protected static final String RESOURCE_ACCESS_RULES = "accessRules";
96 5211 jones
97
    /*
98
     * API Functions used as URL parameters
99
     */
100 6247 leinfelder
    protected static final String FUNCTION_NAME_INSERT = "insert";
101
    protected static final String FUNCTION_NAME_UPDATE = "update";
102 5414 berkley
103 6247 leinfelder
    protected ServletContext servletContext;
104
    protected Logger logMetacat;
105
    protected MetacatHandler handler;
106
    protected HttpServletRequest request;
107
    protected HttpServletResponse response;
108 5211 jones
109 6247 leinfelder
    protected Hashtable<String, String[]> params;
110 6269 leinfelder
    protected Map<String, List<String>> multipartparams;
111 6244 leinfelder
112
    // D1 certificate-based authentication
113 6247 leinfelder
    protected Session session;
114 5211 jones
115
    /**Initializes new instance by setting servlet context,request and response*/
116 6267 leinfelder
    public D1ResourceHandler(ServletContext servletContext,
117 5211 jones
            HttpServletRequest request, HttpServletResponse response) {
118
        this.servletContext = servletContext;
119
        this.request = request;
120
        this.response = response;
121
    }
122
123
    /**
124 6268 leinfelder
     * This function is called from REST API servlet and handles each request
125 5211 jones
     *
126
     * @param httpVerb (GET, POST, PUT or DELETE)
127
     */
128
    public void handle(byte httpVerb) {
129 6267 leinfelder
        logMetacat = Logger.getLogger(D1ResourceHandler.class);
130 5211 jones
        try {
131 6268 leinfelder
132 6244 leinfelder
            // load session from certificate in request
133
            session = CertificateManager.getInstance().getSession(request);
134 5211 jones
135 6268 leinfelder
            // initialize the parameters
136
            params = new Hashtable<String, String[]>();
137
            initParams();
138 5211 jones
139 6268 leinfelder
            // create the handler for interacting with Metacat
140
            Timer timer = new Timer();
141
            handler = new MetacatHandler(timer);
142 5211 jones
143
        } catch (Exception e) {
144 6268 leinfelder
        	// TODO: more D1 structure when failing here
145
        	response.setStatus(400);
146
            printError("Incorrect resource!", response);
147
            logMetacat.error(e.getClass() + ": " + e.getMessage(), e);
148 5211 jones
        }
149
    }
150 5374 berkley
151 6367 leinfelder
    protected SystemMetadata collectSystemMetadata() throws IOException, FileUploadException, ServiceFailure, InvalidRequest, JiBXException, InstantiationException, IllegalAccessException  {
152 6269 leinfelder
153
		// Read the incoming data from its Mime Multipart encoding
154
		logMetacat.debug("Disassembling MIME multipart form");
155
		InputStream sysmeta = null;
156
157
		// handle MMP inputs
158
		File tmpDir = getTempDirectory();
159
		logMetacat.debug("temp dir: " + tmpDir.getAbsolutePath());
160
		MultipartRequestResolver mrr =
161
			new MultipartRequestResolver(tmpDir.getAbsolutePath(), 1000000000, 0);
162
		MultipartRequest mr = null;
163
		try {
164
			mr = mrr.resolveMultipart(request);
165
		} catch (Exception e) {
166
			throw new ServiceFailure("1202",
167
					"Could not resolve multipart: " + e.getMessage());
168
		}
169
		logMetacat.debug("resolved multipart request");
170
		Map<String, File> files = mr.getMultipartFiles();
171
		if (files == null) {
172
			throw new ServiceFailure("1202",
173
					"register meta must have multipart file with name 'sysmeta'");
174
		}
175
		logMetacat.debug("got multipart files");
176
177
		if (files.keySet() == null) {
178
			logMetacat.error("No file keys in MMP request.");
179
			throw new ServiceFailure(
180
					"1202",
181
					"No file keys found in MMP.  "
182
							+ "register meta must have multipart file with name 'sysmeta'");
183
		}
184
185
		// for logging purposes, dump out the key-value pairs that
186
		// constitute the request
187
		// 3 types exist: request params, multipart params, and
188
		// multipart files
189
		Iterator it = files.keySet().iterator();
190
		logMetacat.debug("iterating through request parts: " + it);
191
		while (it.hasNext()) {
192
			String key = (String) it.next();
193
			logMetacat.debug("files key: " + key);
194
			logMetacat.debug("files value: " + files.get(key));
195
		}
196
197
		multipartparams = mr.getMultipartParameters();
198
		it = multipartparams.keySet().iterator();
199
		while (it.hasNext()) {
200
			String key = (String) it.next();
201
			logMetacat.debug("multipartparams key: " + key);
202
			logMetacat.debug("multipartparams value: " + multipartparams.get(key));
203
		}
204
205
		it = params.keySet().iterator();
206
		while (it.hasNext()) {
207
			String key = (String) it.next();
208
			logMetacat.debug("param key: " + key);
209
			logMetacat.debug("param value: " + params.get(key));
210
		}
211
		logMetacat.debug("done iterating the request...");
212
213
		File smFile = files.get("sysmeta");
214
		if (smFile == null) {
215
			throw new InvalidRequest("1102",
216
					"Missing the required file-part 'sysmeta' from the multipart request.");
217
		}
218
		logMetacat.debug("smFile: " + smFile.getAbsolutePath());
219
		sysmeta = new FileInputStream(smFile);
220
221
		logMetacat.debug("Commence creation...");
222 6367 leinfelder
		SystemMetadata systemMetadata = TypeMarshaller.unmarshalTypeFromStream(SystemMetadata.class, sysmeta);
223 6269 leinfelder
		return systemMetadata;
224
	}
225
226
    protected Map<String, File> collectMultipartFiles() throws ServiceFailure, InvalidRequest {
227
228
        // Read the incoming data from its Mime Multipart encoding
229
        logMetacat.debug("Disassembling MIME multipart form");
230
        InputStream object = null;
231
        InputStream sysmeta = null;
232
233
234
        // handle MMP inputs
235
        File tmpDir = getTempDirectory();
236 6272 leinfelder
        logMetacat.debug("temp dir: " + tmpDir.getAbsolutePath());
237 6269 leinfelder
        MultipartRequestResolver mrr =
238
        	new MultipartRequestResolver(tmpDir.getAbsolutePath(), 1000000000, 0);
239
        MultipartRequest mr = null;
240
		try {
241
			mr = mrr.resolveMultipart(request);
242
		} catch (Exception e) {
243
            throw new ServiceFailure("1202",
244
            		"Could not resolve multipart files: " + e.getMessage());
245
		}
246 6272 leinfelder
        logMetacat.debug("resolved multipart request");
247 6269 leinfelder
        Map<String, File> files = mr.getMultipartFiles();
248
        if (files == null) {
249
            throw new ServiceFailure("1202", "create/update must have multipart files with names 'object' and 'sysmeta'");
250
        }
251 6272 leinfelder
        logMetacat.debug("got multipart files");
252 6269 leinfelder
253
        if (files.keySet() == null) {
254 6272 leinfelder
            logMetacat.error("No file keys in MMP request.");
255 6269 leinfelder
            throw new ServiceFailure("1202", "No file keys found in MMP.  " +
256
                    "create/update must have multipart files with names 'object' and 'sysmeta'");
257
        }
258
259
		// for logging purposes, dump out the key-value pairs that constitute the request
260
		// 3 types exist: request params, multipart params, and multipart files
261
        Iterator it = files.keySet().iterator();
262 6272 leinfelder
        logMetacat.debug("iterating through files");
263 6269 leinfelder
        while (it.hasNext()) {
264
            String key = (String)it.next();
265 6272 leinfelder
            logMetacat.debug("files key: " + key);
266
            logMetacat.debug("files value: " + files.get(key));
267 6269 leinfelder
        }
268
269
        multipartparams = mr.getMultipartParameters();
270
        it = multipartparams.keySet().iterator();
271 6272 leinfelder
        logMetacat.debug("iterating through multipartparams");
272 6269 leinfelder
        while (it.hasNext()) {
273
            String key = (String)it.next();
274 6272 leinfelder
            logMetacat.debug("multipartparams key: " + key);
275
            logMetacat.debug("multipartparams value: " + multipartparams.get(key));
276 6269 leinfelder
        }
277
278
        it = params.keySet().iterator();
279 6272 leinfelder
        logMetacat.debug("iterating through params");
280 6269 leinfelder
        while (it.hasNext()) {
281
            String key = (String)it.next();
282 6272 leinfelder
            logMetacat.debug("param key: " + key);
283
            logMetacat.debug("param value: " + params.get(key));
284 6269 leinfelder
        }
285 6272 leinfelder
        logMetacat.debug("done iterating the request...");
286 6269 leinfelder
287
        File smFile = files.get("sysmeta");
288
		if (smFile == null) {
289
		    throw new InvalidRequest("1102", "Missing the required file-part 'sysmeta' from the multipart request.");
290
		}
291 6272 leinfelder
        logMetacat.debug("smFile: " + smFile.getAbsolutePath());
292 6269 leinfelder
        File objFile = files.get("object");
293
		if (objFile == null) {
294
		    throw new InvalidRequest("1102", "Missing the required file-part 'object' from the multipart request.");
295
		}
296 6272 leinfelder
        logMetacat.debug("objectfile: " + objFile.getAbsolutePath());
297 6269 leinfelder
298
        return files;
299
    }
300
301 6140 cjones
		/**
302 5374 berkley
     *  copies request parameters to a hashtable which is given as argument to native metacathandler functions
303
     */
304 6247 leinfelder
    protected void initParams() {
305 5211 jones
306 5374 berkley
        String name = null;
307
        String[] value = null;
308
        Enumeration paramlist = request.getParameterNames();
309
        while (paramlist.hasMoreElements()) {
310
            name = (String) paramlist.nextElement();
311
            value = request.getParameterValues(name);
312
            params.put(name, value);
313
        }
314
    }
315 6469 leinfelder
316 5211 jones
    /**
317 5808 berkley
     * locate the boundary marker for an MMP
318 5462 berkley
     * @param is
319
     * @return
320 5808 berkley
     * @throws IOException
321 5462 berkley
     */
322 5639 berkley
    protected static String[] findBoundaryString(InputStream is)
323 6268 leinfelder
        throws IOException {
324 5639 berkley
        String[] endResult = new String[2];
325 5637 berkley
        String boundary = "";
326 5639 berkley
        String searchString = "boundary=";
327 5637 berkley
        byte[] b = new byte[1024];
328 5639 berkley
        int numbytes = is.read(b, 0, 1024);
329 5826 berkley
330 5639 berkley
        while(numbytes != -1)
331 5636 berkley
        {
332 5639 berkley
            String s = new String(b, 0, numbytes);
333 5826 berkley
            int searchStringIndex = s.indexOf(searchString);
334 5639 berkley
335
            if(s.indexOf("\"", searchStringIndex + searchString.length() + 1) == -1)
336
            { //the end of the boundary is in the next byte array
337
                boundary = s.substring(searchStringIndex + searchString.length() + 1, s.length());
338
            }
339
            else if(!boundary.startsWith("--"))
340
            { //we can read the whole boundary from this byte array
341
                boundary = s.substring(searchStringIndex + searchString.length() + 1,
342
                    s.indexOf("\"", searchStringIndex + searchString.length() + 1));
343
                boundary = "--" + boundary;
344
                endResult[0] = boundary;
345
                endResult[1] = s.substring(s.indexOf("\"", searchStringIndex + searchString.length() + 1) + 1,
346
                        s.length());
347
                break;
348
            }
349
            else
350
            { //we're now reading the 2nd byte array to get the rest of the boundary
351
                searchString = "\"";
352
                searchStringIndex = s.indexOf(searchString);
353
                boundary += s.substring(0, searchStringIndex);
354
                boundary = "--" + boundary;
355
                endResult[0] = boundary;
356
                endResult[1] = s.substring(s.indexOf("\"", searchStringIndex + searchString.length() + 1) + 1,
357
                        s.length());
358
                break;
359
            }
360 5637 berkley
        }
361 5639 berkley
        return endResult;
362
    }
363
364 5808 berkley
    /**
365 5838 berkley
     * return the directory where temp files are stored
366
     * @return
367
     */
368 6248 leinfelder
    protected static File getTempDirectory()
369 5838 berkley
    {
370
        File tmpDir = null;
371 6267 leinfelder
        Logger logMetacat = Logger.getLogger(D1ResourceHandler.class);
372 6268 leinfelder
        try {
373 5639 berkley
            tmpDir = new File(PropertyService.getProperty("application.tempDir"));
374
        }
375 6268 leinfelder
        catch(PropertyNotFoundException pnfe) {
376 6267 leinfelder
            logMetacat.error("D1ResourceHandler.writeMMPPartstoFiles: " +
377 5639 berkley
                    "application.tmpDir not found.  Using /tmp instead.");
378
            tmpDir = new File("/tmp");
379
        }
380 5838 berkley
        return tmpDir;
381 5639 berkley
    }
382
383 5637 berkley
    /**
384 5211 jones
     * Prints xml response
385
     * @param message Message to be displayed
386
     * @param response Servlet response that xml message will be printed
387
     * */
388 6247 leinfelder
    protected void printError(String message, HttpServletResponse response) {
389 5211 jones
        try {
390 6267 leinfelder
            logMetacat.error("D1ResourceHandler: Printing error to servlet response: " + message);
391 5211 jones
            PrintWriter out = response.getWriter();
392
            response.setContentType("text/xml");
393
            out.println("<?xml version=\"1.0\"?>");
394
            out.println("<error>");
395
            out.println(message);
396
            out.println("</error>");
397
            out.close();
398
        } catch (IOException e) {
399
            e.printStackTrace();
400
        }
401
    }
402 5370 berkley
403 5692 berkley
    /**
404
     * serialize a D1 exception using jibx
405
     * @param e
406
     * @param out
407
     */
408 6247 leinfelder
    protected void serializeException(BaseException e, OutputStream out) {
409 5319 jones
        // TODO: Use content negotiation to determine which return format to use
410
        response.setContentType("text/xml");
411
        response.setStatus(e.getCode());
412 5512 berkley
413 6267 leinfelder
        logMetacat.error("D1ResourceHandler: Serializing exception with code " + e.getCode() + ": " + e.getMessage());
414 5512 berkley
        e.printStackTrace();
415
416 5319 jones
        try {
417
            IOUtils.write(e.serialize(BaseException.FMT_XML), out);
418
        } catch (IOException e1) {
419
            logMetacat.error("Error writing exception to stream. "
420
                    + e1.getMessage());
421
        }
422
    }
423
424 5211 jones
}