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