Skip to content

Commit

Permalink
Fix: Cache update implemented
Browse files Browse the repository at this point in the history
  • Loading branch information
Chris Wiechmann committed Jan 14, 2022
1 parent 38e7367 commit 6e344c1
Show file tree
Hide file tree
Showing 8 changed files with 92 additions and 16 deletions.
7 changes: 4 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- Backslashes in user passwords are ignored (See issue [#244](https://github.com/Axway-API-Management-Plus/apim-cli/issues/244))

### Added
- Added updateOnly toggle (See issue [#251](https://github.com/Axway-API-Management-Plus/apim-cli/issues/251))
- Added support to delete applications from API-Manager
- Added support for API-Method level quotas
- Support to use a cache for import actions (See issue [#253](https://github.com/Axway-API-Management-Plus/apim-cli/issues/253))
- UpdateOnly toggle (See issue [#251](https://github.com/Axway-API-Management-Plus/apim-cli/issues/251))
- Support to delete applications from API-Manager
- Support for API-Method level quotas

## [1.6.1] 2021-12-20

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ public class APIManagerAdapter {

private static CoreParameters cmd;

private static FilteredCacheManager cacheManager;
public static FilteredCacheManager cacheManager;

public APIManagerConfigAdapter configAdapter;
public APIManagerCustomPropertiesAdapter customPropertiesAdapter;
Expand All @@ -126,10 +126,20 @@ public static enum CacheType {
oauthClientProviderCache,
applicationsCache,
applicationsSubscriptionCache,
applicationsQuotaCache,
applicationsQuotaCache(true),
applicationsCredentialCache,
organizationCache,
userCache;

public boolean supportsImportActions = false;

private CacheType() {
this.supportsImportActions = false;
}

private CacheType(boolean supportsImportActions) {
this.supportsImportActions = supportsImportActions;
}
}

public static synchronized APIManagerAdapter getInstance() throws AppException {
Expand All @@ -149,9 +159,9 @@ public static synchronized APIManagerAdapter getInstance() throws AppException {

public static synchronized void deleteInstance() throws AppException {
if(APIManagerAdapter.cacheManager!=null && APIManagerAdapter.cacheManager.getStatus()==Status.AVAILABLE) {
LOG.debug("Closing cache begin");
LOG.debug("Closing cache ...");
APIManagerAdapter.cacheManager.close();
LOG.debug("Closing cache end");
LOG.trace("Cache Closed.");
}
if(APIManagerAdapter.instance!=null) {
if(hasOrgAdmin()) APIManagerAdapter.instance.logoutFromAPIManager(false); // Logout potentially logged in OrgAdmin
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ public List<APIAccess> getAPIAccess(AbstractEntity entity, Type type, boolean in
for(APIAccess apiAccess : allApiAccess) {
API api = APIManagerAdapter.getInstance().apiAdapter.getAPI(new APIFilter.Builder().hasId(apiAccess.getApiId()).build(), false);
if(api==null) {
throw new AppException("Unable to find API with ID: " + apiAccess.getApiId() + " referenced by "+type.niceName+": " + entity.getName(), ErrorCode.UNKNOWN_API);
throw new AppException("Unable to find API with ID: " + apiAccess.getApiId() + " referenced by "+type.niceName+": " + entity.getName() + ". You may try again with -clearCache", ErrorCode.UNKNOWN_API);
}
apiAccess.setApiName(api.getName());
apiAccess.setApiVersion(api.getVersion());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ private void readConfigFromAPIManager(boolean useAdmin) throws AppException {
HttpResponse httpResponse = null;
try {
uri = new URIBuilder(cmd.getAPIManagerURL()).setPath(cmd.getApiBasepath() + "/config").build();
LOG.debug("Load configuration API-Manager.");
LOG.debug("Load API-Manager configuration.");
RestAPICall getRequest = new GETRequest(uri, useAdmin);
httpResponse = getRequest.execute();
String response = EntityUtils.toString(httpResponse.getEntity());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,11 +70,14 @@ public class APIMgrAppsAdapter {
Cache<String, String> applicationsCache;
Cache<String, String> applicationsSubscriptionCache;
Cache<String, String> applicationsCredentialCache;
Cache<String, String> applicationsQuotaCache;

public APIMgrAppsAdapter() throws AppException {
applicationsCache = APIManagerAdapter.getCache(CacheType.applicationsCache, String.class, String.class);
applicationsSubscriptionCache = APIManagerAdapter.getCache(CacheType.applicationsSubscriptionCache, String.class, String.class);
applicationsCredentialCache = APIManagerAdapter.getCache(CacheType.applicationsCredentialCache, String.class, String.class);
// Must be refactored to use Quota-Adapter instead of doing this in
applicationsQuotaCache = APIManagerAdapter.getCache(CacheType.applicationsQuotaCache, String.class, String.class);
}

/**
Expand Down Expand Up @@ -352,10 +355,6 @@ void addImage(ClientApplication app, boolean addImage) throws Exception {
app.setImage(image);
}

void addQuota(ClientApplication app, boolean addQuota) {

}

public ClientApplication updateApplication(ClientApplication desiredApp, ClientApplication actualApp) throws AppException {
return createOrUpdateApplication(desiredApp, actualApp);
}
Expand Down Expand Up @@ -594,6 +593,8 @@ private void saveQuota(ClientApplication app, ClientApplication actualApp) throw
LOG.error("Error creating/updating application quota. Response-Code: "+statusCode+". Got response: '"+EntityUtils.toString(httpResponse.getEntity())+"'");
throw new AppException("Error creating application' Response-Code: "+statusCode+"", ErrorCode.API_MANAGER_COMMUNICATION);
}
// Force reload of this quota next time
applicationsQuotaCache.remove(app.getId());
} catch (Exception e) {
throw new AppException("Error creating application quota. Error: " + e.getMessage(), ErrorCode.CANT_CREATE_API_PROXY, e);
} finally {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,16 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.axway.apim.adapter.APIManagerAdapter;
import com.axway.apim.adapter.APIManagerAdapter.CacheType;
import com.axway.apim.lib.DoNothingCacheManager.DoNothingCache;
import com.axway.apim.lib.errorHandling.AppException;
import com.axway.apim.lib.errorHandling.ErrorCode;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.node.TextNode;

public class FilteredCacheManager implements CacheManager {

Expand All @@ -37,7 +46,11 @@ public void setEnabledCaches(List<CacheType> enabledCaches) {
if(enabledCaches==null || cacheManager instanceof DoNothingCacheManager) return;
this.enabledCaches = new ArrayList<String>();
for(CacheType cacheType : enabledCaches) {
this.enabledCaches.add(cacheType.name());
if(cacheType.supportsImportActions) {
this.enabledCaches.add(cacheType.name());
} else {
LOG.error("The cache: " + cacheType.name() + " is currently not supported for import actions.");
}
}
LOG.info("Enabled caches: " + this.enabledCaches);
}
Expand Down Expand Up @@ -95,5 +108,46 @@ public void removeCache(String arg0) {
cacheManager.removeCache(arg0);
}


/**
* There are a number of entities which have references to an API (e.g. QuotaRestrictions).
* These are stored/maintained with their own ID (quotaId) and cached in Ehcache.
* But, if the API-ID changes, the cached reference points to an API that no longer exists.
* This method is used to update all entities in the cache when the API ID of an API
* changes (e.g. with a Replace Action).
* @param oldApiId the ID currently used by the cached entities
* @param newApiId the new ID that must be replaced
* @throws AppException when the cache cannot be updated.
*/
public void flipApiId(String oldApiId, String newApiId) throws AppException {
ObjectMapper mapper = new ObjectMapper();
Cache<String, String> appQuotaCached = getCache(CacheType.applicationsQuotaCache.name(), String.class, String.class);
if(appQuotaCached instanceof DoNothingCache) return;
LOG.debug("Updating ApplicationQuotaCache: Flip API-ID: " + oldApiId + " --> " + newApiId);
try {
appQuotaCached.forEach(entry -> {
try {
String cachedValueString = entry.getValue();
JsonNode cachedValue = mapper.readTree(cachedValueString);
// As System- and App-Default-Quotas are not cached, they can be ignored
if(APIManagerAdapter.APPLICATION_DEFAULT_QUOTA.equals(cachedValue.get("id").asText()) ||
APIManagerAdapter.SYSTEM_API_QUOTA.equals(cachedValue.get("id").asText())) {
// Do nothing
} else {
ArrayNode restrictions = cachedValue.withArray("restrictions");
for(JsonNode restriction : restrictions) {
if(oldApiId.equals(restriction.get("api").asText())) {
((ObjectNode)restriction).replace("api", new TextNode(newApiId));
appQuotaCached.replace(entry.getKey(), cachedValue.toString());
}
}
}
} catch (Exception e) {
throw new RuntimeException("There was an error updating the cache.", e);
}
});
} catch (Exception e) {
appQuotaCached.clear();
throw new AppException("Error updating the cache. Cache has been cleared.", ErrorCode.UNXPECTED_ERROR, e);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,11 @@
public class CreateNewAPI {

static Logger LOG = LoggerFactory.getLogger(CreateNewAPI.class);

private API createdAPI = null;

public void execute(APIChangeState changes, boolean reCreation) throws AppException {

API createdAPI = null;
API desiredAPI = changes.getDesiredAPI();
API actualAPI = changes.getActualAPI();

Expand Down Expand Up @@ -91,4 +92,8 @@ public void execute(APIChangeState changes, boolean reCreation) throws AppExcept
}
}
}

public API getCreatedAPI() {
return createdAPI;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.axway.apim.adapter.APIManagerAdapter;
import com.axway.apim.adapter.APIStatusManager;
import com.axway.apim.api.API;
import com.axway.apim.apiimport.APIChangeState;
Expand Down Expand Up @@ -37,6 +38,10 @@ public void execute(APIChangeState changes) throws AppException {
LOG.info("New API successfuly created. Going to delete old API: '"+actualAPI.getName()+"' "+actualAPI.getVersion()+" (ID: "+actualAPI.getId()+")");
// Delete the existing old API!
new APIStatusManager().update(actualAPI, API.STATE_DELETED, true);

// Maintain the Ehcache
// All cached entities referencing this API must be updated with the correct API-ID
APIManagerAdapter.cacheManager.flipApiId(changes.getActualAPI().getId(), createNewAPI.getCreatedAPI().getId());
}

}

0 comments on commit 6e344c1

Please sign in to comment.