/* * Copyright (c) 2015, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * */ package org.wso2.carbon.rest.api.service; import org.apache.axiom.om.OMAttribute; import org.apache.axiom.om.OMElement; import org.apache.axiom.om.util.AXIOMUtil; import org.apache.axis2.AxisFault; import org.apache.axis2.Constants; import org.apache.axis2.description.Parameter; import org.apache.axis2.engine.AxisConfiguration; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.synapse.config.SynapseConfiguration; import org.apache.synapse.config.xml.XMLConfigConstants; import org.apache.synapse.config.xml.rest.APIFactory; import org.apache.synapse.rest.API; import org.apache.synapse.rest.RESTConstants; import org.wso2.carbon.context.PrivilegedCarbonContext; import org.wso2.carbon.mediation.initializer.ServiceBusConstants; import org.wso2.carbon.mediation.initializer.ServiceBusUtils; import org.wso2.carbon.mediation.initializer.persistence.MediationPersistenceManager; import org.wso2.carbon.rest.api.APIData; import org.wso2.carbon.rest.api.APIException; import org.wso2.carbon.rest.api.ConfigHolder; import org.wso2.carbon.rest.api.RestApiAdminUtils; import org.wso2.carbon.utils.multitenancy.MultitenantConstants; import javax.xml.namespace.QName; import javax.xml.stream.XMLStreamException; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class RestApiAdminService { private static final Log log = LogFactory.getLog(RestApiAdminService.class); private static final String TENANT_DELIMITER = "/t/"; public boolean addApi(APIData apiData) throws APIException { final Lock lock = getLock(); try { lock.lock(); String tenantDomain = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantDomain(true); if (tenantDomain != null && !tenantDomain.isEmpty() && !MultitenantConstants.SUPER_TENANT_DOMAIN_NAME.equals(tenantDomain)) { String tenantApiContext = apiData.getContext(); apiData.setContext(TENANT_DELIMITER + tenantDomain + tenantApiContext); } addApi(RestApiAdminUtils.retrieveAPIOMElement(apiData), null, false); return true; } finally { lock.unlock(); } } public boolean addApiFromString(String apiData) throws APIException { final Lock lock = getLock(); try { lock.lock(); OMElement apiElement = AXIOMUtil.stringToOM(apiData); addApi(apiElement, null, false); return true; } catch (XMLStreamException e) { handleException(log, "Could not parse String to OMElement", e); return false; } finally { lock.unlock(); } } /** * Set the tenant domain when a publisher publishes his API in MT mode. When publisher publishes * the API, we login the gateway as super tenant. But we need to publish the API in the particular * tenant domain. * * @param apiData * @param tenantDomain * @return * @throws APIException */ public boolean addApiForTenant(String apiData, String tenantDomain) throws APIException { try { PrivilegedCarbonContext.startTenantFlow(); PrivilegedCarbonContext.getThreadLocalCarbonContext().setTenantDomain(tenantDomain, true); return addApiFromString(apiData); } finally { PrivilegedCarbonContext.endTenantFlow(); } } /** * Set the tenant domain when a publisher tries to retrieve API his API in MT mode. When * publisher gets * the API, we login the gateway as super tenant. But we need to get the * API,which is in the particular tenant domain. * * @param apiName * @param tenantDomain * @return * @throws APIException */ public APIData getApiForTenant(String apiName, String tenantDomain) throws APIException { try { PrivilegedCarbonContext.startTenantFlow(); PrivilegedCarbonContext.getThreadLocalCarbonContext().setTenantDomain(tenantDomain, true); return getApiByName(apiName); } finally { PrivilegedCarbonContext.endTenantFlow(); } } public APIData getApiByName(String apiName) throws APIException { final Lock lock = getLock(); try { lock.lock(); SynapseConfiguration synapseConfiguration = RestApiAdminUtils.getSynapseConfiguration(); API api = synapseConfiguration.getAPI(apiName); return RestApiAdminUtils.convertApiToAPIData(api); } finally { lock.unlock(); } } public boolean updateApiFromString(String apiName, String apiData) throws APIException { final Lock lock = getLock(); try { lock.lock(); assertNameNotEmpty(apiName); OMElement apiElement = AXIOMUtil.stringToOM(apiData); //Set API name to old value since we do not allow editing the API name. OMAttribute nameAttribute = apiElement.getAttribute(new QName("name")); if (nameAttribute == null || "".equals(nameAttribute.getAttributeValue().trim())) { apiElement.addAttribute("name", apiName, null); } API oldAPI = null; API api = APIFactory.createAPI(apiElement); SynapseConfiguration synapseConfiguration = RestApiAdminUtils.getSynapseConfiguration(); oldAPI = synapseConfiguration.getAPI(apiName); if (oldAPI != null) { oldAPI.destroy(); api.setFileName(oldAPI.getFileName()); } synapseConfiguration.removeAPI(apiName); synapseConfiguration.addAPI(api.getName(), api); api.init(RestApiAdminUtils.getSynapseEnvironment()); if ((oldAPI != null ? oldAPI.getArtifactContainerName() : null) != null) { api.setArtifactContainerName(oldAPI.getArtifactContainerName()); api.setIsEdited(true); getApiByName(apiName).setIsEdited(true); } else { MediationPersistenceManager pm = RestApiAdminUtils.getMediationPersistenceManager(); String fileName = api.getFileName(); pm.deleteItem(apiName, fileName, ServiceBusConstants.ITEM_TYPE_REST_API); pm.saveItem(apiName, ServiceBusConstants.ITEM_TYPE_REST_API); } return true; } catch (XMLStreamException e) { handleException(log, "Could not parse String to OMElement", e); return false; } finally { lock.unlock(); } } /** * Set the tenant domain when a publisher updates his API in MT mode. When * publisher updates the API, we login the gateway as super tenant. But we need to update the * API,which is in the particular tenant domain. * * @param apiName * @param apiData * @return * @throws APIException */ public boolean updateApiForTenant(String apiName, String apiData, String tenantDomain) throws APIException { try { PrivilegedCarbonContext.startTenantFlow(); PrivilegedCarbonContext.getThreadLocalCarbonContext().setTenantDomain(tenantDomain, true); return updateApiFromString(apiName, apiData); } finally { PrivilegedCarbonContext.endTenantFlow(); } } public boolean deleteApi(String apiName) throws APIException { final Lock lock = getLock(); try { lock.lock(); assertNameNotEmpty(apiName); apiName = apiName.trim(); SynapseConfiguration synapseConfiguration = RestApiAdminUtils.getSynapseConfiguration(); API api = synapseConfiguration.getAPI(apiName); if (api.getArtifactContainerName() == null) { if (log.isDebugEnabled()) { log.debug("Deleting API : " + apiName + " from the configuration"); } api.destroy(); synapseConfiguration.removeAPI(apiName); MediationPersistenceManager pm = RestApiAdminUtils.getMediationPersistenceManager(); String fileName = api.getFileName(); pm.deleteItem(apiName, fileName, ServiceBusConstants.ITEM_TYPE_REST_API); if (log.isDebugEnabled()) { log.debug("Api : " + apiName + " removed from the configuration"); } } } finally { lock.unlock(); } return true; } private void handleException(Log log, String message, Exception e) throws APIException { if (e == null) { APIException apiException = new APIException(message); log.error(message, apiException); throw apiException; } else { message = message + " :: " + e.getMessage(); log.error(message, e); throw new APIException(message, e); } } protected Lock getLock() throws APIException { AxisConfiguration axisConfig = ConfigHolder.getInstance().getAxisConfiguration(); Parameter p = axisConfig.getParameter(ServiceBusConstants.SYNAPSE_CONFIG_LOCK); if (p != null) { return (Lock) p.getValue(); } else { log.warn(ServiceBusConstants.SYNAPSE_CONFIG_LOCK + " is null, Recreating a new lock"); Lock lock = new ReentrantLock(); try { axisConfig.addParameter(ServiceBusConstants.SYNAPSE_CONFIG_LOCK, lock); return lock; } catch (AxisFault axisFault) { log.error("Error while setting " + ServiceBusConstants.SYNAPSE_CONFIG_LOCK); } } return null; } private void assertNameNotEmpty(String apiName) throws APIException { if (apiName == null || "".equals(apiName.trim())) { handleFault("Invalid name : Name is empty.", null); } } /** * Add an api described by the given OMElement * * @param apiElement configuration of the api which needs to be added * @param fileName Name of the file in which this configuration should be saved or null * @throws APIException if the element is not an api or if an api with the * same name exists */ private void addApi(OMElement apiElement, String fileName, boolean updateMode) throws APIException { try { if (apiElement.getQName().getLocalPart() .equals(XMLConfigConstants.API_ELT.getLocalPart())) { String apiName = apiElement.getAttributeValue(new QName("name")); String apiTransports = apiElement.getAttributeValue(new QName("transports")); if (RestApiAdminUtils.getSynapseConfiguration().getAxisConfiguration().getService( apiName) != null) { handleException(log, "A service named " + apiName + " already exists", null); } else { API api = APIFactory.createAPI(apiElement); try { RestApiAdminUtils.getSynapseConfiguration().addAPI(api.getName(), api); //addParameterObserver(api.getName()); if (log.isDebugEnabled()) { log.debug("Added API : " + apiName); log.debug("Authorized Transports : " + apiTransports); } if (apiTransports != null) { if (Constants.TRANSPORT_HTTP.equalsIgnoreCase(apiTransports)) { api.setProtocol(RESTConstants.PROTOCOL_HTTP_ONLY); } else if (Constants.TRANSPORT_HTTPS.equalsIgnoreCase(apiTransports)) { api.setProtocol(RESTConstants.PROTOCOL_HTTPS_ONLY); } } if (updateMode) { api.setFileName(fileName); } else { if (fileName != null) { api.setFileName(fileName); } else { api.setFileName(ServiceBusUtils.generateFileName(api.getName())); } } api.init(RestApiAdminUtils.getSynapseEnvironment()); RestApiAdminUtils.persistApi(api); } catch (Exception e) { api.destroy(); RestApiAdminUtils.getSynapseConfiguration().removeAPI(api.getName()); try { AxisConfiguration axisConfig = ConfigHolder.getInstance().getAxisConfiguration(); if (axisConfig.getService(api.getName()) != null) { axisConfig.removeService(api.getName()); } } catch (Exception ignore) { } handleException(log, "Error trying to add the API to the ESB " + "configuration : " + api.getName(), e); } } } else { handleException(log, "Invalid API definition", null); } } catch (AxisFault af) { handleException(log, "Invalid API definition", af); } } private void handleFault(String message, Exception e) throws APIException { if (e != null) { log.error(message, e); throw new APIException(e.getMessage(), e); } else { log.error(message); throw new APIException(message); } } }