001    package org.hackystat.telemetry.service.resource.telemetry;
002    
003    import java.util.Date;
004    import java.util.List;
005    import java.util.Map;
006    
007    import org.hackystat.dailyprojectdata.client.DailyProjectDataClient;
008    import org.hackystat.sensorbase.client.SensorBaseClient;
009    import org.hackystat.telemetry.analyzer.configuration.TelemetryDefinitionManager;
010    import org.hackystat.telemetry.analyzer.configuration.TelemetryDefinitionManagerFactory;
011    import org.hackystat.telemetry.analyzer.configuration.jaxb.TelemetryDefinition;
012    import org.hackystat.telemetry.service.server.Server;
013    import org.hackystat.telemetry.service.server.ServerProperties;
014    import org.restlet.Context;
015    import org.restlet.data.CharacterSet;
016    import org.restlet.data.Language;
017    import org.restlet.data.MediaType;
018    import org.restlet.data.Request;
019    import org.restlet.data.Response;
020    import org.restlet.data.Status;
021    import org.restlet.resource.Representation;
022    import org.restlet.resource.Resource;
023    import org.restlet.resource.StringRepresentation;
024    import org.restlet.resource.Variant;
025    
026    import static org.hackystat.telemetry.service.server.Authenticator.AUTHENTICATOR_DPD_CLIENTS_KEY;
027    import static 
028    org.hackystat.telemetry.service.server.Authenticator.AUTHENTICATOR_SENSORBASE_CLIENTS_KEY;
029    import static org.hackystat.telemetry.service.server.ServerProperties.DAILYPROJECTDATA_FULLHOST_KEY;
030    import static org.hackystat.telemetry.service.server.ServerProperties.SENSORBASE_FULLHOST_KEY;
031    
032    /**
033     * An abstract superclass for all Telemetry resources that supplies common 
034     * initialization processing. 
035     * This includes:
036     * <ul>
037     * <li> Extracting the authenticated user identifier (when authentication available)
038     * <li> Extracting the URI elements and parameters. 
039     * <li> Declares that the TEXT/XML representational variant is supported.
040     * </ul>
041     * 
042     * @author Philip Johnson
043     *
044     */
045    public abstract class TelemetryResource extends Resource {
046      
047      /** To be retrieved from the URL as the 'email' template parameter, or null. */
048      protected String uriUser = null; 
049    
050      /** To be retrieved from the URL as the 'project' template parameter, or null. */
051      protected String projectName = null; 
052    
053      /** To be retrieved from the URL as the 'chart' template parameter, or null. */
054      protected String chart = null; 
055    
056      /** To be retrieved from the URL as the 'granularity' template parameter, or null. */
057      protected String granularity = null; 
058    
059      /** To be retrieved from the URL as the 'start' template parameter, or null. */
060      protected String start = null; 
061    
062      /** To be retrieved from the URL as the 'end' template parameter, or null. */
063      protected String end = null; 
064    
065      /** To be retrieved from the URL as the 'params' template parameter, or null. */
066      protected String params = null; 
067      
068      /** The authenticated user, retrieved from the ChallengeResponse, or null. */
069      protected String authUser = null;
070      
071      /** This server (telemetry). */
072      protected Server telemetryServer;
073      
074      /** The sensorbase host (for authentication). */
075      protected String sensorBaseHost;
076      
077      /** The dailyprojectdata host (for analysis). */
078      protected String dpdHost;
079      
080      /** The standard error message returned from invalid authentication. */
081      protected String badAuth = "User is not admin and authenticated user does not not match URI user";
082      
083      /** Records the time at which each HTTP request was initiated. */
084      protected long requestStartTime = new Date().getTime();
085     
086      /**
087       * Provides the following representational variants: TEXT_XML.
088       * @param context The context.
089       * @param request The request object.
090       * @param response The response object.
091       */
092      public TelemetryResource(Context context, Request request, Response response) {
093        super(context, request, response);
094        if (request.getChallengeResponse() != null) {
095          this.authUser = request.getChallengeResponse().getIdentifier();
096        }
097        this.telemetryServer = (Server)getContext().getAttributes().get("TelemetryServer");
098        ServerProperties properties = this.telemetryServer.getServerProperties();
099        this.dpdHost = properties.get(DAILYPROJECTDATA_FULLHOST_KEY);
100        this.sensorBaseHost = properties.get(SENSORBASE_FULLHOST_KEY);
101        this.chart = (String) request.getAttributes().get("chart");
102        this.uriUser = (String) request.getAttributes().get("email");
103        this.projectName = (String) request.getAttributes().get("project");
104        this.granularity = (String) request.getAttributes().get("granularity");
105        this.start = (String) request.getAttributes().get("start");
106        this.end = (String) request.getAttributes().get("end");
107        this.params = (String) request.getAttributes().get("params");
108        getVariants().clear(); // copied from BookmarksResource.java, not sure why needed.
109        getVariants().add(new Variant(MediaType.TEXT_XML));
110      }
111    
112      /**
113       * The Restlet getRepresentation method which must be overridden by all concrete Resources.
114       * @param variant The variant requested.
115       * @return The Representation. 
116       */
117      @Override
118      public abstract Representation represent(Variant variant);
119      
120      /**
121       * Creates and returns a new Restlet StringRepresentation built from xmlData.
122       * The xmlData will be prefixed with a processing instruction indicating UTF-8 and version 1.0.
123       * @param xmlData The xml data as a string. 
124       * @return A StringRepresentation of that xmldata. 
125       */
126      public StringRepresentation getStringRepresentation(String xmlData) {
127        return new StringRepresentation(xmlData, MediaType.TEXT_XML, Language.ALL, CharacterSet.UTF_8);
128      }
129      
130      /**
131       * Returns a DailyProjectDataClient instance associated with the User in this request. 
132       * @return The DailyProjectDataClient instance. 
133       */
134      @SuppressWarnings("unchecked")
135      public DailyProjectDataClient getDailyProjectDataClient() {
136        Map<String, DailyProjectDataClient> userClientMap = 
137          (Map<String, DailyProjectDataClient>)this.telemetryServer.getContext().getAttributes()
138          .get(AUTHENTICATOR_DPD_CLIENTS_KEY);
139        return userClientMap.get(this.authUser);
140      }
141      
142      /**
143       * Returns a SensorBaseClient instance associated with the User in this request. 
144       * @return The SensorBaseClient instance. 
145       */
146      @SuppressWarnings("unchecked")
147      public SensorBaseClient getSensorBaseClient() {
148        Map<String, SensorBaseClient> userClientMap = 
149          (Map<String, SensorBaseClient>)this.telemetryServer.getContext().getAttributes()
150          .get(AUTHENTICATOR_SENSORBASE_CLIENTS_KEY);
151        return userClientMap.get(this.authUser);
152      }
153      
154      /**
155       * Returns a list of TelemetryDefinition instances corresponding to all definitions in 
156       * telemetry.definitions.xml. 
157       * @return A List of TelemetryDefinition instances. 
158       */
159      public List<TelemetryDefinition> getTelemetryDefinitions() {
160        TelemetryDefinitionManager manager =  
161          TelemetryDefinitionManagerFactory.getGlobalPersistentInstance();
162        return manager.getDefinitions();
163      }
164      
165      /**
166       * Generates a log message indicating the type of request, the elapsed time required, 
167       * the user who requested the data, and the day.
168       */
169      protected void logRequest() {
170        long elapsed = new Date().getTime() - requestStartTime;
171        String msg = elapsed + " ms: " + this.chart + " " + uriUser + " " + this.projectName;
172        telemetryServer.getLogger().info(msg);
173      }
174      
175      /**
176       * Generates a log message indicating the command. 
177       * @param command The command (typically cache).
178       */
179      protected void logRequest(String command) {
180        long elapsed = new Date().getTime() - requestStartTime;
181        String msg = elapsed + " ms: " + command;
182        telemetryServer.getLogger().info(msg);
183      }
184      
185      /**
186       * Helper function that removes any newline characters from the supplied string and 
187       * replaces them with a blank line. 
188       * @param msg The msg whose newlines are to be removed. 
189       * @return The string without newlines. 
190       */
191      private String removeNewLines(String msg) {
192        return msg.replace(System.getProperty("line.separator"), " ");
193      }
194      
195      /**
196       * Called when an error resulting from an exception is caught during processing. 
197       * @param msg A description of the error.
198       * @param e A chained exception.
199       */
200      protected void setStatusError (String msg, Exception e) {
201        String responseMsg = String.format("%s:%n  Request: %s %s%n  Caused by: %s", 
202            msg,  
203            this.getRequest().getMethod().getName(),
204            this.getRequest().getResourceRef().toString(),
205            e.getMessage());
206        this.getLogger().info(responseMsg);
207        getResponse().setStatus(Status.CLIENT_ERROR_BAD_REQUEST, removeNewLines(responseMsg));
208      }
209      
210      /**
211       * Called when an error occurs during processing. 
212       * @param msg A description of the error.
213       */
214      protected void setStatusError (String msg) {
215        String responseMsg = String.format("%s:%n  Request: %s %s", 
216            msg,  
217            this.getRequest().getMethod().getName(),
218            this.getRequest().getResourceRef().toString());
219        this.getLogger().info(responseMsg);
220        getResponse().setStatus(Status.CLIENT_ERROR_BAD_REQUEST, removeNewLines(responseMsg));
221      }
222      
223      /**
224       * Called when an internal error occurs during processing. 
225       * @param msg A description of the error.
226       */
227      protected void setStatusInternalError (String msg) {
228        String responseMsg = String.format("%s:%n  Request: %s %s", 
229            msg,  
230            this.getRequest().getMethod().getName(),
231            this.getRequest().getResourceRef().toString());
232        this.getLogger().info(responseMsg);
233        getResponse().setStatus(Status.SERVER_ERROR_INTERNAL, removeNewLines(responseMsg));
234      }
235      
236      
237    
238    }