/*
* Copyright (c) 2007-2009, Osmorc Development Team
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright notice, this list
* of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice, this
* list of conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution.
* * Neither the name of 'Osmorc Development Team' nor the names of its contributors may be
* used to endorse or promote products derived from this software without specific
* prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.osmorc.obrimport.springsource;
import com.intellij.ide.IdeBundle;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.util.io.StreamUtil;
import com.intellij.util.io.UrlConnectionUtil;
import org.jetbrains.annotations.NotNull;
import org.osmorc.obrimport.MavenRepository;
import org.osmorc.obrimport.Obr;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Implementation of {@link org.osmorc.obrimport.Obr} for the springsource bundle repository.
*
* @author <a href="mailto:janthomae@janthomae.de">Jan Thomä</a>
* @version $Id:$
*/
public class SpringSourceObr implements Obr
{
public String getDisplayName()
{
return "Springsource Enterprise Bundle Repository";
}
public boolean supportsMaven()
{
return true;
}
@NotNull
public ObrMavenResult[] queryForMavenArtifact(@NotNull String queryString,
@NotNull ProgressIndicator progressIndicator) throws
IOException
{
try
{
// TODO: make this more robust against URL changes.
List<ObrMavenResult> result = new ArrayList<ObrMavenResult>();
progressIndicator.setText("Connecting to " + getDisplayName() + "...");
// http://www.springsource.com/repository/app/search?query=log4j
// http://www.springsource.com/repository/app/bundle/version/detail?name=com.springsource.org.apache.log4j&version=1.2.15&searchType=bundlesByName&searchQuery=log4j
String url = "http://www.springsource.com/repository/app/search?query=" + URLEncoder.encode(queryString, "utf-8");
InputStream is = getInputStream(url, progressIndicator);
String contents = StreamUtil.readText(is, "utf-8");
progressIndicator.setText("Search completed. Getting results.");
progressIndicator.checkCanceled();
// now it's happy regexp-parsing
// first, get the results fragment, it's a single div
// <div id="results_fragment"> ... content ... </div> and no divs inbetween.
// we can do this with cheap indexof
int start = contents.indexOf("<div id=\"results-fragment\">");
int end = contents.indexOf("</div>", start);
contents = contents.substring(start, end);
is.close();
// next up, we extract all links in there which leads us to the search results
// <li>*whitespace*<a href="url">Package Name</a>Package Version*whitespace*</li>
// on multiple lines. We in fact only care for the URLs.
Matcher m = RESULT_PARSING_PATTERN.matcher(contents);
while (m.find())
{
String detailUrl = m.group(1);
// cut out the session id.
detailUrl = detailUrl.replaceAll(";jsessionid.*?\\?", "?");
// replace & with &
detailUrl = detailUrl.replace("&", "&");
String packageName = m.group(2);
progressIndicator.setText("Loading details for result " + packageName + "...");
// read the detail page.
// the detail url always starts with a / so we can just concatenate it
is = getInputStream("http://www.springsource.com" + detailUrl, progressIndicator);
String detail = StreamUtil.readText(is, "utf-8");
is.close();
progressIndicator.checkCanceled();
progressIndicator.setText("Details retrieved. Getting detail information...");
// we have the maven dependency in some nice html gibberish in there
// <groupId>org.apache.log4j</groupId>
// <artifactId>com.springsource.org.apache.log4j</artifactId>
// <version>1.2.15</version>
String groupId;
String artifactId;
String version;
String classifier = null;
Matcher groupMatcher = GROUP_ID_PATTERN.matcher(detail);
if (groupMatcher.find())
{
groupId = groupMatcher.group(1);
}
else
{
continue;
}
Matcher artifactMatcher = ARTIFACT_ID_PATTERN.matcher(detail);
if (artifactMatcher.find())
{
artifactId = artifactMatcher.group(1);
}
else
{
continue;
}
Matcher versionMatcher = VERSION_PATTERN.matcher(detail);
if (versionMatcher.find())
{
version = versionMatcher.group(1);
}
else
{
continue;
}
Matcher classifierMatcher = CLASSIFIER_PATTERN.matcher(detail);
if (classifierMatcher.find())
{
classifier = classifierMatcher.group(1);
}
// no else here ,since the classifier is optional
// finally add the result to the list
result.add(new ObrMavenResult(groupId, artifactId, version, classifier, this));
}
progressIndicator.setText("Done. " + result.size() + " artifacts found.");
return result.toArray(new ObrMavenResult[result.size()]);
}
catch (ProcessCanceledException ignored)
{
progressIndicator.setText("Canceled.");
return new ObrMavenResult[0];
}
finally
{
progressIndicator.setIndeterminate(false);
}
}
/**
* Helper function which gets an input stream for an url. It handles the process of creating a connection and and
* throws the appropriate exceptions should something not work out as expected.
*
* @param url the url to be loaded
* @param progressIndicator a progress indicator
* @return
* @throws IOException
*/
private static InputStream getInputStream(@NotNull String url, @NotNull ProgressIndicator progressIndicator)
throws IOException
{
HttpURLConnection urlConnection =
(HttpURLConnection) new URL(url).openConnection();
InputStream is = UrlConnectionUtil.getConnectionInputStreamWithException(urlConnection, progressIndicator);
int j = urlConnection.getResponseCode();
switch (j)
{
default:
//noinspection UnresolvedPropertyKey
throw new IOException(IdeBundle.message("error.connection.failed.with.http.code.N", j));
case 200:
progressIndicator.setIndeterminate(urlConnection.getContentLength() == -1);
break;
}
return is;
}
@NotNull
public MavenRepository[] getMavenRepositories()
{
return SPRINGSOURCE_REPOS;
}
private static final Pattern RESULT_PARSING_PATTERN = Pattern.compile("<a\\s+href=\"([^\"]+)\"[^>]*>([^<]+)");
private static final Pattern GROUP_ID_PATTERN = Pattern.compile("<groupId>(.*?)</groupId>");
private static final Pattern ARTIFACT_ID_PATTERN = Pattern.compile("<artifactId>(.*?)</artifactId>");
private static final Pattern VERSION_PATTERN = Pattern.compile("<version>(.*?)</version>");
private static final Pattern CLASSIFIER_PATTERN = Pattern.compile("<classifier>(.*?)</classifier>");
private static final MavenRepository[] SPRINGSOURCE_REPOS = new MavenRepository[]{
new MavenRepository("repository.springsource.com.release", "SpringSource OBR - Release",
"http://repository.springsource.com/maven/bundles/release"),
new MavenRepository("repository.springsource.com.external", "SpringSource OBR - External",
"http://repository.springsource.com/maven/bundles/external")
};
}