Java tutorial
/* * Copyright (C) 2006-2016 Talend Inc. - www.talend.com * * This source code is available under agreement available at * %InstallDIR%\features\org.talend.rcp.branding.%PRODUCTNAME%\%PRODUCTNAME%license.txt * * You should have received a copy of the agreement along with this program; if not, write to Talend SA 9 rue Pages * 92150 Suresnes, France */ package com.amalto.core.storage.services; import static com.amalto.core.query.user.UserQueryBuilder.eq; import static com.amalto.core.query.user.UserQueryBuilder.from; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiParam; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.io.StringWriter; import java.util.ArrayList; import java.util.Collections; import java.util.EnumMap; import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.PUT; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.QueryParam; import javax.xml.stream.XMLOutputFactory; import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamWriter; import org.apache.commons.io.IOUtils; import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; import org.talend.mdm.commmon.metadata.ComplexTypeMetadata; import org.talend.mdm.commmon.metadata.MetadataRepository; import org.talend.mdm.commmon.metadata.compare.Change; import org.talend.mdm.commmon.metadata.compare.Compare; import org.talend.mdm.commmon.metadata.compare.ImpactAnalyzer; import com.amalto.commons.core.datamodel.synchronization.DMUpdateEvent; import com.amalto.commons.core.datamodel.synchronization.DataModelChangeNotifier; import com.amalto.core.objects.datamodel.DataModelPOJO; import com.amalto.core.query.user.UserQueryBuilder; import com.amalto.core.save.SaverSession; import com.amalto.core.server.MetadataRepositoryAdmin; import com.amalto.core.server.ServerContext; import com.amalto.core.server.StorageAdmin; import com.amalto.core.storage.Storage; import com.amalto.core.storage.StorageResults; import com.amalto.core.storage.StorageType; import com.amalto.core.storage.record.DataRecord; import com.amalto.core.util.XtentisException; @Path("/system/models") @Api(value = "Models management", tags = "Administration") public class SystemModels { private static final Logger LOGGER = Logger.getLogger(SystemModels.class); private final Storage systemStorage; private final ComplexTypeMetadata dataModelType; public SystemModels() { StorageAdmin storageAdmin = ServerContext.INSTANCE.get().getStorageAdmin(); systemStorage = storageAdmin.get(StorageAdmin.SYSTEM_STORAGE, StorageType.SYSTEM); MetadataRepository repository = systemStorage.getMetadataRepository(); dataModelType = repository.getComplexType("data-model-pOJO"); //$NON-NLS-1$ } private static void reloadDataModel(String modelName) { // Force update in metadata repository admin MetadataRepositoryAdmin metadataRepositoryAdmin = ServerContext.INSTANCE.get().getMetadataRepositoryAdmin(); metadataRepositoryAdmin.update(modelName); // Invalidate concept session SaverSession session = SaverSession.newSession(); session.getSaverSource().invalidateTypeCache(modelName); session.end(); } @GET @Path("{model}") @ApiOperation("Returns the requested data model XML schema") public String getSchema(@ApiParam("The model name") @PathParam("model") String modelName) { if (!isSystemStorageAvailable()) { return StringUtils.EMPTY; } UserQueryBuilder qb = from(dataModelType).where(eq(dataModelType.getField("name"), modelName)); //$NON-NLS-1$ systemStorage.begin(); try { StorageResults results = systemStorage.fetch(qb.getSelect()); Iterator<DataRecord> iterator = results.iterator(); String modelContent = StringUtils.EMPTY; if (iterator.hasNext()) { DataRecord model = iterator.next(); if (iterator.hasNext()) { throw new IllegalStateException("Found multiple data models for '" + modelName + "'."); //$NON-NLS-1$ //$NON-NLS-2$ } modelContent = String.valueOf(model.get("schema")); //$NON-NLS-1$ } systemStorage.commit(); return modelContent; } catch (Exception e) { systemStorage.rollback(); throw new RuntimeException("Could not update data model.", e); //$NON-NLS-1$ } } @POST @Path("/") @ApiOperation("Create a new data model given its name and XSD provided as request content") public void createDataModel(@ApiParam("New model name") @QueryParam("name") String modelName, InputStream dataModel) { try { DataModelPOJO dataModelPOJO = new DataModelPOJO(modelName); dataModelPOJO.setSchema(IOUtils.toString(dataModel, "UTF-8")); //$NON-NLS-1$ dataModelPOJO.store(); // synchronize with outer agents DataModelChangeNotifier dmUpdateEventNotifier = DataModelChangeNotifier.createInstance(); dmUpdateEventNotifier.notifyChange(new DMUpdateEvent(modelName)); } catch (IOException e) { throw new RuntimeException("Could not fully read new data model.", e); //$NON-NLS-1$ } catch (XtentisException e) { throw new RuntimeException("Could not store new data model.", e); //$NON-NLS-1$ } } @PUT @Path("{model}") @ApiOperation("Updates the requested model with the XSD provided as request content") public void updateModel(@ApiParam("Model name") @PathParam("model") String modelName, @ApiParam("Update model even if HIGH or MEDIUM impacts were found") @QueryParam("force") boolean force, InputStream dataModel) { if (!isSystemStorageAvailable()) { // If no system storage is available, store new schema version. try { DataModelPOJO dataModelPOJO = new DataModelPOJO(modelName); dataModelPOJO.setSchema(IOUtils.toString(dataModel, "UTF-8")); //$NON-NLS-1$ dataModelPOJO.store(); return; } catch (IOException e) { throw new RuntimeException("Could not fully read new data model.", e); //$NON-NLS-1$ } catch (XtentisException e) { throw new RuntimeException("Could not store new data model.", e); //$NON-NLS-1$ } } StorageAdmin storageAdmin = ServerContext.INSTANCE.get().getStorageAdmin(); Storage storage = storageAdmin.get(modelName, StorageType.MASTER); if (storage == null) { throw new IllegalArgumentException("Container '" + modelName + "' does not exist."); //$NON-NLS-1$ //$NON-NLS-2$ } // Parses new data model version for comparison with existing MetadataRepository previousRepository = storage.getMetadataRepository(); MetadataRepository newRepository = new MetadataRepository(); String content; try { content = IOUtils.toString(dataModel, "UTF-8"); //$NON-NLS-1$ newRepository.load(new ByteArrayInputStream(content.getBytes("UTF-8"))); //$NON-NLS-1$ } catch (IOException e) { throw new RuntimeException("Could not read data model from body.", e); //$NON-NLS-1$ } // Ask the storage to adapt its structure following the changes storage.adapt(newRepository, force); if (storageAdmin.supportStaging(modelName)) { Storage stagingStorage = storageAdmin.get(modelName, StorageType.STAGING); if (stagingStorage != null) { // TMDM-7312: Don't forget to add staging types (otherwise, staging storage will complain about removed // types). MetadataRepository stagingRepository = newRepository.copy(); stagingRepository .load(MetadataRepositoryAdmin.class.getResourceAsStream("stagingInternalTypes.xsd")); stagingStorage.adapt(stagingRepository, force); } else { LOGGER.warn("No SQL staging storage defined for data model '" + modelName //$NON-NLS-1$ + "'. No SQL staging storage to update."); //$NON-NLS-1$ } } else { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Storage '" + modelName + "' does not support staging (forbidden by storage admin)."); //$NON-NLS-1$ //$NON-NLS-2$ } } // Update the system storage with the new data model (if there was any change). Compare.DiffResults diffResults = Compare.compare(previousRepository, newRepository); if (!diffResults.getActions().isEmpty()) { UserQueryBuilder qb = from(dataModelType).where(eq(dataModelType.getField("name"), modelName)); //$NON-NLS-1$ systemStorage.begin(); try { StorageResults results = systemStorage.fetch(qb.getSelect()); Iterator<DataRecord> iterator = results.iterator(); if (iterator.hasNext()) { // Updates the data model in system db DataRecord model = iterator.next(); model.set(dataModelType.getField("schema"), content); //$NON-NLS-1$ if (iterator.hasNext()) { throw new IllegalStateException("Found multiple data models for '" + modelName + "'."); //$NON-NLS-1$ //$NON-NLS-2$ } systemStorage.update(model); } systemStorage.commit(); reloadDataModel(modelName); } catch (Exception e) { systemStorage.rollback(); throw new RuntimeException("Could not update data model.", e); //$NON-NLS-1$ } } // synchronize with outer agents DataModelChangeNotifier dmUpdateEventNotifier = DataModelChangeNotifier.createInstance(); dmUpdateEventNotifier.notifyChange(new DMUpdateEvent(modelName)); } private boolean isSystemStorageAvailable() { return !dataModelType.getName().isEmpty(); } @POST @Path("{model}") @ApiOperation("Get impacts of the model update with the new XSD provided as request content. Changes will not be performed !") public String analyzeModelChange(@ApiParam("Model name") @PathParam("model") String modelName, @ApiParam("Optional language to get localized result") @QueryParam("lang") String locale, InputStream dataModel) { Map<ImpactAnalyzer.Impact, List<Change>> impacts; List<String> typeNamesToDrop = new ArrayList<String>(); if (!isSystemStorageAvailable()) { impacts = new EnumMap<>(ImpactAnalyzer.Impact.class); for (ImpactAnalyzer.Impact impact : impacts.keySet()) { impacts.put(impact, Collections.<Change>emptyList()); } } else { StorageAdmin storageAdmin = ServerContext.INSTANCE.get().getStorageAdmin(); Storage storage = storageAdmin.get(modelName, StorageType.MASTER); if (storage == null) { LOGGER.warn( "Container '" + modelName + "' does not exist. Skip impact analyzing for model change."); //$NON-NLS-1$//$NON-NLS-2$ return StringUtils.EMPTY; } if (storage.getType() == StorageType.SYSTEM) { LOGGER.debug("No model update for system storage"); //$NON-NLS-1$ return StringUtils.EMPTY; } // Compare new data model with existing data model MetadataRepository previousRepository = storage.getMetadataRepository(); MetadataRepository newRepository = new MetadataRepository(); newRepository.load(dataModel); Compare.DiffResults diffResults = Compare.compare(previousRepository, newRepository); // Analyzes impacts on the select storage impacts = storage.getImpactAnalyzer().analyzeImpacts(diffResults); List<ComplexTypeMetadata> typesToDrop = storage.findSortedTypesToDrop(diffResults, true); Set<String> tableNamesToDrop = storage.findTablesToDrop(typesToDrop); for (String tableName : tableNamesToDrop) { if (previousRepository.getInstantiableTypes() .contains(previousRepository.getComplexType(tableName))) { typeNamesToDrop.add(tableName); } } } // Serialize results to XML StringWriter resultAsXml = new StringWriter(); XMLStreamWriter writer = null; try { writer = XMLOutputFactory.newFactory().createXMLStreamWriter(resultAsXml); writer.writeStartElement("result"); //$NON-NLS-1$ { for (Map.Entry<ImpactAnalyzer.Impact, List<Change>> category : impacts.entrySet()) { writer.writeStartElement(category.getKey().name().toLowerCase()); List<Change> changes = category.getValue(); for (Change change : changes) { writer.writeStartElement("change"); //$NON-NLS-1$ { writer.writeStartElement("message"); //$NON-NLS-1$ { Locale messageLocale; if (StringUtils.isEmpty(locale)) { messageLocale = Locale.getDefault(); } else { messageLocale = new Locale(locale); } writer.writeCharacters(change.getMessage(messageLocale)); } writer.writeEndElement(); } writer.writeEndElement(); } writer.writeEndElement(); } writer.writeStartElement("entitiesToDrop"); //$NON-NLS-1$ for (String typeName : typeNamesToDrop) { writer.writeStartElement("entity"); //$NON-NLS-1$ writer.writeCharacters(typeName); writer.writeEndElement(); } writer.writeEndElement(); } writer.writeEndElement(); } catch (XMLStreamException e) { throw new RuntimeException(e); } finally { if (writer != null) { try { writer.flush(); } catch (XMLStreamException e) { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Could not flush XML content.", e); //$NON-NLS-1$ } } } } return resultAsXml.toString(); } }