001    package org.hackystat.dailyprojectdata.client;
002    
003    import java.io.StringReader;
004    import java.util.Date;
005    import java.util.logging.Logger;
006    
007    import javax.xml.bind.JAXBContext;
008    import javax.xml.bind.JAXBException;
009    import javax.xml.bind.Unmarshaller;
010    import javax.xml.datatype.XMLGregorianCalendar;
011    
012    import org.hackystat.dailyprojectdata.resource.build.jaxb.BuildDailyProjectData;
013    import org.hackystat.dailyprojectdata.resource.codeissue.jaxb.CodeIssueDailyProjectData;
014    import org.hackystat.dailyprojectdata.resource.commit.jaxb.CommitDailyProjectData;
015    import org.hackystat.dailyprojectdata.resource.complexity.jaxb.ComplexityDailyProjectData;
016    import org.hackystat.dailyprojectdata.resource.coupling.jaxb.CouplingDailyProjectData;
017    import org.hackystat.dailyprojectdata.resource.coverage.jaxb.CoverageDailyProjectData;
018    import org.hackystat.dailyprojectdata.resource.devtime.jaxb.DevTimeDailyProjectData;
019    import org.hackystat.dailyprojectdata.resource.filemetric.jaxb.FileMetricDailyProjectData;
020    import org.hackystat.dailyprojectdata.resource.issue.jaxb.IssueDailyProjectData;
021    import org.hackystat.dailyprojectdata.resource.issuechange.jaxb.IssueChangeDailyProjectData;
022    import org.hackystat.dailyprojectdata.resource.unittest.jaxb.UnitTestDailyProjectData;
023    import org.hackystat.utilities.logger.HackystatLogger;
024    import org.hackystat.utilities.tstamp.Tstamp;
025    import org.hackystat.utilities.uricache.UriCache;
026    import org.restlet.Client;
027    import org.restlet.data.ChallengeResponse;
028    import org.restlet.data.ChallengeScheme;
029    import org.restlet.data.MediaType;
030    import org.restlet.data.Method;
031    import org.restlet.data.Preference;
032    import org.restlet.data.Protocol;
033    import org.restlet.data.Reference;
034    import org.restlet.data.Request;
035    import org.restlet.data.Response;
036    import org.restlet.data.Status;
037    import org.restlet.resource.Representation;
038    
039    /**
040     * Provides a client to support access to the DailyProjectData service.
041     * 
042     * @author Philip Johnson
043     */
044    public class DailyProjectDataClient {
045    
046      /** Holds the userEmail to be associated with this client. */
047      private String userEmail;
048      /** Holds the password to be associated with this client. */
049      private String password;
050      /**
051       * The DailyProjectData host, such as "http://localhost:9877/dailyprojectdata".
052       */
053      private String dailyProjectDataHost;
054      /** The Restlet Client instance used to communicate with the server. */
055      private Client client;
056      /** DevTime JAXBContext. */
057      private JAXBContext devTimeJAXB;
058      /** FileMetric JAXBContext. */
059      private JAXBContext fileMetricJAXB;
060      /** UnitTest JAXBContext. */
061      private JAXBContext unitTestJAXB;
062      /** CodeIssue JAXBContext. */
063      private JAXBContext codeIssueJAXB;
064      /** CodeIssue JAXBContext. */
065      private JAXBContext coverageJAXB;
066      /** Commit JAXBContext. */
067      private JAXBContext commitJAXB;
068      /** Build JAXB Context. */
069      private JAXBContext buildJAXB;
070      /** Complexity JAXB Context. */
071      private JAXBContext complexityJAXB;
072      /** Coupling JAXB Context. */
073      private JAXBContext couplingJAXB;
074      /** Issue JAXB Context. */
075      private JAXBContext issueJAXB;
076      /** IssueChange JAXB Context. */
077      private JAXBContext issuechangeJAXB;
078      /** The http authentication approach. */
079      private ChallengeScheme scheme = ChallengeScheme.HTTP_BASIC;
080      /** The preferred representation type. */
081      private Preference<MediaType> xmlMedia = new Preference<MediaType>(MediaType.TEXT_XML);
082      /** To facilitate debugging of problems using this system. */
083      private boolean isTraceEnabled = false;
084      /** For logging. */
085      private Logger logger;
086      /** The System property key used to retrieve the default timeout value in milliseconds. */
087      public static final String DAILYPROJECTDATACLIENT_TIMEOUT_KEY = "dailyprojectdataclient.timeout";
088    
089    
090      /** An associated UriCache to improve responsiveness. */
091      private UriCache uriCache;
092    
093      /** Indicates whether or not cache is enabled. */
094      private boolean isCacheEnabled = false;
095    
096      /**
097       * Initializes a new DailyProjectDataClient, given the host, userEmail, and password. Note that
098       * the userEmail and password refer to the underlying SensorBase client associated with this
099       * DailyProjectData service. This service does not keep its own independent set of userEmails and
100       * passwords. Authentication is not actually performed in this constructor. Use the authenticate()
101       * method to explicitly check the authentication credentials.
102       * 
103       * @param host The host, such as 'http://localhost:9877/dailyprojectdata'.
104       * @param email The user's email used for authentication.
105       * @param password The password used for authentication.
106       */
107      public DailyProjectDataClient(String host, String email, String password) {
108        this.logger = HackystatLogger.getLogger(
109            "org.hackystat.dailyprojectdata.client.DailyProjectDataClient", "dailyprojectdata", false);
110        this.logger.info("Instantiating client for: " + host + " " + email);
111        validateArg(host);
112        validateArg(email);
113        validateArg(password);
114        this.userEmail = email;
115        this.password = password;
116        this.dailyProjectDataHost = host;
117        if (!this.dailyProjectDataHost.endsWith("/")) {
118          this.dailyProjectDataHost = this.dailyProjectDataHost + "/";
119        }
120        if (this.isTraceEnabled) {
121          System.out.println("DailyProjectDataClient Tracing: INITIALIZE " + "host='" + host
122              + "', email='" + email + "', password='" + password + "'");
123        }
124        this.client = new Client(Protocol.HTTP);
125        setTimeoutFromSystemProperty();
126        try {
127          this.devTimeJAXB = JAXBContext
128              .newInstance(org.hackystat.dailyprojectdata.resource.devtime.jaxb.ObjectFactory.class);
129          this.unitTestJAXB = JAXBContext
130              .newInstance(org.hackystat.dailyprojectdata.resource.unittest.jaxb.ObjectFactory.class);
131          this.fileMetricJAXB = JAXBContext
132              .newInstance(org.hackystat.dailyprojectdata.resource.filemetric.jaxb.ObjectFactory.class);
133          this.codeIssueJAXB = JAXBContext
134              .newInstance(org.hackystat.dailyprojectdata.resource.codeissue.jaxb.ObjectFactory.class);
135          this.coverageJAXB = JAXBContext
136              .newInstance(org.hackystat.dailyprojectdata.resource.coverage.jaxb.ObjectFactory.class);
137          this.buildJAXB = JAXBContext
138              .newInstance(org.hackystat.dailyprojectdata.resource.build.jaxb.ObjectFactory.class);
139          this.commitJAXB = JAXBContext
140              .newInstance(org.hackystat.dailyprojectdata.resource.commit.jaxb.ObjectFactory.class);
141          this.complexityJAXB = JAXBContext
142              .newInstance(org.hackystat.dailyprojectdata.resource.complexity.jaxb.ObjectFactory.class);
143          this.couplingJAXB = JAXBContext
144              .newInstance(org.hackystat.dailyprojectdata.resource.coupling.jaxb.ObjectFactory.class);
145          this.issueJAXB = JAXBContext
146          .newInstance(org.hackystat.dailyprojectdata.resource.issue.jaxb.ObjectFactory.class);
147          this.issuechangeJAXB = JAXBContext
148          .newInstance(org.hackystat.dailyprojectdata.resource.issuechange.jaxb.ObjectFactory.class);
149        }
150        catch (Exception e) {
151          throw new RuntimeException("Couldn't create JAXB context instances.", e);
152        }
153      }
154    
155      /**
156       * Throws an unchecked illegal argument exception if the arg is null or empty.
157       * 
158       * @param arg The String that must be non-null and non-empty.
159       */
160      private void validateArg(String arg) {
161        if ((arg == null) || ("".equals(arg))) {
162          throw new IllegalArgumentException(arg + " cannot be null or the empty string.");
163        }
164      }
165    
166      /**
167       * Sets the timeout value for this client.
168       * 
169       * @param milliseconds The number of milliseconds to wait before timing out.
170       */
171      public final synchronized void setTimeout(int milliseconds) {
172        client.getContext().getParameters().removeAll("connectTimeout");
173        client.getContext().getParameters().add("connectTimeout", String.valueOf(milliseconds));
174        // For the Apache Commons client.
175        client.getContext().getParameters().removeAll("readTimeout");
176        client.getContext().getParameters().add("readTimeout", String.valueOf(milliseconds));
177        client.getContext().getParameters().removeAll("connectionManagerTimeout");
178        client.getContext().getParameters().add("connectionManagerTimeout",
179            String.valueOf(milliseconds));
180      }
181    
182      /**
183       * Sets the timeout for this client if the system property 
184       * dailyprojectdataclient.timeout is set
185       * and if it can be parsed to an integer.
186       */
187      private void setTimeoutFromSystemProperty() {
188        String systemTimeout = System.getProperty(DAILYPROJECTDATACLIENT_TIMEOUT_KEY);
189        // if not set, then return immediately.
190        if (systemTimeout == null) {
191          return;
192        }
193        // systemTimeout has a value, so set it if we can.
194        try {
195          int timeout = Integer.parseInt(systemTimeout);
196          setTimeout(timeout);
197          this.logger.info("DdpClient timeout set to: " + timeout + " milliseconds");
198        }
199        catch (Exception e) {
200          this.logger.warning("dailyprojectdataclient.timeout has non integer value: " + systemTimeout);
201        }
202      }
203    
204      /**
205       * Does the housekeeping for making HTTP requests to the SensorBase by a test or admin user.
206       * 
207       * @param method The type of Method.
208       * @param requestString A string, such as "users". No preceding slash.
209       * @param entity The representation to be sent with the request, or null if not needed.
210       * @return The Response instance returned from the server.
211       */
212      private Response makeRequest(Method method, String requestString, Representation entity) {
213        Reference reference = new Reference(this.dailyProjectDataHost + requestString);
214        Request request = (entity == null) ? new Request(method, reference) : new Request(method,
215            reference, entity);
216        request.getClientInfo().getAcceptedMediaTypes().add(xmlMedia);
217        ChallengeResponse authentication = new ChallengeResponse(scheme, this.userEmail, this.password);
218        request.setChallengeResponse(authentication);
219        if (this.isTraceEnabled) {
220          System.out.println("DailyProjectDataClient Tracing: " + method + " " + reference);
221          if (entity != null) {
222            try {
223              System.out.println(entity.getText());
224            }
225            catch (Exception e) {
226              System.out.println("  Problems with getText() on entity.");
227            }
228          }
229        }
230        Response response = this.client.handle(request);
231        if (this.isTraceEnabled) {
232          Status status = response.getStatus();
233          System.out.println("  => " + status.getCode() + " " + status.getDescription());
234        }
235        return response;
236      }
237    
238      /**
239       * Takes a String encoding of a DevTimeDailyProjectData in XML format and converts it.
240       * 
241       * @param xmlString The XML string representing a DevTimeDailyProjectData.
242       * @return The corresponding DevTimeDailyProjectData instance.
243       * @throws Exception If problems occur during unmarshalling.
244       */
245      private DevTimeDailyProjectData makeDevTimeDailyProjectData(String xmlString) throws Exception {
246        Unmarshaller unmarshaller = this.devTimeJAXB.createUnmarshaller();
247        return (DevTimeDailyProjectData) unmarshaller.unmarshal(new StringReader(xmlString));
248      }
249    
250      /**
251       * Authenticates this user and password with this DailyProjectData service, throwing a
252       * DailyProjectDataException if the user and password associated with this instance are not valid
253       * credentials. Note that authentication is performed by checking these credentials with the
254       * SensorBase; this service does not keep its own independent set of usernames and passwords.
255       * 
256       * @return This DailyProjectDataClient instance.
257       * @throws DailyProjectDataClientException If authentication is not successful.
258       */
259      public synchronized DailyProjectDataClient authenticate() throws DailyProjectDataClientException {
260        // Performs authentication by invoking ping with user and password as form
261        // params.
262        String uri = "ping?user=" + this.userEmail + "&password=" + this.password;
263        Response response = makeRequest(Method.GET, uri, null);
264        if (!response.getStatus().isSuccess()) {
265          throw new DailyProjectDataClientException(response.getStatus());
266        }
267        String responseString;
268        try {
269          responseString = response.getEntity().getText();
270        }
271        catch (Exception e) {
272          throw new DailyProjectDataClientException("Bad response", e);
273        }
274        if (!"DailyProjectData authenticated".equals(responseString)) {
275          throw new DailyProjectDataClientException("Authentication failed");
276        }
277        return this;
278      }
279    
280      /**
281       * Returns a DevTimeDailyProjectData instance from this server, or throws a DailyProjectData
282       * exception if problems occurred.
283       * 
284       * @param user The user that owns the project.
285       * @param project The project owned by user.
286       * @param timestamp The Timestamp indicating the start of the 24 hour period of DevTime.
287       * @return A DevTimeDailyProjectData instance.
288       * @throws DailyProjectDataClientException If the credentials associated with this instance are
289       *         not valid, or if the underlying SensorBase service cannot be reached, or if one or more
290       *         of the supplied user, password, or timestamp is not valid.
291       */
292      public synchronized DevTimeDailyProjectData getDevTime(String user, String project,
293          XMLGregorianCalendar timestamp) throws DailyProjectDataClientException {
294        Date startTime = new Date();
295        DevTimeDailyProjectData devTime;
296        String uri = "devtime/" + user + "/" + project + "/" + timestamp;
297        // Check the cache, and return the instance from it if available.
298        if (this.isCacheEnabled) {
299          devTime = (DevTimeDailyProjectData) this.uriCache.getFromGroup(uri, user + project);
300          if (devTime != null) {
301            return devTime;
302          }
303        }
304        // Otherwise get it from the DPD service.
305        Response response = makeRequest(Method.GET, uri, null);
306        if (!response.getStatus().isSuccess()) {
307          throw new DailyProjectDataClientException(response.getStatus());
308        }
309        try {
310          String xmlData = response.getEntity().getText();
311          devTime = makeDevTimeDailyProjectData(xmlData);
312          // Add it to the cache if we're using one.
313          if (this.isCacheEnabled && !Tstamp.isTodayOrLater(timestamp)) {
314            this.uriCache.putInGroup(uri, user + project, devTime);
315          }
316        }
317        catch (Exception e) {
318          logElapsedTime(uri, startTime, e);
319          throw new DailyProjectDataClientException(response.getStatus(), e);
320        }
321        logElapsedTime(uri, startTime);
322        return devTime;
323      }
324    
325      /**
326       * Returns a UnitTestDailyProjectData instance from this server, or throws a DailyProjectData
327       * exception if problems occurred.
328       * 
329       * @param user The user that owns the project.
330       * @param project The project owned by user.
331       * @param timestamp The Timestamp indicating the start of the 24 hour period of DevTime.
332       * @return A DevTimeDailyProjectData instance.
333       * @throws DailyProjectDataClientException If the credentials associated with this instance are
334       *         not valid, or if the underlying SensorBase service cannot be reached, or if one or more
335       *         of the supplied user, password, or timestamp is not valid.
336       */
337      public synchronized UnitTestDailyProjectData getUnitTest(String user, String project,
338          XMLGregorianCalendar timestamp) throws DailyProjectDataClientException {
339        Date startTime = new Date();
340        String uri = "unittest/" + user + "/" + project + "/" + timestamp;
341        UnitTestDailyProjectData unitDPD;
342        // Check the cache, and return the instance from it if available.
343        if (this.isCacheEnabled) {
344          unitDPD = (UnitTestDailyProjectData) this.uriCache.getFromGroup(uri, user + project);
345          if (unitDPD != null) {
346            return unitDPD;
347          }
348        }
349        Response response = makeRequest(Method.GET, uri, null);
350        if (!response.getStatus().isSuccess()) {
351          throw new DailyProjectDataClientException(response.getStatus());
352        }
353        try {
354          String xmlData = response.getEntity().getText();
355          unitDPD = makeUnitTestDailyProjectData(xmlData);
356          // Add it to the cache if we're using one.
357          if (this.isCacheEnabled && !Tstamp.isTodayOrLater(timestamp)) {
358            this.uriCache.putInGroup(uri, user + project, unitDPD);
359          }
360        }
361        catch (Exception e) {
362          logElapsedTime(uri, startTime, e);
363          throw new DailyProjectDataClientException(response.getStatus(), e);
364        }
365        logElapsedTime(uri, startTime);
366        return unitDPD;
367      }
368    
369      /**
370       * Takes a String encoding of a UnitTestDailyProjectData in XML format and converts it.
371       * 
372       * @param xmlString The XML string representing a UnitTestDailyProjectData.
373       * @return The corresponding UnitTestDailyProjectData instance.
374       * @throws Exception If problems occur during unmarshalling.
375       */
376      private UnitTestDailyProjectData makeUnitTestDailyProjectData(String xmlString) throws Exception {
377        Unmarshaller unmarshaller = this.unitTestJAXB.createUnmarshaller();
378        return (UnitTestDailyProjectData) unmarshaller.unmarshal(new StringReader(xmlString));
379      }
380    
381      /**
382       * Returns true if the passed host is a DailyProjectData host.
383       * 
384       * @param host The URL of a DailyProjectData host, "http://localhost:9876/dailyprojectdata".
385       * @return True if this URL responds as a DailyProjectData host.
386       */
387      public static boolean isHost(String host) {
388        // All sensorbase hosts use the HTTP protocol.
389        if (!host.startsWith("http://")) {
390          return false;
391        }
392        // Create the host/register URL.
393        try {
394          String registerUri = host.endsWith("/") ? host + "ping" : host + "/ping";
395          Request request = new Request();
396          request.setResourceRef(registerUri);
397          request.setMethod(Method.GET);
398          Client client = new Client(Protocol.HTTP);
399          Response response = client.handle(request);
400          String pingText = response.getEntity().getText();
401          return (response.getStatus().isSuccess() && "DailyProjectData".equals(pingText));
402        }
403        catch (Exception e) {
404          return false;
405        }
406      }
407    
408      /**
409       * Returns a FileMetricDailyProjectData instance from this server, or throws a DailyProjectData
410       * exception if problems occurred.
411       * 
412       * @param user The user that owns the project.
413       * @param project The project owned by user.
414       * @param timestamp The Timestamp indicating the start of the 24 hour period of DevTime.
415       * @param sizeMetric The size metric to be retrieved.
416       * @return A FileMetricDailyProjectData instance.
417       * @throws DailyProjectDataClientException If the credentials associated with this instance are
418       *         not valid, or if the underlying SensorBase service cannot be reached, or if one or more
419       *         of the supplied user, password, or timestamp is not valid.
420       */
421      public synchronized FileMetricDailyProjectData getFileMetric(String user, String project,
422          XMLGregorianCalendar timestamp, String sizeMetric) throws DailyProjectDataClientException {
423        return getFileMetric(user, project, timestamp, sizeMetric, null);
424      }
425    
426      /**
427       * Returns a FileMetricDailyProjectData instance from this server, or throws a DailyProjectData
428       * exception if problems occurred.
429       * 
430       * @param user The user that owns the project.
431       * @param project The project owned by user.
432       * @param timestamp The Timestamp indicating the start of the 24 hour period of DevTime.
433       * @param sizeMetric The size metric to be retrieved.
434       * @param tool The tool whose data is to be retrieved, or null for no tool.
435       * @return A FileMetricDailyProjectData instance.
436       * @throws DailyProjectDataClientException If the credentials associated with this instance are
437       *         not valid, or if the underlying SensorBase service cannot be reached, or if one or more
438       *         of the supplied user, password, or timestamp is not valid.
439       */
440      public synchronized FileMetricDailyProjectData getFileMetric(String user, String project,
441          XMLGregorianCalendar timestamp, String sizeMetric, String tool)
442          throws DailyProjectDataClientException {
443        Date startTime = new Date();
444        FileMetricDailyProjectData fileMetric;
445        String param = (tool == null) ? "" : "?Tool=" + tool;
446        String uri = "filemetric/" + user + "/" + project + "/" + timestamp + "/" + sizeMetric + param;
447        // Check the cache, and return the instance from it if available.
448        if (this.isCacheEnabled) {
449          fileMetric = (FileMetricDailyProjectData) this.uriCache.getFromGroup(uri, user + project);
450          if (fileMetric != null) {
451            return fileMetric;
452          }
453        }
454        Response response = makeRequest(Method.GET, uri, null);
455        if (!response.getStatus().isSuccess()) {
456          System.err.println("filemetric/" + user + "/" + project + "/" + timestamp + "/" + sizeMetric);
457          throw new DailyProjectDataClientException(response.getStatus());
458        }
459        try {
460          String xmlData = response.getEntity().getText();
461          fileMetric = makeFileMetricDailyProjectData(xmlData);
462          // Add it to the cache if we're using one.
463          if (this.isCacheEnabled && !Tstamp.isTodayOrLater(timestamp)) {
464            this.uriCache.putInGroup(uri, user + project, fileMetric);
465          }
466        }
467        catch (Exception e) {
468          logElapsedTime(uri, startTime, e);
469          throw new DailyProjectDataClientException(response.getStatus(), e);
470        }
471        logElapsedTime(uri, startTime);
472        return fileMetric;
473      }
474    
475      /**
476       * Returns a ComplexityDailyProjectData instance from this server, or throws a DailyProjectData
477       * exception if problems occurred.
478       * 
479       * @param user The user that owns the project.
480       * @param project The project owned by user.
481       * @param timestamp The Timestamp indicating the start of the 24 hour period of DevTime.
482       * @param type The type of complexity, such as "Cyclometric".
483       * @param tool The tool that provided the complexity data, such as "JavaNCSS".
484       * @return A ComplexityDailyProjectData instance.
485       * @throws DailyProjectDataClientException If the credentials associated with this instance are
486       *         not valid, or if the underlying SensorBase service cannot be reached, or if one or more
487       *         of the supplied user, password, or timestamp is not valid.
488       */
489      public synchronized ComplexityDailyProjectData getComplexity(String user, String project,
490          XMLGregorianCalendar timestamp, String type, String tool)
491          throws DailyProjectDataClientException {
492        Date startTime = new Date();
493        ComplexityDailyProjectData complexity;
494        String uri = "complexity/" + user + "/" + project + "/" + timestamp + "/" + type + "?Tool="
495            + tool;
496        // Check the cache, and return the instance from it if available.
497        if (this.isCacheEnabled) {
498          complexity = (ComplexityDailyProjectData) this.uriCache.getFromGroup(uri, user + project);
499          if (complexity != null) {
500            return complexity;
501          }
502        }
503        Response response = makeRequest(Method.GET, uri, null);
504        if (!response.getStatus().isSuccess()) {
505          System.err.println("complexity/" + user + "/" + project + "/" + timestamp + "/" + type);
506          throw new DailyProjectDataClientException(response.getStatus());
507        }
508        try {
509          String xmlData = response.getEntity().getText();
510          complexity = makeComplexityDailyProjectData(xmlData);
511          // Add it to the cache if we're using one.
512          if (this.isCacheEnabled && !Tstamp.isTodayOrLater(timestamp)) {
513            this.uriCache.putInGroup(uri, user + project, complexity);
514          }
515        }
516        catch (Exception e) {
517          logElapsedTime(uri, startTime, e);
518          throw new DailyProjectDataClientException(response.getStatus(), e);
519        }
520        logElapsedTime(uri, startTime);
521        return complexity;
522      }
523    
524      /**
525       * Returns a CouplingDailyProjectData instance from this server, or throws a DailyProjectData
526       * exception if problems occurred.
527       * 
528       * @param user The user that owns the project.
529       * @param project The project owned by user.
530       * @param timestamp The Timestamp indicating the start of the 24 hour period of DevTime.
531       * @param type The type of coupling, such as "class".
532       * @param tool The tool that provided the coupling data, such as "DependencyFinder".
533       * @return A CouplingDailyProjectData instance.
534       * @throws DailyProjectDataClientException If the credentials associated with this instance are
535       *         not valid, or if the underlying SensorBase service cannot be reached, or if one or more
536       *         of the supplied user, password, or timestamp is not valid.
537       */
538      public synchronized CouplingDailyProjectData getCoupling(String user, String project,
539          XMLGregorianCalendar timestamp, String type, String tool)
540          throws DailyProjectDataClientException {
541        Date startTime = new Date();
542        CouplingDailyProjectData coupling;
543        String uri = "coupling/" + user + "/" + project + "/" + timestamp + "/" + type + "?Tool="
544            + tool;
545        // Check the cache, and return the instance from it if available.
546        if (this.isCacheEnabled) {
547          coupling = (CouplingDailyProjectData) this.uriCache.getFromGroup(uri, user + project);
548          if (coupling != null) {
549            return coupling;
550          }
551        }
552        Response response = makeRequest(Method.GET, uri, null);
553        if (!response.getStatus().isSuccess()) {
554          System.err.println("coupling/" + user + "/" + project + "/" + timestamp + "/" + type);
555          throw new DailyProjectDataClientException(response.getStatus());
556        }
557        try {
558          String xmlData = response.getEntity().getText();
559          coupling = makeCouplingDailyProjectData(xmlData);
560          // Add it to the cache if we're using one.
561          if (this.isCacheEnabled && !Tstamp.isTodayOrLater(timestamp)) {
562            this.uriCache.putInGroup(uri, user + project, coupling);
563          }
564        }
565        catch (Exception e) {
566          logElapsedTime(uri, startTime, e);
567          throw new DailyProjectDataClientException(response.getStatus(), e);
568        }
569        logElapsedTime(uri, startTime);
570        return coupling;
571      }
572    
573      /**
574       * Takes a String encoding of a FileMetricDailyProjectData in XML format and converts it.
575       * 
576       * @param xmlString The XML string representing a DevTimeDailyProjectData.
577       * @return The corresponding DevTimeDailyProjectData instance.
578       * @throws Exception If problems occur during unmarshalling.
579       */
580      private FileMetricDailyProjectData makeFileMetricDailyProjectData(String xmlString)
581          throws Exception {
582        Unmarshaller unmarshaller = this.fileMetricJAXB.createUnmarshaller();
583        return (FileMetricDailyProjectData) unmarshaller.unmarshal(new StringReader(xmlString));
584      }
585    
586      /**
587       * Takes a String encoding of a ComplexityDailyProjectData in XML format and converts it.
588       * 
589       * @param xmlString The XML string representing a ComplexityDailyProjectData.
590       * @return The corresponding ComplexityDailyProjectData instance.
591       * @throws Exception If problems occur during unmarshalling.
592       */
593      private ComplexityDailyProjectData makeComplexityDailyProjectData(String xmlString)
594          throws Exception {
595        Unmarshaller unmarshaller = this.complexityJAXB.createUnmarshaller();
596        return (ComplexityDailyProjectData) unmarshaller.unmarshal(new StringReader(xmlString));
597      }
598    
599      /**
600       * Takes a String encoding of a CouplingDailyProjectData in XML format and converts it.
601       * 
602       * @param xmlString The XML string representing a CouplingDailyProjectData.
603       * @return The corresponding CouplingDailyProjectData instance.
604       * @throws Exception If problems occur during unmarshalling.
605       */
606      private CouplingDailyProjectData makeCouplingDailyProjectData(String xmlString) throws Exception {
607        Unmarshaller unmarshaller = this.couplingJAXB.createUnmarshaller();
608        return (CouplingDailyProjectData) unmarshaller.unmarshal(new StringReader(xmlString));
609      }
610    
611      /**
612       * Returns a CodeIssueDailyProjectData instance from this server, or throws a DailyProjectData
613       * exception if problems occurred.
614       * 
615       * @param user The user that owns the project.
616       * @param project The project owned by user.
617       * @param timestamp The Timestamp indicating the start of the 24 hour period of CodeIssue data.
618       * @param tool An optional tool for matching CodeIssue data.
619       * @param type An optional type for matching CodeIssue types.
620       * @return A CodeIssueDailyProjectData instance.
621       * @throws DailyProjectDataClientException If the credentials associated with this instance are
622       *         not valid, or if the underlying SensorBase service cannot be reached, or if one or more
623       *         of the supplied user, password, or timestamp is not valid.
624       */
625      public synchronized CodeIssueDailyProjectData getCodeIssue(String user, String project,
626          XMLGregorianCalendar timestamp, String tool, String type)
627          throws DailyProjectDataClientException {
628        CodeIssueDailyProjectData codeIssue;
629        Date startTime = new Date();
630        StringBuilder requestStringBuilder = new StringBuilder("codeissue/");
631        requestStringBuilder.append(user);
632        requestStringBuilder.append("/");
633        requestStringBuilder.append(project);
634        requestStringBuilder.append("/");
635        requestStringBuilder.append(timestamp);
636    
637        boolean questionMarkAppended = false;
638        if (tool != null) {
639          requestStringBuilder.append("?");
640          requestStringBuilder.append("Tool=");
641          requestStringBuilder.append(tool);
642          questionMarkAppended = true;
643        }
644        if (type != null) {
645          if (questionMarkAppended) {
646            requestStringBuilder.append("&");
647          }
648          else {
649            requestStringBuilder.append("?");
650          }
651          requestStringBuilder.append("Type=");
652          requestStringBuilder.append(type);
653        }
654        String uri = requestStringBuilder.toString();
655        // Check the cache, and return the instance from it if available.
656        if (this.isCacheEnabled) {
657          codeIssue = (CodeIssueDailyProjectData) this.uriCache.getFromGroup(uri, user + project);
658          if (codeIssue != null) {
659            return codeIssue;
660          }
661        }
662        Response response = makeRequest(Method.GET, uri, null);
663    
664        if (!response.getStatus().isSuccess()) {
665          logElapsedTime(uri, startTime);
666          throw new DailyProjectDataClientException(response.getStatus());
667        }
668        try {
669          String xmlData = response.getEntity().getText();
670          codeIssue = makeCodeIssueDailyProjectData(xmlData);
671          // Add it to the cache if we're using one.
672          if (this.isCacheEnabled && !Tstamp.isTodayOrLater(timestamp)) {
673            this.uriCache.putInGroup(uri, user + project, codeIssue);
674          }
675        }
676        catch (Exception e) {
677          logElapsedTime(uri, startTime, e);
678          throw new DailyProjectDataClientException(response.getStatus(), e);
679        }
680        logElapsedTime(uri, startTime);
681        return codeIssue;
682      }
683    
684      /**
685       * Returns a CoverageDailyProjectData instance from this server, or throws a DailyProjectData
686       * exception if problems occurred.
687       * 
688       * @param user The user that owns the project.
689       * @param project The project owned by user.
690       * @param timestamp The Timestamp indicating the start of the 24 hour period of CodeIssue data.
691       * @param granularity the granularity of the coverage data.
692       * @return A CoverageDailyProjectData instance.
693       * @throws DailyProjectDataClientException If the credentials associated with this instance are
694       *         not valid, or if the underlying SensorBase service cannot be reached, or if one or more
695       *         of the supplied user, password, or timestamp is not valid.
696       */
697      public synchronized CoverageDailyProjectData getCoverage(String user, String project,
698          XMLGregorianCalendar timestamp, String granularity) throws DailyProjectDataClientException {
699        Date startTime = new Date();
700        StringBuilder requestStringBuilder = new StringBuilder("coverage/");
701        requestStringBuilder.append(user);
702        requestStringBuilder.append("/");
703        requestStringBuilder.append(project);
704        requestStringBuilder.append("/");
705        requestStringBuilder.append(timestamp);
706    
707        if (granularity != null) {
708          requestStringBuilder.append("/");
709          requestStringBuilder.append(granularity);
710        }
711    
712        String uri = requestStringBuilder.toString();
713        CoverageDailyProjectData coverage;
714        // Check the cache, and return the instance from it if available.
715        if (this.isCacheEnabled) {
716          coverage = (CoverageDailyProjectData) this.uriCache.getFromGroup(uri, user + project);
717          if (coverage != null) {
718            return coverage;
719          }
720        }
721        Response response = makeRequest(Method.GET, uri, null);
722    
723        if (!response.getStatus().isSuccess()) {
724          logElapsedTime(uri, startTime);
725          throw new DailyProjectDataClientException(response.getStatus());
726        }
727        try {
728          String xmlData = response.getEntity().getText();
729          coverage = makeCoverageDailyProjectData(xmlData);
730          // Add it to the cache if we're using one.
731          if (this.isCacheEnabled && !Tstamp.isTodayOrLater(timestamp)) {
732            this.uriCache.putInGroup(uri, user + project, coverage);
733          }
734        }
735        catch (Exception e) {
736          logElapsedTime(uri, startTime, e);
737          throw new DailyProjectDataClientException(response.getStatus(), e);
738        }
739        logElapsedTime(uri, startTime);
740        return coverage;
741      }
742    
743      /**
744       * Takes a String encoding of a CoverageDailyProjectData in XML format and converts it.
745       * 
746       * @param xmlString The XML string representing a CoverageDailyProjectData.
747       * @return The corresponding CoverageDailyProjectData instance.
748       * @throws Exception If problems occur during unmarshalling.
749       */
750      private CoverageDailyProjectData makeCoverageDailyProjectData(String xmlString) throws Exception {
751        Unmarshaller unmarshaller = this.coverageJAXB.createUnmarshaller();
752        return (CoverageDailyProjectData) unmarshaller.unmarshal(new StringReader(xmlString));
753      }
754    
755      /**
756       * Returns a CommitDailyProjectData instance from this server, or throws a DailyProjectData
757       * exception if problems occurred.
758       * 
759       * @param user The user that owns the project.
760       * @param project The project owned by user.
761       * @param timestamp The Timestamp indicating the start of the 24 hour period of CodeIssue data.
762       * @return A CommitDailyProjectData instance.
763       * @throws DailyProjectDataClientException If the credentials associated with this instance are
764       *         not valid, or if the underlying SensorBase service cannot be reached, or if one or more
765       *         of the supplied user, password, or timestamp is not valid.
766       */
767      public synchronized CommitDailyProjectData getCommit(String user, String project,
768          XMLGregorianCalendar timestamp) throws DailyProjectDataClientException {
769        Date startTime = new Date();
770        StringBuilder requestStringBuilder = new StringBuilder("commit/");
771        requestStringBuilder.append(user);
772        requestStringBuilder.append("/");
773        requestStringBuilder.append(project);
774        requestStringBuilder.append("/");
775        requestStringBuilder.append(timestamp);
776    
777        String uri = requestStringBuilder.toString();
778        CommitDailyProjectData commit;
779        // Check the cache, and return the instance from it if available.
780        if (this.isCacheEnabled) {
781          commit = (CommitDailyProjectData) this.uriCache.getFromGroup(uri, user + project);
782          if (commit != null) {
783            return commit;
784          }
785        }
786        Response response = makeRequest(Method.GET, uri, null);
787    
788        if (!response.getStatus().isSuccess()) {
789          logElapsedTime(uri, startTime);
790          throw new DailyProjectDataClientException(response.getStatus());
791        }
792        try {
793          String xmlData = response.getEntity().getText();
794          commit = makeCommitDailyProjectData(xmlData);
795          // Add it to the cache if we're using one.
796          // Since CM sensors typically run on yesterday's data, don't cache unless 2 days or older.
797          if (this.isCacheEnabled && !Tstamp.isYesterdayOrLater(timestamp)) {
798            this.uriCache.putInGroup(uri, user + project, commit);
799          }
800        }
801        catch (Exception e) {
802          logElapsedTime(uri, startTime, e);
803          throw new DailyProjectDataClientException(response.getStatus(), e);
804        }
805        logElapsedTime(uri, startTime);
806        return commit;
807      }
808    
809      /**
810       * Takes a String encoding of a CommitDailyProjectData in XML format and converts it.
811       * 
812       * @param xmlString The XML string representing a CommitDailyProjectData.
813       * @return The corresponding CommitsDailyProjectData instance.
814       * @throws Exception If problems occur during unmarshalling.
815       */
816      private CommitDailyProjectData makeCommitDailyProjectData(String xmlString) throws Exception {
817        Unmarshaller unmarshaller = this.commitJAXB.createUnmarshaller();
818        return (CommitDailyProjectData) unmarshaller.unmarshal(new StringReader(xmlString));
819      }
820    
821      /**
822       * Takes a String encoding of a CodeIssueDailyProjectData in XML format and converts it.
823       * 
824       * @param xmlString The XML string representing a CodeIssueDailyProjectData.
825       * @return The corresponding CodeIssueDailyProjectData instance.
826       * @throws Exception If problems occur during unmarshalling.
827       */
828      private CodeIssueDailyProjectData makeCodeIssueDailyProjectData(String xmlString)
829          throws Exception {
830        Unmarshaller unmarshaller = this.codeIssueJAXB.createUnmarshaller();
831        return (CodeIssueDailyProjectData) unmarshaller.unmarshal(new StringReader(xmlString));
832      }
833    
834      /**
835       * Returns a BuildDailyProjectData instance from this server, or throws a DailyProjectData
836       * exception if problems occurred.
837       * 
838       * @param user The user that owns the project.
839       * @param project The project owned by user.
840       * @param timestamp The Timestamp indicating the start of the 24 hour period of build data.
841       * @param type The type of build to retrieve data for.
842       * @return A BuildDailyProjectData instance.
843       * @throws DailyProjectDataClientException If the credentials associated with this instance are
844       *         not valid, or if the underlying SensorBase service cannot be reached, or if one or more
845       *         of the supplied user, password, or timestamp is not valid.
846       */
847      public synchronized BuildDailyProjectData getBuild(String user, String project,
848          XMLGregorianCalendar timestamp, String type) throws DailyProjectDataClientException {
849        Date startTime = new Date();
850    
851        StringBuilder requestStringBuilder = new StringBuilder("build/");
852        requestStringBuilder.append(user);
853        requestStringBuilder.append("/");
854        requestStringBuilder.append(project);
855        requestStringBuilder.append("/");
856        requestStringBuilder.append(timestamp);
857    
858        if (type != null) {
859          requestStringBuilder.append("?");
860          requestStringBuilder.append("Type=");
861          requestStringBuilder.append(type);
862        }
863    
864        BuildDailyProjectData build;
865        String uri = requestStringBuilder.toString();
866        // Check the cache, and return the instance from it if available.
867        if (this.isCacheEnabled) {
868          build = (BuildDailyProjectData) this.uriCache.getFromGroup(uri, user + project);
869          if (build != null) {
870            return build;
871          }
872        }
873        Response response = makeRequest(Method.GET, uri, null);
874    
875        if (!response.getStatus().isSuccess()) {
876          logElapsedTime(uri, startTime);
877          throw new DailyProjectDataClientException(response.getStatus());
878        }
879        try {
880          String xmlData = response.getEntity().getText();
881          build = makeBuildDailyProjectData(xmlData);
882          // Add it to the cache if we're using one.
883          if (this.isCacheEnabled && !Tstamp.isTodayOrLater(timestamp)) {
884            this.uriCache.putInGroup(uri, user + project, build);
885          }
886        }
887        catch (Exception e) {
888          logElapsedTime(uri, startTime, e);
889          throw new DailyProjectDataClientException(response.getStatus(), e);
890        }
891        logElapsedTime(uri, startTime);
892        return build;
893      }
894    
895      /**
896       * Returns a BuildDailyProjectData instance from this server, or throws a DailyProjectData
897       * exception if problems occurred.
898       * 
899       * @param user The user that owns the project.
900       * @param project The project owned by user.
901       * @param timestamp The Timestamp indicating the start of the 24 hour period of build data.
902       * @return A BuildDailyProjectData instance.
903       * @throws DailyProjectDataClientException If the credentials associated with this instance are
904       *         not valid, or if the underlying SensorBase service cannot be reached, or if one or more
905       *         of the supplied user, password, or timestamp is not valid.
906       */
907      public synchronized BuildDailyProjectData getBuild(String user, String project,
908          XMLGregorianCalendar timestamp) throws DailyProjectDataClientException {
909        return getBuild(user, project, timestamp, null);
910      }
911    
912      /**
913       * Takes a String encoding of a BuildDailyProjectData in XML format and converts it.
914       * 
915       * @param xmlString The XML string representing a DevTimeDailyProjectData.
916       * @return The corresponding BuildDailyProjectData instance.
917       * @throws Exception If problems occur during unmarshalling.
918       */
919      private BuildDailyProjectData makeBuildDailyProjectData(String xmlString) throws Exception {
920        Unmarshaller unmarshaller = this.buildJAXB.createUnmarshaller();
921        return (BuildDailyProjectData) unmarshaller.unmarshal(new StringReader(xmlString));
922      }
923      
924      /**
925       * Returns a IssueDailyProjectData instance from this server, or throws a DailyProjectData
926       * exception if problems occurred.
927       * @param user The user that owns the project.
928       * @param project The project owned by user.
929       * @param timestamp The Timestamp indicating the start of the 24 hour period of build data.
930       * @param status The status of the issue, open or closed, 
931       *          or a specified status such as "Accepted" or "Fixed"
932       * @return A IssueDailyProjectData instance.
933       * @throws DailyProjectDataClientException If the credentials associated with this instance are
934       *         not valid, or if the underlying SensorBase service cannot be reached, or if one or more
935       *         of the supplied user, password, or timestamp is not valid.
936       */
937      public synchronized IssueDailyProjectData getIssue(String user, String project,
938          XMLGregorianCalendar timestamp, String status) throws DailyProjectDataClientException {
939        Date startTime = new Date();
940    
941        StringBuilder requestStringBuilder = new StringBuilder("issue/");
942        requestStringBuilder.append(user);
943        requestStringBuilder.append("/");
944        requestStringBuilder.append(project);
945        requestStringBuilder.append("/");
946        requestStringBuilder.append(timestamp);
947    
948        if (status != null) {
949          requestStringBuilder.append("?");
950          requestStringBuilder.append("Status=");
951          requestStringBuilder.append(status);
952        }
953        
954        IssueDailyProjectData issue;
955        String uri = requestStringBuilder.toString();
956        // Check the cache, and return the instance from it if available.
957        if (this.isCacheEnabled) {
958          issue = (IssueDailyProjectData) this.uriCache.getFromGroup(uri, user + project);
959          if (issue != null) {
960            return issue;
961          }
962        }
963        Response response = makeRequest(Method.GET, uri, null);
964    
965        if (!response.getStatus().isSuccess()) {
966          logElapsedTime(uri, startTime);
967          throw new DailyProjectDataClientException(response.getStatus());
968        }
969        try {
970          String xmlData = response.getEntity().getText();
971          issue = makeIssueDailyProjectData(xmlData);
972          // Add it to the cache if we're using one.
973          if (this.isCacheEnabled && !Tstamp.isTodayOrLater(timestamp)) {
974            this.uriCache.putInGroup(uri, user + project, issue);
975          }
976        }
977        catch (Exception e) {
978          logElapsedTime(uri, startTime, e);
979          throw new DailyProjectDataClientException(response.getStatus(), e);
980        }
981        logElapsedTime(uri, startTime);
982        return issue;
983      }
984      /**
985       * Returns a IssueDailyProjectData instance from this server, or throws a DailyProjectData
986       * exception if problems occurred.
987       * @param user The user that owns the project.
988       * @param project The project owned by user.
989       * @param timestamp The Timestamp indicating the start of the 24 hour period of build data.
990       * @return A IssueDailyProjectData instance.
991       * @throws DailyProjectDataClientException If the credentials associated with this instance are
992       *         not valid, or if the underlying SensorBase service cannot be reached, or if one or more
993       *         of the supplied user, password, or timestamp is not valid.
994       */
995      public synchronized IssueDailyProjectData getIssue(String user, String project,
996          XMLGregorianCalendar timestamp) throws DailyProjectDataClientException {
997        return getIssue(user, project, timestamp, null);
998      }
999      /**
1000       * Takes a String encoding of a IssueDailyProjectData in XML format and converts it.
1001       * 
1002       * @param xmlData The XML string representing a DevTimeDailyProjectData.
1003       * @return The corresponding IssueDailyProjectData instance.
1004       * @throws JAXBException If problems occur during unmarshalling.
1005       */
1006      private IssueDailyProjectData makeIssueDailyProjectData(String xmlData) throws JAXBException {
1007        Unmarshaller unmarshaller = this.issueJAXB.createUnmarshaller();
1008        return (IssueDailyProjectData) unmarshaller.unmarshal(new StringReader(xmlData));
1009      }
1010    
1011    
1012      /**
1013       * Returns a IssueDailyProjectData instance from this server, or throws a DailyProjectData
1014       * exception if problems occurred.
1015       * @param user The user that owns the project.
1016       * @param project The project owned by user.
1017       * @param timestamp The Timestamp indicating the start of the 24 hour period of build data.
1018       * @return A IssueDailyProjectData instance.
1019       * @throws DailyProjectDataClientException If the credentials associated with this instance are
1020       *         not valid, or if the underlying SensorBase service cannot be reached, or if one or more
1021       *         of the supplied user, password, or timestamp is not valid.
1022       */
1023      public synchronized IssueChangeDailyProjectData getIssueChange(String user, String project,
1024          XMLGregorianCalendar timestamp) throws DailyProjectDataClientException {
1025        Date startTime = new Date();
1026    
1027        StringBuilder requestStringBuilder = new StringBuilder("issuechange/");
1028        requestStringBuilder.append(user);
1029        requestStringBuilder.append("/");
1030        requestStringBuilder.append(project);
1031        requestStringBuilder.append("/");
1032        requestStringBuilder.append(timestamp);
1033        
1034        IssueChangeDailyProjectData issue;
1035        String uri = requestStringBuilder.toString();
1036        // Check the cache, and return the instance from it if available.
1037        if (this.isCacheEnabled) {
1038          issue = (IssueChangeDailyProjectData) this.uriCache.getFromGroup(uri, user + project);
1039        }
1040        Response response = makeRequest(Method.GET, uri, null);
1041    
1042        if (!response.getStatus().isSuccess()) {
1043          logElapsedTime(uri, startTime);
1044          throw new DailyProjectDataClientException(response.getStatus());
1045        }
1046        try {
1047          String xmlData = response.getEntity().getText();
1048          issue = makeIssueChangeDailyProjectData(xmlData);
1049          // Add it to the cache if we're using one.
1050          if (this.isCacheEnabled && !Tstamp.isTodayOrLater(timestamp)) {
1051            this.uriCache.putInGroup(uri, user + project, issue);
1052          }
1053        }
1054        catch (Exception e) {
1055          logElapsedTime(uri, startTime, e);
1056          throw new DailyProjectDataClientException(response.getStatus(), e);
1057        }
1058        logElapsedTime(uri, startTime);
1059        return issue;
1060      }
1061    
1062      /**
1063       * Takes a String encoding of a IssueDailyProjectData in XML format and converts it.
1064       * 
1065       * @param xmlData The XML string representing a DevTimeDailyProjectData.
1066       * @return The corresponding IssueDailyProjectData instance.
1067       * @throws JAXBException If problems occur during unmarshalling.
1068       */
1069      private IssueChangeDailyProjectData makeIssueChangeDailyProjectData(String xmlData) 
1070          throws JAXBException {
1071        Unmarshaller unmarshaller = this.issuechangeJAXB.createUnmarshaller();
1072        return (IssueChangeDailyProjectData) unmarshaller.unmarshal(new StringReader(xmlData));
1073      }
1074      
1075      /**
1076       * Logs info to the logger about the elapsed time for this request.
1077       * 
1078       * @param uri The URI requested.
1079       * @param startTime The startTime of the call.
1080       * @param e The exception thrown, or null if no exception.
1081       */
1082      private void logElapsedTime(String uri, Date startTime, Exception e) {
1083        long millis = (new Date()).getTime() - startTime.getTime();
1084        String msg = millis + " millis: " + uri + ((e == null) ? "" : " " + e);
1085        this.logger.info(msg);
1086      }
1087    
1088      /**
1089       * Logs info to the logger about the elapsed time for this request.
1090       * 
1091       * @param uri The URI requested.
1092       * @param startTime The startTime of the call.
1093       */
1094      private void logElapsedTime(String uri, Date startTime) {
1095        logElapsedTime(uri, startTime, null);
1096      }
1097    
1098      /**
1099       * Enables caching in this client. We do not cache DPDs for the current day, since not all data
1100       * might be have been sent yet.
1101       * 
1102       * @param cacheName The name of the cache.
1103       * @param subDir The subdirectory in which the cache backend store is saved.
1104       * @param maxLife The default expiration time for cached objects in days.
1105       * @param capacity The maximum number of instances to be held in-memory.
1106       */
1107      public synchronized void enableCaching(String cacheName, String subDir, Double maxLife,
1108          Long capacity) {
1109        this.uriCache = new UriCache(cacheName, subDir, maxLife, capacity);
1110        this.isCacheEnabled = true;
1111      }
1112    
1113      /**
1114       * Delete all entries from the local cache of DailyProjectData instances associated with this
1115       * DailyProjectDataClient instance. All DPD-specific caches are cleared. If this DPDClient
1116       * instance does not have caching enabled, then this method has no effect.
1117       */
1118      public synchronized void clearLocalCache() {
1119        if (this.uriCache != null) {
1120          this.uriCache.clearAll();
1121        }
1122      }
1123    
1124      /**
1125       * Delete all cache entries associated with the specified project and its owner. 
1126       * If this DPDClient does not have caching enabled, then this has no effect.
1127       * 
1128       * @param user The user. 
1129       * @param project The project. 
1130       */
1131      public synchronized void clearLocalCache(String user, String project) {
1132        if (this.uriCache != null) {
1133          this.uriCache.clearGroup(user + project);
1134        }
1135      }
1136    
1137      /**
1138       * Returns the number of cached entries for the given project and its owner. 
1139       * If this DPDClient does not have caching enabled, then returns 0.
1140       * 
1141       * @param user The owner of this project. 
1142       * @param project The project.
1143       * @return The number of entries in the cache for that project. 
1144       */
1145      public synchronized int localCacheSize(String user, String project) {
1146        int size = 0;
1147        if (this.uriCache != null) {
1148          size = this.uriCache.getGroupSize(user + project);
1149        }
1150        return size;
1151      }
1152    
1153      /**
1154       * Clears the (front side) DPD cache associated with this user on the DailyProjectData server
1155       * to which this DailyProjectDataClient instance is connected.
1156       * 
1157       * @return True if the command succeeded.
1158       * @throws DailyProjectDataClientException If problems occur.
1159       */
1160      public synchronized boolean clearServerCache() throws DailyProjectDataClientException {
1161        Date startTime = new Date();
1162        String uri = "cache";
1163        Response response = makeRequest(Method.DELETE, uri, null);
1164        if (!response.getStatus().isSuccess()) {
1165          logElapsedTime(uri, startTime);
1166          throw new DailyProjectDataClientException(response.getStatus());
1167        }
1168        logElapsedTime(uri, startTime);
1169        return true;
1170      }
1171      
1172      /**
1173       * Clears the (front side) DPD cache entries associated with the specified project and its owner 
1174       * on the DailyProjectData server to which this DailyProjectDataClient instance is connected.
1175       * 
1176       * @param owner The owner of the project whose entries are to be cleared.
1177       * @param project The project DPDs to be cleared on the server. 
1178       * @return True if the command succeeded.
1179       * @throws DailyProjectDataClientException If problems occur.
1180       */
1181      public synchronized boolean clearServerCache(String owner, String project) 
1182      throws DailyProjectDataClientException {
1183        Date startTime = new Date();
1184        String uri = String.format("cache/%s/%s", owner, project);
1185        Response response = makeRequest(Method.DELETE, uri, null);
1186        if (!response.getStatus().isSuccess()) {
1187          logElapsedTime(uri, startTime);
1188          throw new DailyProjectDataClientException(response.getStatus());
1189        }
1190        logElapsedTime(uri, startTime);
1191        return true;
1192      }
1193    
1194    
1195    }