/******************************************************************************* * 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; } final IMetaDataModelContext context = Aliases.get(modelContext); StandardModelFactory.debug(">START getModel: " + context, StandardModelFactory.DEBUG_MD_GET); //$NON-NLS-1$ MetaDataModel model = models.get(context); if (model == null) { // long in = System.currentTimeMillis(); model = loadMetadata(context); //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(context); } } // if (model != null && model.getRoot() != null) // ((Model) model.getRoot()) // .setCurrentModelContext(modelContext); StandardModelFactory.debug(">END getModel: " + context, 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(); } } private static class Aliases { private static final HashMap<String, String> aliasMap = new HashMap<String, String>(6); static { aliasMap.put( "http://xmlns.jcp.org/jsf/composite", //$NON-NLS-1$ "http://java.sun.com/jsf/composite"); //$NON-NLS-1$ aliasMap.put( "http://xmlns.jcp.org/jsf/core", //$NON-NLS-1$ "http://java.sun.com/jsf/core"); //$NON-NLS-1$ aliasMap.put( "http://xmlns.jcp.org/jsf/html", //$NON-NLS-1$ "http://java.sun.com/jsf/html"); //$NON-NLS-1$ aliasMap.put( "http://xmlns.jcp.org/jsf/jstl/core", //$NON-NLS-1$ "http://java.sun.com/jsf/jstl/core"); //$NON-NLS-1$ aliasMap.put( "http://xmlns.jcp.org/jsf/jstl/functions", //$NON-NLS-1$ "http://java.sun.com/jsf/jstl/functions"); //$NON-NLS-1$ aliasMap.put( "http://xmlns.jcp.org/jsf/facelets", //$NON-NLS-1$ "http://java.sun.com/jsf/facelets"); //$NON-NLS-1$ } private static IMetaDataModelContext get(final IMetaDataModelContext context) { IMetaDataModelContext copy = context; if (context instanceof MetaDataModelContext) { final String alias = aliasMap.get(context.getModelIdentifier()); if (alias != null) { copy = new MetaDataModelContext( (IProject) context.getAdapter(IProject.class), context.getDomainId(), alias); } } return copy; } } }