Add improvements for api publishing procedure

improvements-for-api-publishing-flow
Rajitha Kumara 3 months ago
parent 0cd0d98ecd
commit 26d4ffdf8a

@ -20,6 +20,7 @@ package io.entgra.device.mgt.core.apimgt.webapp.publisher;
import com.google.gson.Gson;
import io.entgra.device.mgt.core.apimgt.extension.rest.api.constants.Constants;
import io.entgra.device.mgt.core.apimgt.webapp.publisher.dto.ApiScope;
import io.entgra.device.mgt.core.apimgt.webapp.publisher.exception.APIManagerPublisherException;
import io.entgra.device.mgt.core.apimgt.webapp.publisher.internal.APIPublisherDataHolder;
import io.entgra.device.mgt.core.device.mgt.common.exceptions.MetadataKeyAlreadyExistsException;
@ -34,20 +35,22 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.wso2.carbon.core.ServerStartupObserver;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Stack;
public class APIPublisherStartupHandler implements ServerStartupObserver {
private static final Log log = LogFactory.getLog(APIPublisherStartupHandler.class);
private static int retryTime = 2000;
private static final int CONNECTION_RETRY_FACTOR = 2;
private static final int MAX_RETRY_COUNT = 5;
private static Stack<APIConfig> failedAPIsStack = new Stack<>();
private static Stack<APIConfig> currentAPIsStack;
private static final Gson gson = new Gson();
private static final Stack<APIConfig> failedAPIsStack = new Stack<>();
private static int retryTime = 2000;
private static Stack<APIConfig> currentAPIsStack;
private final List<String> publishedAPIs = new ArrayList<>();
private APIPublisherService publisher;
@Override
@ -59,9 +62,7 @@ public class APIPublisherStartupHandler implements ServerStartupObserver {
public void completedServerStartup() {
APIPublisherDataHolder.getInstance().setServerStarted(true);
currentAPIsStack = APIPublisherDataHolder.getInstance().getUnpublishedApis();
Thread t = new Thread(new Runnable() {
@Override
public void run() {
Thread t = new Thread(() -> {
if (log.isDebugEnabled()) {
log.debug("Server has just started, hence started publishing unpublished APIs");
log.debug("Total number of unpublished APIs: "
@ -93,7 +94,7 @@ public class APIPublisherStartupHandler implements ServerStartupObserver {
error.append(api.getName() + ",");
}
error.append("']");
log.error(error.toString());
log.info(error.toString());
}
}
@ -105,6 +106,10 @@ public class APIPublisherStartupHandler implements ServerStartupObserver {
}
updateScopeMetadataEntryWithDefaultScopes();
log.info("Successfully published : [" + publishedAPIs + "]. " +
"and failed : [" + failedAPIsStack + "] " +
"Total successful count : [" + publishedAPIs.size() + "]. " +
"Failed count : [" + failedAPIsStack.size() + "]");
// execute after api publishing
for (PostApiPublishingObsever observer : APIPublisherDataHolder.getInstance().getPostApiPublishingObseverList()) {
@ -114,9 +119,9 @@ public class APIPublisherStartupHandler implements ServerStartupObserver {
observer.execute();
}
log.info("Finish executing PostApiPublishingObsevers");
}
});
t.start();
log.info("Starting API publishing procedure");
}
private void publishAPIs(Stack<APIConfig> apis, Stack<APIConfig> failedStack) {
@ -124,6 +129,11 @@ public class APIPublisherStartupHandler implements ServerStartupObserver {
APIConfig api = apis.pop();
try {
publisher.publishAPI(api);
for (ApiScope scope : api.getScopes()) {
APIPublisherDataHolder.getInstance().getPermScopeMapping().putIfAbsent(scope.getPermissions(), scope.getKey());
}
publishedAPIs.add(api.getName());
log.info("Successfully published API [" + api.getName() + "]");
} catch (APIManagerPublisherException e) {
log.error("failed to publish api.", e);
failedStack.push(api);
@ -136,34 +146,42 @@ public class APIPublisherStartupHandler implements ServerStartupObserver {
* will create that entry and update the value with default permissions.
*/
private void updateScopeMetadataEntryWithDefaultScopes() {
Map<String, String> permScopeMap = APIPublisherDataHolder.getInstance().getPermScopeMapping();
Metadata permScopeMapping;
MetadataManagementService metadataManagementService = APIPublisherDataHolder.getInstance().getMetadataManagementService();
try {
DeviceManagementConfig deviceManagementConfig = DeviceConfigurationManager.getInstance().getDeviceManagementConfig();
DefaultPermissions defaultPermissions = deviceManagementConfig.getDefaultPermissions();
Metadata permScopeMapping = metadataManagementService.retrieveMetadata(Constants.PERM_SCOPE_MAPPING_META_KEY);
Map<String, String> permScopeMap = (permScopeMapping != null) ? gson.fromJson(permScopeMapping.getMetaValue(), HashMap.class) :
try {
permScopeMapping = metadataManagementService.retrieveMetadata(Constants.PERM_SCOPE_MAPPING_META_KEY);
boolean entryAlreadyExists = permScopeMapping != null;
if (permScopeMap == null || permScopeMap.isEmpty()) {
permScopeMap = entryAlreadyExists ? gson.fromJson(permScopeMapping.getMetaValue(), HashMap.class) :
new HashMap<>();
for (DefaultPermission defaultPermission : defaultPermissions.getDefaultPermissions()) {
permScopeMap.putIfAbsent(defaultPermission.getName(),
defaultPermission.getScopeMapping().getKey());
}
APIPublisherDataHolder.getInstance().setPermScopeMapping(permScopeMap);
if (permScopeMapping != null) {
permScopeMapping.setMetaValue(gson.toJson(permScopeMap));
metadataManagementService.updateMetadata(permScopeMapping);
return;
for (DefaultPermission defaultPermission : defaultPermissions.getDefaultPermissions()) {
permScopeMap.putIfAbsent(defaultPermission.getName(), defaultPermission.getScopeMapping().getKey());
}
permScopeMapping = new Metadata();
permScopeMapping.setMetaKey(Constants.PERM_SCOPE_MAPPING_META_KEY);
permScopeMapping.setMetaValue(gson.toJson(permScopeMap));
if (entryAlreadyExists) {
metadataManagementService.updateMetadata(permScopeMapping);
} else {
metadataManagementService.createMetadata(permScopeMapping);
}
APIPublisherDataHolder.getInstance().setPermScopeMapping(permScopeMap);
log.info(Constants.PERM_SCOPE_MAPPING_META_KEY + "entry updated successfully");
} catch (MetadataManagementException e) {
log.error("Error encountered while updating permission scope mapping metadata with default scopes");
} catch (MetadataKeyAlreadyExistsException e) {
log.error("Metadata entry already exists for " + Constants.PERM_SCOPE_MAPPING_META_KEY);
}
}
}

@ -24,25 +24,33 @@ import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.reflect.TypeToken;
import io.entgra.device.mgt.core.apimgt.annotations.Scope;
import io.entgra.device.mgt.core.apimgt.extension.rest.api.constants.Constants;
import io.entgra.device.mgt.core.apimgt.webapp.publisher.config.APIResource;
import io.entgra.device.mgt.core.apimgt.webapp.publisher.config.APIResourceConfiguration;
import io.entgra.device.mgt.core.apimgt.webapp.publisher.config.WebappPublisherConfig;
import io.entgra.device.mgt.core.apimgt.webapp.publisher.dto.ApiScope;
import io.entgra.device.mgt.core.apimgt.webapp.publisher.dto.ApiUriTemplate;
import io.entgra.device.mgt.core.apimgt.webapp.publisher.internal.APIPublisherDataHolder;
import io.entgra.device.mgt.core.apimgt.webapp.publisher.lifecycle.util.AnnotationProcessor;
import io.entgra.device.mgt.core.device.mgt.common.exceptions.MetadataManagementException;
import io.entgra.device.mgt.core.device.mgt.common.metadata.mgt.Metadata;
import org.apache.catalina.core.StandardContext;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.wso2.carbon.base.MultitenantConstants;
import org.wso2.carbon.context.PrivilegedCarbonContext;
import org.wso2.carbon.core.util.Utils;
import org.wso2.carbon.user.api.TenantManager;
import org.wso2.carbon.user.api.UserStoreException;
import javax.servlet.ServletContext;
import java.io.IOException;
import java.util.*;
public class APIPublisherUtil {
public static final String API_VERSION_PARAM = "{version}";
public static final String PROPERTY_PROFILE = "profile";
private static final Log log = LogFactory.getLog(APIPublisherUtil.class);
private static final String DEFAULT_API_VERSION = "1.0.0";
private static final String API_CONFIG_DEFAULT_VERSION = "1.0.0";
@ -53,10 +61,10 @@ public class APIPublisherUtil {
private static final String PARAM_MANAGED_API_IS_SECURED = "managed-api-isSecured";
private static final String PARAM_SHARED_WITH_ALL_TENANTS = "isSharedWithAllTenants";
private static final String PARAM_PROVIDER_TENANT_DOMAIN = "providerTenantDomain";
private static final String NON_SECURED_RESOURCES = "nonSecuredEndPoints";
private static final String AUTH_TYPE_NON_SECURED = "None";
private static final String PARAM_IS_DEFAULT = "isDefault";
private static final Gson gson = new Gson();
public static String getServerBaseUrl() {
WebappPublisherConfig webappPublisherConfig = WebappPublisherConfig.getInstance();
@ -255,7 +263,7 @@ public class APIPublisherUtil {
policy = null;
}
apiConfig.setPolicy(policy);
setResourceAuthTypes(servletContext, apiConfig);
return apiConfig;
}
@ -318,7 +326,7 @@ public class APIPublisherUtil {
}
}
if (log.isDebugEnabled()) {
log.debug("API swagger definition: " + swaggerDefinition.toString());
log.debug("API swagger definition: " + swaggerDefinition);
}
return swaggerDefinition.toString();
}
@ -336,8 +344,7 @@ public class APIPublisherUtil {
String fullPaath = "";
if (!template.getUriTemplate().equals(AnnotationProcessor.WILD_CARD)) {
fullPaath = apiConfig.getContext() + template.getUriTemplate();
}
else {
} else {
fullPaath = apiConfig.getContext();
}
for (String context : resourcesList) {
@ -349,4 +356,65 @@ public class APIPublisherUtil {
}
apiConfig.setUriTemplates(templates);
}
public static List<APIResourceConfiguration> getAPIResourceConfiguration(StandardContext standardContext, ServletContext servletContext)
throws IOException, ClassNotFoundException {
List<APIResourceConfiguration> apiResourceConfigurations = new ArrayList<>();
String profile = System.getProperty(PROPERTY_PROFILE);
if (WebappPublisherConfig.getInstance().getProfiles().getProfile().contains(profile.toLowerCase())) {
AnnotationProcessor annotationProcessor = new AnnotationProcessor(standardContext);
Set<String> annotatedSwaggerAPIClasses = annotationProcessor.
scanStandardContext(io.swagger.annotations.SwaggerDefinition.class.getName());
apiResourceConfigurations = annotationProcessor.extractAPIInfo(servletContext,
annotatedSwaggerAPIClasses);
}
return apiResourceConfigurations;
}
/**
* This method can use to publish the apis after the server startup complete.
*
* @param apiConfig {@link APIConfig} Contains API definition
*/
public static void publishAPIAfterServerStartup(APIConfig apiConfig) {
APIPublisherDataHolder apiPublisherDataHolder = APIPublisherDataHolder.getInstance();
if (!apiPublisherDataHolder.isServerStarted()) {
if (log.isDebugEnabled()) {
log.debug("Abort publishing the API [" + apiConfig.getName() + "]. Server still starting");
}
throw new IllegalStateException("Server starting procedure is still not completed");
}
TenantManager tenantManager = apiPublisherDataHolder.getTenantManager();
if (tenantManager == null) {
throw new IllegalStateException("Tenant manager service not initialized properly");
}
try {
if (tenantManager.isTenantActive(tenantManager.getTenantId(apiConfig.getTenantDomain()))) {
APIPublisherService apiPublisherService = apiPublisherDataHolder.getApiPublisherService();
if (apiPublisherService == null) {
throw new IllegalStateException("API Publisher service is not initialized properly");
}
apiPublisherService.publishAPI(apiConfig);
for (ApiScope scope : apiConfig.getScopes()) {
apiPublisherDataHolder.getPermScopeMapping().putIfAbsent(scope.getPermissions(), scope.getKey());
}
Metadata permScopeMapping = new Metadata();
permScopeMapping.setMetaKey(Constants.PERM_SCOPE_MAPPING_META_KEY);
permScopeMapping.setMetaValue(gson.toJson(apiPublisherDataHolder.getPermScopeMapping()));
try {
apiPublisherDataHolder.getMetadataManagementService().updateMetadata(permScopeMapping);
} catch (MetadataManagementException e) {
log.error("Error encountered while updating the " + Constants.PERM_SCOPE_MAPPING_META_KEY + "entry");
}
} else {
log.error("Can't find an active tenant under tenant domain " + apiConfig.getTenantDomain());
}
} catch (Throwable e) {
log.error("Error occurred while publishing API '" + apiConfig.getName() + "' with the context '" +
apiConfig.getContext() + "' and version '" + apiConfig.getVersion() + "'", e);
}
}
}

@ -33,6 +33,7 @@ import org.wso2.carbon.user.core.service.RealmService;
import org.wso2.carbon.user.core.tenant.TenantManager;
import org.wso2.carbon.utils.ConfigurationContextService;
import java.util.HashMap;
import java.util.Map;
import java.util.Stack;
import java.util.List;
@ -48,7 +49,7 @@ public class APIPublisherDataHolder {
private RegistryService registryService;
private boolean isServerStarted;
private Stack<APIConfig> unpublishedApis = new Stack<>();
private Map<String, String> permScopeMapping;
private Map<String, String> permScopeMapping = new HashMap<>();
private APIApplicationServices apiApplicationServices;
private PublisherRESTAPIServices publisherRESTAPIServices;
private MetadataManagementService metadataManagementService;

@ -17,24 +17,11 @@
*/
package io.entgra.device.mgt.core.apimgt.webapp.publisher.lifecycle.listener;
import com.google.gson.Gson;
import io.entgra.device.mgt.core.apimgt.webapp.publisher.dto.ApiScope;
import io.entgra.device.mgt.core.device.mgt.common.exceptions.MetadataManagementException;
import io.entgra.device.mgt.core.device.mgt.common.metadata.mgt.Metadata;
import io.entgra.device.mgt.core.device.mgt.common.metadata.mgt.MetadataManagementService;
import org.apache.catalina.Lifecycle;
import org.apache.catalina.LifecycleEvent;
import org.apache.catalina.LifecycleListener;
import org.apache.catalina.core.StandardContext;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import io.entgra.device.mgt.core.apimgt.webapp.publisher.APIConfig;
import io.entgra.device.mgt.core.apimgt.webapp.publisher.APIPublisherService;
import io.entgra.device.mgt.core.apimgt.webapp.publisher.APIPublisherUtil;
import io.entgra.device.mgt.core.apimgt.webapp.publisher.config.APIResourceConfiguration;
import io.entgra.device.mgt.core.apimgt.webapp.publisher.config.WebappPublisherConfig;
import io.entgra.device.mgt.core.apimgt.webapp.publisher.internal.APIPublisherDataHolder;
import io.entgra.device.mgt.core.apimgt.webapp.publisher.lifecycle.util.AnnotationProcessor;
import org.apache.catalina.Lifecycle;
import org.apache.catalina.LifecycleEvent;
import org.apache.catalina.LifecycleListener;
@ -46,104 +33,42 @@ import org.wso2.carbon.user.api.UserStoreException;
import javax.servlet.ServletContext;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
@SuppressWarnings("unused")
public class APIPublisherLifecycleListener implements LifecycleListener {
public static final String PROFILE_DEFAULT = "default";
private static final Log log = LogFactory.getLog(APIPublisherLifecycleListener.class);
private static final String PARAM_MANAGED_API_ENABLED = "managed-api-enabled";
public static final String PROPERTY_PROFILE = "profile";
public static final String PROFILE_DT_WORKER = "dtWorker";
public static final String PROFILE_DEFAULT = "default";
@Override
public void lifecycleEvent(LifecycleEvent lifecycleEvent) {
if (Lifecycle.AFTER_START_EVENT.equals(lifecycleEvent.getType()) ) {
if (WebappPublisherConfig.getInstance()
.isPublished()) {
if (Lifecycle.AFTER_START_EVENT.equals(lifecycleEvent.getType())) {
APIPublisherDataHolder apiPublisherDataHolder = APIPublisherDataHolder.getInstance();
StandardContext context = (StandardContext) lifecycleEvent.getLifecycle();
ServletContext servletContext = context.getServletContext();
String param = servletContext.getInitParameter(PARAM_MANAGED_API_ENABLED);
boolean isManagedApi = (param != null && !param.isEmpty()) && Boolean.parseBoolean(param);
String profile = System.getProperty(PROPERTY_PROFILE);
if (WebappPublisherConfig.getInstance().getProfiles().getProfile().contains(profile.toLowerCase())
&& isManagedApi) {
try {
AnnotationProcessor annotationProcessor = new AnnotationProcessor(context);
Set<String> annotatedSwaggerAPIClasses = annotationProcessor.
scanStandardContext(io.swagger.annotations.SwaggerDefinition.class.getName());
List<APIResourceConfiguration> apiDefinitions = annotationProcessor.extractAPIInfo(servletContext,
annotatedSwaggerAPIClasses);
APIPublisherDataHolder apiPublisherDataHolder = APIPublisherDataHolder.getInstance();
MetadataManagementService metadataManagementService =
apiPublisherDataHolder.getMetadataManagementService();
Metadata metadata = metadataManagementService.retrieveMetadata("perm-scope-mapping");
if (metadata != null) {
HashMap<String, String> permScopeMapping =
new Gson().fromJson(metadata.getMetaValue().toString(), HashMap.class);
apiPublisherDataHolder.setPermScopeMapping(permScopeMapping);
}
if (isManagedApi) {
if (WebappPublisherConfig.getInstance().isPublished() || WebappPublisherConfig.getInstance().isEnabledUpdateApi()) {
Map<String, String> permScopeMap = apiPublisherDataHolder.getPermScopeMapping();
for (APIResourceConfiguration apiDefinition : apiDefinitions) {
APIConfig apiConfig = APIPublisherUtil.buildApiConfig(servletContext, apiDefinition);
for (ApiScope scope : apiConfig.getScopes()) {
permScopeMap.put(scope.getPermissions(), scope.getKey());
}
APIPublisherUtil.setResourceAuthTypes(servletContext,apiConfig);
try {
int tenantId = APIPublisherDataHolder.getInstance().getTenantManager().
getTenantId(apiConfig.getTenantDomain());
List<APIResourceConfiguration> apiResourceConfigurations =
APIPublisherUtil.getAPIResourceConfiguration(context, servletContext);
boolean isTenantActive = APIPublisherDataHolder.getInstance().
getTenantManager().isTenantActive(tenantId);
if (isTenantActive) {
boolean isServerStarted = APIPublisherDataHolder.getInstance().isServerStarted();
if (isServerStarted) {
APIPublisherService apiPublisherService =
APIPublisherDataHolder.getInstance().getApiPublisherService();
if (apiPublisherService == null) {
throw new IllegalStateException(
"API Publisher service is not initialized properly");
}
apiPublisherService.publishAPI(apiConfig);
} else {
if (log.isDebugEnabled()) {
log.debug("Server has not started yet. Hence adding API '" +
apiConfig.getName() + "' to the queue");
}
APIPublisherDataHolder.getInstance().getUnpublishedApis().push(apiConfig);
}
if (WebappPublisherConfig.getInstance().isPublished()) {
for (APIResourceConfiguration apiDefinition : apiResourceConfigurations) {
APIConfig apiConfig = APIPublisherUtil.buildApiConfig(servletContext, apiDefinition);
if (apiPublisherDataHolder.isServerStarted()) {
APIPublisherUtil.publishAPIAfterServerStartup(apiConfig);
} else {
log.error("No tenant [" + apiConfig.getTenantDomain() + "] " +
"found when publishing the Web app");
}
} catch (Throwable e) {
log.error("Error occurred while publishing API '" + apiConfig.getName() +
"' with the context '" + apiConfig.getContext() +
"' and version '" + apiConfig.getVersion() + "'", e);
apiPublisherDataHolder.getUnpublishedApis().push(apiConfig);
}
}
Metadata existingMetaData = metadataManagementService.retrieveMetadata("perm-scope" +
"-mapping");
if (existingMetaData != null) {
existingMetaData.setMetaValue(new Gson().toJson(permScopeMap));
metadataManagementService.updateMetadata(existingMetaData);
} else {
Metadata newMetaData = new Metadata();
newMetaData.setMetaKey("perm-scope-mapping");
newMetaData.setMetaValue(new Gson().toJson(permScopeMap));
metadataManagementService.createMetadata(newMetaData);
}
apiPublisherDataHolder.setPermScopeMapping(permScopeMap);
} catch (IOException e) {
log.error("Error encountered while discovering annotated classes", e);
} catch (ClassNotFoundException e) {
@ -156,24 +81,6 @@ public class APIPublisherLifecycleListener implements LifecycleListener {
log.error("Failed to Publish api from " + servletContext.getContextPath(), e);
}
}
} else {
APIPublisherDataHolder apiPublisherDataHolder = APIPublisherDataHolder.getInstance();
MetadataManagementService metadataManagementService =
apiPublisherDataHolder.getMetadataManagementService();
try {
Metadata existingMetaData = metadataManagementService.retrieveMetadata("perm-scope" +
"-mapping");
if (existingMetaData != null) {
existingMetaData.setMetaValue(new Gson().toJson(apiPublisherDataHolder.getPermScopeMapping()
));
metadataManagementService.updateMetadata(existingMetaData);
} else {
log.error("Couldn't find 'perm-scope-mapping' Meta entry while API publishing has been turned" +
" off.");
}
} catch (MetadataManagementException e) {
log.error("Failed to Load Meta-Mgt data.", e);
}
}
}
}

Loading…
Cancel
Save