blob: 64a4009709e40f13380efd6be77b2e1051e9278f [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2007 Oracle Corporation.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Oracle - initial API and implementation
*
********************************************************************************/
package org.eclipse.jst.jsf.common.metadata.internal;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jst.jsf.common.JSFCommonPlugin;
import org.eclipse.jst.jsf.common.metadata.Model;
/**
* Singleton instance for each IProject used to manage all standard metdata models for that project.
*
* Manager is responsible for loading and caching MetaDataModels. Models are keyed by URI.
*
* The manager listens for project closing resource events so that the resources can be freed up.
*/
public final class MetaDataModelManager extends AbstractMetaDataModelManager {
private static IMetaDataModelManager SHARED_INSTANCE;
// used to lock all instance calls for getModel
private static final Lock GLOBAL_INSTANCE_LOCK = new ReentrantLock();
private final ModelMap models;
/**
* @return instance that is project agnostic.
*/
public synchronized static IMetaDataModelManager getSharedInstance(){
if (SHARED_INSTANCE == null) {
SHARED_INSTANCE = new MetaDataModelManager(null);
}
return SHARED_INSTANCE;
}
/**
* @param project
*/
MetaDataModelManager(final IProject project) {
// this.project = project;
models = new ModelMap();
}
public Model getModel(
final IMetaDataModelContext modelContext)
{
boolean gotLock = false;
try
{
final int maxTries = 6;
int numTries = 0;
final Job currentJob = Job.getJobManager().currentJob();
while (numTries < maxTries &&
!(gotLock = GLOBAL_INSTANCE_LOCK.tryLock(5000, TimeUnit.MILLISECONDS)))
{
numTries++;
if (currentJob != null)
{
currentJob.yieldRule(null);
}
}
if (!gotLock)
{
return null;
}
StandardModelFactory.debug(">START getModel: "+modelContext, StandardModelFactory.DEBUG_MD_GET); //$NON-NLS-1$
MetaDataModel model = models.get(modelContext);
if (model == null) {
// long in = System.currentTimeMillis();
model = loadMetadata(modelContext);
//System.out.println("Time to load "+modelContext.getURI()+": "+
// String.valueOf(System.currentTimeMillis() - in));
} else if (model.needsRefresh()) {
try {
model.reload();
} catch (ModelNotSetException e) {
// simply load it - should not get here
model = loadMetadata(modelContext);
}
}
// if (model != null && model.getRoot() != null)
// ((Model) model.getRoot())
// .setCurrentModelContext(modelContext);
StandardModelFactory.debug(">END getModel: "+modelContext, StandardModelFactory.DEBUG_MD_GET); //$NON-NLS-1$
if (model != null && !model.isEmpty()){
return (Model)model.getRoot();
}
return null;
}
catch (final InterruptedException e)
{
return null;
}
finally
{
if (gotLock)
{
GLOBAL_INSTANCE_LOCK.unlock();
}
}
}
private MetaDataModel loadMetadata(final IMetaDataModelContext context) {
// if (!Thread.holdsLock(GLOBAL_INSTANCE_LOCK.)) {
// JSFCommonPlugin
// .log(IStatus.ERROR,
// "Internal Error: loadMetadata must not be called if class lock not held"); //$NON-NLS-1$
// return null;
// }
final IDomainLoadingStrategy strategy = DomainLoadingStrategyRegistry
.getInstance().getLoadingStrategy(
context.getDomainId());
;
if (strategy == null) {
JSFCommonPlugin
.log(
IStatus.ERROR,
"Internal Error: Unable to locate metadata loading strategy for: " + context.toString()); //$NON-NLS-1$
return null;
}
final MetaDataModel model = StandardModelFactory.getInstance().createModel(
context, strategy);// new MetaDataModel(modelKey,
// strategy);
model.load();
addModel(model);
return model;
}
private void addModel(final MetaDataModel model) {
if (model != null)
models.put(model);
}
@Override
public void dispose() {
super.dispose();
models.dispose();
}
@Override
public void destroy() {
// no persistent data to cleanup. just call dispose
dispose();
}
/**
* Map of models keyed by DOMAIN_ID:MODEL_ID from the context. Project is not part of key.
*
*/
private static class ModelMap
{
final Map<String, MetaDataModel> map;
private final AtomicBoolean _isDisposed = new AtomicBoolean(false);
ModelMap() {
map = new HashMap<String, MetaDataModel>();
}
/**
* @param model
* adds model to the map using the given key descriptor
*/
public void put(final MetaDataModel model) {
assert !_isDisposed.get();
final String key = calculateKey(model);
synchronized (this) {
map.put(key, model);
}
}
/**
* @param context
* @return MetaDataModel for this context. May return null.
*/
public MetaDataModel get(final IMetaDataModelContext context) {
assert !_isDisposed.get();
final String key = calculateKey(context);
synchronized (this)
{
return map.get(key);
}
}
public void dispose() {
if (_isDisposed.compareAndSet(false, true)) {
synchronized(this)
{
for (final Iterator<Map.Entry<String, MetaDataModel>> it = map.entrySet().iterator(); it.hasNext();)
{
// System.out.println("kill mmModel: "+model.toString());
final Map.Entry<String, MetaDataModel> entry = it.next();
final MetaDataModel model = entry.getValue();
if (model != null)
{
model.cleanup();
}
it.remove();
}
}
}
}
private String calculateKey(final MetaDataModel model) {
return calculateKey(model.getModelContext());
}
private String calculateKey(final IMetaDataModelContext context) {
final StringBuffer buf = new StringBuffer(context.getDomainId()).append(":").append(context.getModelIdentifier()); //$NON-NLS-1$;
return buf.toString();
}
}
}