001    package org.hackystat.telemetry.service.server;
002    
003    import java.util.Map;
004    
005    import org.hackystat.dailyprojectdata.client.DailyProjectDataClient;
006    import org.hackystat.sensorbase.client.SensorBaseClient;
007    import org.hackystat.telemetry.analyzer.configuration.TelemetryDefinitionManagerFactory;
008    import org.hackystat.telemetry.service.prefetch.PrefetchManager;
009    import org.hackystat.telemetry.service.resource.cache.CacheResource;
010    import org.hackystat.telemetry.service.resource.chart.ChartDataResource;
011    import org.hackystat.telemetry.service.resource.chart.ChartDefinitionResource;
012    import org.hackystat.telemetry.service.resource.chart.ChartsResource;
013    import org.hackystat.telemetry.service.resource.ping.PingResource;
014    import org.hackystat.utilities.logger.HackystatLogger;
015    import org.hackystat.utilities.logger.RestletLoggerUtil;
016    import org.restlet.Application;
017    import org.restlet.Component;
018    import org.restlet.Guard;
019    import org.restlet.Restlet;
020    import org.restlet.Router;
021    import org.restlet.data.Protocol;
022    
023    import static org.hackystat.telemetry.service.server.ServerProperties.CONTEXT_ROOT_KEY;
024    import static org.hackystat.telemetry.service.server.ServerProperties.DAILYPROJECTDATA_FULLHOST_KEY;
025    import static org.hackystat.telemetry.service.server.ServerProperties.HOSTNAME_KEY;
026    import static org.hackystat.telemetry.service.server.ServerProperties.LOGGING_LEVEL_KEY;
027    import static org.hackystat.telemetry.service.server.ServerProperties.PORT_KEY;
028    import static org.hackystat.telemetry.service.server.ServerProperties.SENSORBASE_FULLHOST_KEY;
029    
030    import java.util.logging.Logger;
031    
032    import javax.xml.bind.JAXBContext;
033    
034    /**
035     * Sets up the HTTP Server process and dispatching to the associated resources. 
036     * @author Philip Johnson
037     */
038    public class Server extends Application { 
039    
040      /** Holds the Restlet Component associated with this Server. */
041      private Component component; 
042      
043      /** Holds the host name associated with this Server. */
044      private String hostName;
045      
046      /** Holds the HackystatLogger for this Service. */
047      private Logger logger; 
048      
049      /** Holds the ServerProperties instance for this Service. */
050      private ServerProperties properties;
051      
052      /**
053       * Creates a new instance of a Telemetry HTTP server using the telemetry.properties configuration.
054       * @return The Server instance created. 
055       * @throws Exception If problems occur starting up this server. 
056       */
057      public static Server newInstance() throws Exception {
058        return newInstance(new ServerProperties());
059      }
060      
061      /**
062       * Creates a new instance of a Telemetry HTTP server suitable for unit testing. 
063       * Telemetry properties are initialized from the User's telemetry.properties file, 
064       * then set to their "testing" versions.   
065       * @return The Server instance created. 
066       * @throws Exception If problems occur starting up this server. 
067       */
068      public static Server newTestInstance() throws Exception {
069        ServerProperties properties = new ServerProperties();
070        properties.setTestProperties();
071        return newInstance(properties);
072      }
073      
074      /**
075       * Creates a new instance of a Telemetry HTTP server, listening on the supplied port.  
076       * @param properties The ServerProperties instance. 
077       * @return The Server instance created. 
078       * @throws Exception If problems occur starting up this server. 
079       */
080      public static Server newInstance(ServerProperties properties) throws Exception {
081        Server server = new Server();
082        server.logger = HackystatLogger.getLogger("org.hackystat.telemetry.server", "telemetry");
083        server.properties = properties;
084        server.hostName = "http://" +
085                          server.properties.get(HOSTNAME_KEY) + 
086                          ":" + 
087                          server.properties.get(PORT_KEY) + 
088                          "/" +
089                          server.properties.get(CONTEXT_ROOT_KEY) +
090                          "/";
091        int port = Integer.valueOf(server.properties.get(PORT_KEY));
092        server.component = new Component();
093        server.component.getServers().add(Protocol.HTTP, port);
094        server.component.getDefaultHost()
095          .attach("/" + server.properties.get(CONTEXT_ROOT_KEY), server);
096     
097        
098        // Create and store the JAXBContext instances on the server context.
099        // They are supposed to be thread safe. 
100        Map<String, Object> attributes = server.getContext().getAttributes();
101        JAXBContext chartJAXB = JAXBContext.newInstance(
102            org.hackystat.telemetry.service.resource.chart.jaxb.ObjectFactory.class);
103        attributes.put("ChartJAXB", chartJAXB);
104        
105        // Provide a pointer to this server in the Context so that Resources can get at this server.
106        attributes.put("TelemetryServer", server);
107        
108        // Disable restlet logging unless dont.disable.restlet.logging is "true".
109        RestletLoggerUtil.disableLogging();
110        
111        // Now let's open for business. 
112        server.logger.warning("Host: " + server.hostName);
113        HackystatLogger.setLoggingLevel(server.logger, server.properties.get(LOGGING_LEVEL_KEY));
114        server.logger.info(server.properties.echoProperties());
115        String sensorBaseHost = server.properties.get(SENSORBASE_FULLHOST_KEY);
116    
117        // Read in all telemetry definitions and create the singleton.
118        String defDir = server.properties.get(ServerProperties.DEF_DIR_KEY);
119        TelemetryDefinitionManagerFactory.buildGlobalPersistentInstance(defDir);
120        
121        String dailyProjectDataHost = server.properties.get(DAILYPROJECTDATA_FULLHOST_KEY);
122        boolean sensorBaseOK = SensorBaseClient.isHost(sensorBaseHost);
123        boolean dailyProjectDataOK = DailyProjectDataClient.isHost(dailyProjectDataHost);
124        server.logger.warning("Service SensorBase " + sensorBaseHost + 
125            ((sensorBaseOK) ? " was contacted successfully." : 
126              " NOT AVAILABLE. Therefore, the Telemetry service will not run correctly."));
127        server.logger.warning("Service DailyProjectData " + dailyProjectDataHost + 
128            ((dailyProjectDataOK) ? " was contacted successfully." : 
129              " NOT AVAILABLE. Therefore, the Telemetry service will not run correctly."));
130        server.logger.warning("Telemetry (Version " + getVersion() + ") now running.");
131        server.component.start();
132        // Now start up the Prefetch service.
133        new PrefetchManager(server);
134        return server;
135      }
136      
137      /**
138       * Starts up the web service.  Control-c to exit. 
139       * @param args Ignored. 
140       * @throws Exception if problems occur.
141       */
142      public static void main(final String[] args) throws Exception {
143        Server.newInstance();
144      }
145    
146      /**
147       * Dispatch to the specific Telemetry resource based upon the URI.
148       * We will authenticate all requests.
149       * @return The router Restlet.
150       */
151      @Override
152      public Restlet createRoot() {
153        // First, create a Router that will have a Guard placed in front of it so that this Router's
154        // requests will require authentication.
155        Router authRouter = new Router(getContext());
156        authRouter.attach("/charts", ChartsResource.class);
157        authRouter.attach("/chart/{chart}", ChartDefinitionResource.class);
158        authRouter.attach("/chart/{chart}/{email}/{project}/{granularity}/{start}/{end}",
159            ChartDataResource.class);
160        authRouter.attach("/chart/{chart}/{email}/{project}/{granularity}/{start}/{end}?params={params}"
161            , ChartDataResource.class);
162        authRouter.attach("/cache/", CacheResource.class);
163        authRouter.attach("/cache/{email}/{project}", CacheResource.class);
164        // Here's the Guard that we will place in front of authRouter.
165        Guard guard = new Authenticator(getContext(), 
166            this.getServerProperties().get(SENSORBASE_FULLHOST_KEY),
167            this.getServerProperties().get(DAILYPROJECTDATA_FULLHOST_KEY));
168        guard.setNext(authRouter);
169        
170        // Now create our "top-level" router which will allow the Ping URI to proceed without
171        // authentication, but all other URI patterns will go to the guarded Router. 
172        Router router = new Router(getContext());
173        router.attach("/ping", PingResource.class);
174        router.attach("/ping?user={user}&password={password}", PingResource.class);    
175        router.attachDefault(guard);
176        return router;
177      }
178    
179    
180      /**
181       * Returns the version associated with this Package, if available from the jar file manifest.
182       * If not being run from a jar file, then returns "Development". 
183       * @return The version.
184       */
185      public static String getVersion() {
186        String version = 
187          Package.getPackage("org.hackystat.telemetry.service.server").getImplementationVersion();
188        return (version == null) ? "Development" : version; 
189      }
190      
191      /**
192       * Returns the host name associated with this server. 
193       * Example: "http://localhost:9878/telemetry"
194       * @return The host name. 
195       */
196      public String getHostName() {
197        return this.hostName;
198      }
199      
200      /**
201       * Returns the ServerProperties instance associated with this server. 
202       * @return The server properties.
203       */
204      public ServerProperties getServerProperties() {
205        return this.properties;
206      }
207      
208      /**
209       * Returns the logger for this service.
210       * @return The logger.
211       */
212      @Override
213      public Logger getLogger() {
214        return this.logger;
215      }
216    }
217