001package org.kuali.common.devops.metadata.logic; 002 003import static com.google.common.base.Optional.absent; 004import static com.google.common.base.Stopwatch.createStarted; 005import static com.google.common.collect.Lists.newArrayList; 006import static java.lang.String.format; 007import static org.apache.commons.lang3.StringUtils.isBlank; 008import static org.kuali.common.http.model.HttpStatus.SUCCESS; 009import static org.kuali.common.util.FormatUtils.getTime; 010import static org.kuali.common.util.base.Precondition.checkNotBlank; 011import static org.kuali.common.util.base.Precondition.checkNotNull; 012 013import java.util.List; 014import java.util.Properties; 015 016import org.kuali.common.devops.cache.Caches; 017import org.kuali.common.devops.metadata.function.FirstGCTimestampFunction; 018import org.kuali.common.devops.metadata.function.ManifestFunction; 019import org.kuali.common.devops.metadata.function.ProjectConfigUrlFragmentFunction; 020import org.kuali.common.devops.metadata.function.ProjectFunction; 021import org.kuali.common.devops.metadata.function.ProjectPropertiesUrlFragmentFunction; 022import org.kuali.common.devops.metadata.function.RemoteEnvironmentFunction; 023import org.kuali.common.devops.metadata.function.RicePropertiesFunction; 024import org.kuali.common.devops.metadata.function.TomcatVersionFunction; 025import org.kuali.common.devops.metadata.model.EnvironmentMetadata; 026import org.kuali.common.devops.metadata.model.MetadataUrl; 027import org.kuali.common.devops.metadata.model.RemoteEnvironment; 028import org.kuali.common.http.model.HttpContext; 029import org.kuali.common.http.model.HttpRequestResult; 030import org.kuali.common.http.model.HttpWaitResult; 031import org.kuali.common.util.log.LoggerUtils; 032import org.kuali.common.util.project.model.Project; 033import org.slf4j.Logger; 034 035import com.google.common.base.Function; 036import com.google.common.base.Optional; 037import com.google.common.base.Stopwatch; 038import com.google.common.cache.CacheBuilder; 039import com.google.common.cache.CacheLoader; 040import com.google.common.cache.LoadingCache; 041import com.google.common.collect.ImmutableList; 042 043public class DefaultEnvironmentMetadataService implements EnvironmentMetadataService { 044 045 private static final Logger logger = LoggerUtils.make(); 046 private static final String PREFIX = "http://"; 047 private static final String VERSION_SUFFIX = "/tomcat"; 048 private static final String JSP_SUFFIX = "/tomcat/logs/env.jsp"; 049 private static final String MANIFEST_SUFFIX = "/tomcat/webapps/ROOT/META-INF/MANIFEST.MF"; 050 private static final String HEAP_LOG_SUFFIX = "/tomcat/logs/heap.log"; 051 private final LoadingCache<String, HttpWaitResult> urlCache = getFastFileSystemCacher(); 052 053 @Override 054 public EnvironmentMetadata getMetadata(String fqdn) { 055 return getMetadata(ImmutableList.of(fqdn)).get(0); 056 } 057 058 @Override 059 public List<EnvironmentMetadata> getMetadata(List<String> fqdns) { 060 List<EnvironmentMetadata> list = newArrayList(); 061 for (String fqdn : fqdns) { 062 logger.debug(format("examining -> [%s]", fqdn)); 063 EnvironmentMetadata meta = build(fqdn, urlCache); 064 list.add(meta); 065 } 066 return list; 067 } 068 069 protected EnvironmentMetadata build(String fqdn, LoadingCache<String, HttpWaitResult> urlCache) { 070 MetadataUrlHelper helper = new MetadataUrlHelper(PREFIX, fqdn, urlCache); 071 HttpWaitResult fqdnStatus = urlCache.getUnchecked(PREFIX + fqdn); 072 073 EnvironmentMetadata.Builder builder = EnvironmentMetadata.builder(); 074 builder.withFqdnStatus(fqdnStatus); 075 builder.tomcatVersion(build(helper, VERSION_SUFFIX, TomcatVersionFunction.create())); 076 builder.tomcatStartupTime(build(helper, HEAP_LOG_SUFFIX, new FirstGCTimestampFunction())); 077 builder.remoteEnvironment(build(helper, JSP_SUFFIX, new RemoteEnvironmentFunction())); 078 builder.manifest(build(helper, MANIFEST_SUFFIX, new ManifestFunction())); 079 addProject(helper, builder); 080 addConfig(helper, builder); 081 return builder.build(); 082 } 083 084 protected void addProject(MetadataUrlHelper helper, EnvironmentMetadata.Builder builder) { 085 Optional<Properties> manifest = builder.getManifest().getMetadata(); 086 if (manifest.isPresent()) { 087 Function<Properties, Optional<String>> function = new ProjectPropertiesUrlFragmentFunction(); 088 Optional<String> suffix = function.apply(manifest.get()); 089 if (suffix.isPresent()) { 090 builder.project(build(helper, suffix.get(), new ProjectFunction())); 091 } 092 } else { 093 builder.projectIsAbsent(); 094 } 095 } 096 097 protected void addConfig(MetadataUrlHelper helper, EnvironmentMetadata.Builder builder) { 098 Optional<MetadataUrl<Project>> optionalProjectUrl = builder.getProject(); 099 if (optionalProjectUrl == null || !optionalProjectUrl.isPresent()) { 100 builder.configIsAbsent(); 101 return; 102 } 103 Optional<Project> optionalProject = optionalProjectUrl.get().getMetadata(); 104 if (!optionalProject.isPresent()) { 105 builder.configIsAbsent(); 106 return; 107 } 108 Optional<RemoteEnvironment> env = builder.getRemoteEnvironment().getMetadata(); 109 if (optionalProject.isPresent()) { 110 Function<Project, Optional<String>> function1 = new ProjectConfigUrlFragmentFunction(env); 111 Function<Project, Optional<String>> function2 = new ProjectConfigUrlFragmentFunction(env, Optional.of("tomcat")); 112 Optional<String> suffix1 = function1.apply(optionalProject.get()); 113 Optional<String> suffix2 = function2.apply(optionalProject.get()); 114 if (suffix1.isPresent() || suffix2.isPresent()) { 115 builder.config(build(helper, suffix1, suffix2, new RicePropertiesFunction())); 116 } else { 117 builder.configIsAbsent(); 118 } 119 } 120 } 121 122 public static <T> MetadataUrl<T> build(MetadataUrlHelper helper, Function<String, T> converter) { 123 return build(helper, Optional.<String> absent(), Optional.<String> absent(), converter); 124 } 125 126 public static <T> MetadataUrl<T> build(MetadataUrlHelper helper, String suffix1, Function<String, T> converter) { 127 return build(helper, Optional.of(suffix1), Optional.<String> absent(), converter); 128 } 129 130 private static <T> MetadataUrl<T> build(MetadataUrlHelper helper, Optional<String> suffix1, Optional<String> suffix2, Function<String, T> converter) { 131 checkNotNull(helper, "helper"); 132 checkNotBlank(suffix1, "suffix1"); 133 checkNotBlank(suffix2, "suffix2"); 134 checkNotNull(converter, "converter"); 135 String url = helper.prefix + helper.fqdn + (suffix1.isPresent() ? suffix1.get() : ""); 136 Stopwatch sw = createStarted(); 137 HttpWaitResult result = helper.urlCache.getUnchecked(url); 138 logger.info(format("[%s] - %s", url, getTime(sw))); 139 Optional<String> content = getContent(result); 140 if (!content.isPresent() && suffix2.isPresent()) { 141 sw = createStarted(); 142 url = helper.prefix + helper.fqdn + (suffix2.isPresent() ? suffix2.get() : ""); 143 logger.info(String.format("[%s] - %s", url, getTime(sw))); 144 result = helper.urlCache.getUnchecked(url); 145 content = getContent(result); 146 } 147 Optional<T> metadata = content.isPresent() ? Optional.of(converter.apply(content.get())) : Optional.<T> absent(); 148 MetadataUrl.Builder<T> builder = MetadataUrl.builder(); 149 return builder.url(url).content(content).converter(converter).metadata(metadata).build(); 150 } 151 152 private static Optional<String> getContent(HttpWaitResult result) { 153 if (!SUCCESS.equals(result.getStatus())) { 154 return absent(); 155 } 156 HttpRequestResult request = result.getFinalRequestResult(); 157 Optional<String> responseBody = request.getResponseBody(); 158 if (!responseBody.isPresent() || isBlank(responseBody.get())) { 159 return absent(); 160 } else { 161 return responseBody; 162 } 163 } 164 165 /** 166 * Grabs the first 25k in content from a URL and stashes it onto the local file system. Times out after 5 seconds, no re-tries. 167 */ 168 protected LoadingCache<String, HttpWaitResult> getFastFileSystemCacher() { 169 HttpContext context = HttpContext.builder().quiet(true).maxBytes("25k").maxRetries(0).requestTimeout("5s").overallTimeout("5s").build(); 170 CacheLoader<String, HttpWaitResult> loader = Caches.buildUrlCache(context); 171 return CacheBuilder.newBuilder().build(loader); 172 } 173 174 private static class MetadataUrlHelper { 175 176 private final String prefix; 177 private final String fqdn; 178 private final LoadingCache<String, HttpWaitResult> urlCache; 179 180 public MetadataUrlHelper(String prefix, String fqdn, LoadingCache<String, HttpWaitResult> urlCache) { 181 this.prefix = checkNotBlank(prefix, "prefix"); 182 this.fqdn = checkNotBlank(fqdn, "fqdn"); 183 this.urlCache = checkNotNull(urlCache, "urlCache"); 184 } 185 } 186}