com.day.cq.polling.importer.HCImporter Maven / Gradle / Ivy
Show all versions of aem-sdk-api Show documentation
/*************************************************************************
*
* ADOBE CONFIDENTIAL
* ___________________
*
* Copyright 2014 Adobe Systems Incorporated
* All Rights Reserved.
*
* NOTICE: All information contained herein is, and remains
* the property of Adobe Systems Incorporated and its suppliers,
* if any. The intellectual and technical concepts contained
* herein are proprietary to Adobe Systems Incorporated and its
* suppliers and are protected by trade secret or copyright law.
* Dissemination of this information or reproduction of this material
* is strictly forbidden unless prior written permission is obtained
* from Adobe Systems Incorporated.
**************************************************************************/
package com.day.cq.polling.importer;
import java.io.IOException;
import java.io.InputStream;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Properties;
import org.apache.felix.scr.annotations.Property;
import org.apache.felix.scr.annotations.Service;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.Credentials;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.client.utils.HttpClientUtils;
import org.apache.http.entity.ContentType;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.sling.api.resource.Resource;
import org.osgi.service.component.ComponentContext;
import aQute.bnd.annotation.ConsumerType;
/**
* The HCImporter
is an abstract implementation of the
* {@link Importer} service interface supporting access to data using the HTTP
* protocol.
*
* Extensions of this base class must implement the
* {@link #importData(String, InputStream, String, Resource)} method to actually
* import the retrieved data.
*
* Instances of this base class must be initialized before being used and
* be destroyed when not used any more. If the extension is registered as an
* Declarative Services Component, this initialization and destroyal is being
* taken care of. Otherwise, the extension class must explicitly call the
* {@link #init()} and {@link #destroy()} methods respectively.
*
* The {@link #activate(ComponentContext)} and
* {@link #deactivate(ComponentContext)} methods may be overwritten by extension
* classes but the base class implementation must called in this case to
* ensure proper initialization and destroyal.
*/
@Component(metatype = false, componentAbstract = true)
@Service(Importer.class)
@Properties({
@Property(name = "service.description", value = "Abstract HTTP based Importer")
})
@ConsumerType
public abstract class HCImporter implements Importer {
/**
* The HTTP Client used by this importer instance. This field is initialized
* on demand by the {@link #getHttpClient()} method and cleared by the
* {@link #destroy()} method.
*/
private HttpClient httpClient;
/**
* Actually imports the data received from the data source into the target
* Resource
.
*
* Implementations of this method need not (and should not) close the
* data
input stream. This will be taken care of the caller of
* this method.
*
* @param scheme The scheme describing the data to import.
* @param data The data to be imported.
* @param characterEncoding If the data contains character data, such as
* XML, this parameter (may) provide the character encoding of
* the character data. The value of this parameter is extracted
* from the Content-Type
header of the HTTP response
* and may thus be empty or null
if not set by the
* server.
* @param target The Resource
into which the data is to be
* imported.
* @throws IOException May be thrown if an error occurs reading or writing
* the imported data.
* @throws ImportException May be thrown in any other error case.
*/
protected abstract void importData(String scheme, InputStream data,
String characterEncoding, Resource target) throws IOException;
// ---------- Importer interface
/**
* Imports data from the given source
which is expected to be
* an HTTP URL. This method uses the Apache Http Client library to
* request the source with a method provided by {@link #newHttpMethod(String)} the
* response stream is then provided to the
* {@link #importData(String, InputStream, String, Resource)} method, which
* must be implemented by the extension.
*
* @param scheme The scheme of the data source. This parameter is handed
* over to the
* {@link #importData(String, InputStream, String, Resource)}
* method.
* @param dataSource The HTTP URL to the data to be imported.
* @param target The Resource
into which the data is to be
* imported.
* @throws ImportException If an error occurs while accessing the source or
* if this class has not been initialized or if an error occurs
* while importing the actual data through
* {@link #importData(String, InputStream, String, Resource)}.
*/
public void importData(String scheme, String dataSource, Resource target) {
importData(scheme, dataSource, target, null, null);
}
/**
* Imports data from the given source
which is expected to be
* an HTTP URL. This method uses the Apache Http Client library to
* request the source with a method provided by {@link #newHttpMethod(String)} the
* response stream is then provided to the
* {@link #importData(String, InputStream, String, Resource)} method, which
* must be implemented by the extension.
*
* @param scheme The scheme of the data source. This parameter is handed
* over to the
* {@link #importData(String, InputStream, String, Resource)}
* method.
* @param dataSource The HTTP URL to the data to be imported.
* @param target The Resource
into which the data is to be
* imported.
* @param login The login to authenticate the request.
* @param password The password to authenticate the request.
* @throws ImportException If an error occurs while accessing the source or
* if this class has not been initialized or if an error occurs
* while importing the actual data through
* {@link #importData(String, InputStream, String, Resource)}.
*/
public void importData(String scheme, String dataSource, Resource target, String login, String password) {
// the method to execute
HttpRequestBase method = newHttpMethod(dataSource);
InputStream ins = null;
try {
// if login information is present, setup the authentication on the httpclient
if (login != null && login.length() > 0 && password != null && password.length() > 0) {
CredentialsProvider credsProvider = new BasicCredentialsProvider();
Credentials defaultcreds = new UsernamePasswordCredentials(login, password);
credsProvider.setCredentials(AuthScope.ANY, defaultcreds);
httpClient = HttpClientBuilder.create().setDefaultCredentialsProvider(credsProvider).build();
}
// execute the request and check the status
HttpResponse httpResp = getHttpClient().execute(method);
if(httpResp != null) {
int statusCode = httpResp.getStatusLine().getStatusCode();
switch (statusCode) {
case HttpStatus.SC_OK:
// this what we expect
break;
case HttpStatus.SC_NOT_MODIFIED:
// no change in data, so just get back out of here
return;
default:
// this is what we don't expect
throw new ImportException("Failed retreiving data"
+ httpResp.getStatusLine());
}
}
// access the raw response stream and import it
if(httpResp.getEntity() != null) {
ins = httpResp.getEntity().getContent();
}
String charset = null;
if(null != ContentType.getOrDefault(httpResp.getEntity()).getCharset()) {
charset = ContentType.getOrDefault(httpResp.getEntity()).getCharset().toString();
}
importData(scheme, ins, charset, target);
} catch (IOException e) {
throw new ImportException("Fatal transport error", e);
} catch (IllegalStateException ise) {
throw new ImportException(
"HttpImporter has not been initialized yet", ise);
} catch (Exception e) {
throw new ImportException(
"Unexpected problem while importing data", e);
} finally {
// close the response input stream
if (ins != null) {
try {
ins.close();
} catch (IOException ioe) {
}
}
// Release the connection.
method.releaseConnection();
}
}
/**
* Returns a HttpClient
instance used by the
* {@link #importData(String, InputStream, String, Resource)} method to
* access the data source URL. The HttpClient
instance is
* backed by a PoolingHttpClientConnectionManager
.
*
* This method cannot be overwritten. To configure the HttpClient, the
* {@link #init()} method should be called with appropriate parameters which
* are used to setup the client with an instance of the
* HttpClientParams
class.
*
* @return the HttpClient
instance.
* @see #init()
*/
protected final HttpClient getHttpClient() {
PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
if (httpClient == null) {
httpClient = HttpClients.custom()
.setConnectionManager(cm)
.build();
}
return httpClient;
}
/**
* Returns a HttpRequestBase
instance used by the
* {@link #importData(String, String, Resource)} method to actually retrieve
* the data to be imported from the data source URL.
*
* This base class implementation returns a plain HttpGet
.
* Extensions may overwrite to implement more sophisticated method setup.
*
* Each call to this method must return a new instance of the
* HttpRequestBase
implementation. The
* {@link #importData(String, String, Resource)} method using this instance
* takes care to release the connection of the method when being done.
*
* @param source The data source URL for which to prepare the method
* instance.
* @return The HttpRequestBase
to import the data, which is in
* this base class implementation a HttpGet
instance.
*/
protected HttpRequestBase newHttpMethod(String source) {
return new HttpGet(source);
}
/**
* Initializes this base class implementation. This method must be
* called before the {@link #importData(String, String, Resource)} may be
* used.
*
* If the class is used as a Declarative Services component, this method
* need not be called explicitly, since the
* {@link #activate(ComponentContext)} method will call it.
*
* This method may be called multiple times. After a first initialization
* further successive calls without {@link #destroy() destroyal} will have
* no effect.
*/
protected final void init() {
// nothing at the moment, might set configuration
}
/**
* Destroys this base class implementation. This method must be
* called when the instance is not used any more.
*
* If the class is used as a Declarative Services component, this method
* need not be called explicitly, since the
* {@link #deactivate(ComponentContext)} method will call it.
*
* This method may be called multiple times. After a first destroyal further
* successive calls without {@link #init() initialization} will have no
* effect.
*/
protected final void destroy() {
if (httpClient != null) {
HttpClientUtils.closeQuietly(httpClient);
}
httpClient = null;
}
// ---------- SCR integration
/**
* This base class implementation currently just calls the {@link #init()}
* method. Nevertheless it must be called if overwritten!
*
* @param context Component context
*/
protected void activate(ComponentContext context) {
init();
}
/**
* This base class implementation currently just calls the
* {@link #destroy()} method. Nevertheless it must be called if
* overwritten!
*
* @param context Component context
*/
protected void deactivate(ComponentContext context) {
destroy();
}
}