diff --git a/README.md b/README.md
index c7b4eef..e0862a7 100755
--- a/README.md
+++ b/README.md
@@ -2,11 +2,11 @@
## Description
-This bundle provides a "ready to use" OSGI connector for the new Akamai CCU REST API. This connector has been written in groovy using the http-builder framework.
+This bundle provides a "ready to use" OSGI connector for the new Akamai Open CCU V2 REST API. This connector has been written in groovy using the http-builder framework.
It is designed to enable cache invalidation for AEM/CQ CMS when assets get invalidated. It can be easily configured with your own credentials
and settings. The connector provides all services that you can request via the REST API like getPurgeStatus(), getQueueStatus(), and the most important purge().
-The bundle is made to be as light as possible and can be installed just by itself. You will need groovy-all to be installed along with some others bundles like
+The bundle is made to be as light as possible and can be installed just by itself. You will need groovy-all version 2.4.7 to be installed along with some others bundles like
httpclient, commons-collections, commons-lang ... but they usually are already there.
## Implementation
@@ -45,12 +45,13 @@ Each of these classes can be configured to fit you need and your Akamai credenti
```
-userName/password: The credentials that you use to connect to the Akamai control website.
+For more info on credentials see https://developer.akamai.com/introduction/Prov_Creds.html
defaultPurgeAction : The default purge if not specified.
- remove: (default) Remove the asset from the edge server and force the next request to the asset to reach the origin.
@@ -93,4 +94,4 @@ This project is open source under MIT License.
## More information
-If you want to learn more about the CCU REST API: https://api.ccu.akamai.com/ccu/v2/docs/index.html
+If you want to learn more about the CCU REST API: https://developer.akamai.com/introduction/
diff --git a/pom.xml b/pom.xml
index 01f165e..299991f 100755
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
com.velir.aem
akamai-ccu-rest-api-connector
- 1.1-SNAPSHOT
+ 2.0-SNAPSHOT
Akamai CCU REST API Connector
Provide a osgi bundle to manage requests to the Akamai CCU REST API.
https://github.com/Velir/akamai-ccu-rest-api-connector
@@ -60,9 +60,7 @@
UTF-8
1.6
1.6.4
- 2.1.3
- 1.4
- 2.0
+ 2.4.7
@@ -148,6 +146,13 @@
provided
+
+ commons-codec
+ commons-codec
+ 1.5
+ provided
+
+
org.codehaus.groovy
groovy-all
@@ -158,7 +163,7 @@
org.codehaus.groovy.modules.http-builder
http-builder
- 0.7
+ 0.7.1
@@ -259,7 +264,36 @@
+ src/main/groovy
+ src/test/groovy
+
+
+ src/main/resources
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.1
+
+ groovy-eclipse-compiler
+ 1.7
+ 1.7
+
+
+
+ org.codehaus.groovy
+ groovy-eclipse-compiler
+ 2.9.0-01
+
+
+ org.codehaus.groovy
+ groovy-eclipse-batch
+ 2.3.4-01
+
+
+
org.apache.felix
maven-bundle-plugin
@@ -292,7 +326,7 @@
org.apache.felix
maven-scr-plugin
- 1.16.0
+ 1.17.0
generate-scr-scrdescriptor
@@ -301,18 +335,34 @@
+
+ true
+
org.apache.maven.plugins
maven-compiler-plugin
3.0
+ groovy-eclipse-compiler
${project.build.jdk}
${project.build.jdk}
${project.build.sourceEncoding}
true
true
+
+
+ org.codehaus.groovy
+ groovy-eclipse-compiler
+ 2.9.0-01
+
+
+ org.codehaus.groovy
+ groovy-eclipse-batch
+ 2.3.4-01
+
+
org.apache.maven.plugins
@@ -326,31 +376,6 @@
-
- org.codehaus.gmaven
- gmaven-plugin
- ${gmaven.version}
-
-
-
- ${gmaven.provider.version}
-
-
- generateStubs
- compile
- generateTestStubs
- testCompile
-
-
-
-
-
- org.codehaus.groovy
- groovy-all
- ${groovy.version}
-
-
-
\ No newline at end of file
diff --git a/src/main/groovy/com/velir/aem/akamai/ccu/Activator.groovy b/src/main/groovy/com/velir/aem/akamai/ccu/Activator.groovy
index ab503e6..cf5fe02 100755
--- a/src/main/groovy/com/velir/aem/akamai/ccu/Activator.groovy
+++ b/src/main/groovy/com/velir/aem/akamai/ccu/Activator.groovy
@@ -11,13 +11,13 @@ import org.slf4j.LoggerFactory
* @author Sebastien Bernard
*/
class Activator implements BundleActivator {
- private static final Logger LOG = LoggerFactory.getLogger(Activator.class);
+ private static final Logger LOG = LoggerFactory.getLogger(Activator)
- public void start(final BundleContext context) throws Exception {
- LOG.info(context.getBundle().getSymbolicName() + " started");
+ void start(final BundleContext context) throws Exception {
+ LOG.info(context.getBundle().getSymbolicName() + " started")
}
- public void stop(final BundleContext context) throws Exception {
- LOG.info(context.getBundle().getSymbolicName() + " stopped");
+ void stop(final BundleContext context) throws Exception {
+ LOG.info(context.getBundle().getSymbolicName() + " stopped")
}
}
diff --git a/src/main/groovy/com/velir/aem/akamai/ccu/PurgeResponse.groovy b/src/main/groovy/com/velir/aem/akamai/ccu/PurgeResponse.groovy
index 2d36053..6149f97 100755
--- a/src/main/groovy/com/velir/aem/akamai/ccu/PurgeResponse.groovy
+++ b/src/main/groovy/com/velir/aem/akamai/ccu/PurgeResponse.groovy
@@ -2,6 +2,7 @@ package com.velir.aem.akamai.ccu
import groovy.transform.ToString
+import static org.apache.http.HttpStatus.SC_CREATED
/**
* PurgeStatus -
*
@@ -17,11 +18,11 @@ class PurgeResponse {
long pingAfterSeconds
String supportId
- public static PurgeResponse noResponse() {
- return new PurgeResponse(httpStatus: -1, detail: "Nothing has been sent because the query was not valid")
+ static PurgeResponse noResponse() {
+ new PurgeResponse(httpStatus: -1, detail: "Nothing has been sent because the query was not valid")
}
boolean isSuccess() {
- return httpStatus == 201
+ httpStatus == SC_CREATED
}
}
diff --git a/src/main/groovy/com/velir/aem/akamai/ccu/PurgeStatus.groovy b/src/main/groovy/com/velir/aem/akamai/ccu/PurgeStatus.groovy
index dc62868..d381c31 100755
--- a/src/main/groovy/com/velir/aem/akamai/ccu/PurgeStatus.groovy
+++ b/src/main/groovy/com/velir/aem/akamai/ccu/PurgeStatus.groovy
@@ -12,13 +12,13 @@ class PurgeStatus {
String purgeId
String supportId
int httpStatus
- long completionTime
+ String completionTime
String submittedBy
String purgeStatus
String submissionTime
long pingAfterSeconds
- public static PurgeStatus noStatus(){
- return new PurgeStatus(httpStatus: -1, purgeStatus: "The request was not sent")
+ static PurgeStatus noStatus(){
+ new PurgeStatus(httpStatus: -1, purgeStatus: "The request was not sent")
}
}
diff --git a/src/main/groovy/com/velir/aem/akamai/ccu/Timestamp.groovy b/src/main/groovy/com/velir/aem/akamai/ccu/Timestamp.groovy
new file mode 100755
index 0000000..7121f02
--- /dev/null
+++ b/src/main/groovy/com/velir/aem/akamai/ccu/Timestamp.groovy
@@ -0,0 +1,19 @@
+package com.velir.aem.akamai.ccu
+
+import groovy.transform.CompileStatic
+
+/**
+ * Timestamp - Date Formatter
+ *
+ * @author Kai Rasmussen
+ */
+@CompileStatic
+class Timestamp {
+ private static final String CCU_FORMAT = "yyyyMMdd'T'HH:mm:ssZ"
+ public static final String UTC = "UTC"
+ public static final TimeZone UTCTZ = TimeZone.getTimeZone(UTC)
+
+ static String getTimestamp(Date date){
+ date.format(CCU_FORMAT, UTCTZ)
+ }
+}
\ No newline at end of file
diff --git a/src/main/groovy/com/velir/aem/akamai/ccu/auth/Authorization.groovy b/src/main/groovy/com/velir/aem/akamai/ccu/auth/Authorization.groovy
new file mode 100755
index 0000000..f8e9b67
--- /dev/null
+++ b/src/main/groovy/com/velir/aem/akamai/ccu/auth/Authorization.groovy
@@ -0,0 +1,38 @@
+package com.velir.aem.akamai.ccu.auth
+
+import com.velir.aem.akamai.ccu.Timestamp
+import com.velir.aem.akamai.ccu.impl.CcuManagerImpl
+import groovy.transform.builder.Builder
+import groovyx.net.http.Method
+
+import static java.util.UUID.randomUUID
+
+/**
+ * Authorization -
+ *
+ * @author Kai Rasmussen
+ */
+@Builder
+class Authorization {
+ CcuManagerImpl.Credentials credentials
+ String path, rootCcuUrl
+ HashMap body, headers
+ Method method
+
+ String getAuthorization(){
+ String timeStamp = use(Timestamp){ new Date().timestamp }
+ String nonce = randomUUID().toString()
+ String unsignedAuth = "EG1-HMAC-SHA256 client_token=${credentials.clientToken};access_token=${credentials.accessToken};timestamp=${timeStamp};nonce=${nonce};"
+ String signedAuth = signAuth(path, unsignedAuth, timeStamp, body, method, headers)
+ "${unsignedAuth}signature=${signedAuth}"
+ }
+
+ private String signAuth(String path, String auth, String timestamp, HashMap body, Method method, HashMap headers) {
+ Signature sigBuilder = Signature.builder()
+ .secret(credentials.clientSecret).scheme("https").path(path)
+ .timestamp(timestamp).host(rootCcuUrl.replaceFirst("https://", ""))
+ .requestHeaders(headers).postBody(body).method(method).auth(auth)
+ .build()
+ sigBuilder.signature
+ }
+}
diff --git a/src/main/groovy/com/velir/aem/akamai/ccu/auth/Signature.groovy b/src/main/groovy/com/velir/aem/akamai/ccu/auth/Signature.groovy
new file mode 100755
index 0000000..95c893f
--- /dev/null
+++ b/src/main/groovy/com/velir/aem/akamai/ccu/auth/Signature.groovy
@@ -0,0 +1,71 @@
+package com.velir.aem.akamai.ccu.auth
+
+import groovy.json.JsonOutput
+import groovy.transform.builder.Builder
+import groovyx.net.http.Method
+
+import javax.crypto.Mac
+import javax.crypto.spec.SecretKeySpec
+import java.security.MessageDigest
+
+import static groovyx.net.http.Method.POST
+import static javax.crypto.Mac.getInstance
+import static org.apache.commons.codec.binary.Base64.encodeBase64String
+import static org.apache.commons.lang.StringUtils.EMPTY
+/**
+ * AuthorizationBuilder - Responsible for translating a request into a signature
+ *
+ * @author Kai Rasmussen
+ */
+
+@Builder
+class Signature {
+ private static final String HMAC_ALG = "HmacSHA256"
+ private static final String CHARSET = "UTF-8"
+ public static final String MD_ALG = "SHA-256"
+
+ String secret, auth, scheme, host, path, timestamp
+ HashMap requestHeaders, postBody
+ Method method
+
+ String getSignature() {
+ String signingKey = sign(timestamp, secret.getBytes(CHARSET))
+ String toSign = "${canonicalRequest}${auth}"
+ sign(toSign, signingKey.getBytes(CHARSET))
+ }
+
+ private String getCanonicalRequest(){
+ "${method.toString()}\t${scheme}\t${host}\t${path}\t${canonicalizeHeaders}\t${contentHash}\t"
+ }
+
+ private String getCanonicalizeHeaders(){
+ requestHeaders?requestHeaders.inject(''){ str, key, value ->
+ value = (value.trim() =~ /s+/).replaceAll(' ')
+ if(value){
+ str += "${key.toLowerCase()}:${value}\t"
+ }
+ } : EMPTY
+ }
+
+ private String getContentHash(){
+ String hash = EMPTY
+ if(method == POST && postBody){
+ String body = JsonOutput.toJson(postBody)
+ MessageDigest md = MessageDigest.getInstance(MD_ALG)
+ byte[] bytes = body.bytes
+ md.update(bytes, 0, bytes.length)
+ byte[] digest = md.digest()
+ hash = encodeBase64String(digest)
+ }
+ hash
+ }
+
+ private static String sign(String s, byte[] key) {
+ SecretKeySpec signingKey = new SecretKeySpec(key, HMAC_ALG)
+ Mac mac = getInstance(HMAC_ALG)
+ mac.init(signingKey)
+ byte[] valueBytes = s.getBytes(CHARSET)
+ byte[] bytes = mac.doFinal(valueBytes)
+ encodeBase64String(bytes)
+ }
+}
diff --git a/src/main/groovy/com/velir/aem/akamai/ccu/impl/AkamaiEventHandler.groovy b/src/main/groovy/com/velir/aem/akamai/ccu/impl/AkamaiEventHandler.groovy
index e71ca88..5b34b26 100755
--- a/src/main/groovy/com/velir/aem/akamai/ccu/impl/AkamaiEventHandler.groovy
+++ b/src/main/groovy/com/velir/aem/akamai/ccu/impl/AkamaiEventHandler.groovy
@@ -18,28 +18,28 @@ import org.slf4j.LoggerFactory
* @author Sebastien Bernard
*/
@Component(label = "Akamai Event Handler", description = "Listen to repository replication notification to invalidate Akamai cache when it is needed", metatype = true, immediate = true)
-@Service(value = [AkamaiEventHandler.class, EventHandler.class])
+@Service(value = [AkamaiEventHandler, EventHandler])
@org.apache.felix.scr.annotations.Properties(value = [
@Property(name = EventConstants.EVENT_TOPIC, value = ReplicationAction.EVENT_TOPIC, label = "Event topic"),
@Property(name = "pathsHandled", value = ["/content/dam"], label = "Handled paths")
])
class AkamaiEventHandler implements EventHandler {
- private final static Logger LOG = LoggerFactory.getLogger(AkamaiEventHandler.class)
+ private final static Logger LOG = LoggerFactory.getLogger(AkamaiEventHandler)
@org.apache.felix.scr.annotations.Reference
- private JobManager jobManager;
+ private JobManager jobManager
- private Set pathsHandled;
+ private Set pathsHandled
@Override
- public void handleEvent(Event event) {
+ void handleEvent(Event event) {
if (EventUtil.isLocal(event)) {
LOG.debug("Start handling event to add Akamai job")
String[] paths = PropertiesUtil.toStringArray(event.getProperty(FlushAkamaiItemsJob.PATHS))
- Set validPaths = filterValidPath(paths);
+ Set validPaths = filterValidPath(paths)
if (!validPaths.isEmpty()) {
- jobManager.addJob(FlushAkamaiItemsJob.JOB_TOPIC, buildJobProperties(validPaths));
+ jobManager.addJob(FlushAkamaiItemsJob.JOB_TOPIC, buildJobProperties(validPaths))
LOG.debug("Akamai job Added")
} else {
LOG.debug("{} has no valid path(s) to purge. No Job added", paths)
@@ -48,7 +48,7 @@ class AkamaiEventHandler implements EventHandler {
}
private Set filterValidPath(String[] paths) {
- Set validPaths = new HashSet<>();
+ Set validPaths = new HashSet<>()
for (String path : paths) {
if (path) {
for (String validPath : pathsHandled) {
@@ -58,23 +58,23 @@ class AkamaiEventHandler implements EventHandler {
}
}
}
- return validPaths
+ validPaths
}
private static Map buildJobProperties(Set paths) {
- Map jobProperties = new HashMap();
- jobProperties.put(FlushAkamaiItemsJob.PATHS, paths);
- return jobProperties;
+ Map jobProperties = new HashMap()
+ jobProperties.put(FlushAkamaiItemsJob.PATHS, paths)
+ jobProperties
}
@SuppressWarnings("GroovyUnusedDeclaration")
protected void activate(ComponentContext context) {
pathsHandled = new HashSet()
- pathsHandled.addAll(PropertiesUtil.toStringArray(context.getProperties().get("pathsHandled")));
+ pathsHandled.addAll(PropertiesUtil.toStringArray(context.getProperties().get("pathsHandled")))
}
@SuppressWarnings("GroovyUnusedDeclaration")
protected void deactivate() {
- pathsHandled = Collections.emptySet();
+ pathsHandled = Collections.emptySet()
}
}
diff --git a/src/main/groovy/com/velir/aem/akamai/ccu/impl/CcuManagerImpl.groovy b/src/main/groovy/com/velir/aem/akamai/ccu/impl/CcuManagerImpl.groovy
index 21c52aa..074a7cd 100755
--- a/src/main/groovy/com/velir/aem/akamai/ccu/impl/CcuManagerImpl.groovy
+++ b/src/main/groovy/com/velir/aem/akamai/ccu/impl/CcuManagerImpl.groovy
@@ -1,115 +1,132 @@
package com.velir.aem.akamai.ccu.impl
-import java.security.InvalidParameterException
-import java.util.concurrent.Future
-import java.util.concurrent.TimeUnit
-
-import com.velir.aem.akamai.ccu.CcuManager
-import com.velir.aem.akamai.ccu.PurgeAction
-import com.velir.aem.akamai.ccu.PurgeDomain
-import com.velir.aem.akamai.ccu.PurgeResponse
-import com.velir.aem.akamai.ccu.PurgeStatus
-import com.velir.aem.akamai.ccu.PurgeType
-import com.velir.aem.akamai.ccu.QueueStatus
-import groovyx.net.http.AsyncHTTPBuilder
-import groovyx.net.http.HttpResponseException
-import org.apache.felix.scr.annotations.Activate
-import org.apache.felix.scr.annotations.Component
-import org.apache.felix.scr.annotations.ConfigurationPolicy
-import org.apache.felix.scr.annotations.Deactivate
-import org.apache.felix.scr.annotations.Property
-import org.apache.felix.scr.annotations.Service
+
+import com.velir.aem.akamai.ccu.*
+import com.velir.aem.akamai.ccu.auth.Authorization
+import groovyx.net.http.HTTPBuilder
+import groovyx.net.http.Method
+import org.apache.felix.scr.annotations.*
import org.osgi.service.component.ComponentContext
import org.slf4j.Logger
import org.slf4j.LoggerFactory
+
+import java.security.InvalidParameterException
+
+import static groovyx.net.http.ContentType.JSON
+import static groovyx.net.http.Method.GET
+import static groovyx.net.http.Method.POST
+import static org.apache.commons.lang.StringUtils.EMPTY
+import static org.apache.sling.commons.osgi.PropertiesUtil.toString
/**
* CcuManagerImpl -
*
* @author Sebastien Bernard
*/
@Component(label = "Akamai CCU REST API Manager", description = "Manage calls to the Akamai CCU REST API", metatype = true, immediate = true, policy = ConfigurationPolicy.REQUIRE)
-@Service(value = [CcuManager.class])
+@Service(value = [CcuManager])
+@Properties([
+ @Property(name = "clientToken", description = "", label = "Client Token"),
+ @Property(name = "clientSecret", description = "", label = "Client Secret", passwordValue = ""),
+ @Property(name= "accessToken", description = "", label="Access Token")
+])
class CcuManagerImpl implements CcuManager {
- private static final Logger LOG = LoggerFactory.getLogger(CcuManagerImpl.class)
+ private static final Logger LOG = LoggerFactory.getLogger(CcuManagerImpl)
private static final String DEFAULT_CCU_URL = "https://api.ccu.akamai.com"
- public static final PurgeAction DEFAULT_PURGE_ACTION = PurgeAction.REMOVE
- public static final PurgeDomain DEFAULT_PURGE_DOMAIN = PurgeDomain.PRODUCTION
- public static final String CONTENT_TYPE = "application/json"
+ static final PurgeAction DEFAULT_PURGE_ACTION = PurgeAction.REMOVE
+ static final PurgeDomain DEFAULT_PURGE_DOMAIN = PurgeDomain.PRODUCTION
+ static final String AUTHORIZATION = 'Authorization'
+ static final String QUEUES_PATH = "/ccu/v2/queues/default"
+ static final String UTF_8 = "UTF-8"
+ public static final Closure VAL_NOT_NULL = { key, value -> value }
+
+ public class Credentials {
+ String clientSecret, clientToken, accessToken
+ }
@Property(name = "rootCcuUrl", label = "Akamai CCU API URL", value = "https://api.ccu.akamai.com")
- private String rootCcuUrl;
- @Property(name = "userName", label = "Username")
- private String userName;
- @Property(name = "password", label = "Password")
- private String password;
+ private String rootCcuUrl
@Property(name = "defaultPurgeAction", label = "Default purge action", description = "Can be invalidate, remove (default)", value = "remove")
- private PurgeAction defaultPurgeAction;
+ private PurgeAction defaultPurgeAction
@Property(name = "defaultPurgeDomain", label = "Default purge domain", description = "Can be staging, production (default)", value = "production")
- private PurgeDomain defaultPurgeDomain;
+ private PurgeDomain defaultPurgeDomain
+
+ private Credentials credentials
- private AsyncHTTPBuilder httpBuilder;
+ private HTTPBuilder httpBuilder
/**
* {@inheritDoc}
*/
@Override
- public PurgeResponse purgeByUrl(String url) {
- purgeByUrls([url]);
+ PurgeResponse purgeByUrl(String url) {
+ purgeByUrls([url])
}
/**
* {@inheritDoc}
*/
@Override
- public PurgeResponse purgeByUrls(Collection urls) {
- return purge(urls, PurgeType.ARL, defaultPurgeAction, defaultPurgeDomain);
+ PurgeResponse purgeByUrls(Collection urls) {
+ purge(urls, PurgeType.ARL, defaultPurgeAction, defaultPurgeDomain)
}
/**
* {@inheritDoc}
*/
@Override
- public PurgeResponse purgeByCpCode(String cpCode) {
- return purgeByCpCodes([cpCode]);
+ PurgeResponse purgeByCpCode(String cpCode) {
+ purgeByCpCodes([cpCode])
}
/**
* {@inheritDoc}
*/
@Override
- public PurgeResponse purgeByCpCodes(Collection cpCodes) {
- return purge(cpCodes, PurgeType.CPCODE, defaultPurgeAction, defaultPurgeDomain);
+ PurgeResponse purgeByCpCodes(Collection cpCodes) {
+ purge(cpCodes, PurgeType.CPCODE, defaultPurgeAction, defaultPurgeDomain)
}
/**
* {@inheritDoc}
*/
@Override
- public PurgeResponse purge(Collection objects, PurgeType purgeType, PurgeAction purgeAction, PurgeDomain purgeDomain) {
+ PurgeResponse purge(Collection objects, PurgeType purgeType, PurgeAction purgeAction, PurgeDomain purgeDomain) {
Collection uniqueObjects = removeDuplicate(objects)
if (!uniqueObjects) {
LOG.warn("No objects to invalidate")
- return PurgeResponse.noResponse();
+ return PurgeResponse.noResponse()
}
logDebug(purgeType, purgeAction, purgeDomain, uniqueObjects)
+ PurgeResponse purgeResponse = null
+ HashMap postBody = [
+ type : purgeType.name().toLowerCase(),
+ action : purgeAction.name().toLowerCase(),
+ domain : purgeDomain.name().toLowerCase(),
+ objects: uniqueObjects,
+ ]
+ httpBuilder.request(POST, JSON){
+ uri.path = QUEUES_PATH
+ headers[AUTHORIZATION] = getAuth(QUEUES_PATH, postBody, POST, headers as HashMap)
+ body = postBody
+ response.success = { resp, json ->
+ purgeResponse = new PurgeResponse(json.findAll(VAL_NOT_NULL))
+ }
+ response.failure = { resp, json ->
+ throw new RuntimeException("Error purging ${resp.status} ${json.detail}")
+ }
+ }
+ LOG.debug("Response {}", purgeResponse)
+ purgeResponse
+ }
- Future result = httpBuilder.post(
- path: "/ccu/v2/queues/default",
- requestContentType: CONTENT_TYPE,
- body: [
- type : purgeType.name().toLowerCase(),
- action : purgeAction.name().toLowerCase(),
- domain : purgeDomain.name().toLowerCase(),
- objects: uniqueObjects,
- ]) { resp, json -> return new PurgeResponse(json) }
-
-
- def response = result.get()
- LOG.debug("Response {}", response);
- return response;
+ private String getAuth(String path, HashMap body, Method method, HashMap headers) {
+ Authorization.builder()
+ .credentials(credentials).path(path).body(body)
+ .method(method).headers(headers).rootCcuUrl(rootCcuUrl)
+ .build().authorization
}
- private void logDebug(PurgeType purgeType, PurgeAction purgeAction, PurgeDomain purgeDomain, Collection uniqueObjects) {
+ private static void logDebug(PurgeType purgeType, PurgeAction purgeAction, PurgeDomain purgeDomain, Collection uniqueObjects) {
if(LOG.isDebugEnabled()){
LOG.debug("Request:")
LOG.debug("Type: {}", purgeType)
@@ -124,9 +141,9 @@ class CcuManagerImpl implements CcuManager {
* @param objects the list of objects
* @return a ordered set of objects
*/
- private Collection removeDuplicate(Collection objects) {
+ private static Collection removeDuplicate(Collection objects) {
objects.removeAll([null])
- return objects.unique()
+ objects.unique()
}
/**
@@ -135,56 +152,61 @@ class CcuManagerImpl implements CcuManager {
@Override
public PurgeStatus getPurgeStatus(String progressUri) {
if (!progressUri) {
- return PurgeStatus.noStatus();
+ return PurgeStatus.noStatus()
+ }
+ PurgeStatus purgeStatus = null
+ httpBuilder.request(GET, JSON){
+ uri.path = progressUri
+ headers[AUTHORIZATION] = getAuth(progressUri, [:] as HashMap, GET, headers as HashMap)
+ response.success = { resp, json ->
+ purgeStatus = new PurgeStatus(json.findAll(VAL_NOT_NULL))
+ }
+ response.failure = { resp, json ->
+ LOG.error("Error getting status", json)
+ }
}
- Future result = httpBuilder.get(
- path: progressUri,
- requestContentType: CONTENT_TYPE
- ) { resp, json -> return new PurgeStatus(json) }
-
- return result.get();
+ purgeStatus
}
/**
* {@inheritDoc}
*/
@Override
- public QueueStatus getQueueStatus() {
- Future result = httpBuilder.get(
- path: "/ccu/v2/queues/default",
- requestContentType: CONTENT_TYPE
- ) { resp, json -> return new QueueStatus(json) }
+ QueueStatus getQueueStatus() {
+ QueueStatus queueStatus = null
+ httpBuilder.request(GET, JSON){
+ uri.path = QUEUES_PATH
+ headers[AUTHORIZATION] = getAuth(QUEUES_PATH, [:] as HashMap, GET, headers as HashMap)
+ response.success = { resp, json ->
+ queueStatus = new QueueStatus(json.findAll(VAL_NOT_NULL))
+ }
+ response.failure = { resp, json ->
+ LOG.error("Error getting status", json)
+ }
+ }
- return result.get();
+ queueStatus
}
@Activate
protected void activate(ComponentContext context) {
- setRootCcuUrl(context.getProperties().get("rootCcuUrl"))
- setUserName(context.getProperties().get("userName"))
- setPassword(context.getProperties().get("password"))
- setDefaultPurgeAction(context.getProperties().get("defaultPurgeAction"))
- setDefaultPurgeDomain(context.getProperties().get("defaultPurgeDomain"))
-
- httpBuilder = new AsyncHTTPBuilder(
- timeout: TimeUnit.SECONDS.toMillis(5).toInteger(),
- poolSize: 5,
- uri: rootCcuUrl,
- )
- httpBuilder.setContentEncoding("utf-8")
- httpBuilder.auth.basic userName, password
- httpBuilder.handler.failure = { resp, json ->
- LOG.error("Error response : ${json}" )
- throw new HttpResponseException(resp)
- }
+ Dictionary props = context.properties
+ this.credentials = new Credentials()
+ setRootCcuUrl(toString(props.get("rootCcuUrl"), EMPTY))
+ setClientToken(toString(props.get("clientToken"), EMPTY))
+ setClientSecret(toString(props.get("clientSecret"), EMPTY))
+ setDefaultPurgeAction(toString(props.get("defaultPurgeAction"), EMPTY))
+ setDefaultPurgeDomain(toString(props.get("defaultPurgeDomain"), EMPTY))
+ setAccessToken(toString(props.get("accessToken"), EMPTY))
+ httpBuilder = new HTTPBuilder(rootCcuUrl)
+ httpBuilder.contentEncoding = UTF_8
}
@Deactivate
protected void deactivate() {
httpBuilder = null
- userName = null
- password = null
+ credentials = null
defaultPurgeAction = null
defaultPurgeDomain = null
}
@@ -193,24 +215,32 @@ class CcuManagerImpl implements CcuManager {
if (rootCcuUrl) {
this.rootCcuUrl = rootCcuUrl
} else {
- this.rootCcuUrl = DEFAULT_CCU_URL;
+ this.rootCcuUrl = DEFAULT_CCU_URL
}
}
- private void setUserName(String userName) {
- if (!userName) {
- throw InvalidParameterException("The username is mandatory");
+ private void setClientToken(String clientToken) {
+ if (!clientToken) {
+ throw new InvalidParameterException("The Client Token is mandatory")
}
- this.userName = userName
+ this.credentials.clientToken = clientToken
}
- private void setPassword(String password) {
- if (!password) {
- throw InvalidParameterException("The username is mandatory");
+ private void setClientSecret(String clientSecret) {
+ if (!clientSecret) {
+ throw new InvalidParameterException("The Client Secret is mandatory")
}
- this.password = password
+ this.credentials.clientSecret = clientSecret
}
+ private void setAccessToken(String accessToken) {
+ if (!accessToken) {
+ throw new InvalidParameterException("The Access token is mandatory")
+ }
+ this.credentials.accessToken = accessToken
+ }
+
+
private void setDefaultPurgeAction(String purgeAction) {
if (purgeAction) {
this.defaultPurgeAction = PurgeAction.valueOf(purgeAction.toUpperCase())
@@ -224,7 +254,7 @@ class CcuManagerImpl implements CcuManager {
if (purgeDomain) {
this.defaultPurgeDomain = PurgeDomain.valueOf(purgeDomain.toUpperCase())
} else {
- this.defaultPurgeDomain = DEFAULT_PURGE_DOMAIN;
+ this.defaultPurgeDomain = DEFAULT_PURGE_DOMAIN
}
}
}
diff --git a/src/main/groovy/com/velir/aem/akamai/ccu/impl/FlushAkamaiItemsJob.groovy b/src/main/groovy/com/velir/aem/akamai/ccu/impl/FlushAkamaiItemsJob.groovy
index a285f38..03b7965 100755
--- a/src/main/groovy/com/velir/aem/akamai/ccu/impl/FlushAkamaiItemsJob.groovy
+++ b/src/main/groovy/com/velir/aem/akamai/ccu/impl/FlushAkamaiItemsJob.groovy
@@ -18,23 +18,23 @@ import org.slf4j.LoggerFactory
* @author Sebastien Bernard
*/
@Component(label = "Akamai Flush Job", description = "Job that execute Akamai flush using Akamai CCU manager", metatype = true, immediate = true)
-@Service(value = [FlushAkamaiItemsJob.class, JobConsumer.class])
+@Service(value = [FlushAkamaiItemsJob, JobConsumer])
@org.apache.felix.scr.annotations.Properties(value = [
@Property(name = JobConsumer.PROPERTY_TOPICS, value = FlushAkamaiItemsJob.JOB_TOPIC),
@Property(name = "rootSiteUrl", value = "", label = "Root site url", description = "Scheme and domain to append at the beginning of the paths like http://www.velir.com")
])
class FlushAkamaiItemsJob implements JobConsumer {
- private static final Logger LOG = LoggerFactory.getLogger(FlushAkamaiItemsJob.class)
+ private static final Logger LOG = LoggerFactory.getLogger(FlushAkamaiItemsJob)
public static final String JOB_TOPIC = "com/velir/aem/akamai/ccu/impl/FlushAkamaiItemsJob"
public static final String PATHS = "paths"
@org.apache.felix.scr.annotations.Reference
private CcuManager ccuManager
- private String rootSiteUrl;
+ private String rootSiteUrl
@Override
- public JobConsumer.JobResult process(Job job) {
+ JobConsumer.JobResult process(Job job) {
Set pathsToPurge = getPathsToPurge(job)
LOG.debug("Start processing job to purge Akamai cache")
if (pathsToPurge.isEmpty()) {
@@ -42,15 +42,15 @@ class FlushAkamaiItemsJob implements JobConsumer {
return JobConsumer.JobResult.CANCEL
}
- Set absoluteUrls = prependPathWithRootUrl(pathsToPurge);
+ Set absoluteUrls = prependPathWithRootUrl(pathsToPurge)
logUrls(absoluteUrls)
PurgeResponse response = ccuManager.purgeByUrls(absoluteUrls)
- return convertToJobResult(response);
+ convertToJobResult(response)
}
- private logUrls(Set pathsToPurge) {
+ private static logUrls(Set pathsToPurge) {
if (LOG.isInfoEnabled()) {
LOG.info("Path(s) to purge:")
for (path in pathsToPurge) {
@@ -61,7 +61,7 @@ class FlushAkamaiItemsJob implements JobConsumer {
private Set prependPathWithRootUrl(Collection paths) {
if (!rootSiteUrl) {
- return paths;
+ return paths
}
Set urls = new HashSet(paths.size())
@@ -71,22 +71,22 @@ class FlushAkamaiItemsJob implements JobConsumer {
}
}
- return urls
+ urls
}
private static Set getPathsToPurge(Job job) {
- String[] pathsToInvalidate = PropertiesUtil.toStringArray(job.getProperty(PATHS));
+ String[] pathsToInvalidate = PropertiesUtil.toStringArray(job.getProperty(PATHS))
if (pathsToInvalidate == null) {
LOG.error("The property {} is mandatory to execute the job", PATHS)
return Collections.emptySet()
}
Set results = new HashSet()
results.addAll(pathsToInvalidate)
- return results;
+ results
}
static JobConsumer.JobResult convertToJobResult(PurgeResponse response) {
- return response.isSuccess() ? JobConsumer.JobResult.OK : JobConsumer.JobResult.FAILED;
+ response.isSuccess() ? JobConsumer.JobResult.OK : JobConsumer.JobResult.FAILED
}
public void activate(ComponentContext context) {
diff --git a/src/test/groovy/com/velir/aem/akamai/ccu/impl/CcuManagerImplTest.groovy b/src/test/groovy/com/velir/aem/akamai/ccu/impl/CcuManagerImplTest.groovy
index 5c15de8..359d97b 100755
--- a/src/test/groovy/com/velir/aem/akamai/ccu/impl/CcuManagerImplTest.groovy
+++ b/src/test/groovy/com/velir/aem/akamai/ccu/impl/CcuManagerImplTest.groovy
@@ -1,7 +1,5 @@
package com.velir.aem.akamai.ccu.impl
-import java.util.concurrent.ExecutionException
-
import com.github.tomakehurst.wiremock.WireMockServer
import com.velir.aem.akamai.ccu.PurgeAction
import com.velir.aem.akamai.ccu.PurgeDomain
@@ -10,22 +8,23 @@ import org.osgi.service.component.ComponentContext
import spock.lang.Specification
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig
+import static org.apache.http.HttpStatus.SC_CREATED
/**
* CcuManagerImplTest -
*
* @author Sebastien Bernard
*/
class CcuManagerImplTest extends Specification {
- private static WireMockServer wireMockServer;
- private static CcuManagerImpl ccuManager = new CcuManagerImpl();
+ private static WireMockServer wireMockServer
+ private static CcuManagerImpl ccuManager = new CcuManagerImpl()
def setupSpec() {
wireMockServer = new WireMockServer(wireMockConfig().port(4444))
wireMockServer.start()
- ComponentContext context = Mock(ComponentContext.class)
- context.getProperties() >> new Hashtable([rootCcuUrl: "http://localhost:4444", userName: "test", password: "test", defaultPurgeDomain: "staging"])
- ccuManager.activate(context);
+ ComponentContext context = Mock(ComponentContext)
+ context.getProperties() >> new Hashtable([rootCcuUrl: "http://localhost:4444", clientToken: "test", clientSecret: "test", accessToken : 'test', defaultPurgeDomain: "staging"])
+ ccuManager.activate(context)
}
def "PurgeByUrl"() {
@@ -56,7 +55,7 @@ class CcuManagerImplTest extends Specification {
def response = ccuManager.purgeByUrls(["http://test", "http://test2"])
then:
- response.httpStatus == 201
+ response.httpStatus == SC_CREATED
response.detail == "Request accepted."
response.estimatedSeconds == 420
response.purgeId == "95b5a092-043f-4af0-843f-aaf0043faaf0"
@@ -70,7 +69,7 @@ class CcuManagerImplTest extends Specification {
def response = ccuManager.purgeByCpCode("123456")
then:
- response.httpStatus == 201
+ response.httpStatus == SC_CREATED
response.detail == "Request accepted."
response.estimatedSeconds == 420
response.purgeId == "95b5a092-043f-4af0-843f-aaf0043faaf0"
@@ -84,7 +83,7 @@ class CcuManagerImplTest extends Specification {
def response = ccuManager.purgeByCpCodes(["123456", "789456"])
then:
- response.httpStatus == 201
+ response.httpStatus == SC_CREATED
response.detail == "Request accepted."
response.estimatedSeconds == 420
response.purgeId == "95b5a092-043f-4af0-843f-aaf0043faaf0"
@@ -98,7 +97,7 @@ class CcuManagerImplTest extends Specification {
def response = ccuManager.purge(["123456", "789456"], PurgeType.CPCODE, PurgeAction.INVALIDATE, PurgeDomain.PRODUCTION)
then:
- response.httpStatus == 201
+ response.httpStatus == SC_CREATED
response.detail == "Request accepted."
response.estimatedSeconds == 420
response.purgeId == "95b5a092-043f-4af0-843f-aaf0043faaf0"
@@ -118,7 +117,7 @@ class CcuManagerImplTest extends Specification {
status.purgeId == "142eac1d-99ab-11e3-945a-7784545a7784"
status.supportId == "17SY1392844709041263-238396512"
status.httpStatus == 200
- status.completionTime == 10
+ status.completionTime == '2016-06-17T19:28:15Z'
status.submittedBy == "test1"
status.purgeStatus == "In-Progress"
status.submissionTime == "2014-02-19T21:16:20Z"
@@ -141,7 +140,7 @@ class CcuManagerImplTest extends Specification {
ccuManager.purgeByUrls(["http://error-403"])
then:
- thrown(ExecutionException)
+ thrown(RuntimeException)
}
def cleanupSpec() {
diff --git a/src/test/resources/mappings/getPurgeStatus.json b/src/test/resources/mappings/getPurgeStatus.json
index 2cab79a..fa8b83b 100755
--- a/src/test/resources/mappings/getPurgeStatus.json
+++ b/src/test/resources/mappings/getPurgeStatus.json
@@ -9,6 +9,6 @@
"Content-Type": "application/json",
"Cache-Control": "no-cache"
},
- "body": "{\"originalEstimatedSeconds\": 480,\"progressUri\": \"/ccu/v2/purges/142eac1d-99ab-11e3-945a-7784545a7784\",\"originalQueueLength\": 6,\"purgeId\": \"142eac1d-99ab-11e3-945a-7784545a7784\",\"supportId\": \"17SY1392844709041263-238396512\",\"httpStatus\": 200,\"completionTime\": 10,\"submittedBy\": \"test1\",\"purgeStatus\": \"In-Progress\",\"submissionTime\": \"2014-02-19T21:16:20Z\",\"pingAfterSeconds\": 60}"
+ "body": "{\"originalEstimatedSeconds\": 480,\"progressUri\": \"/ccu/v2/purges/142eac1d-99ab-11e3-945a-7784545a7784\",\"originalQueueLength\": 6,\"purgeId\": \"142eac1d-99ab-11e3-945a-7784545a7784\",\"supportId\": \"17SY1392844709041263-238396512\",\"httpStatus\": 200,\"completionTime\": \"2016-06-17T19:28:15Z\",\"submittedBy\": \"test1\",\"purgeStatus\": \"In-Progress\",\"submissionTime\": \"2014-02-19T21:16:20Z\",\"pingAfterSeconds\": 60}"
}
}
\ No newline at end of file