blob: c40f8cf2393781dfb2a01967b772345e1ab65781 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.chemistry.opencmis.util.repository;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import org.apache.chemistry.opencmis.commons.PropertyIds;
import org.apache.chemistry.opencmis.commons.data.Acl;
import org.apache.chemistry.opencmis.commons.data.ContentStream;
import org.apache.chemistry.opencmis.commons.data.ExtensionsData;
import org.apache.chemistry.opencmis.commons.data.ObjectData;
import org.apache.chemistry.opencmis.commons.data.ObjectInFolderData;
import org.apache.chemistry.opencmis.commons.data.ObjectInFolderList;
import org.apache.chemistry.opencmis.commons.data.Properties;
import org.apache.chemistry.opencmis.commons.data.PropertyData;
import org.apache.chemistry.opencmis.commons.definitions.TypeDefinition;
import org.apache.chemistry.opencmis.commons.definitions.TypeDefinitionList;
import org.apache.chemistry.opencmis.commons.enums.BaseTypeId;
import org.apache.chemistry.opencmis.commons.enums.IncludeRelationships;
import org.apache.chemistry.opencmis.commons.enums.UnfileObject;
import org.apache.chemistry.opencmis.commons.enums.VersioningState;
import org.apache.chemistry.opencmis.commons.impl.IOUtils;
import org.apache.chemistry.opencmis.commons.impl.dataobjects.ContentStreamImpl;
import org.apache.chemistry.opencmis.commons.spi.BindingsObjectFactory;
import org.apache.chemistry.opencmis.commons.spi.NavigationService;
import org.apache.chemistry.opencmis.commons.spi.ObjectService;
import org.apache.chemistry.opencmis.commons.spi.RepositoryService;
import org.apache.chemistry.opencmis.util.content.fractal.FractalGenerator;
import org.apache.chemistry.opencmis.util.content.loremipsum.LoremIpsum;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* A simple helper class for the tests that generates a sample folder hierarchy
* and optionally documents in it.
*/
public class ObjectGenerator {
private static final int KILO = 1024;
private static final Logger LOG = LoggerFactory.getLogger(ObjectGenerator.class);
private final BindingsObjectFactory fFactory;
NavigationService fNavSvc;
ObjectService fObjSvc;
RepositoryService fRepSvc;
private final String fRepositoryId;
private final TimeLogger fTimeLoggerCreateDoc;
private final TimeLogger fTimeLoggerCreateFolder;
private final TimeLogger fTimeLoggerDelete;
private final TimeLogger fTimeLoggerCreateType;
private boolean fCleanup;
List<String> fTopLevelDocsCreated; // list of ids created on first level
List<String> fTopLevelFoldersCreated; // list of ids created on first level
/**
* supported kinds of content
*
*/
public enum ContentKind {
STATIC_TEXT, LOREM_IPSUM_TEXT, LOREM_IPSUM_HTML, IMAGE_FRACTAL_JPEG
};
/**
* Indicates if / how many documents are created in each folder
*/
private int fNoDocumentsToCreate;
/**
* The type id of the document id that is created.
*/
private String fDocTypeId = BaseTypeId.CMIS_DOCUMENT.value();
/**
* The type id of the folder that is created.
*/
private String fFolderTypeId = BaseTypeId.CMIS_FOLDER.value();
/**
* A list of property ids. For each element in this list a String property
* value is created for each creation of a document. All ids must be valid
* string property id of the type fDocTypeId
*/
private List<String> fStringPropertyIdsToSetForDocument;
/**
* A list of property ids. For each element in this list a String property
* value is created for each creation of a folder. All ids must be valid
* string property id of the type fFolderTypeId
*/
private List<String> fStringPropertyIdsToSetForFolder;
/**
* number of documents created in total
*/
private int fDocumentsInTotalCount = 0;
/**
* number of folders created in total
*/
private int fFoldersInTotalCount = 0;
/**
* size of content in KB, if 0 create documents without content
*/
private int fContentSizeInK = 0;
/**
* Kind of content to create
*/
private ContentKind fContentKind;
private static final String NAMEPROPVALPREFIXDOC = "My_Document-";
private static final String NAMEPROPVALPREFIXFOLDER = "My_Folder-";
private static final String STRINGPROPVALPREFIXDOC = "My Doc StringProperty ";
private static final String STRINGPROPVALPREFIXFOLDER = "My Folder StringProperty ";
private int propValCounterDocString = 0;
private int propValCounterFolderString = 0;
/**
* use UUIDs to generate folder and document names
*/
private boolean fUseUuids;
/**
* generator for images
*/
private FractalGenerator fractalGenerator = null;
public ObjectGenerator(BindingsObjectFactory factory, NavigationService navSvc, ObjectService objSvc,
RepositoryService repSvc, String repositoryId, ContentKind contentKind) {
super();
fFactory = factory;
fNavSvc = navSvc;
fObjSvc = objSvc;
fRepSvc = repSvc;
fRepositoryId = repositoryId;
// create an empty list of properties to generate by default for folder
// and document
fStringPropertyIdsToSetForDocument = new ArrayList<String>();
fStringPropertyIdsToSetForFolder = new ArrayList<String>();
fNoDocumentsToCreate = 0;
fUseUuids = false;
fTimeLoggerCreateDoc = new TimeLogger("createDocument()");
fTimeLoggerCreateFolder = new TimeLogger("createFolder()");
fTimeLoggerDelete = new TimeLogger("Delete");
fTimeLoggerCreateType = new TimeLogger("createType()");
fCleanup = false;
fTopLevelDocsCreated = new ArrayList<String>();
fTopLevelFoldersCreated = new ArrayList<String>();
fContentKind = contentKind;
}
public void setNumberOfDocumentsToCreatePerFolder(int noDocumentsToCreate) {
fNoDocumentsToCreate = noDocumentsToCreate;
}
public void setFolderTypeId(String folderTypeId) {
fFolderTypeId = folderTypeId;
}
public void setDocumentTypeId(String docTypeId) {
fDocTypeId = docTypeId;
}
public void setDocumentPropertiesToGenerate(List<String> propertyIds) {
fStringPropertyIdsToSetForDocument = propertyIds;
}
public void setFolderPropertiesToGenerate(List<String> propertyIds) {
fStringPropertyIdsToSetForFolder = propertyIds;
}
public void setContentSizeInKB(int sizeInK) {
fContentSizeInK = sizeInK;
}
public ContentKind getContentKind() {
return fContentKind;
}
public void setLoreIpsumGenerator(ContentKind contentKind) {
fContentKind = contentKind;
}
public void setCleanUpAfterCreate(boolean doCleanup) {
fCleanup = doCleanup;
}
public TimeLogger getCreateDocumentTimeLogger() {
return fTimeLoggerCreateDoc;
}
public TimeLogger getCreateFolderTimeLogger() {
return fTimeLoggerCreateFolder;
}
public TimeLogger getDeleteTimeLogger() {
return fTimeLoggerDelete;
}
public void createFolderHierachy(int levels, int childrenPerLevel, String rootFolderId) {
resetCounters();
fTimeLoggerCreateDoc.reset();
fTimeLoggerCreateFolder.reset();
fTopLevelFoldersCreated.clear();
fTopLevelDocsCreated.clear();
createFolderHierachy(rootFolderId, 0, levels, childrenPerLevel);
if (fCleanup) {
deleteTree();
}
}
public void setUseUuidsForNames(boolean useUuids) {
/**
* use UUIDs to generate folder and document names
*/
fUseUuids = useUuids;
}
/**
* retrieve the index-th folder from given level of the hierarchy starting
* at rootId
*
* @param rootId
* @param level
* @param index
* @return
*/
public String getFolderId(String rootId, int level, int index) {
String objectId = rootId;
final String requiredProperties = PropertyIds.OBJECT_ID + "," + PropertyIds.OBJECT_TYPE_ID + ","
+ PropertyIds.BASE_TYPE_ID;
// Note: This works because first folders are created then documents
for (int i = 0; i < level; i++) {
ObjectInFolderList result = fNavSvc.getChildren(fRepositoryId, objectId, requiredProperties,
PropertyIds.OBJECT_TYPE_ID, false, IncludeRelationships.NONE, null, true, BigInteger.valueOf(-1),
BigInteger.valueOf(-1), null);
List<ObjectInFolderData> children = result.getObjects();
ObjectData child = children.get(index).getObject();
objectId = (String) child.getProperties().getProperties().get(PropertyIds.OBJECT_ID).getFirstValue();
}
return objectId;
}
/**
* retrieve the index-th document from given folder
*
* @param folderId
* folder to retrieve document from
* @param index
* index of document to retrieve from this folder
* @return
*/
public String getDocumentId(String folderId, int index) {
String docId = null;
final String requiredProperties = PropertyIds.OBJECT_ID + "," + PropertyIds.OBJECT_TYPE_ID + ","
+ PropertyIds.BASE_TYPE_ID;
ObjectInFolderList result = fNavSvc.getChildren(fRepositoryId, folderId, requiredProperties,
PropertyIds.OBJECT_TYPE_ID, false, IncludeRelationships.NONE, null, true, BigInteger.valueOf(-1),
BigInteger.valueOf(-1), null);
List<ObjectInFolderData> children = result.getObjects();
int numDocsFound = 0;
for (int i = 0; i < children.size(); i++) {
ObjectData child = children.get(i).getObject();
docId = (String) child.getProperties().getProperties().get(PropertyIds.OBJECT_ID).getFirstValue();
if (child.getBaseTypeId().equals(BaseTypeId.CMIS_DOCUMENT)) {
if (numDocsFound == index) {
return docId;
} else {
numDocsFound++;
}
}
}
return docId;
}
/**
* return the total number of documents created
*
* @return
*/
public int getDocumentsInTotal() {
return fDocumentsInTotalCount;
}
/**
* return the total number of folders created
*
* @return
*/
public int getFoldersInTotal() {
return fFoldersInTotalCount;
}
/**
* return the total number of objects created
*/
public int getObjectsInTotal() {
return fDocumentsInTotalCount + fFoldersInTotalCount;
}
public String createSingleDocument(String folderId) {
fTimeLoggerCreateDoc.reset();
String objectId = createDocument(folderId, 0, 0);
if (fCleanup) {
deleteObject(objectId);
}
return objectId;
}
public String[] createDocuments(String folderId, int count) {
String[] result;
fTimeLoggerCreateDoc.reset();
for (int i = 0; i < count; i++) {
String id = createDocument(folderId, 0, 0);
fTopLevelDocsCreated.add(id);
}
if (fCleanup) {
deleteTree();
result = null;
} else {
result = new String[count];
for (int i = 0; i < fTopLevelDocsCreated.size(); i++) {
result[i] = fTopLevelDocsCreated.get(i);
}
}
return result;
}
public String[] createFolders(String folderId, int count) {
String[] result;
fTimeLoggerCreateFolder.reset();
for (int i = 0; i < count; i++) {
createFolder(folderId);
}
if (fCleanup) {
deleteTree();
result = null;
} else {
result = new String[count];
for (int i = 0; i < fTopLevelFoldersCreated.size(); i++) {
result[i] = fTopLevelFoldersCreated.get(i);
}
}
return result;
}
public void resetCounters() {
fDocumentsInTotalCount = 0;
fFoldersInTotalCount = 0;
}
public void printTimings() {
fTimeLoggerCreateDoc.printTimes();
fTimeLoggerCreateFolder.printTimes();
if (fCleanup) {
fTimeLoggerDelete.printTimes();
}
}
public void logTimings() {
fTimeLoggerCreateDoc.logTimes();
fTimeLoggerCreateFolder.logTimes();
if (fCleanup) {
fTimeLoggerDelete.logTimes();
}
}
private void createFolderHierachy(String parentId, int level, int levels, int childrenPerLevel) {
String id = null;
if (level >= levels) {
return;
}
LOG.debug(" create folder for parent id: " + parentId + ", in level " + level + ", max levels " + levels);
for (int i = 0; i < childrenPerLevel; i++) {
Properties props = createFolderProperties(i, level);
try {
fTimeLoggerCreateFolder.start();
id = fObjSvc.createFolder(fRepositoryId, props, parentId, null, null, null, null);
if (level == 0) {
fTopLevelFoldersCreated.add(id);
}
} finally {
fTimeLoggerCreateFolder.stop();
}
if (id != null) {
++fFoldersInTotalCount;
createFolderHierachy(id, level + 1, levels, childrenPerLevel);
}
}
for (int j = 0; j < fNoDocumentsToCreate; j++) {
id = createDocument(parentId, j, level);
if (level == 0) {
fTopLevelDocsCreated.add(id);
}
}
}
private String createFolder(String parentId) {
Properties props = createFolderProperties(0, 0);
String id = null;
try {
fTimeLoggerCreateFolder.start();
id = fObjSvc.createFolder(fRepositoryId, props, parentId, null, null, null, null);
fTopLevelFoldersCreated.add(id);
} finally {
fTimeLoggerCreateFolder.stop();
}
return id;
}
private String createDocument(String folderId, int no, int level) {
ContentStream contentStream = null;
VersioningState versioningState = VersioningState.NONE;
List<String> policies = null;
Acl addACEs = null;
Acl removeACEs = null;
ExtensionsData extension = null;
LOG.debug("create document in folder " + folderId);
Properties props = createDocumentProperties(no, level);
String id = null;
if (fContentSizeInK > 0) {
switch (fContentKind) {
case STATIC_TEXT:
contentStream = createContentStaticText();
break;
case LOREM_IPSUM_TEXT:
contentStream = createContentLoremIpsumText();
break;
case LOREM_IPSUM_HTML:
contentStream = createContentLoremIpsumHtml();
break;
case IMAGE_FRACTAL_JPEG:
contentStream = createContentFractalimageJpeg();
break;
}
}
try {
fTimeLoggerCreateDoc.start();
id = fObjSvc.createDocument(fRepositoryId, props, folderId, contentStream, versioningState, policies,
addACEs, removeACEs, extension);
} finally {
fTimeLoggerCreateDoc.stop();
}
if (null == id) {
LOG.error("createDocument failed.");
}
++fDocumentsInTotalCount;
return id;
}
private void deleteTree() {
// delete all documents from first level
for (String id : fTopLevelDocsCreated) {
deleteObject(id);
}
// delete recursively all folders from first level
for (String id : fTopLevelFoldersCreated) {
try {
fTimeLoggerDelete.start();
fObjSvc.deleteTree(fRepositoryId, id, true, UnfileObject.DELETE, true, null);
} finally {
fTimeLoggerDelete.stop();
}
}
}
private void deleteObject(String objectId) {
try {
fTimeLoggerDelete.start();
fObjSvc.deleteObject(fRepositoryId, objectId, true, null);
} finally {
fTimeLoggerDelete.stop();
}
}
public ContentStream createContentLoremIpsumHtml() {
ContentStreamImpl content = new ContentStreamImpl();
content.setFileName("data.html");
content.setMimeType("text/html");
int len = fContentSizeInK * KILO; // size of document in K
LoremIpsum ipsum = new LoremIpsum();
String text = ipsum.generateParagraphsFullHtml(len, true);
content.setStream(new ByteArrayInputStream(IOUtils.toUTF8Bytes(text)));
return content;
}
public ContentStream createContentLoremIpsumText() {
ContentStreamImpl content = new ContentStreamImpl();
content.setFileName("data.txt");
content.setMimeType("text/plain");
int len = fContentSizeInK * 1024; // size of document in K
LoremIpsum ipsum = new LoremIpsum();
String text = ipsum.generateParagraphsPlainText(len, 80, true);
content.setStream(new ByteArrayInputStream(IOUtils.toUTF8Bytes(text)));
return content;
}
public ContentStream createContentStaticText() {
ContentStreamImpl content = new ContentStreamImpl();
content.setFileName("data.txt");
content.setMimeType("text/plain");
int len = fContentSizeInK * 1024; // size of document in K
byte[] b = { 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x0c, 0x0a,
0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x0c, 0x0a }; // 32
// Bytes
ByteArrayOutputStream ba = new ByteArrayOutputStream(len);
try {
for (int j = 0; j < fContentSizeInK; j++) {
// write 1K of data
for (int i = 0; i < 32; i++) {
ba.write(b);
}
}
} catch (IOException e) {
throw new RuntimeException("Failed to fill content stream with data", e);
}
content.setStream(new ByteArrayInputStream(ba.toByteArray()));
return content;
}
public ContentStream createContentFractalimageJpeg() {
if (null == fractalGenerator) {
fractalGenerator = new FractalGenerator();
}
ContentStreamImpl content = null;
try {
ByteArrayOutputStream bos = fractalGenerator.generateFractal();
content = new ContentStreamImpl();
content.setFileName("image.jpg");
content.setMimeType("image/jpeg");
content.setStream(new ByteArrayInputStream(bos.toByteArray()));
bos.close();
} catch (IOException e) {
System.err.println("Error when generating fractal image: " + e);
e.printStackTrace();
}
return content;
}
private Properties createFolderProperties(int no, int level) {
List<PropertyData<?>> properties = new ArrayList<PropertyData<?>>();
properties.add(fFactory.createPropertyStringData(PropertyIds.NAME, generateFolderNameValue(no, level)));
properties.add(fFactory.createPropertyIdData(PropertyIds.OBJECT_TYPE_ID, fFolderTypeId));
// Generate some property values for custom attributes
for (String stringPropId : fStringPropertyIdsToSetForFolder) {
properties.add(fFactory.createPropertyStringData(stringPropId, generateStringPropValueFolder()));
}
Properties props = fFactory.createPropertiesData(properties);
return props;
}
private Properties createDocumentProperties(int no, int level) {
List<PropertyData<?>> properties = new ArrayList<PropertyData<?>>();
properties.add(fFactory.createPropertyStringData(PropertyIds.NAME, generateDocNameValue(no, level)));
properties.add(fFactory.createPropertyIdData(PropertyIds.OBJECT_TYPE_ID, fDocTypeId));
// Generate some property values for custom attributes
for (String stringPropId : fStringPropertyIdsToSetForDocument) {
properties.add(fFactory.createPropertyStringData(stringPropId, generateStringPropValueDoc()));
}
Properties props = fFactory.createPropertiesData(properties);
return props;
}
private synchronized int incrementPropCounterDocStringProp() {
return propValCounterDocString++;
}
private synchronized int incrementPropCounterFolderStringProp() {
return propValCounterFolderString++;
}
private String generateDocNameValue(int no, int level) {
if (fUseUuids) {
return UUID.randomUUID().toString();
} else {
return NAMEPROPVALPREFIXDOC + level + "-" + no;
}
}
private String generateFolderNameValue(int no, int level) {
if (fUseUuids) {
return UUID.randomUUID().toString();
} else {
return NAMEPROPVALPREFIXFOLDER + level + "-" + no;
}
}
private String generateStringPropValueDoc() {
return STRINGPROPVALPREFIXDOC + incrementPropCounterDocStringProp();
}
private String generateStringPropValueFolder() {
return STRINGPROPVALPREFIXFOLDER + incrementPropCounterFolderStringProp();
}
public void dumpFolder(String folderId, String propertyFilter) {
LOG.debug("starting dumpFolder() id " + folderId + " ...");
boolean allRequiredPropertiesArePresent = propertyFilter != null && propertyFilter.equals("*"); // can
// be
// optimized
final String requiredProperties = allRequiredPropertiesArePresent ? propertyFilter : PropertyIds.OBJECT_ID
+ "," + PropertyIds.NAME + "," + PropertyIds.OBJECT_TYPE_ID + "," + PropertyIds.BASE_TYPE_ID;
// if all required properties are contained in the filter use we use the
// filter otherwise
// we use our own set and get those from the filter later in an extra
// call
String propertyFilterIntern = allRequiredPropertiesArePresent ? propertyFilter : requiredProperties;
dumpFolder(folderId, propertyFilterIntern, 0);
}
private void dumpFolder(String folderId, String propertyFilter, int depth) {
boolean allRequiredPropertiesArePresent = propertyFilter.equals("*"); // can
// be
// optimized
StringBuilder prefix = new StringBuilder();
for (int i = 0; i < depth; i++) {
prefix.append(" ");
}
ObjectInFolderList result = fNavSvc.getChildren(fRepositoryId, folderId, propertyFilter, null, false,
IncludeRelationships.NONE, null, true, BigInteger.valueOf(-1), BigInteger.valueOf(-1), null);
List<ObjectInFolderData> folders = result.getObjects();
if (null != folders) {
LOG.debug(prefix + "found " + folders.size() + " children in folder " + folderId);
int no = 0;
for (ObjectInFolderData folder : folders) {
LOG.debug(prefix.toString() + ++no + ": found object with id: " + folder.getObject().getId()
+ " and path segment: " + folder.getPathSegment());
dumpObjectProperties(folder.getObject(), depth, propertyFilter, !allRequiredPropertiesArePresent);
String objectTypeBaseId = folder.getObject().getBaseTypeId().value();
if (objectTypeBaseId.equals(BaseTypeId.CMIS_FOLDER.value())) {
dumpFolder(folder.getObject().getId(), propertyFilter, depth + 1);
} else if (objectTypeBaseId.equals(BaseTypeId.CMIS_DOCUMENT.value())) {
dumpObjectProperties(folder.getObject(), depth + 1, propertyFilter,
!allRequiredPropertiesArePresent);
}
}
}
LOG.debug(""); // add empty line
}
private void dumpObjectProperties(ObjectData object, int depth, String propertyFilter, boolean mustFetchProperties) {
final SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");
StringBuilder prefix = new StringBuilder();
for (int i = 0; i < depth; i++) {
prefix.append(" ");
}
LOG.debug(prefix + "found object id " + object.getId());
Map<String, PropertyData<?>> propMap;
if (mustFetchProperties) {
String objId = (String) object.getProperties().getProperties().get(PropertyIds.OBJECT_ID).getFirstValue();
Properties props = fObjSvc.getProperties(fRepositoryId, objId, propertyFilter, null);
propMap = props.getProperties();
} else {
propMap = object.getProperties().getProperties();
}
StringBuilder valueStr = new StringBuilder("[");
for (Map.Entry<String, PropertyData<?>> entry : propMap.entrySet()) {
if (entry.getValue().getValues().size() > 1) {
if (entry.getValue().getFirstValue() instanceof GregorianCalendar) {
for (Object obj : entry.getValue().getValues()) {
GregorianCalendar cal = (GregorianCalendar) obj;
valueStr.append(df.format(cal.getTime())).append(", ");
}
valueStr.append("]");
} else {
valueStr = new StringBuilder(entry.getValue().getValues().toString());
}
} else {
Object value = entry.getValue().getFirstValue();
if (null != value) {
valueStr = new StringBuilder(value.toString());
if (value instanceof GregorianCalendar) {
valueStr = new StringBuilder(df.format(((GregorianCalendar) entry.getValue().getFirstValue())
.getTime()));
}
}
}
LOG.debug(prefix + entry.getKey() + ": " + valueStr);
}
LOG.debug(""); // add empty line
}
public void createTypes(TypeDefinitionList typeDefList) {
fTimeLoggerCreateType.reset();
for (TypeDefinition td : typeDefList.getList()) {
// TODO: enable this if available!
// fRepSvc.createTypeDefinition(fRepositoryId, td);
}
}
}