/*
* Copyright (c) 2010, CollabNet
* 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 CollabNet 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 COLLABNET 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 com.collabnet.labmanagement.api;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpMethodBase;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.protocol.Protocol;
import org.apache.commons.httpclient.protocol.ProtocolSocketFactory;
import org.cyberneko.html.parsers.SAXParser;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
/**
* Connects to LabManagement and returns a proxy {@link LabManagement} object.
*/
public class LabManagementConnector {
/**
* Configures the JVM to bypass HTTPS certificate checks, so that we can talk to HTTPS servers with bogus certificates.
*/
public static void acceptInvalidCertificates() {
Protocol acceptAllSsl = new Protocol("https", (ProtocolSocketFactory)new InsecureSSLProtocolSocketFactory(), 443);
Protocol.registerProtocol("https", acceptAllSsl);
}
/**
* Connects anonymously to the LabManager service.
*/
public static LabManagement connect(URL cubit) {
return connect(cubit,null,null);
}
/**
* Connects to the LabManager service.
*
* <p>
* Note that the actual authentication doesn't happen until you make a method call that requires
* authentication.
*
* @param cubit
* The URL of the labmanager, such as "https://mgr.cubit.example.org/"
* @param user
* The login id of the user to connect as.
* @param key
* The cubit api key of the user.
*/
public static LabManagement connect(URL cubit, String user, String key) {
return connect(cubit,new HttpClient(),user,key);
}
/**
* Connects to the LabManager service.
*
* <p>
* Note that the actual authentication doesn't happen until you make a method call that requires
* authentication.
*
* @param cubit
* The URL of the labmanager, such as "https://mgr.cubit.example.org/"
* @param user
* The login id of the user to connect as.
* @param key
* The cubit api key of the user.
* @param httpClient
* HTTP client to be used to talk to the service. By instantiating this on your own
* and configuring it upfront, you can tweak the way the client talks to the service
* (for example, setting the HTTP proxy.)
*/
public static LabManagement connect(URL cubit, HttpClient httpClient, String user, String key) {
return connect(cubit,httpClient,user,key,null);
}
public static LabManagement connect(URL cubit, HttpClient httpClient, String user, String key, String password) {
return new LabManagementImpl(cubit,httpClient,user,key,password).asProxy();
}
/**
* Retrieves the API key by the username and password.
* If necessary, this method requests the manager to generate a key.
*/
public static String retrieveApiKey(URL cubit, String user, String password) throws IOException {
return retrieveApiKey(cubit,new HttpClient(),user,password);
}
private static PostMethod postWithRetry(HttpClient httpClient, String location, String... nameAndValues) throws IOException {
while (true) {
PostMethod page = new PostMethod(location);
try {
for (int i=0; i<nameAndValues.length; i+=2) {
page.addParameter(nameAndValues[i],nameAndValues[i+1]);
}
int r = httpClient.executeMethod(page);
if (300<=r && r<309) {
location = page.getResponseHeader("Location").getValue();
continue;
}
if (r !=200)
throw new IOException("Failed to POST "+location+": "+ page.getResponseBodyAsString());
// don't let the connection released on this one
PostMethod result = page;
page = null;
return result;
} catch (IOException e) {
throw (IOException)new IOException("Failed to access "+location).initCause(e);
} finally {
if (page!=null)
page.releaseConnection();
}
}
}
public static String loginAndRetrieve(URL cubit, String pageYouWant, HttpClient httpClient, String user, String password) throws IOException {
login(cubit, httpClient, user, password);
GetMethod page = new GetMethod(new URL(cubit, pageYouWant).toExternalForm());
try {
httpClient.executeMethod(page);
return page.getResponseBodyAsString();
} finally {
page.releaseConnection();
}
}
public static Document loginAndParse(URL cubit, String pageYouWant, HttpClient httpClient, String user, String password) throws IOException {
login(cubit, httpClient, user, password);
GetMethod page = new GetMethod(new URL(cubit, pageYouWant).toExternalForm());
try {
httpClient.executeMethod(page);
return parsePage(page);
} finally {
page.releaseConnection();
}
}
private static PostMethod login(URL cubit, HttpClient httpClient, String user, String password) throws IOException {
PostMethod r = postWithRetry(httpClient, new URL(cubit, "/cubit/login").toExternalForm(),
"userid", user,
"password", password,
"auth", "Login");
r.getResponseBody(); // buffer the response
if (parsePage(r).selectSingleNode("//FORM[@id='loginform']")!=null)
throw new IOException("login failed");
return r;
}
/**
* Retrieves the API key by the username and password.
* If necessary, this method requests the manager to generate a key.
*/
public static String retrieveApiKey(URL cubit, HttpClient httpClient, String user, String password) throws IOException {
PostMethod page = login(cubit,httpClient,user,password);
for (int i=0; true; i++) {
Document rsp = parsePage(page);
Element key = (Element)rsp.selectSingleNode("//*[@id='api_key_shown']");
if (key!=null) return key.getTextTrim();
if (i!=0)
throw new IOException("Server responded with 200 but we still can't find the API key");
// API key doesn't exist yet. create one
page = postWithRetry(httpClient, new URL(cubit,"/cubit/user").toExternalForm(),
"create_apikey","create_apikey");
}
}
private static Document parsePage(HttpMethodBase page) throws IOException {
// configure a non-validating parser that ignores DTD
SAXParser sp = new SAXParser();
// sp.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd",false);
try {
return new SAXReader(sp).read(
new InputStreamReader(page.getResponseBodyAsStream(), page.getResponseCharSet()));
} catch (DocumentException e) {
throw (IOException)new IOException("Failed to parse the response HTML into DOM").initCause(e);
}
}
}