001    package org.hackystat.dailyprojectdata.resource.dailyprojectdata;
002    
003    import java.util.Date;
004    import java.util.Map;
005    
006    import org.hackystat.dailyprojectdata.server.Server;
007    import org.hackystat.sensorbase.client.SensorBaseClient;
008    import org.restlet.Context;
009    import org.restlet.data.CharacterSet;
010    import org.restlet.data.Language;
011    import org.restlet.data.MediaType;
012    import org.restlet.data.Request;
013    import org.restlet.data.Response;
014    import org.restlet.data.Status;
015    import org.restlet.resource.Representation;
016    import org.restlet.resource.Resource;
017    import org.restlet.resource.StringRepresentation;
018    import org.restlet.resource.Variant;
019    import static 
020    org.hackystat.dailyprojectdata.server.Authenticator.AUTHENTICATOR_SENSORBASECLIENTS_KEY;
021    
022    /**
023     * An abstract superclass for all DailyProjectData resources that supplies common 
024     * initialization processing. 
025     * This includes:
026     * <ul>
027     * <li> Extracting the authenticated user identifier (when authentication available)
028     * <li> Extracting the user email from the URI (when available)
029     * <li> Declares that the TEXT/XML representational variant is supported.
030     * </ul>
031     * 
032     * @author Philip Johnson
033     *
034     */
035    public abstract class DailyProjectDataResource extends Resource {
036      
037      /** To be retrieved from the URL as the 'email' template parameter, or null. */
038      protected String uriUser = null; 
039    
040      /** To be retrieved from the URL as the 'project' template parameter, or null. */
041      protected String project = null; 
042    
043      /** To be retrieved from the URL as the 'timestamp' template parameter, or null. */
044      protected String timestamp = null; 
045    
046      /** The authenticated user, retrieved from the ChallengeResponse, or null. */
047      protected String authUser = null;
048      
049      /** The server. */
050      protected Server server;
051      
052      /** The standard error message returned from invalid authentication. */
053      protected String badAuth = "User is not admin and authenticated user does not not match URI user";
054      
055      /** Records the time at which each HTTP request was initiated. */
056      protected long requestStartTime = new Date().getTime();
057      
058      protected String uriString; 
059      
060      /**
061       * Provides the following representational variants: TEXT_XML.
062       * @param context The context.
063       * @param request The request object.
064       * @param response The response object.
065       */
066      public DailyProjectDataResource(Context context, Request request, Response response) {
067        super(context, request, response);
068        if (request.getChallengeResponse() != null) {
069          this.authUser = request.getChallengeResponse().getIdentifier();
070        }
071        this.server = (Server)getContext().getAttributes().get("DailyProjectDataServer");
072        this.uriUser = (String) request.getAttributes().get("user");
073        this.project = (String) request.getAttributes().get("project");
074        this.timestamp = (String) request.getAttributes().get("timestamp");
075        this.uriString = this.getRequest().getResourceRef().toString();
076        getVariants().clear(); // copied from BookmarksResource.java, not sure why needed.
077        getVariants().add(new Variant(MediaType.TEXT_XML));
078      }
079    
080      /**
081       * The Restlet getRepresentation method which must be overridden by all concrete Resources.
082       * @param variant The variant requested.
083       * @return The Representation. 
084       */
085      @Override
086      public abstract Representation represent(Variant variant);
087      
088      /**
089       * Creates and returns a new Restlet StringRepresentation built from xmlData.
090       * The xmlData will be prefixed with a processing instruction indicating UTF-8 and version 1.0.
091       * @param xmlData The xml data as a string. 
092       * @return A StringRepresentation of that xmldata. 
093       */
094      public StringRepresentation getStringRepresentation(String xmlData) {
095        return new StringRepresentation(xmlData, MediaType.TEXT_XML, Language.ALL, CharacterSet.UTF_8);
096      }
097      
098      /**
099       * Returns a SensorBaseClient instance associated with the User in this request. 
100       * @return The SensorBaseClient instance. 
101       */
102      @SuppressWarnings("unchecked")
103      public SensorBaseClient getSensorBaseClient() {
104        Map<String, SensorBaseClient> userClientMap = 
105          (Map<String, SensorBaseClient>)this.server.getContext()
106          .getAttributes().get(AUTHENTICATOR_SENSORBASECLIENTS_KEY);
107        return userClientMap.get(this.authUser);
108      }
109      
110      /**
111       * Generates a log message indicating the type of request, the elapsed time required, 
112       * the user who requested the data, and the day.
113       * @param requestType The type of DPD request, such as "Commit", "FileMetric", etc.
114       */
115      protected void logRequest(String requestType) {
116        logRequest(requestType, "");
117      }
118      
119      /**
120       * Generates a log message indicating the type of request, the elapsed time required, 
121       * the user who requested the data, and the day.
122       * @param requestType The type of DPD request, such as "Commit", "FileMetric", etc.
123       * @param optionalParams Any additional parameters to the request.
124       */
125      protected void logRequest(String requestType, String... optionalParams) {
126        long elapsed = new Date().getTime() - requestStartTime;
127        String sp = " ";
128        StringBuffer msg = new StringBuffer(20);
129        msg.append(elapsed).append(" ms: ").append(requestType).append(sp).append(uriUser).append(sp);
130        msg.append(project).append(sp).append(timestamp);
131        for (String param : optionalParams) {
132          msg.append(sp).append(param);
133        }
134        server.getLogger().info(msg.toString());
135      }
136      
137      /**
138       * Called when an error resulting from an exception is caught during processing. 
139       * @param msg A description of the error.
140       * @param e A chained exception.
141       */
142      protected void setStatusError (String msg, Exception e) {
143        String responseMsg = String.format("%s:%n  Request: %s %s%n  Caused by: %s", 
144            msg,  
145            this.getRequest().getMethod().getName(),
146            this.getRequest().getResourceRef().toString(),
147            e.getMessage());
148        this.getLogger().info(responseMsg);
149        getResponse().setStatus(Status.CLIENT_ERROR_BAD_REQUEST, 
150            responseMsg.replace(System.getProperty("line.separator"), " "));
151      }
152      
153      /**
154       * Called when an error resulting from an exception is caught during processing. 
155       * @param msg A description of the error.
156       */
157      protected void setStatusError (String msg) {
158        String responseMsg = String.format("%s:%n  Request: %s %s%n", 
159            msg,  
160            this.getRequest().getMethod().getName(),
161            this.getRequest().getResourceRef().toString());
162        this.getLogger().info(responseMsg);
163        getResponse().setStatus(Status.CLIENT_ERROR_BAD_REQUEST, 
164            responseMsg.replace(System.getProperty("line.separator"), " "));
165      }
166    
167    }