private boolean resourceIsExpirable(RepoResource resource, HttpRepo repo) {
   // If the file is expirable then the expirable mechanism is responsible to update the file and
   // the properties
   String path = resource.getRepoPath().getPath();
   CacheExpiry cacheExpiry = ContextHelper.get().beanForType(CacheExpiry.class);
   return cacheExpiry.isExpirable(repo.getLocalCacheRepo(), path);
 }
 /**
  * Performs local properties update
  *
  * @param resource
  * @return success/failure
  */
 private boolean doPull(RepoResource resource) {
   log.debug("Downloading properties for artifact {}", resource);
   String repoKey = StringUtils.replaceLast(resource.getRepoPath().getRepoKey(), "-cache", "");
   HttpRepo repo = (HttpRepo) repositoryService.remoteRepositoryByKey(repoKey);
   // If file doesn't exist then do mot update properties since it will be updated during file
   // download
   if (!repositoryService.exists(resource.getRepoPath())) {
     return false;
   }
   // If properties not expire the return false, no need to get properties drom the remote repo
   if (repo == null
       || resourceIsExpirable(resource, repo)
       || !isPropertiesExpired(resource, repo)) {
     return false;
   }
   String remotePath = repo.getUrl() + "/" + resource.getRepoPath().getPath() + ":properties";
   HttpGet getMethod = new HttpGet(HttpUtils.encodeQuery(remotePath));
   try {
     CloseableHttpResponse getResponse = repo.executeMethod(getMethod);
     boolean ok = HttpStatus.SC_OK == getResponse.getStatusLine().getStatusCode();
     boolean notFound = HttpStatus.SC_NOT_FOUND == getResponse.getStatusLine().getStatusCode();
     if (ok || notFound) {
       InputStream stream = getResponse.getEntity().getContent();
       Properties properties = (Properties) InfoFactoryHolder.get().createProperties();
       if (ok && stream != null) {
         RepoRequests.logToContext("Received remote property content");
         Properties remoteProperties =
             (Properties) InfoFactoryHolder.get().getFileSystemXStream().fromXML(stream);
         for (String remotePropertyKey : remoteProperties.keySet()) {
           Set<String> values = remoteProperties.get(remotePropertyKey);
           RepoRequests.logToContext(
               "Found remote property key '{}' with values '%s'", remotePropertyKey, values);
           if (!remotePropertyKey.startsWith(ReplicationAddon.PROP_REPLICATION_PREFIX)) {
             properties.putAll(remotePropertyKey, values);
           }
         }
       }
       updateRemoteProperties(resource, properties);
     }
     repositoryService.unexpireIfExists(
         repo.getLocalCacheRepo(), resource.getRepoPath().getPath());
   } catch (IOException e) {
     log.debug("Cannot update remote properties", e);
     return false;
   }
   return true;
 }
 /**
  * Updates properties from remote repository (if expired)
  *
  * @param resource an resource to update properties for
  * @return success/failure
  */
 @Override
 public boolean update(RepoResource resource) {
   try {
     String repoKey = StringUtils.replaceLast(resource.getRepoPath().getRepoKey(), "-cache", "");
     HttpRepoDescriptor descriptor =
         (HttpRepoDescriptor) repositoryService.remoteRepoDescriptorByKey(repoKey);
     if (!shouldPull(descriptor)) {
       return false;
     }
     return doPull(resource);
   } catch (Exception e) {
     log.debug(
         "Smart repo failed to get properties from remote repo :" + resource.getRepoPath(), e);
     log.error(
         "Smart repo failed to get properties from remote repo '{}'", resource.getRepoPath());
     return false;
   }
 }
 /**
  * Checks whether given resource's properties cache has expired
  *
  * @param resource - repo resource
  * @param repo - http repo
  * @return yes/no
  */
 private boolean isPropertiesExpired(RepoResource resource, HttpRepo repo) {
   RepoPath repoPath = resource.getRepoPath();
   long lastUpdated = repositoryService.getFileInfo(repoPath).getLastUpdated();
   long cacheAge = System.currentTimeMillis() - lastUpdated;
   long retrievalCachePeriodMillis = repo.getRetrievalCachePeriodSecs() * 1000L;
   // If cache age is less than retrieval cache period then do not update properties.
   if (cacheAge < retrievalCachePeriodMillis) {
     return false;
   }
   return true;
 }
 /**
  * update remote Properties
  *
  * @param resource - repo resource
  * @param properties - properties
  */
 private void updateRemoteProperties(RepoResource resource, Properties properties) {
   AddonsManager addonsManager = ContextHelper.get().beanForType(AddonsManager.class);
   PropertiesAddon propertiesAddon = addonsManager.addonByType(PropertiesAddon.class);
   propertiesAddon.setProperties(resource.getRepoPath(), properties);
 }
 @Override
 public boolean isMetadata() {
   return wrappedResource.isMetadata();
 }
 @Override
 public String getMimeType() {
   return wrappedResource.getMimeType();
 }
 @Override
 public boolean isExactQueryMatch() {
   return wrappedResource.isExactQueryMatch();
 }
 @Override
 public long getCacheAge() {
   return wrappedResource.getCacheAge();
 }
 @Override
 public long getLastModified() {
   return wrappedResource.getLastModified();
 }
 @Override
 public long getSize() {
   return wrappedResource.getSize();
 }
 @Override
 public RepoResourceInfo getInfo() {
   return wrappedResource.getInfo();
 }
 @Override
 public void setResponseRepoPath(RepoPath responsePath) {
   wrappedResource.setResponseRepoPath(responsePath);
 }
 @Override
 public RepoPath getResponseRepoPath() {
   return wrappedResource.getResponseRepoPath();
 }
 @Override
 public String toString() {
   return wrappedResource.getRepoPath().toString();
 }