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