com.sap.cds.mtx.impl.MetaDataAccessorSingleModelImpl Maven / Gradle / Ivy
/*
* ----------------------------------------------------------------
* © 2019-2021 SAP SE or an SAP affiliate company. All rights reserved.
* ----------------------------------------------------------------
*
*/
package com.sap.cds.mtx.impl;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.sap.cds.CdsException;
import com.sap.cds.mtx.MetaDataAccessor;
import com.sap.cds.mtx.ModelId;
import com.sap.cds.mtx.impl.MetaDataAccessorImpl.EdmxModelCreator;
import com.sap.cds.reflect.CdsModel;
import com.sap.cds.reflect.impl.CdsModelReader;
/**
* Class that provides access to CDS and Edmx models read from java resource
* folders
*/
public class MetaDataAccessorSingleModelImpl implements MetaDataAccessor {
private static final Logger logger = LoggerFactory.getLogger(MetaDataAccessorSingleModelImpl.class);
private final CdsModel cdsModel;
private final String edmxFolder;
private final ConcurrentHashMap keyToEdmx = new ConcurrentHashMap();
// edmx as string,service name, model
private final EdmxModelCreator edmxstrAndServiceToEdmxModel;
/**
* @param cdsModelPath Path to the csn-file,including file name
* @param edmxFolder Path of edmx folder, null and empty are
* allowed
* @param edmxstrAndServiceToEdmxModel Function that converts edmx model read as
* string into generic type M
* @throws IOException
*/
public MetaDataAccessorSingleModelImpl(String cdsModelPath, String edmxFolder,
EdmxModelCreator edmxstrAndServiceToEdmxModel) throws IOException {
if (cdsModelPath != null && !cdsModelPath.isEmpty()) {
try (InputStream is = MetaDataAccessorSingleModelImpl.class.getClassLoader()
.getResourceAsStream(cdsModelPath)) {
if (is == null) {
throw new IOException("Cds model " + cdsModelPath + " not found");
}
cdsModel = CdsModelReader.read(is);
}
} else {
logger.warn("No model path provided");
cdsModel = null;
}
this.edmxFolder = edmxFolder;
this.edmxstrAndServiceToEdmxModel = edmxstrAndServiceToEdmxModel;
}
@Override
public M getEdmx(ModelId id, int maxAgeSeconds) throws CdsException {
String serviceName = id.getServiceName().orElseThrow(() -> new CdsException("No service name specified"));
String language = id.getLanguage().orElse("");
ServiceAndLanguage key = new ServiceAndLanguage(language, serviceName);
try {
return keyToEdmx.computeIfAbsent(key, this::readEmdxFromResource);
} catch (RuntimeException e) {
throw new CdsException(e);
}
}
private M readEmdxFromResource(ServiceAndLanguage key) throws RuntimeException {
if (edmxstrAndServiceToEdmxModel == null) {
throw new RuntimeException("No function to convert string into edmx model provided");
}
String edmxAsString;
try (InputStream is = MetaDataAccessorSingleModelImpl.class.getClassLoader()
.getResourceAsStream(getResourceName(key.service, key.language))) {
if (is == null) {
throw new IOException("Edmx file " + getResourceName(key.service, key.language) + " not found");
}
edmxAsString = IOUtils.toString(is, StandardCharsets.UTF_8);
} catch (IOException e) {
throw new RuntimeException(e);
}
return edmxstrAndServiceToEdmxModel.parse(edmxAsString, key.service);
}
private String getResourceName(String serviceName, String language) {
String completePathStr;
if (edmxFolder == null || edmxFolder.isEmpty()) {
completePathStr = serviceName;
} else {
completePathStr = edmxFolder + "/" + serviceName;
}
if (language != null && !language.isEmpty()) {
completePathStr += "_" + language.toLowerCase(Locale.ENGLISH);
}
completePathStr += ".xml";
return completePathStr;
}
@Override
public I18n getI18n(ModelId modelId, int maxAgeSeconds) {
return new I18n(null, Collections.emptyMap());
}
@Override
public CdsModel getCdsModel(ModelId modelId, int maxAgeSeconds) throws CdsException {
return cdsModel;
}
@Override
public void refresh(String tenantId, int maxAgeSeconds) {
// intentionally left empty, no cache
}
@Override
public void evict(String tenantId) {
// intentionally left empty, no cache
}
private static class ServiceAndLanguage {
private final String language;
private final String service;
public ServiceAndLanguage(String language, String service) {
this.language = language;
this.service = service;
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
ServiceAndLanguage that = (ServiceAndLanguage) o;
return Objects.equals(language, that.language) && service.equals(that.service);
}
@Override
public int hashCode() {
return Objects.hash(language, service);
}
}
}