Java tutorial
/******************************************************************************* * Copyright 2012 The Regents of the University of California * * 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.ohmage.request.mobility; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.log4j.Logger; import org.joda.time.DateTime; import org.joda.time.DateTimeZone; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import org.ohmage.annotator.Annotator.ErrorCode; import org.ohmage.cache.PreferenceCache; import org.ohmage.domain.ColumnKey; import org.ohmage.domain.DataStream; import org.ohmage.domain.DataStream.MetaData; import org.ohmage.domain.Location.LocationColumnKey; import org.ohmage.domain.MobilityPoint; import org.ohmage.domain.MobilityPoint.MobilityColumnKey; import org.ohmage.domain.MobilityPoint.SubType; import org.ohmage.exception.CacheMissException; import org.ohmage.exception.DomainException; import org.ohmage.exception.InvalidRequestException; import org.ohmage.exception.ServiceException; import org.ohmage.exception.ValidationException; import org.ohmage.request.InputKeys; import org.ohmage.request.Request; import org.ohmage.request.UserRequest.TokenLocation; import org.ohmage.request.observer.StreamReadRequest; import org.ohmage.service.MobilityServices; import org.ohmage.service.UserClassServices; import org.ohmage.service.UserServices; import org.ohmage.util.StringUtils; import org.ohmage.validator.MobilityValidators; import org.ohmage.validator.UserValidators; /** * Reads the Mobility information about a user during a single day.<br /> * <br /> * <table border="1"> * <tr> * <td>Parameter Name</td> * <td>Description</td> * <td>Required</td> * </tr> * <tr> * <td>{@value org.ohmage.request.InputKeys#CLIENT}</td> * <td>A string describing the client that is making this request.</td> * <td>true</td> * </tr> * <tr> * <td>{@value org.ohmage.request.InputKeys#DATE}</td> * <td>The date for which the data is desired.</td> * <td>true</td> * </tr> * <tr> * <td>{@value org.ohmage.request.InputKeys#USERNAME}</td> * <td>The username of the user for whom the data is desired. If omitted, * the requesting user is used.</td> * <td>false</td> * </tr> * <tr> * <td>{@value org.ohmage.request.InputKeys#MOBILITY_WITH_SENSOR_DATA} * </td> * <td>A boolean flag indicating whether or not to include sensor data with * each point. This includes things like the accelerometer data and the * WiFi scan results.</td> * <td>false</td> * </tr> * <tr> * <td>{@value org.ohmage.request.InputKeys#COLUMN_LIST} * </td> * <td>A list of the columns to return data. The order in this list will be * reflected in the resulting list. If omitted, the result will be all of * the columns available.</td> * <td>false</td> * </tr> * </table> * * @author John Jenkins */ public class MobilityReadRequest extends Request { private static final Logger LOGGER = Logger.getLogger(MobilityReadRequest.class); private static final Collection<ColumnKey> DEFAULT_COLUMNS; static { Collection<ColumnKey> columnKeys = new ArrayList<ColumnKey>(); columnKeys.add(MobilityColumnKey.ID); columnKeys.add(MobilityColumnKey.MODE); columnKeys.add(MobilityColumnKey.TIME); columnKeys.add(MobilityColumnKey.TIMEZONE); columnKeys.add(MobilityColumnKey.TIMESTAMP); columnKeys.add(LocationColumnKey.STATUS); columnKeys.add(LocationColumnKey.LATITUDE); columnKeys.add(LocationColumnKey.LONGITUDE); columnKeys.add(LocationColumnKey.PROVIDER); columnKeys.add(LocationColumnKey.ACCURACY); columnKeys.add(LocationColumnKey.TIME); columnKeys.add(LocationColumnKey.TIMEZONE); DEFAULT_COLUMNS = Collections.unmodifiableCollection(columnKeys); } private final String username; private final DateTime startDate; private final StreamReadRequest regularReadRequest; private final StreamReadRequest extendedReadRequest; private final Collection<ColumnKey> columns; private final List<MobilityPoint> points; /** * Creates a Mobility read request. * * @param httpRequest The HttpServletRequest with the parameters for this * request. * * @throws InvalidRequestException Thrown if the parameters cannot be * parsed. * * @throws IOException There was an error reading from the request. */ public MobilityReadRequest(HttpServletRequest httpRequest) throws IOException, InvalidRequestException { super(httpRequest, null); LOGGER.info("Creating a Mobility read request."); String tUsername = null; DateTime tStartDate = null; StreamReadRequest tRegularReadRequest = null; StreamReadRequest tExtendedReadRequest = null; Collection<ColumnKey> tColumns = null; if (!isFailed()) { try { String[] t; t = getParameterValues(InputKeys.DATE); if (t.length == 0) { throw new ValidationException(ErrorCode.SERVER_INVALID_DATE, "The date value is missing: " + InputKeys.DATE); } else if (t.length == 1) { tStartDate = MobilityValidators.validateDate(t[0]); if (tStartDate == null) { throw new ValidationException(ErrorCode.SERVER_INVALID_DATE, "The date value is missing: " + InputKeys.DATE); } else { tStartDate = new DateTime(tStartDate.getYear(), tStartDate.getMonthOfYear(), tStartDate.getDayOfMonth(), 0, 0, DateTimeZone.UTC); } } else { throw new ValidationException(ErrorCode.SERVER_INVALID_DATE, "Multiple date values were given: " + InputKeys.DATE); } tColumns = null; t = getParameterValues(InputKeys.MOBILITY_WITH_SENSOR_DATA); if (t.length > 1) { throw new ValidationException(ErrorCode.MOBILITY_INVALID_INCLUDE_SENSOR_DATA_VALUE, "Multiple \"include sensor data\" values to query were given: " + InputKeys.MOBILITY_WITH_SENSOR_DATA); } else if (t.length == 1) { if (MobilityValidators.validateIncludeSensorDataValue(t[0])) { tColumns = MobilityColumnKey.ALL_COLUMNS; } } t = getParameterValues(InputKeys.COLUMN_LIST); if (t.length > 1) { throw new ValidationException(ErrorCode.MOBILITY_INVALID_COLUMN_LIST, "Multiple column lists were given: " + InputKeys.COLUMN_LIST); } else if (t.length == 1) { if (!StringUtils.isEmptyOrWhitespaceOnly(t[0])) { if (tColumns == null) { tColumns = MobilityValidators.validateColumns(t[0], true); } else { throw new ValidationException(ErrorCode.MOBILITY_INVALID_COLUMN_LIST, "Both '" + InputKeys.MOBILITY_WITH_SENSOR_DATA + "' and '" + InputKeys.COLUMN_LIST + "' were present. Only one may be present."); } } } if (tColumns == null) { tColumns = DEFAULT_COLUMNS; } // Get the user. t = getParameterValues(InputKeys.USERNAME); if (t.length > 1) { throw new ValidationException(ErrorCode.USER_INVALID_USERNAME, "Multiple usernames to query were given: " + InputKeys.USERNAME); } else if (t.length == 1) { tUsername = UserValidators.validateUsername(t[0]); } // Always get all of the columns. try { tRegularReadRequest = new StreamReadRequest(httpRequest, getParameterMap(), false, TokenLocation.EITHER, false, tUsername, "edu.ucla.cens.Mobility", null, "regular", 2012050700, tStartDate.minusMinutes(10), tStartDate.plusDays(1), null, true, null, null); tExtendedReadRequest = new StreamReadRequest(httpRequest, getParameterMap(), false, TokenLocation.EITHER, false, tUsername, "edu.ucla.cens.Mobility", null, "extended", 2012050700, tStartDate.minusMinutes(10), tStartDate.plusDays(1), null, true, null, null); } catch (IllegalArgumentException e) { throw new ValidationException("There was an error creating the request.", e); } } catch (ValidationException e) { e.failRequest(this); e.logException(LOGGER); } } username = tUsername; startDate = tStartDate; regularReadRequest = tRegularReadRequest; extendedReadRequest = tExtendedReadRequest; columns = tColumns; points = new ArrayList<MobilityPoint>(); } /** * Services the request. */ @Override public void service() { // If any of the sub-requests have failed, then return. if (regularReadRequest.isFailed() || extendedReadRequest.isFailed()) { return; } LOGGER.info("Servicing the Mobility read request."); try { if ((username != null) && (!username.equals(regularReadRequest.getUser().getUsername()))) { try { LOGGER.info("Checking if the user is an admin."); UserServices.instance().verifyUserIsAdmin(regularReadRequest.getUser().getUsername()); } catch (ServiceException notAdmin) { LOGGER.info("The user is not an admin."); LOGGER.info("Checking if reading Mobility points about another user is even allowed."); boolean isPlausible; try { isPlausible = StringUtils.decodeBoolean(PreferenceCache.instance().lookup( PreferenceCache.KEY_PRIVILEGED_USER_IN_CLASS_CAN_VIEW_MOBILITY_FOR_EVERYONE_IN_CLASS)); } catch (CacheMissException e) { throw new ServiceException(e); } if (isPlausible) { LOGGER.info("Checking if the requester is allowed to read Mobility points about the user."); UserClassServices.instance().userIsPrivilegedInAnotherUserClass( regularReadRequest.getUser().getUsername(), username); } else { throw new ServiceException(ErrorCode.MOBILITY_INSUFFICIENT_PERMISSIONS, "A user is not allowed to query Mobility information about another user."); } } } // Service the read requests. regularReadRequest.service(); if (regularReadRequest.isFailed()) { return; } extendedReadRequest.service(); if (extendedReadRequest.isFailed()) { return; } LOGGER.info("Aggregating the resulting points."); Collection<DataStream> regularResults = regularReadRequest.getResults(); for (DataStream dataStream : regularResults) { MetaData metaData = dataStream.getMetaData(); if (metaData == null) { LOGGER.info("A Mobility point is missing meta-data."); continue; } DateTime timestamp = metaData.getTimestamp(); if (timestamp == null) { LOGGER.info("A Mobility point is missing a timestamp: " + metaData.getId()); continue; } try { points.add( new MobilityPoint(dataStream, SubType.MODE_ONLY, MobilityPoint.PrivacyState.PRIVATE)); } catch (DomainException e) { throw new ServiceException("One of the points was invalid.", e); } } Collection<DataStream> extendedResults = extendedReadRequest.getResults(); for (DataStream dataStream : extendedResults) { MetaData metaData = dataStream.getMetaData(); if (metaData == null) { LOGGER.info("A Mobility point is missing meta-data."); continue; } DateTime timestamp = metaData.getTimestamp(); if (timestamp == null) { LOGGER.info("A Mobility point is missing a timestamp: " + metaData.getId()); continue; } try { points.add( new MobilityPoint(dataStream, SubType.SENSOR_DATA, MobilityPoint.PrivacyState.PRIVATE)); } catch (DomainException e) { throw new ServiceException("One of the points was invalid.", e); } } LOGGER.info("Sorting the aggregated points."); Collections.sort(points); // Run them through the classifier. LOGGER.info("Classifying the points."); MobilityServices.instance().classifyData(regularReadRequest.getUser().getUsername(), points); } catch (ServiceException e) { e.failRequest(this); e.logException(LOGGER); } } /** * Responds to the request. */ @Override public void respond(HttpServletRequest httpRequest, HttpServletResponse httpResponse) { LOGGER.info("Responding to the Mobiltiy read request."); if (isFailed()) { super.respond(httpRequest, httpResponse, null); } else if (regularReadRequest.isFailed()) { regularReadRequest.respond(httpRequest, httpResponse); } else if (extendedReadRequest.isFailed()) { extendedReadRequest.respond(httpRequest, httpResponse); } JSONObject resultObject = new JSONObject(); try { JSONArray resultArray = new JSONArray(); long startDateMillis = startDate.getMillis(); for (MobilityPoint mobilityPoint : points) { if ((mobilityPoint.getTime() + mobilityPoint.getTimezone().getOffset(mobilityPoint.getTime())) >= startDateMillis) { resultArray.put(mobilityPoint.toJson(true, columns)); } } resultObject.put(JSON_KEY_DATA, resultArray); } catch (JSONException e) { LOGGER.error("Error creating the JSONObject.", e); setFailed(); } catch (DomainException e) { LOGGER.error("Error creating the JSONObject.", e); setFailed(); } super.respond(httpRequest, httpResponse, resultObject); } }