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; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Set; import org.apache.commons.io.FileUtils; import org.apache.commons.lang.NotImplementedException; import org.apache.commons.lang.StringEscapeUtils; import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; import org.talend.mdm.commmon.metadata.ComplexTypeMetadata; import org.talend.mdm.commmon.metadata.CompoundFieldMetadata; import org.talend.mdm.commmon.metadata.ContainedTypeFieldMetadata; import org.talend.mdm.commmon.metadata.FieldMetadata; import org.talend.mdm.commmon.metadata.MetadataRepository; import org.talend.mdm.commmon.metadata.MetadataUtils; import org.talend.mdm.commmon.metadata.ReferenceFieldMetadata; import org.talend.mdm.commmon.util.webapp.XSystemObjects; import org.w3c.dom.Element; import org.xml.sax.InputSource; import org.xml.sax.XMLReader; import com.amalto.core.load.io.ResettableStringWriter; import com.amalto.core.metadata.ClassRepository; import com.amalto.core.query.user.Condition; import com.amalto.core.query.user.Select; import com.amalto.core.query.user.Split; import com.amalto.core.query.user.UserQueryBuilder; import com.amalto.core.query.user.UserQueryHelper; import com.amalto.core.query.user.metadata.Timestamp; import com.amalto.core.server.ServerContext; import com.amalto.core.server.StorageAdmin; import com.amalto.core.storage.record.DataRecord; import com.amalto.core.storage.record.DataRecordReader; import com.amalto.core.storage.record.DataRecordWriter; import com.amalto.core.storage.record.DataRecordIncludeNullValueXmlWriter; import com.amalto.core.storage.record.DataRecordXmlWriter; import com.amalto.core.storage.record.SystemDataRecordXmlWriter; import com.amalto.core.storage.record.XmlDOMDataRecordReader; import com.amalto.core.storage.record.XmlSAXDataRecordReader; import com.amalto.core.storage.record.XmlStringDataRecordReader; import com.amalto.xmlserver.interfaces.IWhereItem; import com.amalto.xmlserver.interfaces.IXmlServerSLWrapper; import com.amalto.xmlserver.interfaces.ItemPKCriteria; import com.amalto.xmlserver.interfaces.XmlServerException; import static com.amalto.core.query.user.UserQueryBuilder.*; public class StorageWrapper implements IXmlServerSLWrapper { private static final Logger LOGGER = Logger.getLogger(StorageWrapper.class); protected static final String PROVISIONING_PREFIX_INFO = "PROVISIONING.User."; //$NON-NLS-1$ private final DataRecordReader<String> xmlStringReader = new XmlStringDataRecordReader(); protected StorageAdmin storageAdmin; public StorageWrapper() { } private Select getSelectTypeById(ComplexTypeMetadata type, String uniqueID) { ComplexTypeMetadata typeForSelect = type; while (typeForSelect.getSuperTypes() != null && !typeForSelect.getSuperTypes().isEmpty() && typeForSelect.getSuperTypes().size() > 0) { typeForSelect = (ComplexTypeMetadata) typeForSelect.getSuperTypes().iterator().next(); } String[] splitUniqueID = uniqueID.split("\\."); //$NON-NLS-1$ UserQueryBuilder qb = UserQueryBuilder.from(typeForSelect); Collection<FieldMetadata> keyFields = type.getKeyFields(); if (splitUniqueID.length < (2 + keyFields.size())) { throw new IllegalArgumentException("ID '" + uniqueID //$NON-NLS-1$ + "' does not contain all required values for key of type '" + type.getName()); //$NON-NLS-1$ } if (keyFields.size() == 1) { String uniqueIDPrefix = splitUniqueID[0] + '.' + splitUniqueID[1] + '.'; String key = StringUtils.removeStart(uniqueID, uniqueIDPrefix); qb.where(eq(keyFields.iterator().next(), key)); } else { int currentIndex = 2; for (FieldMetadata keyField : keyFields) { qb.where(eq(keyField, splitUniqueID[currentIndex++])); } } return qb.getSelect(); } protected static String getTypeName(String uniqueID) { if (uniqueID == null) { throw new IllegalArgumentException("Unique id can not be null."); //$NON-NLS-1$ } String[] splitUniqueID = uniqueID.split("\\."); //$NON-NLS-1$ if (splitUniqueID.length < 3) { throw new IllegalArgumentException("Unique id is not valid."); //$NON-NLS-1$ } return splitUniqueID[1]; } @Override public boolean isUpAndRunning() { return getStorageAdmin() != null; } @Override public String[] getAllClusters() throws XmlServerException { return getStorageAdmin().getAll(); } @Override public long deleteCluster(String clusterName) throws XmlServerException { long start = System.currentTimeMillis(); { // TMDM-4692 Delete both master and staging data containers. getStorageAdmin().delete(clusterName, StorageType.MASTER, true); getStorageAdmin().delete(clusterName, StorageType.STAGING, true); } return System.currentTimeMillis() - start; } @Override public long deleteAllClusters() throws XmlServerException { long start = System.currentTimeMillis(); { getStorageAdmin().deleteAll(true); } return System.currentTimeMillis() - start; } @Override public long createCluster(String clusterName) throws XmlServerException { long start = System.currentTimeMillis(); { StorageAdmin admin = getStorageAdmin(); String dataSourceName = admin.getDatasource(clusterName); admin.create(clusterName, clusterName, admin.getType(clusterName), dataSourceName); } return System.currentTimeMillis() - start; } @Override public boolean existCluster(String cluster) throws XmlServerException { StorageType storageType = cluster.endsWith(StorageAdmin.STAGING_SUFFIX) ? StorageType.STAGING : StorageType.MASTER; return getStorageAdmin().exist(cluster, storageType); } @Override public long putDocumentFromFile(String fileName, String uniqueID, String clusterName) throws XmlServerException { return putDocumentFromFile(fileName, uniqueID, clusterName, IXmlServerSLWrapper.TYPE_DOCUMENT); } @Override public long putDocumentFromFile(String fileName, String uniqueID, String clusterName, String documentType) throws XmlServerException { long startTime = System.currentTimeMillis(); File file = new File(fileName); if (!file.canRead()) { throw new XmlServerException("Can not read file '" + fileName + "'."); //$NON-NLS-1$ //$NON-NLS-2$ } String content; try { if ("BINARY".equals(documentType)) { //$NON-NLS-1$ content = "<filename>" + file.getName() + "</filename>"; //$NON-NLS-1$ //$NON-NLS-2$ } else { content = FileUtils.readFileToString(file); } putDocumentFromString(content, uniqueID, clusterName); } catch (Exception e) { throw new XmlServerException("Can not save document file.", e); //$NON-NLS-1$ } return System.currentTimeMillis() - startTime; } @Override public long putDocumentFromString(String xmlString, String uniqueID, String clusterName) throws XmlServerException { return putDocumentFromString(xmlString, uniqueID, clusterName, null); } @Override public long putDocumentFromString(String xmlString, String uniqueID, String clusterName, String documentType) throws XmlServerException { String typeName = getTypeName(uniqueID); long start = System.currentTimeMillis(); { Storage storage = getStorage(clusterName); MetadataRepository repository = storage.getMetadataRepository(); DataRecord record = xmlStringReader.read(repository, repository.getComplexType(typeName), xmlString); try { storage.update(record); } catch (Exception e) { throw new XmlServerException(e); } } return System.currentTimeMillis() - start; } @Override public long putDocumentFromDOM(Element root, String uniqueID, String clusterName) throws XmlServerException { String typeName = getTypeName(uniqueID); long start = System.currentTimeMillis(); { DataRecordReader<Element> reader = new XmlDOMDataRecordReader(); Storage storage = getStorage(clusterName); MetadataRepository repository = storage.getMetadataRepository(); DataRecord record = reader.read(repository, repository.getComplexType(typeName), root); storage.update(record); } return System.currentTimeMillis() - start; } @Override public long putDocumentFromSAX(String dataClusterName, XMLReader docReader, InputSource input) throws XmlServerException { String typeName = getTypeName(input.getPublicId()); long start = System.currentTimeMillis(); { Storage storage = getStorage(dataClusterName); if (storage == null) { throw new XmlServerException("Data cluster '" + dataClusterName + "' does not exist."); //$NON-NLS-1$ //$NON-NLS-2$ } DataRecordReader<XmlSAXDataRecordReader.Input> reader = new XmlSAXDataRecordReader(); XmlSAXDataRecordReader.Input readerInput = new XmlSAXDataRecordReader.Input(docReader, input); MetadataRepository repository = storage.getMetadataRepository(); DataRecord record = reader.read(repository, repository.getComplexType(typeName), readerInput); storage.update(record); } return System.currentTimeMillis() - start; } @Override public String getDocumentAsString(String clusterName, String uniqueID) throws XmlServerException { return getDocumentAsString(clusterName, uniqueID, "UTF-8"); //$NON-NLS-1$ } @Override public String getDocumentAsString(String clusterName, String uniqueID, String encoding) throws XmlServerException { // TODO Web UI sends incomplete ids (in case of save of instance with auto increment). Fix caller code in IXtentisRMIPort. String[] splitUniqueID = uniqueID.split("\\."); //$NON-NLS-1$ if (splitUniqueID.length < 3) { return null; } if (encoding == null) { encoding = "UTF-8"; //$NON-NLS-1$ } Storage storage = getStorage(clusterName); MetadataRepository repository = storage.getMetadataRepository(); ComplexTypeMetadata type = repository.getComplexType(splitUniqueID[1]); Select select = getSelectTypeById(type, uniqueID); StorageResults results = null; try { storage.begin(); results = storage.fetch(select); String xmlString = getXmlString(clusterName, type, results.iterator(), uniqueID, encoding, true); storage.commit(); return xmlString; } catch (Exception e) { storage.rollback(); throw new XmlServerException(e); } finally { if (results != null) { results.close(); } } } @Override public byte[] getDocumentBytes(String clusterName, String uniqueID, String documentType) throws XmlServerException { try { return getDocumentAsString(clusterName, uniqueID).getBytes("UTF-8"); //$NON-NLS-1$ } catch (UnsupportedEncodingException e) { throw new XmlServerException(e); } } @Override public String[] getAllDocumentsUniqueID(String clusterName) throws XmlServerException { return getAllDocumentsUniqueID(clusterName, true); } protected String[] getAllDocumentsUniqueID(String clusterName, boolean includeClusterAndTypeName) throws XmlServerException { String pureClusterName = getPureClusterName(clusterName); Storage storage = getStorage(pureClusterName); MetadataRepository repository = storage.getMetadataRepository(); Collection<ComplexTypeMetadata> typeToQuery; if (clusterName.contains("/")) { //$NON-NLS-1$ String typeName = StringUtils.substringAfter(clusterName, "/"); //$NON-NLS-1$ ComplexTypeMetadata complexType = repository.getComplexType(typeName); if (complexType == null) { throw new IllegalArgumentException( "Type '" + typeName + "' does not exist in container '" + pureClusterName + "'"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ } typeToQuery = Collections.singletonList(complexType); } else { typeToQuery = getClusterTypes(clusterName); } try { List<String> uniqueIDs = new LinkedList<String>(); storage.begin(); for (ComplexTypeMetadata currentType : typeToQuery) { UserQueryBuilder qb = from(currentType).selectId(currentType); StorageResults results = storage.fetch(qb.getSelect()); try { for (DataRecord result : results) { uniqueIDs.add(getUniqueID(pureClusterName, currentType, result, includeClusterAndTypeName)); } } finally { results.close(); } } storage.commit(); return uniqueIDs.toArray(new String[uniqueIDs.size()]); } catch (Exception e) { storage.rollback(); throw new XmlServerException(e); } } @Override public long deleteDocument(String clusterName, final String uniqueID, String documentType) throws XmlServerException { long start = System.currentTimeMillis(); { String typeName = getTypeName(uniqueID); Storage storage = getStorage(clusterName); ComplexTypeMetadata type = storage.getMetadataRepository().getComplexType(typeName); Select select = getSelectTypeById(type, uniqueID); try { storage.begin(); storage.delete(select); storage.commit(); } catch (Exception e) { storage.rollback(); throw new XmlServerException(e); } finally { storage.end(); } } return System.currentTimeMillis() - start; } @Override public int deleteItems(String clusterName, String conceptName, IWhereItem whereItem) throws XmlServerException { if (conceptName == null) { throw new IllegalArgumentException("Concept name can not be null."); //$NON-NLS-1$ } if (clusterName == null) { throw new IllegalArgumentException("Could not find cluster name for concept '" + conceptName + "'."); //$NON-NLS-1$ //$NON-NLS-2$ } Storage storage = getStorage(clusterName); MetadataRepository repository = storage.getMetadataRepository(); ComplexTypeMetadata type = repository.getComplexType(conceptName); if (type == null) { throw new IllegalArgumentException( "Type '" + conceptName + "' does not exist in cluster '" + storage.getName() //$NON-NLS-1$ //$NON-NLS-2$ + "'."); //$NON-NLS-1$ } UserQueryBuilder qb = from(type); qb.where(UserQueryHelper.buildCondition(qb, whereItem, repository)); try { int count; storage.begin(); StorageResults results = storage.fetch(qb.getSelect()); try { count = results.getCount(); } finally { results.close(); } storage.delete(qb.getSelect()); storage.commit(); return count; } catch (Exception e) { storage.rollback(); throw new XmlServerException(e); } } @Override public long moveDocumentById(String sourceClusterName, String uniqueID, String targetClusterName) throws XmlServerException { throw new NotImplementedException(); } @Override public long countItems(String clusterName, String conceptName, IWhereItem whereItem) throws XmlServerException { if (conceptName == null) { throw new IllegalArgumentException("Concept name can not be null."); //$NON-NLS-1$ } if (clusterName == null) { throw new IllegalArgumentException("Could not find cluster name for concept '" + conceptName + "'."); //$NON-NLS-1$ //$NON-NLS-2$ } Storage storage = getStorage(clusterName); MetadataRepository repository = storage.getMetadataRepository(); Collection<ComplexTypeMetadata> types; if ("*".equals(conceptName)) { //$NON-NLS-1$ types = repository.getUserComplexTypes(); } else { ComplexTypeMetadata type = repository.getComplexType(conceptName); if (type == null) { throw new IllegalArgumentException("Type '" + conceptName + "' does not exist."); //$NON-NLS-1$ //$NON-NLS-2$ } types = Collections.singletonList(type); } try { int count = 0; storage.begin(); for (ComplexTypeMetadata type : types) { UserQueryBuilder qb = from(type); qb.where(UserQueryHelper.buildCondition(qb, whereItem, repository)); StorageResults results = storage.fetch(qb.getSelect()); try { count += results.getCount(); } finally { results.close(); } } storage.commit(); return count; } catch (Exception e) { storage.rollback(); throw new XmlServerException(e); } } @Override public ArrayList<String> runQuery(String clusterName, String query, String[] parameters) throws XmlServerException { return runQuery(clusterName, query, parameters, 0, 0, false); } @Override public ArrayList<String> runQuery(String clusterName, String query, String[] parameters, boolean includeNullValue) throws XmlServerException { return runQuery(clusterName, query, parameters, 0, 0, false, true); } @Override public ArrayList<String> runQuery(String clusterName, String query, String[] parameters, int start, int limit, boolean withTotalCount) throws XmlServerException { return runQuery(clusterName, query, parameters, 0, 0, false, false); } public ArrayList<String> runQuery(String clusterName, String query, String[] parameters, int start, int limit, boolean withTotalCount, boolean includeNullValue) throws XmlServerException { Storage storage = getStorage(clusterName); // replace parameters in the procedure if (parameters != null) { for (int i = 0; i < parameters.length; i++) { String param = parameters[i]; query = query.replaceAll("([^\\\\])%" + i + "([^\\d]*)", "$1" + param + "$2"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ } } StorageResults results = null; try { storage.begin(); results = storage.fetch(from(query).getExpression()); ResettableStringWriter writer = new ResettableStringWriter(); DataRecordWriter xmlWriter; if (includeNullValue == false) { xmlWriter = new DataRecordXmlWriter("result"); //$NON-NLS-1$ } else { xmlWriter = new DataRecordIncludeNullValueXmlWriter("result"); //$NON-NLS-1$ } ArrayList<String> resultsAsString = new ArrayList<String>(results.getCount()); for (DataRecord result : results) { xmlWriter.write(result, writer); resultsAsString.add(writer.toString()); writer.reset(); } storage.commit(); return resultsAsString; } catch (Exception e) { storage.rollback(); throw new RuntimeException("Could not create query results", e); //$NON-NLS-1$ } finally { if (results != null) { results.close(); } } } @Override public List<String> getItemPKsByCriteria(ItemPKCriteria criteria) throws XmlServerException { String clusterName = criteria.getClusterName(); Storage storage = getStorage(clusterName); MetadataRepository repository = storage.getMetadataRepository(); int totalCount = 0; List<String> itemPKResults = new LinkedList<String>(); String typeName = criteria.getConceptName(); try { storage.begin(); if (typeName != null && !typeName.isEmpty()) { totalCount = getTypeItemCount(criteria, repository.getComplexType(typeName), storage); itemPKResults.addAll(getTypeItems(criteria, repository.getComplexType(typeName), storage, repository.getComplexType(typeName).getName())); } else { // TMDM-4651: Returns type in correct dependency order. Collection<ComplexTypeMetadata> types = getClusterTypes(clusterName); int maxCount = criteria.getMaxItems(); if (criteria.getSkip() < 0) { // MDM Studio may send negative values criteria.setSkip(0); } List<String> currentInstanceResults; for (ComplexTypeMetadata type : types) { int count = getTypeItemCount(criteria, type, storage); totalCount += count; if (itemPKResults.size() < maxCount) { if (count > criteria.getSkip()) { currentInstanceResults = getTypeItems(criteria, type, storage, type.getName()); int n = maxCount - itemPKResults.size(); if (n <= currentInstanceResults.size()) { itemPKResults.addAll(currentInstanceResults.subList(0, n)); } else { itemPKResults.addAll(currentInstanceResults); } criteria.setMaxItems(criteria.getMaxItems() - currentInstanceResults.size()); criteria.setSkip(0); } else { criteria.setSkip(criteria.getSkip() - count); } } } } itemPKResults.add(0, "<totalCount>" + totalCount + "</totalCount>"); //$NON-NLS-1$ //$NON-NLS-2$ storage.commit(); } catch (Exception e) { storage.rollback(); throw new XmlServerException(e); } return itemPKResults; } protected Collection<ComplexTypeMetadata> getClusterTypes(String clusterName) { Storage storage = getStorage(clusterName); MetadataRepository repository = storage.getMetadataRepository(); return MetadataUtils.sortTypes(repository, MetadataUtils.SortType.LENIENT); } protected int getTypeItemCount(ItemPKCriteria criteria, ComplexTypeMetadata type, Storage storage) { StorageResults results = storage .fetch(buildQueryBuilder(from(type).selectId(type), criteria, type).getSelect()); try { return results.getCount(); } finally { results.close(); } } protected List<String> getTypeItems(ItemPKCriteria criteria, ComplexTypeMetadata type, Storage storage, String resultElementName) throws XmlServerException { // Build base query UserQueryBuilder qb = from(type).select(alias(timestamp(), "timestamp")) //$NON-NLS-1$ .select(alias(taskId(), "taskid")) //$NON-NLS-1$ .selectId(type).limit(criteria.getMaxItems()).start(criteria.getSkip()); List<String> list = new LinkedList<String>(); StorageResults results = storage.fetch(buildQueryBuilder(qb, criteria, type).getSelect()); DataRecordWriter writer = new ItemPKCriteriaResultsWriter(resultElementName, type); ResettableStringWriter stringWriter = new ResettableStringWriter(); try { for (DataRecord result : results) { writer.write(result, stringWriter); list.add(stringWriter.toString()); stringWriter.reset(); } } catch (Exception e) { storage.rollback(); throw new XmlServerException(e); } finally { results.close(); } return list; } private UserQueryBuilder buildQueryBuilder(UserQueryBuilder qb, ItemPKCriteria criteria, ComplexTypeMetadata type) { // Filter by keys: expected format here: $EntityTypeName/Path/To/Field$[id_for_lookup] String keysString = criteria.getKeys(); if (keysString != null && !keysString.isEmpty()) { buildKeyCondition(qb, type, criteria.getClusterName(), keysString); } String keysKeywords = criteria.getKeysKeywords(); if (keysKeywords != null && !keysKeywords.isEmpty()) { if (keysKeywords.charAt(0) == '$') { char[] chars = keysKeywords.toCharArray(); List<String> paths = new LinkedList<String>(); StringBuilder currentPath = new StringBuilder(); StringBuilder id = new StringBuilder(); StringBuilder idForLookup = new StringBuilder(); int dollarCount = 0; int slashCount = 0; for (char current : chars) { switch (current) { case '$': dollarCount++; break; case ',': paths.add(currentPath.toString()); currentPath = new StringBuilder(); slashCount = 0; break; case '/': slashCount++; default: if (dollarCount == 0) { id.append(current); } else if (dollarCount >= 2) { idForLookup.append(current); } else if (slashCount > 0) { if (slashCount != 1 || '/' != current) { currentPath.append(current); } } } } paths.add(currentPath.toString()); if (currentPath.toString().isEmpty()) { // TODO Implement compound key support qb.where(eq(type.getKeyFields().iterator().next(), id.toString())); } else { Condition globalCondition = UserQueryHelper.FALSE; for (String path : paths) { Condition pathCondition = UserQueryHelper.FALSE; // TMDM-10201, if FK point to a inheritance type, like: keysKeywords = $Feature/Value/EnumFK,EnumValueType/EnumFK$[1] // Only need to query "Feature" by "Value/EnumFK=1" FieldMetadata field = null; try { field = type.getField(path); } catch (IllegalArgumentException e) { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Ignoring field '" + path + "'", e); //$NON-NLS-1$ //$NON-NLS-2$ } } if (field != null) { Set<FieldMetadata> candidateFields = Collections.singleton(type.getField(path)); for (FieldMetadata candidateField : candidateFields) { if (candidateField instanceof ReferenceFieldMetadata && ((ReferenceFieldMetadata) candidateField) .getReferencedField() instanceof CompoundFieldMetadata) { // composite key: fkValue of the form '[value1.value2.value3...]' pathCondition = or( eq(candidateField, StringUtils.replace(idForLookup.toString(), ".", "][")), //$NON-NLS-1$//$NON-NLS-2$ pathCondition); } else { pathCondition = or(eq(candidateField, idForLookup.toString()), pathCondition); } } globalCondition = or(globalCondition, pathCondition); } } qb.where(globalCondition); } } else { buildKeyCondition(qb, type, criteria.getClusterName(), keysKeywords); } } // Filter by timestamp if (criteria.getFromDate() > 0) { qb.where(gte(timestamp(), String.valueOf(criteria.getFromDate()))); } if (criteria.getToDate() > 0) { qb.where(lte(timestamp(), String.valueOf(criteria.getToDate()))); } // Content keywords String contentKeywords = criteria.getContentKeywords(); if (contentKeywords != null && !contentKeywords.isEmpty()) { if (criteria.isUseFTSearch()) { qb.where(fullText(contentKeywords)); } else { Condition condition = null; for (FieldMetadata field : type.getFields()) { if (StorageMetadataUtils.isValueAssignable(contentKeywords, field.getType().getName())) { if (!(field instanceof ContainedTypeFieldMetadata)) { if (condition == null) { condition = contains(field, contentKeywords); } else { condition = or(condition, contains(field, contentKeywords)); } } } } if (condition != null) { qb.where(condition); } } } return qb; } @Override public void clearCache() { } @Override public boolean supportTransaction() { return true; } protected StorageAdmin getStorageAdmin() { if (storageAdmin == null) { storageAdmin = ServerContext.INSTANCE.get().getStorageAdmin(); } return storageAdmin; } protected Storage getStorage(String clusterName) { StorageAdmin admin = getStorageAdmin(); if (!admin.exist(clusterName, admin.getType(clusterName))) { throw new IllegalStateException("Data container '" + clusterName + "' does not exist."); //$NON-NLS-1$ //$NON-NLS-2$ } return admin.get(clusterName, admin.getType(clusterName)); } @Override public void start(String dataClusterName) throws XmlServerException { Storage storage = getStorage(dataClusterName); storage.begin(); } @Override public void commit(String dataClusterName) throws XmlServerException { Storage storage = getStorage(dataClusterName); storage.commit(); } @Override public void rollback(String dataClusterName) throws XmlServerException { Storage storage = getStorage(dataClusterName); storage.rollback(); } @Override public void end(String dataClusterName) throws XmlServerException { Storage storage = getStorage(dataClusterName); storage.end(); } @Override public void close() throws XmlServerException { getStorageAdmin().close(); } @Override public List<String> globalSearch(String dataCluster, String keyword, int start, int end) throws XmlServerException { Storage storage = getStorage(dataCluster); MetadataRepository repository = storage.getMetadataRepository(); Iterator<ComplexTypeMetadata> types = repository.getUserComplexTypes().iterator(); if (types.hasNext()) { ComplexTypeMetadata mainType = types.next(); while (types.hasNext() && mainType.getKeyFields().size() > 1) { ComplexTypeMetadata next = types.next(); if (next.getKeyFields().size() > 1) { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Ignoring type '" + next.getName() + "' (compound key)."); //$NON-NLS-1$ //$NON-NLS-2$ } } mainType = next; } UserQueryBuilder qb = from(mainType); while (types.hasNext()) { ComplexTypeMetadata additionalType = types.next(); if (additionalType.getKeyFields().size() == 1) { qb.and(additionalType); } else { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Ignoring type '" + additionalType.getName() + "' (compound key)."); //$NON-NLS-1$ //$NON-NLS-2$ } } } qb.where(fullText(keyword)); qb.start(start); qb.limit(end); StorageResults results = null; try { storage.begin(); results = Split.fetchAndMerge(storage, qb.getSelect()); // TMDM-7290: Split main query into smaller queries. DataRecordWriter writer = new FullTextResultsWriter(keyword); List<String> resultsAsXmlStrings = new ArrayList<String>(results.getCount() + 1); resultsAsXmlStrings.add(String.valueOf(results.getCount())); for (DataRecord result : results) { ByteArrayOutputStream output = new ByteArrayOutputStream(); writer.write(result, output); output.flush(); resultsAsXmlStrings.add(output.toString()); } storage.commit(); return resultsAsXmlStrings; } catch (Exception e) { storage.rollback(); throw new XmlServerException(e); } finally { if (results != null) { results.close(); } } } else { return Collections.emptyList(); } } @Override public void exportDocuments(String clusterName, int start, int end, boolean includeMetadata, OutputStream outputStream) throws XmlServerException { // No support for bulk export when using SQL storages (this could be in HibernateStorage but would require to define new API). throw new NotImplementedException("No support for bulk export."); //$NON-NLS-1$ } private void buildKeyCondition(UserQueryBuilder qb, ComplexTypeMetadata type, String clusterName, String keysString) { Collection<FieldMetadata> keyFields = type.getKeyFields(); if (clusterName.equals(XSystemObjects.DC_UPDATE_PREPORT.getName()) && type.getName().equals("Update")) { //$NON-NLS-1$ // UpdateReport: Source.TimeInMillis is the key String[] keys = keysString.split("\\."); //$NON-NLS-1$ if (keys.length == 1 || keys.length > 2) { throw new IllegalArgumentException( "The key format is 'Source.TimeInMillis' for type " + type.getName()); //$NON-NLS-1$ } else if (keys.length == 2) { if (keys[1] == null || keys[1].trim().isEmpty() || !StorageMetadataUtils.isValueAssignable(keys[1], Timestamp.INSTANCE.getTypeName())) { throw new IllegalArgumentException( "The key format is 'Source.TimeInMillis' for type '" + type.getName() + "'" + //$NON-NLS-1$//$NON-NLS-2$ " and the TimeInMillis key value must be a long type."); //$NON-NLS-1$ } int i = 0; for (FieldMetadata keyField : keyFields) { qb.where(eq(keyField, keys[i])); i++; } } } else { if (keyFields.size() > 1) { throw new IllegalArgumentException( "Expected type '" + type.getName() + "' to contain only 1 key field."); //$NON-NLS-1$ //$NON-NLS-2$ } String uniqueKeyFieldName = keyFields.iterator().next().getName(); qb.where(eq(type.getField(uniqueKeyFieldName), keysString)); } } @Override public String[] getDocumentsAsString(String clusterName, String[] uniqueIDs) throws XmlServerException { return getDocumentsAsString(clusterName, uniqueIDs, "UTF-8"); //$NON-NLS-1$ } @Override public String[] getDocumentsAsString(String clusterName, String[] uniqueIDs, String encoding) throws XmlServerException { if (uniqueIDs == null || uniqueIDs.length == 0) { return new String[0]; } List<String> xmlStrings = new ArrayList<String>(uniqueIDs.length); for (String uniqueID : uniqueIDs) { xmlStrings.add(getDocumentAsString(clusterName, uniqueID, encoding)); } return xmlStrings.toArray(new String[xmlStrings.size()]); } public static String getPureClusterName(String clusterName) { return clusterName.contains("/") ? StringUtils.substringBefore(clusterName, "/") : clusterName;//$NON-NLS-1$ //$NON-NLS-2$ } protected String getXmlString(String clusterName, ComplexTypeMetadata type, Iterator<DataRecord> iterator, String uniqueID, String encoding, boolean isUserFormat) throws IOException { String xmlString = null; if (iterator.hasNext()) { DataRecord result = iterator.next(); if (iterator.hasNext()) { iterateUnexceptedRecords(LOGGER, uniqueID, iterator); } ByteArrayOutputStream output = new ByteArrayOutputStream(1024); // Enforce root element name in case query returned instance of a subtype. DataRecordWriter dataRecordXmlWriter = isUserFormat ? new DataRecordXmlWriter(type) : new SystemDataRecordXmlWriter( (ClassRepository) getStorage(clusterName).getMetadataRepository(), type); if (isUserFormat) { dataRecordXmlWriter.setSecurityDelegator(SecuredStorage.getDelegator()); String key = uniqueID.startsWith(PROVISIONING_PREFIX_INFO) ? StringUtils.substringAfter(uniqueID, PROVISIONING_PREFIX_INFO) : uniqueID.split("\\.")[2]; //$NON-NLS-1$ long timestamp = result.getRecordMetadata().getLastModificationTime(); String taskId = result.getRecordMetadata().getTaskId(); String modelName = StringUtils.substringBeforeLast(clusterName, StorageAdmin.STAGING_SUFFIX); byte[] start = ("<ii><c>" + clusterName + "</c><dmn>" + modelName + "</dmn><dmr/><sp/><t>" //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$ + timestamp + "</t><taskId>" + taskId + "</taskId><i>" + StringEscapeUtils.escapeXml(key) //$NON-NLS-1$//$NON-NLS-2$ + "</i><p>").getBytes(); //$NON-NLS-1$ output.write(start); } dataRecordXmlWriter.write(result, output); if (isUserFormat) { byte[] end = ("</p></ii>").getBytes(); //$NON-NLS-1$ output.write(end); } output.flush(); xmlString = new String(output.toByteArray(), encoding); } return xmlString; } protected static String getUniqueID(String clusterName, ComplexTypeMetadata type, DataRecord record, boolean includeClusterAndTypeName) { StringBuilder builder = new StringBuilder(); if (includeClusterAndTypeName) { builder.append(clusterName).append('.').append(type.getName()).append('.'); } Iterator<FieldMetadata> iterator = type.getKeyFields().iterator(); while (iterator.hasNext()) { builder.append(String.valueOf(record.get(iterator.next()))); if (iterator.hasNext()) { builder.append('.'); } } return builder.toString(); } /** * Iterate unexcepted records to make sure Session can be truely closed, and log WARN message <br /> * See TMDM-6712: Consumes all results in iterator */ protected static void iterateUnexceptedRecords(Logger logger, String uniqueID, Iterator<DataRecord> iterator) { int recordsLeft = 1; while (iterator.hasNext()) { // TMDM-6712: Consumes all results in iterator iterator.next(); if (recordsLeft % 10 == 0) { logger.warn("Processing query with id '" + uniqueID + "' lead to unexpected number of results (" //$NON-NLS-1$//$NON-NLS-2$ + recordsLeft + " so far)."); //$NON-NLS-1$ } recordsLeft++; } throw new IllegalStateException("Expected only 1 result with id '" + uniqueID + "'."); //$NON-NLS-1$ //$NON-NLS-2$ } }