View Javadoc

1   /*
2    * Copyright 2006-2010 the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package net.sourceforge.domian.repository;
17  
18  
19  import java.io.File;
20  import static java.lang.Boolean.FALSE;
21  import java.util.Collection;
22  import java.util.Iterator;
23  import java.util.Set;
24  
25  import static org.apache.commons.lang.SystemUtils.FILE_SEPARATOR;
26  import org.apache.commons.lang.Validate;
27  
28  import net.sourceforge.domian.entity.Entity;
29  import net.sourceforge.domian.repository.EntityPersistenceMetaData;
30  import net.sourceforge.domian.repository.PersistenceDefinition;
31  import static net.sourceforge.domian.repository.PersistenceDefinition.FILE;
32  import net.sourceforge.domian.repository.PersistentEntity;
33  import net.sourceforge.domian.specification.Specification;
34  import net.sourceforge.domian.util.concurrent.locks.SemaphoreSynchronizer;
35  import net.sourceforge.domian.util.concurrent.locks.Synchronizer;
36  
37  import com.thoughtworks.xstream.persistence.PersistenceStrategy;
38  import com.thoughtworks.xstream.persistence.XmlSet;
39  
40  
41  /**
42   * A persistent repository backed by <a href="http://xstream.codehaus.org">XStream</a> functionality.
43   * <p/>
44   * The only persistence definition supported by this repository is {@code PersistenceDefinition.FILE}.
45   * All repository operations are executed in an exclusive fashion, applying pessimistic locking.
46   * Translated into ACID terms, this corresponds to isolation level <code>SERIALIZABLE</code>.
47   * <p/>
48   * All entities in this repository are persisted into <i>individual XML files</i>.
49   * The typical disc footprint is 1KB per entity.
50   * The persistence format is human readable XStream XML.
51   * The file encoding is UTF-8.
52   * <p/>
53   * <i>
54   * File-based stores counts as persistent storage media,
55   * although the nature of the synchronous writings to disc obviously depends on the file system implementations involved.
56   * </i>
57   * <p/>
58   * <i>
59   * NB! When used as a partition repository, this repository implementation does not reflect altered state between partitions.
60   * When changing state in entities residing in more han one partition,
61   * for which at least one of them is a {@code XStreamXmlFilePerEntityRepository},
62   * explicit updates for all partitions involved are needed.
63   * </i>
64   *
65   * @author Eirik Torske
66   * @see <a href="http://xstream.codehaus.org">XStream</a>
67   * @see <a href="http://domian.sourceforge.net/domian-core/apidocs/net/sourceforge/domian/repository/PersistenceDefinition.html#FILE"><code>PersistenceDefinition.FILE</code></a>
68   * @since 0.4
69   */
70  public class XStreamXmlFilePerEntityRepository<T extends Entity> extends AbstractXStreamXmlFilePerEntityRepository<T> {
71  
72      protected static final String DEFAULT_REPOSITORY_ROOT_DIR_NAME = ".xstream-xml-file-per-entity-repository";
73      protected static final String DEFAULT_REPOSITORY_ROOT_PATH = DEFAULT_DOMIAN_ROOT_PATH + FILE_SEPARATOR + DEFAULT_REPOSITORY_ROOT_DIR_NAME;
74  
75      public XStreamXmlFilePerEntityRepository(final String repositoryId) {
76          this(DEFAULT_REPOSITORY_ROOT_PATH, repositoryId);
77      }
78  
79      public XStreamXmlFilePerEntityRepository(final String repositoryId, final Synchronizer synchronizer) {
80          this(DEFAULT_REPOSITORY_ROOT_PATH, repositoryId, synchronizer);
81      }
82  
83      public XStreamXmlFilePerEntityRepository(final String repositoryRootPath, final String repositoryId) {
84          this(repositoryRootPath, repositoryId, new SemaphoreSynchronizer());
85      }
86  
87      public XStreamXmlFilePerEntityRepository(final String repositoryRootPath, final String repositoryId, final Synchronizer synchronizer) {
88          Validate.notEmpty(repositoryRootPath, "The repository root path cannot be empty");
89          Validate.notEmpty(repositoryId, "The repository ID cannot be empty");
90          Validate.notNull(synchronizer, "The synchronizer parameter cannot be null");
91          this.repositoryRootPath = repositoryRootPath;
92          this.repositoryId = repositoryId;
93          this.synchronizer = synchronizer;
94          createRepositoryFilesIfNotExist();
95      }
96  
97      /** @return the total number of entities residing in this particular repository (partition) */
98      public Long countAllEntities() {
99          return (long) new File(this.repositoryRootPath).list().length;
100     }
101 
102     @Override
103     public <V extends T> Iterator<V> iterateAllEntitiesSpecifiedBy(final Specification<V> specification) {
104         Validate.notNull(specification, "Specification parameter cannot be null");
105         return callExclusivelyWithRetry(new IterateAllEntitiesSpecifiedBy<V>(specification));
106     }
107 
108     @Override
109     public <V extends T> Collection<V> findAllEntitiesSpecifiedBy(final Specification<V> specification) {
110         Validate.notNull(specification, "Specification parameter cannot be null");
111         return callExclusivelyWithRetry(new FindAllEntitiesSpecifiedBy<V>(specification));
112     }
113 
114     @Override
115     public <V extends T> void put(final V entity) {
116         callExclusivelyWithRetry(new Put<V>(entity));
117     }
118 
119     @Override
120     public <V extends T> Long removeAllEntitiesSpecifiedBy(final Specification<V> specification) {
121         Validate.notNull(specification, "Specification parameter cannot be null");
122         return callExclusivelyWithRetry(new RemoveAllEntitiesSpecifiedBy<V>(specification));
123     }
124 
125     @Override
126     public <V extends T> Boolean remove(final V entity) {
127         if (entity == null) {
128             return FALSE;
129         }
130         return callExclusivelyWithRetry(new Remove<V>(entity));
131     }
132 
133     @Override
134     public PersistenceDefinition getPersistenceDefinition() {
135         return FILE;
136     }
137 
138     @Override
139     public String getFormat() {
140         return "XStream XML";
141     }
142 
143     @Override
144     public void load() {
145         log.warn("load() is only applicable for repositories with asynchronous persistence [PersistenceDefinition.INMEMORY_AND_*]");
146     }
147 
148     @Override
149     public void persist() {
150         log.warn("persist() is only applicable for repositories with asynchronous persistence [PersistenceDefinition.INMEMORY_AND_*]");
151     }
152 
153     @Override
154     public <V extends T> void update(V entity) {
155         put(entity);
156     }
157 
158     @Override
159     public EntityMetaData getMetaDataFor(final T entity) {
160         PersistentEntity<T> persistentEntity = null;
161         final PersistenceStrategy persistenceStrategy = new EntityId_NamedXStreamFilePersistenceStrategy(getRepositoryDirectory());
162         final Set<PersistentEntity<T>> persistedEntitySet = new XmlSet(persistenceStrategy);
163         for (final PersistentEntity<T> persistentEntityFound : persistedEntitySet) {
164             final Entity entityFound = persistentEntityFound.getEntity();
165             if (entity.equals(entityFound)) {
166                 persistentEntity = persistentEntityFound;
167                 break;
168             }
169         }
170         return persistentEntity == null ? null : persistentEntity.getMetaData();
171     }
172 
173     @Override
174     public void close() {}
175 }