/*
* JBoss, Home of Professional Open Source
* Copyright 2013, Red Hat, Inc. and/or its affiliates, and individual
* contributors by the @authors tag. See the copyright.txt in the
* distribution for a full listing of individual contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jboss.developer.stacks.client;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import org.apache.http.HttpHost;
import org.apache.http.HttpResponse;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.conn.params.ConnRoutePNames;
import org.apache.http.impl.client.DefaultHttpClient;
import org.jboss.developer.stacks.client.messages.JBossLoggingMessages;
import org.jboss.developer.stacks.client.messages.StacksMessages;
import org.jboss.developer.stacks.model.Stacks;
import org.jboss.developer.stacks.parser.Parser;
/**
* @author <a href="mailto:benevides@redhat.com">Rafael Benevides</a>
*
*/
public class StacksClient {
private StacksClientConfiguration actualConfiguration;
private StacksMessages msg;
public StacksClient() {
this(new DefaultStacksClientConfiguration());
}
public StacksClient(StacksClientConfiguration configuration) {
this(configuration, new JBossLoggingMessages());
}
public StacksClient(StacksClientConfiguration configuration, StacksMessages stacksMessagesImpl) {
this.msg = stacksMessagesImpl;
this.actualConfiguration = configuration;
}
private Stacks initializeStacks() {
InputStream inputStream = null;
try {
// Retrieve inputStream (local cache or remote)
inputStream = getStacksInputStream();
Stacks stacks = null;
if (inputStream != null) {
stacks = new Parser().parse(inputStream);
}
return stacks;
} catch (FileNotFoundException e) {
msg.showErrorMessageWithCause("FileNotFoundException", e);
return null;
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
msg.showErrorMessageWithCause("Something bad happened when closing the inputstream", e);
}
}
}
}
/**
* @return the stacks
*/
public Stacks getStacks() {
return initializeStacks();
}
/**
* @return the actualConfiguration
*/
public StacksClientConfiguration getActualConfiguration() {
return actualConfiguration;
}
/**
* @return
* @throws FileNotFoundException
*
*/
private InputStream getStacksInputStream() throws FileNotFoundException {
InputStream repoStream = getCachedRepoStream(false);
URL url = actualConfiguration.getUrl();
// if cache expired
if (repoStream == null && actualConfiguration.isOnline()) {
msg.showDebugMessage("Local cache file " + getLocalCacheFile() + " doesn't exist or cache has been expired");
try {
msg.showDebugMessage("Retrieving Stacks from Remote repository " + url);
repoStream = retrieveStacksFromRemoteRepository(url);
setCachedRepoStream(repoStream);
msg.showDebugMessage("Forcing the use of local cache after download file without error from " + url);
repoStream = getCachedRepoStream(true);
} catch (Exception e) {
msg.showWarnMessage("It was not possible to contact the repository at " + url + " . Cause " + e.getMessage());
msg.showWarnMessage("Falling back to cache!");
repoStream = getCachedRepoStream(true);
}
}
// If the Repostream stills empty after falling back to cache
if (repoStream == null) {
msg.showWarnMessage("Cache empty. Falling back to embed file");
return this.getClass().getResourceAsStream("/stacks.yaml");
} else {
return repoStream;
}
}
private InputStream retrieveStacksFromRemoteRepository(final URL url) throws Exception {
if (url.getProtocol().startsWith("http")) {
HttpGet httpGet = new HttpGet(url.toURI());
DefaultHttpClient client = new DefaultHttpClient();
configureProxy(client);
HttpResponse httpResponse = client.execute(httpGet);
switch (httpResponse.getStatusLine().getStatusCode()) {
case 200:
msg.showDebugMessage("Connected to repository! Getting available Stacks");
break;
case 404:
msg.showErrorMessage("Failed! (Stacks file not found: " + url + ")");
return null;
default:
msg.showErrorMessage("Failed! (server returned status code: "
+ httpResponse.getStatusLine().getStatusCode());
return null;
}
return httpResponse.getEntity().getContent();
} else if (url.getProtocol().startsWith("file")) {
return new FileInputStream(new File(url.toURI()));
}
return null;
}
private void configureProxy(DefaultHttpClient client) {
if (actualConfiguration.getProxyHost() != null && !actualConfiguration.getProxyHost().isEmpty()) {
String proxyHost = actualConfiguration.getProxyHost();
int proxyPort = actualConfiguration.getProxyPort();
HttpHost proxy = new HttpHost(proxyHost, proxyPort);
client.getParams().setParameter(ConnRoutePNames.DEFAULT_PROXY, proxy);
String proxyUsername = actualConfiguration.getProxyUser();
if (proxyUsername != null && !proxyUsername.isEmpty()) {
String proxyPassword = actualConfiguration.getProxyPassword();
AuthScope authScope = new AuthScope(proxyHost, proxyPort);
UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(proxyUsername, proxyPassword);
client.getCredentialsProvider().setCredentials(authScope, credentials);
}
}
}
/**
* Get the local file cache for each repository url
*
* @return
*/
public File getLocalCacheFile() {
// Remove no word character from the repo url
String repo = actualConfiguration.getUrl().toString().replaceAll("[^a-zA-Z_0-9]", "");
return new File(System.getProperty("java.io.tmpdir"), repo + "stacks.yaml");
}
private InputStream getCachedRepoStream(final boolean force) throws FileNotFoundException {
final String logmessage = "Local file %1s %2s used! Reason: Force:[%3b] - Online:[%4b] - LastModification: %5d/%6d";
File localCacheFile = getLocalCacheFile();
if (localCacheFile.exists()) {
int cacheSeconds = actualConfiguration.getCacheRefreshPeriodInSeconds();
boolean online = actualConfiguration.isOnline();
long cachedvalidity = 1000 * cacheSeconds;
long lastModified = localCacheFile.lastModified();
long timeSinceLastModification = System.currentTimeMillis() - lastModified;
// if online, consider the cache valid until it expires
if (force || !online || timeSinceLastModification <= cachedvalidity) {
msg.showDebugMessage(String.format(logmessage, localCacheFile, "was", force, online, timeSinceLastModification,
cachedvalidity));
return new FileInputStream(localCacheFile);
}
msg.showDebugMessage(String.format(logmessage, localCacheFile, "was not", force, online, timeSinceLastModification,
cachedvalidity));
}
return null;
}
private void setCachedRepoStream(final InputStream stream) throws IOException {
File localCacheFile = getLocalCacheFile();
msg.showDebugMessage("Content stored at " + localCacheFile);
if (!localCacheFile.exists()) {
localCacheFile.createNewFile();
}
FileOutputStream fos = new FileOutputStream(localCacheFile);
int i = 0;
while ((i = stream.read()) != -1) {
fos.write(i);
}
fos.close();
}
/**
* This will drop the local cache to force an online update.
*/
public void eraseRepositoryCache() {
File localFile = getLocalCacheFile();
localFile.delete();
msg.showDebugMessage("Cache erased");
}
}