001    package org.hackystat.dailyprojectdata.server;
002    
003    import static org.hackystat.dailyprojectdata.server.ServerProperties.CONTEXT_ROOT_KEY;
004    import static org.hackystat.dailyprojectdata.server.ServerProperties.HOSTNAME_KEY;
005    import static org.hackystat.dailyprojectdata.server.ServerProperties.LOGGING_LEVEL_KEY;
006    import static org.hackystat.dailyprojectdata.server.ServerProperties.PORT_KEY;
007    import static org.hackystat.dailyprojectdata.server.ServerProperties.SENSORBASE_FULLHOST_KEY;
008    
009    import java.util.Map;
010    import java.util.logging.Logger;
011    
012    import javax.xml.bind.JAXBContext;
013    
014    import org.hackystat.dailyprojectdata.frontsidecache.FrontSideCache;
015    import org.hackystat.dailyprojectdata.resource.build.BuildResource;
016    import org.hackystat.dailyprojectdata.resource.cache.CacheResource;
017    import org.hackystat.dailyprojectdata.resource.codeissue.CodeIssueResource;
018    import org.hackystat.dailyprojectdata.resource.commit.CommitResource;
019    import org.hackystat.dailyprojectdata.resource.complexity.ComplexityResource;
020    import org.hackystat.dailyprojectdata.resource.coupling.CouplingResource;
021    import org.hackystat.dailyprojectdata.resource.coverage.CoverageResource;
022    import org.hackystat.dailyprojectdata.resource.devtime.DevTimeResource;
023    import org.hackystat.dailyprojectdata.resource.filemetric.FileMetricResource;
024    import org.hackystat.dailyprojectdata.resource.issue.IssueResource;
025    import org.hackystat.dailyprojectdata.resource.issuechange.IssueChangeResource;
026    import org.hackystat.dailyprojectdata.resource.ping.PingResource;
027    import org.hackystat.dailyprojectdata.resource.unittest.UnitTestResource;
028    import org.hackystat.sensorbase.client.SensorBaseClient;
029    import org.hackystat.utilities.logger.HackystatLogger;
030    import org.hackystat.utilities.logger.RestletLoggerUtil;
031    import org.restlet.Application;
032    import org.restlet.Component;
033    import org.restlet.Guard;
034    import org.restlet.Restlet;
035    import org.restlet.Router;
036    import org.restlet.data.Protocol;
037    
038    /**
039     * Sets up the HTTP Server process and dispatching to the associated resources. 
040     *
041     * @author Philip Johnson
042     */
043    public class Server extends Application { 
044    
045      /** Holds the Restlet Component associated with this Server. */
046      private Component component; 
047      
048      /** Holds the host name associated with this Server. */
049      private String hostName;
050      
051      /** Holds the HackystatLogger for this Service. */
052      private Logger logger; 
053      
054      /** Holds the ServerProperties instance for this Service. */
055      private ServerProperties properties;
056      
057      private FrontSideCache frontSideCache;
058    
059      /**
060       * Creates a new instance of a DailyProjectData HTTP server, listening on the supplied port.
061       * @return The Server instance created. 
062       * @throws Exception If problems occur starting up this server. 
063       */
064      public static Server newInstance() throws Exception {
065        return newInstance(new ServerProperties());
066      }
067      
068      /**
069       * Creates a new instance of a DailyProjectData HTTP server suitable for unit testing. 
070       * DPD properties are initialized from the User's dailyprojectdata.properties file, 
071       * then set to their "testing" versions.   
072       * @return The Server instance created. 
073       * @throws Exception If problems occur starting up this server. 
074       */
075      public static Server newTestInstance() throws Exception {
076        ServerProperties properties = new ServerProperties();
077        properties.setTestProperties();
078        return newInstance(properties);
079      }
080      
081      /**
082       * Creates a new instance of a DailyProjectData HTTP server, listening on the supplied port.
083       * @param properties The ServerProperties instance used to initialize this server.  
084       * @return The Server instance created. 
085       * @throws Exception If problems occur starting up this server. 
086       */
087      public static Server newInstance(ServerProperties properties) throws Exception {
088        Server server = new Server();
089        server.logger = HackystatLogger.getLogger("org.hackystat.dailyprojectdata", "dailyprojectdata");
090        server.properties = properties;
091        server.hostName = "http://" +
092                          server.properties.get(HOSTNAME_KEY) + 
093                          ":" + 
094                          server.properties.get(PORT_KEY) + 
095                          "/" +
096                          server.properties.get(CONTEXT_ROOT_KEY) +
097                          "/";
098        int port = Integer.valueOf(server.properties.get(PORT_KEY));
099        server.component = new Component();
100        server.component.getServers().add(Protocol.HTTP, port);
101        server.component.getDefaultHost()
102          .attach("/" + server.properties.get(CONTEXT_ROOT_KEY), server);
103        server.frontSideCache = new FrontSideCache(server);
104        
105        // Create and store the JAXBContext instances on the server context.
106        // They are supposed to be thread safe. 
107        Map<String, Object> attributes = server.getContext().getAttributes();
108        
109        JAXBContext devTimeJAXB = JAXBContext.newInstance(
110            org.hackystat.dailyprojectdata.resource.devtime.jaxb.ObjectFactory.class);
111        attributes.put("DevTimeJAXB", devTimeJAXB);
112        JAXBContext fileMetricJAXB = JAXBContext.newInstance(
113            org.hackystat.dailyprojectdata.resource.filemetric.jaxb.ObjectFactory.class);
114        attributes.put("FileMetricJAXB", fileMetricJAXB);
115    
116        
117        JAXBContext unitTestJAXB = JAXBContext.newInstance(
118            org.hackystat.dailyprojectdata.resource.unittest.jaxb.ObjectFactory.class);
119        attributes.put("UnitTestJAXB", unitTestJAXB);
120        
121        JAXBContext codeIssueJAXB = JAXBContext.newInstance(
122            org.hackystat.dailyprojectdata.resource.codeissue.jaxb.ObjectFactory.class);
123        attributes.put("CodeIssueJAXB", codeIssueJAXB);
124        JAXBContext coverageJAXB = JAXBContext.newInstance(
125            org.hackystat.dailyprojectdata.resource.coverage.jaxb.ObjectFactory.class);
126        attributes.put("CoverageJAXB", coverageJAXB);
127        JAXBContext buildJAXB = JAXBContext.newInstance(
128            org.hackystat.dailyprojectdata.resource.build.jaxb.ObjectFactory.class);
129        attributes.put("BuildJAXB", buildJAXB);
130        JAXBContext commitJAXB = JAXBContext.newInstance(
131            org.hackystat.dailyprojectdata.resource.commit.jaxb.ObjectFactory.class);
132        attributes.put("CommitJAXB", commitJAXB);
133        JAXBContext complexityJAXB = JAXBContext.newInstance(
134            org.hackystat.dailyprojectdata.resource.complexity.jaxb.ObjectFactory.class);
135        attributes.put("ComplexityJAXB", complexityJAXB);
136        JAXBContext couplingJAXB = JAXBContext.newInstance(
137            org.hackystat.dailyprojectdata.resource.coupling.jaxb.ObjectFactory.class);
138        attributes.put("CouplingJAXB", couplingJAXB);
139        JAXBContext issueJAXB = JAXBContext.newInstance(
140            org.hackystat.dailyprojectdata.resource.issue.jaxb.ObjectFactory.class);
141        attributes.put("IssueJAXB", issueJAXB);
142        JAXBContext issueChangeJAXB = JAXBContext.newInstance(
143            org.hackystat.dailyprojectdata.resource.issuechange.jaxb.ObjectFactory.class);
144        attributes.put("IssueChangeJAXB", issueChangeJAXB);
145        
146        // Provide a pointer to this server in the Context so that Resources can get at this server.
147        attributes.put("DailyProjectDataServer", server);
148        
149        // Disable restlet logging unless dont.disable.restlet.logging is "true".
150        RestletLoggerUtil.disableLogging();
151        
152        // Now let's open for business. 
153        server.logger.warning("Host: " + server.hostName);
154        HackystatLogger.setLoggingLevel(server.logger, server.properties.get(LOGGING_LEVEL_KEY));
155        server.logger.info(server.properties.echoProperties());
156        String sensorBaseHost = server.properties.get(SENSORBASE_FULLHOST_KEY);
157        boolean sensorBaseOK = SensorBaseClient.isHost(sensorBaseHost);
158        server.logger.warning("SensorBase " + sensorBaseHost + 
159            ((sensorBaseOK) ? " was contacted successfully." : 
160              " NOT AVAILABLE. This service will not run correctly."));
161        server.logger.warning("DailyProjectData (Version " + getVersion() + ") now running.");
162        server.component.start();
163    
164        return server;
165      }
166    
167      
168      /**
169       * Starts up the web service.  Control-c to exit. 
170       * @param args Ignored. 
171       * @throws Exception if problems occur.
172       */
173      public static void main(final String[] args) throws Exception {
174        Server.newInstance();
175      }
176    
177      /**
178       * Dispatch to the specific DailyProjectData resource based upon the URI.
179       * We will authenticate all requests.
180       * @return The router Restlet.
181       */
182      @Override
183      public Restlet createRoot() {
184        // First, create a Router that will have a Guard placed in front of it so that this Router's
185        // requests will require authentication.
186        Router authRouter = new Router(getContext());
187        authRouter.attach("/devtime/{user}/{project}/{timestamp}", DevTimeResource.class);
188        authRouter.attach("/coupling/{user}/{project}/{timestamp}/{type}", 
189            CouplingResource.class);
190        authRouter.attach("/coupling/{user}/{project}/{timestamp}/{type}?Tool={tool}", 
191            CouplingResource.class);
192        authRouter.attach("/complexity/{user}/{project}/{timestamp}/{type}", 
193            ComplexityResource.class);
194        authRouter.attach("/complexity/{user}/{project}/{timestamp}/{type}?Tool={tool}", 
195            ComplexityResource.class);
196        authRouter.attach("/filemetric/{user}/{project}/{timestamp}/{sizemetric}", 
197            FileMetricResource.class);
198        authRouter.attach("/filemetric/{user}/{project}/{timestamp}/{sizemetric}?Tool={tool}", 
199            FileMetricResource.class);
200        authRouter.attach("/unittest/{user}/{project}/{timestamp}", UnitTestResource.class);
201        authRouter.attach("/codeissue/{user}/{project}/{timestamp}", CodeIssueResource.class);
202        authRouter.attach("/codeissue/{user}/{project}/{timestamp}?Tool={Tool}&Type={Type}", 
203            CodeIssueResource.class);
204        authRouter.attach("/codeissue/{user}/{project}/{timestamp}?Tool={Tool}", 
205            CodeIssueResource.class);
206        authRouter.attach("/codeissue/{user}/{project}/{timestamp}?Type={Type}", 
207            CodeIssueResource.class);
208        authRouter.attach("/coverage/{user}/{project}/{timestamp}/{granularity}", 
209            CoverageResource.class);
210        authRouter.attach("/build/{user}/{project}/{timestamp}", BuildResource.class);
211        authRouter.attach("/build/{user}/{project}/{timestamp}?Type={Type}", BuildResource.class);
212        authRouter.attach("/commit/{user}/{project}/{timestamp}", CommitResource.class);
213        authRouter.attach("/issue/{user}/{project}/{timestamp}", IssueResource.class);
214        authRouter.attach("/issue/{user}/{project}/{timestamp}?Status={Status}", IssueResource.class);
215        authRouter.attach("/issuechange/{user}/{project}/{timestamp}", IssueChangeResource.class);
216        authRouter.attach("/cache/{user}/{project}", CacheResource.class);
217        authRouter.attach("/cache", CacheResource.class);
218    
219        // Here's the Guard that we will place in front of authRouter.
220        Guard guard = new Authenticator(getContext(), 
221            this.getServerProperties().get(SENSORBASE_FULLHOST_KEY));
222        guard.setNext(authRouter);
223        
224        // Now create our "top-level" router which will allow the Ping URI to proceed without
225        // authentication, but all other URI patterns will go to the guarded Router.
226        Router router = new Router(getContext());
227        router.attach("/ping", PingResource.class);
228        router.attach("/ping?user={user}&password={password}", PingResource.class);
229        router.attachDefault(guard);
230        return router;
231      }
232    
233    
234      /**
235       * Returns the version associated with this Package, if available from the jar file manifest.
236       * If not being run from a jar file, then returns "Development". 
237       * @return The version.
238       */
239      public static String getVersion() {
240        String version = 
241          Package.getPackage("org.hackystat.dailyprojectdata.server").getImplementationVersion();
242        return (version == null) ? "Development" : version; 
243      }
244      
245      /**
246       * Returns the host name associated with this server. 
247       * Example: "http://localhost:9877/dailyprojectdata"
248       * @return The host name. 
249       */
250      public String getHostName() {
251        return this.hostName;
252      }
253      
254      /**
255       * Returns the ServerProperties instance associated with this server. 
256       * @return The server properties.
257       */
258      public ServerProperties getServerProperties() {
259        return this.properties;
260      }
261      
262      /**
263       * Returns the logger for this service.
264       * @return The logger.
265       */
266      @Override
267      public Logger getLogger() {
268        return this.logger;
269      }
270      
271      /**
272       * Returns the front side cache for this server. 
273       * @return The FrontSideCache.
274       */
275      public FrontSideCache getFrontSideCache() {
276        return this.frontSideCache;
277      }
278    }
279