001/*
002 * (C) Copyright 2006-2008 Nuxeo SA (http://nuxeo.com/) and others.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 *
016 * Contributors:
017 *     Nuxeo - initial API and implementation
018 *
019 * $Id$
020 */
021
022package org.nuxeo.ecm.platform.importer.source;
023
024import java.util.ArrayList;
025import java.util.List;
026import java.util.Random;
027
028import org.nuxeo.ecm.core.api.Blob;
029import org.nuxeo.ecm.core.api.Blobs;
030import org.nuxeo.ecm.core.api.blobholder.BlobHolder;
031import org.nuxeo.ecm.core.api.blobholder.SimpleBlobHolder;
032import org.nuxeo.ecm.platform.importer.random.DictionaryHolder;
033import org.nuxeo.ecm.platform.importer.random.HunspellDictionaryHolder;
034import org.nuxeo.ecm.platform.importer.random.RandomTextGenerator;
035
036/**
037 * Random {@link SourceNode} to be used for load testing
038 *
039 * @author Thierry Delprat
040 */
041public class RandomTextSourceNode implements SourceNode {
042
043    protected static RandomTextGenerator gen;
044
045    protected static int maxNode = 10000;
046
047    public static int maxDepth = 8;
048
049    public static int defaultNbDataNodesPerFolder = 100;
050
051    protected static int minGlobalFolders = 0;
052
053    protected static int minFoldersPerNode = 0;
054
055    protected static Integer nbNodes = 1;
056
057    protected static Integer nbFolders = 0;
058
059    protected static Long size;
060
061    protected Random hazard;
062
063    protected String name;
064
065    protected boolean folderish;
066
067    protected int level = 0;
068
069    protected int idx = 0;
070
071    protected static Integer blobSizeInKB;
072
073    protected List<SourceNode> cachedChildren = null;
074
075    public static boolean CACHE_CHILDREN = false;
076
077    protected boolean onlyText = true;
078
079    public RandomTextSourceNode(boolean folderish, int level, int idx, boolean onlyText) {
080        this.folderish = folderish;
081        hazard = new Random(System.currentTimeMillis());
082        this.level = level;
083        this.idx = idx;
084        this.onlyText = onlyText;
085    }
086
087    public static RandomTextSourceNode init(int maxSize) {
088        return init(maxSize, null, true);
089    }
090
091    public static RandomTextSourceNode init(int maxSize, Integer blobSizeInKB, boolean onlyText) {
092        return init(maxSize, blobSizeInKB, onlyText, new HunspellDictionaryHolder("fr_FR.dic"));
093    }
094
095    public static RandomTextSourceNode init(int maxSize, Integer blobSizeInKB, boolean onlyText,
096            DictionaryHolder dictionaryHolder) {
097        gen = new RandomTextGenerator(dictionaryHolder);
098        gen.prefilCache();
099        maxNode = maxSize;
100        nbNodes = 1;
101        size = new Long(0);
102        RandomTextSourceNode.blobSizeInKB = blobSizeInKB;
103        minGlobalFolders = maxNode / defaultNbDataNodesPerFolder;
104        minFoldersPerNode = 1 + (int) Math.pow(minGlobalFolders, (1.0 / maxDepth));
105        return new RandomTextSourceNode(true, 0, 0, onlyText);
106    }
107
108    protected String getBlobMimeType() {
109        if (onlyText) {
110            return "text/plain";
111        } else {
112            return "text/partial";
113        }
114    }
115
116    public BlobHolder getBlobHolder() {
117        if (folderish) {
118            return null;
119        }
120        String content = null;
121
122        if (blobSizeInKB == null) {
123            content = gen.getRandomText();
124        } else {
125            content = gen.getRandomText(blobSizeInKB);
126        }
127        synchronized (size) {
128            size += content.length();
129        }
130        Blob blob = Blobs.createBlob(content, getBlobMimeType(), null, getName() + ".txt");
131        return new SimpleBlobHolder(blob);
132    }
133
134    protected int getMidRandom(int target) {
135        return 1 + (target / 2) + hazard.nextInt(target);
136    }
137
138    protected int getMaxChildren() {
139        if (maxNode < nbNodes) {
140            return 0;
141        }
142        int targetRemainingFolders = minGlobalFolders - nbFolders;
143        if (targetRemainingFolders <= 0) {
144            return defaultNbDataNodesPerFolder + 1;
145        }
146        int target = ((maxNode - nbNodes) / targetRemainingFolders);
147        if (target <= 0) {
148            return 0;
149        }
150        return getMidRandom(target);
151    }
152
153    protected int getMaxFolderish() {
154        if (maxNode <= nbNodes) {
155            return 0;
156        }
157        return getMidRandom(minFoldersPerNode);
158    }
159
160    @Override
161    public List<SourceNode> getChildren() {
162
163        if (!folderish) {
164            return null;
165        }
166
167        if (cachedChildren != null) {
168            return cachedChildren;
169        }
170
171        List<SourceNode> children = new ArrayList<SourceNode>();
172        if (nbNodes > maxNode) {
173            return children;
174        }
175
176        int nbChildren = getMaxChildren();
177
178        synchronized (nbNodes) {
179            nbNodes = nbNodes + nbChildren;
180        }
181        for (int i = 0; i < nbChildren; i++) {
182            children.add(new RandomTextSourceNode(false, level, i, onlyText));
183        }
184        if (level < maxDepth) {
185            int nbFolderish = getMaxFolderish();
186            for (int i = 0; i < nbFolderish; i++) {
187                children.add(new RandomTextSourceNode(true, level + 1, i, onlyText));
188            }
189            synchronized (nbFolders) {
190                nbFolders = nbFolders + nbFolderish;
191            }
192        }
193        if (CACHE_CHILDREN) {
194            cachedChildren = children;
195        }
196        return children;
197    }
198
199    @Override
200    public String getName() {
201        if (name == null) {
202            if (folderish) {
203                name = "folder";
204            } else {
205                name = "file";
206            }
207            if (level == 0 && folderish) {
208                name = name + "-" + (System.currentTimeMillis() % 10000) + hazard.nextInt(100);
209            } else {
210                name = name + "-" + level + "-" + idx;
211            }
212        }
213        return name;
214    }
215
216    @Override
217    public boolean isFolderish() {
218        return folderish;
219    }
220
221    public static Integer getNbNodes() {
222        return nbNodes;
223    }
224
225    public static Long getSize() {
226        return size;
227    }
228
229    public int getLevel() {
230        return level;
231    }
232
233    @Override
234    public String getSourcePath() {
235        return null;
236    }
237}