001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.netbeans.modules.cnd.repository.impl;
043:
044: import java.lang.ref.ReferenceQueue;
045: import java.lang.ref.SoftReference;
046: import java.util.HashSet;
047: import java.util.Map;
048: import java.util.Set;
049: import java.util.concurrent.ConcurrentHashMap;
050: import java.util.concurrent.locks.Lock;
051: import java.util.concurrent.locks.ReentrantLock;
052: import org.netbeans.modules.cnd.repository.api.Repository;
053: import org.netbeans.modules.cnd.repository.disk.DiskRepositoryManager;
054: import org.netbeans.modules.cnd.repository.spi.Key;
055: import org.netbeans.modules.cnd.repository.spi.Persistent;
056: import org.netbeans.modules.cnd.repository.spi.RepositoryListener;
057: import org.netbeans.modules.cnd.repository.translator.RepositoryTranslatorImpl;
058: import org.netbeans.modules.cnd.repository.util.RepositoryListenersManager;
059:
060: /**
061: *
062: * @author Nickolay Dalmatov
063: */
064: public class HybridRepository implements Repository {
065:
066: private final Map<Key, Object> cache;
067: private final Repository diskRepository;
068: private Lock refQueueLock;
069: private ReferenceQueue refQueue;
070:
071: private static final int DEFAULT_CACHE_CAPACITY = 77165;
072:
073: /** Creates a new instance of HybridRepository */
074: public HybridRepository() {
075: cache = new ConcurrentHashMap<Key, Object>(
076: DEFAULT_CACHE_CAPACITY);
077: diskRepository = DiskRepositoryManager.getInstance();
078: refQueueLock = new ReentrantLock();
079: refQueue = new ReferenceQueue();
080: }
081:
082: public void hang(Key key, Persistent obj) {
083: cache.put(key, obj);
084: }
085:
086: public void put(Key key, Persistent obj) {
087: cache.put(key, new SoftValue(obj, key, refQueue));
088:
089: if (key.getPersistentFactory().canWrite(obj)) {
090: diskRepository.put(key, obj);
091: }
092: }
093:
094: public final Persistent tryGet(Key key) {
095: Object value = cache.get(key);
096: if (value instanceof Persistent) {
097: return (Persistent) value;
098: } else if (value instanceof SoftReference) {
099: return ((SoftReference<Persistent>) value).get();
100: }
101: return null;
102: }
103:
104: public Persistent get(Key key) {
105: Persistent data = tryGet(key);
106: if (data == null) {
107: data = diskRepository.get(key);
108:
109: if (data != null) {
110: processQueue();
111:
112: // no syncronization here!!!
113: // the only possible collision here is lost of element, which is currently being deleted
114: // by processQueue - it will be reread
115: cache.put(key, new SoftValue(data, key, refQueue));
116: }
117: }
118:
119: return data;
120: }
121:
122: public void remove(Key key) {
123: cache.remove(key);
124: diskRepository.remove(key);
125: }
126:
127: public void debugClear() {
128: //cleanWriteHungObjects(null, false);
129: processQueue();
130: Set<Key> keys = new HashSet<Key>(cache.keySet());
131: for (Key key : keys) {
132: Object value = cache.get(key);
133:
134: if (value != null && !(value instanceof Persistent)) {
135: cache.remove(key);
136: }
137: }
138:
139: diskRepository.debugClear();
140: }
141:
142: public void shutdown() {
143: diskRepository.shutdown();
144: RepositoryTranslatorImpl.shutdown();
145: }
146:
147: private void cleanWriteHungObjects(String unitName, boolean clean) {
148: processQueue();
149: Set<Key> keys = new HashSet<Key>(cache.keySet());
150: for (Key key : keys) {
151:
152: boolean fromUnit = ((unitName == null) || ((unitName != null) && unitName
153: .equals(key.getUnit()))) ? true : false;
154:
155: if (fromUnit) {
156: Object value = cache.remove(key);
157:
158: if (value != null) {
159: if (value instanceof Persistent && !clean) {
160: Persistent obj = (Persistent) value;
161: if (key.getPersistentFactory().canWrite(obj)) {
162: diskRepository.put(key, obj);
163: }
164: }
165: }
166: }
167: }
168: }
169:
170: public void openUnit(String unitName) {
171: diskRepository.openUnit(unitName);
172: }
173:
174: public void closeUnit(String unitName, boolean cleanRepository,
175: Set<String> requiredUnits) {
176: cleanWriteHungObjects(unitName, cleanRepository);
177: diskRepository.closeUnit(unitName, cleanRepository, null);
178: RepositoryTranslatorImpl.closeUnit(unitName, requiredUnits);
179: RepositoryListenersManager.getInstance().fireUnitClosedEvent(
180: unitName);
181: }
182:
183: public void removeUnit(String unitName) {
184: RepositoryTranslatorImpl.removeUnit(unitName);
185: diskRepository.removeUnit(unitName);
186: }
187:
188: public void cleanCaches() {
189: diskRepository.cleanCaches();
190: }
191:
192: public void registerRepositoryListener(
193: final RepositoryListener aListener) {
194: }
195:
196: public void unregisterRepositoryListener(
197: final RepositoryListener aListener) {
198: }
199:
200: private static class SoftValue extends SoftReference {
201:
202: private final Object key;
203:
204: private SoftValue(Object k, Object key, ReferenceQueue q) {
205: super (k, q);
206: this .key = key;
207: }
208: }
209:
210: private void processQueue() {
211: if (refQueueLock.tryLock()) {
212: try {
213: SoftValue sv;
214: while ((sv = (SoftValue) refQueue.poll()) != null) {
215: Object value = cache.get(sv.key);
216: // check if the object has already been added by another thread
217: // it is more efficient than blocking puts from the disk
218: if ((value != null)
219: && (value instanceof SoftReference)
220: && (((SoftReference) value).get() == null)) {
221: cache.remove(sv.key);
222: }
223: }
224: } finally {
225: refQueueLock.unlock();
226: }
227: }
228: }
229:
230: public void startup(int persistMechanismVersion) {
231: RepositoryTranslatorImpl.startup(persistMechanismVersion);
232: }
233: }
|