package edu.mayo.cts2.framework.webapp.rest.controller; import java.util.HashMap; import java.util.Map; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import org.apache.commons.lang.StringUtils; import org.springframework.beans.factory.InitializingBean; import org.springframework.http.HttpStatus; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseStatus; import edu.mayo.cts2.framework.model.service.core.BaseReadService; import edu.mayo.cts2.framework.model.service.core.BaseService; import edu.mayo.cts2.framework.service.profile.BaseMaintenanceService; import edu.mayo.cts2.framework.service.profile.BaseQueryService; import edu.mayo.cts2.framework.service.profile.Cts2Profile; import edu.mayo.cts2.framework.service.profile.HistoryService; import edu.mayo.cts2.framework.service.profile.ReadService; import edu.mayo.cts2.framework.service.provider.ServiceProvider; import edu.mayo.cts2.framework.webapp.service.ServiceBuilder; @Controller public class ServiceController extends AbstractMessageWrappingController implements InitializingBean { @Resource private ServiceBuilder serviceBuilder; private class ServiceType { private static final String READ_SUFFIX = "read"; private static final String QUERY_SUFFIX = "query"; private static final String RESOLUTION_SUFFIX = "resolution"; private static final String MAINTENANCE_SUFFIX = "maintenance"; private static final String HISTORY_SUFFIX = "history"; private static final String SERVICE_SUFFIX = "Service"; private static final String MODEL_PACKAGE = "edu.mayo.cts2.framework.model.service"; private static final String SERVICE_PACKAGE = "edu.mayo.cts2.framework.service.profile"; private final Map<String,String> structureCaseMap = new HashMap<String,String>(); { structureCaseMap.put("codesystem", "CodeSystem"); structureCaseMap.put("codesystemversion", "CodeSystemVersion"); structureCaseMap.put("valueset", "valueSet"); structureCaseMap.put("valuesetdefinition", "ValueSetDefinition"); structureCaseMap.put("conceptdomain", "ConceptDomain"); structureCaseMap.put("conceptdomainbinding", "ConceptDomainBinding"); structureCaseMap.put("mapentry", "MapEntry"); structureCaseMap.put("mapversion", "MapVersion"); structureCaseMap.put("entitydescription", "EntityDescription"); } private BaseService service; private ServiceType(String path) { this.service = this.getService(path); } private BaseService getBaseService() { return this.service; } private Class<?> classForName(String name) { try { return Class.forName(name); } catch (ClassNotFoundException e) { throw new InvalidServiceRequest(); } } @SuppressWarnings("unchecked") private BaseService getService(String path) { ServiceProvider serviceProvider = ServiceController.this .getServiceProviderFactory().getServiceProvider(); String suffix = this.getSuffix(path); String serviceClassName = this.getClassNameGivenSuffix(path, SERVICE_PACKAGE, suffix); Class<Cts2Profile> serviceClass = (Class<Cts2Profile>) this .classForName(serviceClassName); String metadataClassName = this.getClassNameGivenSuffix(path, MODEL_PACKAGE, suffix); if (StringUtils.equals(suffix, READ_SUFFIX)) { ReadService<?, ?> service = (ReadService<?, ?>) serviceProvider .getService(serviceClass); return serviceBuilder.buildServiceMetadata(service, (Class<BaseReadService>) this.classForName(metadataClassName)); } else if (StringUtils.equals(suffix, QUERY_SUFFIX)) { BaseQueryService service = (BaseQueryService) serviceProvider .getService(serviceClass); return serviceBuilder .buildServiceMetadata( service, (Class<edu.mayo.cts2.framework.model.service.core.BaseQueryService>) this.classForName(metadataClassName)); } else if (StringUtils.equals(suffix, HISTORY_SUFFIX)) { HistoryService<?, ?> service = (HistoryService<?, ?>) serviceProvider .getService(serviceClass); return serviceBuilder .buildServiceMetadata( service, (Class<edu.mayo.cts2.framework.model.service.core.HistoryService>) this.classForName(metadataClassName)); } else if (StringUtils.equals(suffix, MAINTENANCE_SUFFIX)) { BaseMaintenanceService<?, ?, ?> service = (BaseMaintenanceService<?, ?, ?>) serviceProvider .getService(serviceClass); return serviceBuilder .buildServiceMetadata( service, (Class<edu.mayo.cts2.framework.model.service.core.BaseMaintenanceService>) this.classForName(metadataClassName)); } else if (StringUtils.equals(suffix, RESOLUTION_SUFFIX)) { BaseQueryService service = (BaseQueryService) serviceProvider .getService(serviceClass); return serviceBuilder .buildServiceMetadata( service, (Class<edu.mayo.cts2.framework.model.service.core.BaseService>) this.classForName(metadataClassName)); } else { throw new IllegalStateException(); } } private String getSuffix(String path) { String suffix; if (StringUtils.endsWith(path, READ_SUFFIX)) { suffix = READ_SUFFIX; } else if (StringUtils.endsWith(path, QUERY_SUFFIX)) { suffix = QUERY_SUFFIX; } else if (StringUtils.endsWith(path, HISTORY_SUFFIX)) { suffix = HISTORY_SUFFIX; } else if (StringUtils.endsWith(path, MAINTENANCE_SUFFIX)) { suffix = MAINTENANCE_SUFFIX; }else if (StringUtils.endsWith(path, RESOLUTION_SUFFIX)) { suffix = RESOLUTION_SUFFIX; }else { throw new InvalidServiceRequest(); } return suffix; } private String getClassNameGivenSuffix(String path, String packageName, String suffix) { path = path.toLowerCase(); String structure = StringUtils.substringBefore(path, suffix); String structureName; if(structureCaseMap.containsKey(structure)){ structureName = structureCaseMap.get(structure); } else { structureName = StringUtils.capitalize(structure); } //TODO BUG The nasty little if/else block embedded is because of a naming consistency issue in the schema //https://github.com/cts2/cts2-specification/issues/181 return packageName + "." + structure + "."+ structureName + StringUtils.capitalize(suffix) + ((packageName == MODEL_PACKAGE && suffix == RESOLUTION_SUFFIX) ? "" : SERVICE_SUFFIX); } } @RequestMapping(value = PATH_SERVICE, method = RequestMethod.GET) public Object getService(HttpServletRequest request, @PathVariable(VAR_SERVICEID) String serviceId) { ServiceType serviceType = new ServiceType(serviceId); BaseService result = serviceType.getBaseService(); return this.buildResponse(request, result); } private class InvalidServiceRequest extends RuntimeException { private static final long serialVersionUID = 7588100241967323612L; private InvalidServiceRequest() { super(); } } @ResponseStatus(HttpStatus.NOT_FOUND) @ExceptionHandler({ InvalidServiceRequest.class }) public void handle() { // ... } }