/** * EasySOA Registry * Copyright 2011 Open Wide * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * * Contact : easysoa-dev@googlegroups.com */ package org.easysoa.impl; import java.net.MalformedURLException; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.easysoa.api.EasySOAApiSession; import org.easysoa.api.EasySOADocument; import org.easysoa.api.EasySOALocalDocument; import org.easysoa.doctypes.AppliImpl; import org.easysoa.doctypes.EasySOADoctype; import org.easysoa.doctypes.Service; import org.easysoa.doctypes.ServiceAPI; import org.easysoa.doctypes.ServiceReference; import org.easysoa.doctypes.Workspace; import org.easysoa.properties.ApiUrlProcessor; import org.easysoa.services.DocumentService; import org.nuxeo.ecm.core.api.ClientException; import org.nuxeo.ecm.core.api.CoreSession; import org.nuxeo.ecm.core.api.DocumentModel; import org.nuxeo.ecm.core.api.DocumentModelList; import org.nuxeo.ecm.core.api.IdRef; import org.nuxeo.runtime.api.Framework; /** * * Local implementation of the EasySOA API, and core of the notification handling. * * @author mkalam-alami * */ public class EasySOALocalApi implements EasySOAApiSession { // TODO Take environment in account everywhere private static Log log = LogFactory.getLog(EasySOALocalApi.class); private CoreSession session; private static final Map<String, String> propertyFilter = new HashMap<String, String>(); public EasySOALocalApi(CoreSession session) throws Exception { this.session = session; propertyFilter.put(AppliImpl.PROP_URL, null); propertyFilter.put(ServiceAPI.PROP_URL, null); propertyFilter.put(ServiceAPI.PROP_PARENTURL, null); propertyFilter.put(Service.PROP_URL, null); propertyFilter.put(Service.PROP_PARENTURL, null); } /** * Creates or update an Appli Impl. given the specified properties. * Properties require at least application's URL (PROP_URL). * @param session * @param properties A set of properties of the document, among the AppliImpl.PROP_XXX constants. * @return The created/updated Appli Impl. ID * @throws ClientException * @throws MalformedURLException */ public final EasySOADocument notifyAppliImpl(Map<String, String> properties) throws ClientException, MalformedURLException { // Check mandatory field String url = properties.get(AppliImpl.PROP_URL); if (url != null && !url.isEmpty()) { // Find or create document DocumentService docService = Framework.getRuntime().getService(DocumentService.class); DocumentModel appliImplModel = docService.findAppliImpl(session, url); if (appliImplModel == null) { String title = (properties.get("title") != null) ? properties.get("title") : properties.get(AppliImpl.PROP_URL); String workspace = (properties.get(Service.PROP_ENVIRONMENT) != null) ? properties.get(Service.PROP_ENVIRONMENT) : Workspace.DEFAULT_ENVIRONMENT; appliImplModel = docService.createAppliImpl(session, url, workspace); appliImplModel.setProperty("dublincore", "title", title); appliImplModel = session.saveDocument(appliImplModel); } // Update optional properties if (properties.containsKey(AppliImpl.PROP_DEPLOYABLES)) { // Specific handling for ListProperty 'deployables' List<Map<String, Object>> newDeployablesValue = new LinkedList<Map<String, Object>>(); String deployablesString = properties.remove(AppliImpl.PROP_DEPLOYABLES); String[] deployables = deployablesString.split("\n"); for (String deployable : deployables) { Map<String, Object> newDeployableValue = new HashMap<String, Object>(); String[] deployableInfos = deployable.split("\\|"); newDeployableValue.put(AppliImpl.SUBPROP_DEPLOYABLEID, deployableInfos[0]); if (deployableInfos.length > 1) { newDeployableValue.put(AppliImpl.SUBPROP_DEPLOYABLENAME, deployableInfos[1]); } if (deployableInfos.length > 2) { newDeployableValue.put(AppliImpl.SUBPROP_DEPLOYABLEVERSION, deployableInfos[2]); } newDeployablesValue.add(newDeployableValue); } appliImplModel.setProperty(AppliImpl.SCHEMA, AppliImpl.PROP_DEPLOYABLES, newDeployablesValue); } setPropertiesIfNotNull(appliImplModel, AppliImpl.SCHEMA, AppliImpl.getPropertyList(), properties); setPropertiesIfNotNull(appliImplModel, AppliImpl.FEATURE_SCHEMA, AppliImpl.getFeaturePropertyList(), properties); // Save session.saveDocument(appliImplModel); session.save(); return new EasySOALocalDocument(appliImplModel); } else { throw new ClientException("Appli impl. URL not informed"); } } /** * Creates or update an API given the specified properties. * Properties require at least application's URL (PROP_URL) ; * the parent document URL (PROP_PARENTURL) is also recommended if known. * @param session * @param properties A set of properties of the document, among the ServiceAPI.PROP_XXX constants. * @return The created/updated API ID * @throws ClientException * @throws MalformedURLException */ public final EasySOADocument notifyServiceApi(Map<String, String> properties) throws ClientException, MalformedURLException { // Check mandatory fields String url = properties.get(Service.PROP_URL); if (url != null && !url.isEmpty()) { // Exctract main fields String parentUrl = properties.get(ServiceAPI.PROP_PARENTURL), title = properties.get("title"); if (title == null || title.isEmpty()) { title = url; properties.put("title", title); } // Find or create document and parent DocumentService docService = Framework.getRuntime().getService(DocumentService.class); DocumentModel parentModel = docService.findAppliImpl(session, parentUrl); if (parentModel == null) { parentModel = docService.findServiceApi(session, parentUrl); } if (parentModel == null) { if (parentUrl == null) { parentModel = docService.getDefaultAppliImpl(session); } else { String workspace = (properties.get(Service.PROP_ENVIRONMENT) != null) ? properties.get(Service.PROP_ENVIRONMENT) : "Master"; parentModel = docService.createAppliImpl(session, parentUrl, workspace); } session.save(); } DocumentModel apiModel = docService.findServiceApi(session, url); if (apiModel == null) { apiModel = docService.createServiceAPI(session, parentModel.getPathAsString(), url); } if (!parentModel.getRef().equals(apiModel.getParentRef())) { apiModel = session.move(apiModel.getRef(), parentModel.getRef(), null); } // Update optional properties setPropertiesIfNotNull(apiModel, ServiceAPI.SCHEMA, ServiceAPI.getPropertyList(), properties); // Save apiModel = session.saveDocument(apiModel); session.save(); return new EasySOALocalDocument(apiModel); } else { throw new ClientException("API URL or parent URL not informed"); } } /** * Creates or update a Service given the specified properties. * Properties require at least application's URL (PROP_URL) and parent API URL (PROP_PARENTURL). * If parent API is unknown, you can use the {@link #computeApiUrl} function. * @param session * @param properties A set of properties of the document, among the Service.PROP_XXX constants. * @return The created/updated Service ID * @throws ClientException * @throws MalformedURLException */ public final EasySOADocument notifyService(Map<String, String> properties) throws ClientException, MalformedURLException { // Check mandatory fields String url = properties.get(Service.PROP_URL); if (url != null && !url.isEmpty()) { // Exctract main fields String parentUrl = properties.get(Service.PROP_PARENTURL), title = properties.get(Service.PROP_TITLE); // Store URL as file in case of a WSDL if (url.toLowerCase().endsWith("?wsdl")) { if (properties.get(Service.PROP_FILEURL) == null) { properties.put(Service.PROP_FILEURL, url); } url = url.substring(0, url.length() - 5); } if (parentUrl == null || parentUrl.isEmpty()) { parentUrl = ApiUrlProcessor.computeApiUrl(url); } if (title == null || title.isEmpty()) { title = ApiUrlProcessor.computeServiceTitle(url); properties.put(Service.PROP_TITLE, title); } // Find or create document and parent DocumentService docService = Framework.getRuntime().getService(DocumentService.class); DocumentModel apiModel = docService.findServiceApi(session, parentUrl); if (apiModel == null) { // Guess Appli. Impl. String appliImplUrl = ApiUrlProcessor.computeAppliImplUrl(parentUrl); DocumentModel appliImplModel = docService.findAppliImpl(session, appliImplUrl.toString()); if (appliImplModel == null) { appliImplModel = docService.findAppliImpl(session, ApiUrlProcessor.computeAppliImplUrl(appliImplUrl)); } if (appliImplModel == null) { appliImplModel = docService.getDefaultAppliImpl(session, properties.get(Service.PROP_ENVIRONMENT)); } // Create API apiModel = docService.createServiceAPI(session, appliImplModel.getPathAsString(), parentUrl); apiModel.setProperty("dublincore", "title", title+" API"); session.saveDocument(apiModel); session.save(); } DocumentModel serviceModel = docService.findService(session, url); if (serviceModel == null) { serviceModel = docService.createService(session, apiModel.getPathAsString(), url); } // Update optional properties properties.put(Service.PROP_CALLCOUNT, getNewCallcount(serviceModel, properties.get(Service.PROP_CALLCOUNT)) ); setPropertiesIfNotNull(serviceModel, Service.SCHEMA, Service.getPropertyList(), properties); // Update location (unless the service has just been created) if (!apiModel.getRef().equals(serviceModel.getParentRef()) && serviceModel.getParentRef() != null) { try { serviceModel = session.move(serviceModel.getRef(), apiModel.getRef(), null); } catch (Exception e) { log.warn("Failed to move service: " + e.getMessage()); } } // Save serviceModel = session.saveDocument(serviceModel); session.save(); return new EasySOALocalDocument(serviceModel); } else { throw new ClientException("Service URL not informed"); } } /** * Creates or update a ServiceReference given the specified properties. * Properties require at least an architecture path (PROP_ARCHIPATH). * @param session * @param properties A set of properties of the document, among the ServiceReference.PROP_XXX constants. * @return The created/updated Service * @throws ClientException */ public final EasySOADocument notifyServiceReference( Map<String, String> properties) throws ClientException { String archiPath = properties.get(ServiceReference.PROP_ARCHIPATH); String parentUrl = properties.get(ServiceReference.PROP_PARENTURL); if (archiPath != null && parentUrl != null) { // Find parent application DocumentService docService = Framework.getRuntime().getService(DocumentService.class); DocumentModel appliImplModel = docService.findAppliImpl(session, parentUrl); if (appliImplModel == null) { throw new ClientException("Parent application URL invalid"); } // Find reference, then enrich or create DocumentModel referenceModel = docService.findServiceReference(session, archiPath); if (referenceModel == null){ referenceModel = docService.createReference(session, appliImplModel.getPathAsString(), archiPath); } properties.remove(ServiceReference.PROP_PARENTURL); setPropertiesIfNotNull(referenceModel, ServiceReference.SCHEMA, ServiceReference.getPropertyList(), properties); referenceModel = session.saveDocument(referenceModel); session.save(); return new EasySOALocalDocument(referenceModel); } else { throw new ClientException("Parent application URL or architecture path not informed"); } } private String getNewCallcount(DocumentModel serviceModel, String newCalls) { Long previousCallcount, newCallsLong; try { previousCallcount = (Long) serviceModel.getProperty(Service.SCHEMA, Service.PROP_CALLCOUNT); } catch (Exception e) { previousCallcount = 0L; } if (previousCallcount == null) { previousCallcount = 0L; } try { newCallsLong = Long.parseLong(newCalls); } catch (Exception e) { newCallsLong = 0L; } return ((Long) (newCallsLong + previousCallcount)).toString(); } /** * Sets a property to a model, but only if the value parameter is not null. * @param result * @param callback * @return * @throws ClientException */ private void setPropertyIfNotNull(DocumentModel model, String schema, String property, Object value) throws ClientException { if (value != null && !propertyFilter.containsKey(property)) { // Append value if the property is a discovery field if (property.equals(EasySOADoctype.PROP_DTBROWSING) || property.equals(EasySOADoctype.PROP_DTIMPORT) || property.equals(EasySOADoctype.PROP_DTMONITORING) || property.equals(EasySOADoctype.PROP_DTDESIGN)) { String prevValue = (String) model.getProperty(schema, property); if (prevValue == null) { model.setProperty(schema, property, value); } else if (!prevValue.contains(value.toString())) { model.setProperty(schema, property, prevValue + ", " + value); } } // Otherwise just set the property as expected else { model.setProperty(schema, property, value); } } } /** * Sets properties of given schema to the specified model, but only if the value parameter is not null. * If the property is not found in the given schema, it will try to find a match in SOA Common or Dublin Core property. * @param result * @param callback * @return * @throws ClientException */ private void setPropertiesIfNotNull(DocumentModel model, String schema, Map<String, String> schemaDef, Map<String, String> properties) throws ClientException { properties.putAll(propertyFilter); // Update optional properties for (Entry<String, String> entry : properties.entrySet()) { String key = entry.getKey(); String value = entry.getValue(); if (value != null && !value.isEmpty() && !EasySOADoctype.PROP_ENVIRONMENT.equals(key)) { // Given schema specific properties if (schemaDef.containsKey(key)) { setPropertyIfNotNull(model, schema, key, value); } // EasySOA specific properties else if (EasySOADoctype.getCommonPropertyList(model.getType()).containsKey(key)) { setPropertyIfNotNull(model, EasySOADoctype.SCHEMA_COMMON, key, value); } // Dublin Core properties else if (EasySOADoctype.getDublinCorePropertyList().containsKey(key)) { setPropertyIfNotNull(model, "dublincore", key, value); } } } } @Override public List<EasySOADocument> queryDocuments(String query) throws Exception { List<EasySOADocument> result = new LinkedList<EasySOADocument>(); DocumentModelList list = session.query(query); for (DocumentModel model : list) { result.add(new EasySOALocalDocument(model)); } return result; } @Override public EasySOADocument getDocumentById(String id) throws Exception { return new EasySOALocalDocument(session.getDocument(new IdRef(id))); } }