This project has retired. For details please refer to its Attic page.
InMemoryServiceFactoryImpl xref

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   * http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.chemistry.opencmis.inmemory.server;
20  
21  import java.io.File;
22  import java.io.InputStream;
23  import java.math.BigInteger;
24  import java.text.SimpleDateFormat;
25  import java.util.ArrayList;
26  import java.util.Date;
27  import java.util.LinkedHashMap;
28  import java.util.List;
29  import java.util.Locale;
30  import java.util.Map;
31  import java.util.concurrent.Executors;
32  import java.util.concurrent.ScheduledExecutorService;
33  import java.util.concurrent.ScheduledFuture;
34  import java.util.concurrent.TimeUnit;
35  
36  import javax.xml.bind.JAXBElement;
37  import javax.xml.bind.Unmarshaller;
38  
39  import org.apache.chemistry.opencmis.commons.data.RepositoryInfo;
40  import org.apache.chemistry.opencmis.commons.definitions.PropertyDefinition;
41  import org.apache.chemistry.opencmis.commons.definitions.TypeDefinition;
42  import org.apache.chemistry.opencmis.commons.enums.BaseTypeId;
43  import org.apache.chemistry.opencmis.commons.impl.Converter;
44  import org.apache.chemistry.opencmis.commons.impl.dataobjects.AbstractTypeDefinition;
45  import org.apache.chemistry.opencmis.commons.impl.dataobjects.BindingsObjectFactoryImpl;
46  import org.apache.chemistry.opencmis.commons.impl.jaxb.CmisTypeDefinitionType;
47  import org.apache.chemistry.opencmis.commons.impl.server.AbstractServiceFactory;
48  import org.apache.chemistry.opencmis.commons.server.CallContext;
49  import org.apache.chemistry.opencmis.commons.server.CmisService;
50  import org.apache.chemistry.opencmis.commons.spi.BindingsObjectFactory;
51  import org.apache.chemistry.opencmis.inmemory.ConfigConstants;
52  import org.apache.chemistry.opencmis.inmemory.ConfigurationSettings;
53  import org.apache.chemistry.opencmis.inmemory.storedobj.api.ObjectStore;
54  import org.apache.chemistry.opencmis.inmemory.storedobj.api.StoreManager;
55  import org.apache.chemistry.opencmis.inmemory.storedobj.api.TypeManagerCreatable;
56  import org.apache.chemistry.opencmis.inmemory.storedobj.impl.StoreManagerFactory;
57  import org.apache.chemistry.opencmis.inmemory.storedobj.impl.StoreManagerImpl;
58  import org.apache.chemistry.opencmis.inmemory.types.InMemoryJaxbHelper;
59  import org.apache.chemistry.opencmis.inmemory.types.TypeDefinitions;
60  import org.apache.chemistry.opencmis.server.support.CmisServiceWrapper;
61  import org.apache.chemistry.opencmis.server.support.TypeManager;
62  import org.apache.chemistry.opencmis.util.repository.ObjectGenerator;
63  import org.apache.commons.logging.Log;
64  import org.apache.commons.logging.LogFactory;
65  
66  public class InMemoryServiceFactoryImpl extends AbstractServiceFactory {
67  
68      private static final Log LOG = LogFactory.getLog(InMemoryServiceFactoryImpl.class.getName());
69      private static final BigInteger DEFAULT_MAX_ITEMS_OBJECTS = BigInteger.valueOf(1000);
70      private static final BigInteger DEFAULT_MAX_ITEMS_TYPES = BigInteger.valueOf(100);
71      private static final BigInteger DEFAULT_DEPTH_OBJECTS = BigInteger.valueOf(2);
72      private static final BigInteger DEFAULT_DEPTH_TYPES = BigInteger.valueOf(-1);
73      private static CallContext OVERRIDE_CTX;
74  
75      private Map<String, String> inMemoryServiceParameters;
76      private ThreadLocal<CmisServiceWrapper<InMemoryService>> threadLocalService = new ThreadLocal<CmisServiceWrapper<InMemoryService>>();
77      private boolean fUseOverrideCtx = false;
78      private StoreManager storeManager; // singleton root of everything
79      private CleanManager cleanManager = null;
80      
81      private File tempDir;
82      private int memoryThreshold;
83      
84  
85      @Override
86      public void init(Map<String, String> parameters) {
87          LOG.info("Initializing in-memory repository...");
88  
89          inMemoryServiceParameters = parameters;
90          String overrideCtx = parameters.get(ConfigConstants.OVERRIDE_CALL_CONTEXT);
91          if (null != overrideCtx) {
92              fUseOverrideCtx = true;
93          }
94  
95          ConfigurationSettings.init(parameters);
96  
97          String repositoryClassName = (String) parameters.get(ConfigConstants.REPOSITORY_CLASS);
98          if (null == repositoryClassName) {
99              repositoryClassName = StoreManagerImpl.class.getName();
100         }
101 
102         if (null == storeManager) {
103             storeManager = StoreManagerFactory.createInstance(repositoryClassName);
104         }
105 
106         String tempDirStr = parameters.get(ConfigConstants.TEMP_DIR);
107         tempDir = (tempDirStr == null ? super.getTempDirectory() : new File(tempDirStr));
108         
109         String memoryThresholdStr = parameters.get(ConfigConstants.MEMORY_THRESHOLD);
110         memoryThreshold = (memoryThresholdStr == null ? super.getMemoryThreshold(): Integer.parseInt(memoryThresholdStr));
111         
112         Date deploymentTime = new Date();
113         String strDate = new SimpleDateFormat("EEE MMM dd hh:mm:ss a z yyyy", Locale.US).format(deploymentTime);
114 
115         parameters.put(ConfigConstants.DEPLOYMENT_TIME, strDate);
116 
117         initStorageManager(parameters);
118 
119         fillRepositoryIfConfigured(parameters);
120 
121         Long cleanInterval = ConfigurationSettings
122                 .getConfigurationValueAsLong(ConfigConstants.CLEAN_REPOSITORY_INTERVAL);
123         if (null != cleanInterval && cleanInterval > 0) {
124             scheduleCleanRepositoryJob(cleanInterval);
125         }
126 
127         LOG.info("...initialized in-memory repository.");
128     }
129 
130     public static void setOverrideCallContext(CallContext ctx) {
131         OVERRIDE_CTX = ctx;
132     }
133 
134     @Override
135     public CmisService getService(CallContext context) {
136         LOG.debug("start getService()");
137 
138         // Attach the CallContext to a thread local context that can be
139         // accessed from everywhere
140         // Some unit tests set their own context. So if we find one then we use
141         // this one and ignore the provided one. Otherwise we set a new context.
142         if (fUseOverrideCtx && null != OVERRIDE_CTX) {
143             context = OVERRIDE_CTX;
144         }
145 
146         CmisServiceWrapper<InMemoryService> wrapperService = threadLocalService.get();
147         if (wrapperService == null) {
148             wrapperService = new CmisServiceWrapper<InMemoryService>(new InMemoryService(inMemoryServiceParameters,
149                     storeManager), DEFAULT_MAX_ITEMS_TYPES, DEFAULT_DEPTH_TYPES, DEFAULT_MAX_ITEMS_OBJECTS,
150                     DEFAULT_DEPTH_OBJECTS);
151             threadLocalService.set(wrapperService);
152         }
153 
154         wrapperService.getWrappedService().setCallContext(context);
155 
156         LOG.debug("stop getService()");
157         return wrapperService.getWrappedService(); // wrapperService;
158     }
159 
160     @Override
161     public File getTempDirectory() {
162         return tempDir;
163     }
164 
165     @Override
166     public int getMemoryThreshold() {
167         return memoryThreshold;
168     }
169     
170     
171     @Override
172     public void destroy() {
173         if (null != cleanManager) {
174             cleanManager.stopCleanRepositoryJob();
175         }
176         threadLocalService = null;
177     }
178 
179     public StoreManager getStoreManger() {
180         return storeManager;
181     }
182 
183     private void initStorageManager(Map<String, String> parameters) {
184         // initialize in-memory management
185         String repositoryClassName = (String) parameters.get(ConfigConstants.REPOSITORY_CLASS);
186         if (null == repositoryClassName) {
187             repositoryClassName = StoreManagerImpl.class.getName();
188         }
189 
190         if (null == storeManager) {
191             storeManager = StoreManagerFactory.createInstance(repositoryClassName);
192         }
193 
194         String repositoryId = parameters.get(ConfigConstants.REPOSITORY_ID);
195 
196         List<String> allAvailableRepositories = storeManager.getAllRepositoryIds();
197 
198         // init existing repositories
199         for (String existingRepId : allAvailableRepositories) {
200             storeManager.initRepository(existingRepId);
201         }
202 
203         // create repository if configured as a startup parameter
204         if (null != repositoryId) {
205             if (allAvailableRepositories.contains(repositoryId)) {
206                 LOG.warn("Repostory " + repositoryId + " already exists and will not be created.");
207             } else {
208                 String typeCreatorClassName = parameters.get(ConfigConstants.TYPE_CREATOR_CLASS);
209                 storeManager.createAndInitRepository(repositoryId, typeCreatorClassName);
210             }
211         }
212 
213         // check if a type definitions XML file is configured. if yes import
214         // type definitions
215         String typeDefsFileName = parameters.get(ConfigConstants.TYPE_XML);
216         if (null == typeDefsFileName)
217             LOG.info("No file name for type definitions given, no types will be created.");
218         else {
219             TypeManager typeManager = storeManager.getTypeManager(repositoryId);
220             if (typeManager instanceof TypeManagerCreatable) {
221                 TypeManagerCreatable tmc = (TypeManagerCreatable) typeManager;
222                 importTypesFromFile(tmc, typeDefsFileName);
223             } else {
224                 LOG.warn("Type Definitions are configured in XML file but type manager cannot create types. Type definitions are ignored.");
225             }
226         }
227 
228     }
229 
230     private void importTypesFromFile(TypeManagerCreatable tmc, String typeDefsFileName) {
231 
232         InputStream is = this.getClass().getResourceAsStream("/" + typeDefsFileName);
233 
234         if (null == is) {
235             LOG.warn("Resource file with type definitions " + typeDefsFileName
236                     + " could not be found, no types will be created.");
237             return;
238         }
239 
240         try {
241             TypeDefinition typeDef = null;
242             Unmarshaller u = InMemoryJaxbHelper.createUnmarshaller();
243             JAXBElement<TypeDefinitions> types = (JAXBElement<TypeDefinitions>) u.unmarshal(is);
244             for (CmisTypeDefinitionType td : types.getValue().getTypeDefinitions()) {
245                 LOG.debug("Found type in file: " + td.getLocalName());
246                 typeDef = Converter.convert(td);
247                 if (typeDef.getPropertyDefinitions() == null) {
248                     ((AbstractTypeDefinition) typeDef)
249                             .setPropertyDefinitions(new LinkedHashMap<String, PropertyDefinition<?>>());
250                 }
251                 tmc.addTypeDefinition(typeDef);
252             }
253         } catch (Exception e) {
254             LOG.error("Could not load type definitions from file '" + typeDefsFileName + "': " + e);
255         }
256     }
257 
258     private static List<String> readPropertiesToSetFromConfig(Map<String, String> parameters, String keyPrefix) {
259         List<String> propsToSet = new ArrayList<String>();
260         for (int i = 0;; ++i) {
261             String propertyKey = keyPrefix + Integer.toString(i);
262             String propertyToAdd = parameters.get(propertyKey);
263             if (null == propertyToAdd) {
264                 break;
265             } else {
266                 propsToSet.add(propertyToAdd);
267             }
268         }
269         return propsToSet;
270     }
271 
272     private void fillRepositoryIfConfigured(Map<String, String> parameters) {
273 
274         class DummyCallContext implements CallContext {
275 
276             public String get(String key) {
277                 return null;
278             }
279 
280             public String getBinding() {
281                 return null;
282             }
283 
284             public boolean isObjectInfoRequired() {
285                 return false;
286             }
287 
288             public String getRepositoryId() {
289                 return null;
290             }
291 
292             public String getLocale() {
293                 return null;
294             }
295 
296             public BigInteger getOffset() {
297                 return null;
298             }
299 
300             public BigInteger getLength() {
301                 return null;
302             }
303 
304             public String getPassword() {
305                 return null;
306             }
307 
308             public String getUsername() {
309                 return null;
310             }
311 
312             public File getTempDirectory() {
313 
314                 return null;
315             }
316 
317             public int getMemoryThreshold() {
318                 return 0;
319             }
320         }
321 
322         // List<String> allAvailableRepositories =
323         // storeManager.getAllRepositoryIds();
324         String repositoryId = parameters.get(ConfigConstants.REPOSITORY_ID);
325         String doFillRepositoryStr = parameters.get(ConfigConstants.USE_REPOSITORY_FILER);
326         String contentKindStr = parameters.get(ConfigConstants.CONTENT_KIND);
327         boolean doFillRepository = doFillRepositoryStr == null ? false : Boolean.parseBoolean(doFillRepositoryStr);
328 
329         if (doFillRepository /*
330                               * &&
331                               * !allAvailableRepositories.contains(repositoryId)
332                               */) {
333 
334             // create an initial temporary service instance to fill the
335             // repository
336 
337             InMemoryService svc = new InMemoryService(inMemoryServiceParameters, storeManager);
338 
339             BindingsObjectFactory objectFactory = new BindingsObjectFactoryImpl();
340 
341             String levelsStr = parameters.get(ConfigConstants.FILLER_DEPTH);
342             int levels = 1;
343             if (null != levelsStr) {
344                 levels = Integer.parseInt(levelsStr);
345             }
346 
347             String docsPerLevelStr = parameters.get(ConfigConstants.FILLER_DOCS_PER_FOLDER);
348             int docsPerLevel = 1;
349             if (null != docsPerLevelStr) {
350                 docsPerLevel = Integer.parseInt(docsPerLevelStr);
351             }
352 
353             String childrenPerLevelStr = parameters.get(ConfigConstants.FILLER_FOLDERS_PER_FOLDER);
354             int childrenPerLevel = 2;
355             if (null != childrenPerLevelStr) {
356                 childrenPerLevel = Integer.parseInt(childrenPerLevelStr);
357             }
358 
359             String documentTypeId = parameters.get(ConfigConstants.FILLER_DOCUMENT_TYPE_ID);
360             if (null == documentTypeId) {
361                 documentTypeId = BaseTypeId.CMIS_DOCUMENT.value();
362             }
363 
364             String folderTypeId = parameters.get(ConfigConstants.FILLER_FOLDER_TYPE_ID);
365             if (null == folderTypeId) {
366                 folderTypeId = BaseTypeId.CMIS_FOLDER.value();
367             }
368 
369             int contentSizeKB = 0;
370             String contentSizeKBStr = parameters.get(ConfigConstants.FILLER_CONTENT_SIZE);
371             if (null != contentSizeKBStr) {
372                 contentSizeKB = Integer.parseInt(contentSizeKBStr);
373             }
374 
375             ObjectGenerator.CONTENT_KIND contentKind;
376             if (null == contentKindStr)
377                 contentKind = ObjectGenerator.CONTENT_KIND.LoremIpsumText;
378             else {
379                 if (contentKindStr.equals("static/text"))
380                     contentKind = ObjectGenerator.CONTENT_KIND.StaticText;
381                 else if (contentKindStr.equals("lorem/text"))
382                     contentKind = ObjectGenerator.CONTENT_KIND.LoremIpsumText;
383                 else if (contentKindStr.equals("lorem/html"))
384                     contentKind = ObjectGenerator.CONTENT_KIND.LoremIpsumHtml;
385                 else if (contentKindStr.equals("fractal/jpeg"))
386                     contentKind = ObjectGenerator.CONTENT_KIND.ImageFractalJpeg;
387                 else
388                     contentKind = ObjectGenerator.CONTENT_KIND.StaticText;
389             }
390             // Create a hierarchy of folders and fill it with some documents
391             ObjectGenerator gen = new ObjectGenerator(objectFactory, svc, svc, svc, repositoryId, contentKind);
392 
393             gen.setNumberOfDocumentsToCreatePerFolder(docsPerLevel);
394 
395             // Set the type id for all created documents:
396             gen.setDocumentTypeId(documentTypeId);
397 
398             // Set the type id for all created folders:
399             gen.setFolderTypeId(folderTypeId);
400 
401             // Set contentSize
402             gen.setContentSizeInKB(contentSizeKB);
403 
404             // set properties that need to be filled
405             // set the properties the generator should fill with values for
406             // documents:
407             // Note: must be valid properties in configured document and folder
408             // type
409 
410             List<String> propsToSet = readPropertiesToSetFromConfig(parameters,
411                     ConfigConstants.FILLER_DOCUMENT_PROPERTY);
412             if (null != propsToSet) {
413                 gen.setDocumentPropertiesToGenerate(propsToSet);
414             }
415 
416             propsToSet = readPropertiesToSetFromConfig(parameters, ConfigConstants.FILLER_FOLDER_PROPERTY);
417             if (null != propsToSet) {
418                 gen.setFolderPropertiesToGenerate(propsToSet);
419             }
420 
421             // Simulate a runtime context with configuration parameters
422             // Attach the CallContext to a thread local context that can be
423             // accessed
424             // from everywhere
425             DummyCallContext ctx = new DummyCallContext();
426             svc.setCallContext(ctx);
427 
428             // Build the tree
429             RepositoryInfo rep = svc.getRepositoryInfo(repositoryId, null);
430             String rootFolderId = rep.getRootFolderId();
431 
432             try {
433                 gen.createFolderHierachy(levels, childrenPerLevel, rootFolderId);
434                 // Dump the tree
435                 gen.dumpFolder(rootFolderId, "*");
436             } catch (Exception e) {
437                 LOG.error("Could not create folder hierarchy with documents. " + e);
438                 e.printStackTrace();
439             }
440         } // if
441 
442     } // fillRepositoryIfConfigured
443 
444     class CleanManager {
445 
446         private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
447         ScheduledFuture<?> cleanerHandle = null;
448 
449         public void startCleanRepositoryJob(long intervalInMinutes) {
450 
451             final Runnable cleaner = new Runnable() {
452                 public void run() {
453                     LOG.info("Cleaning repository as part of a scheduled maintenance job.");
454                     for (String repositoryId : storeManager.getAllRepositoryIds()) {
455                         ObjectStore store = storeManager.getObjectStore(repositoryId);
456                         store.clear();
457                         fillRepositoryIfConfigured(ConfigurationSettings.getParameters());
458                     }
459                     LOG.info("Repository cleaned. Freeing memory.");
460                     System.gc();
461                 }
462             };
463 
464             LOG.info("Repository Clean Job starting clean job, interval " + intervalInMinutes + " min");
465             cleanerHandle = scheduler.scheduleAtFixedRate(cleaner, intervalInMinutes, intervalInMinutes,
466                     TimeUnit.MINUTES);
467         }
468 
469         public void stopCleanRepositoryJob() {
470             LOG.info("Repository Clean Job cancelling clean job.");
471             boolean ok = cleanerHandle.cancel(true);
472             LOG.info("Repository Clean Job cancelled with result: " + ok);
473             scheduler.shutdownNow();
474         }
475     }
476 
477     private void scheduleCleanRepositoryJob(long minutes) {
478         cleanManager = new CleanManager();
479         cleanManager.startCleanRepositoryJob(minutes);
480     }
481 
482 }