| package org.eclipse.jst.jsf.facelet.core.internal.registry; |
| |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.concurrent.ConcurrentLinkedQueue; |
| |
| import org.eclipse.core.resources.IProject; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.MultiStatus; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.core.runtime.jobs.ILock; |
| import org.eclipse.core.runtime.jobs.Job; |
| import org.eclipse.jdt.core.JavaCore; |
| import org.eclipse.jdt.core.JavaModelException; |
| import org.eclipse.jst.jsf.common.internal.managedobject.IManagedObject; |
| import org.eclipse.jst.jsf.common.internal.managedobject.ObjectManager.ManagedObjectException; |
| import org.eclipse.jst.jsf.common.internal.policy.IdentifierOrderedIteratorPolicy; |
| import org.eclipse.jst.jsf.common.runtime.internal.view.model.common.Namespace; |
| import org.eclipse.jst.jsf.core.internal.JSFCorePlugin; |
| import org.eclipse.jst.jsf.core.internal.JSFCoreTraceOptions; |
| import org.eclipse.jst.jsf.designtime.internal.view.model.AbstractTagRegistry; |
| import org.eclipse.jst.jsf.designtime.internal.view.model.jsp.CompositeTagResolvingStrategy; |
| import org.eclipse.jst.jsf.facelet.core.internal.FaceletCorePlugin; |
| import org.eclipse.jst.jsf.facelet.core.internal.FaceletCoreTraceOptions; |
| import org.eclipse.jst.jsf.facelet.core.internal.cm.FaceletDocumentFactory; |
| import org.eclipse.jst.jsf.facelet.core.internal.registry.IFaceletTagResolvingStrategy.TLDWrapper; |
| import org.eclipse.jst.jsf.facelet.core.internal.registry.taglib.FaceletTagIndex; |
| import org.eclipse.jst.jsf.facelet.core.internal.registry.taglib.IFaceletTagRecord; |
| import org.eclipse.jst.jsf.facelet.core.internal.registry.taglib.IProjectTaglibDescriptor; |
| import org.eclipse.jst.jsf.facelet.core.internal.registry.taglib.Listener; |
| import org.eclipse.jst.jsf.facelet.core.internal.tagmodel.FaceletNamespace; |
| |
| /** |
| * Registry of all facelet tag registries: at most one per project. |
| * |
| */ |
| public final class FaceletTagRegistry extends AbstractTagRegistry implements |
| IManagedObject |
| { |
| // INSTANCE |
| private final ConcurrentLinkedQueue<LibraryOperation> _changeOperations = new ConcurrentLinkedQueue<LibraryOperation>(); |
| |
| private final IProject _project; |
| private final Map<String, FaceletNamespace> _nsResolved; |
| private final Set<FaceletNamespace> _unResolved; |
| private final CompositeTagResolvingStrategy<TLDWrapper> _resolver; |
| private final FaceletDocumentFactory _factory; |
| private final LibraryOperationFactory _operationFactory = new LibraryOperationFactory( |
| this); |
| private final ILock _lock = Job.getJobManager().newLock(); |
| private volatile boolean _isInitialized; |
| |
| private ChangeJob _changeJob; |
| private MyTaglibListener _listener; |
| |
| FaceletTagRegistry(final IProject project) |
| { |
| _project = project; |
| _nsResolved = new HashMap<String, FaceletNamespace>(); |
| _unResolved = new HashSet<FaceletNamespace>(); |
| |
| final List<String> ids = new ArrayList<String>(); |
| |
| //Commenting out this strategy because of current circular dependency with facelet md locating. See FaceletNamespaceMetaDataLocator. |
| // ids.add(FaceletMetaResolvingStrategy.ID); |
| ids.add(FaceletTagResolvingStrategy.ID); |
| final IdentifierOrderedIteratorPolicy<String> policy = new IdentifierOrderedIteratorPolicy<String>( |
| ids); |
| |
| // exclude things that are not explicitly listed in the policy. That |
| // way preference-based disablement will cause those strategies to |
| // be excluded. |
| policy.setExcludeNonExplicitValues(true); |
| _resolver = new CompositeTagResolvingStrategy<TLDWrapper>(policy); |
| |
| _factory = new FaceletDocumentFactory(project); |
| // add the strategies |
| _resolver.addStrategy(new FaceletTagResolvingStrategy(_project, |
| _factory)); |
| |
| //Commenting out this strategy because of current circular dependency with facelet md locating. See FaceletNamespaceMetaDataLocator. |
| // _resolver.addStrategy(new FaceletMetaResolvingStrategy(_project, _factory)); |
| |
| // _resolver.addStrategy(new DefaultJSPTagResolver(_project)); |
| // makes sure that a tag element will always be created for any |
| // given tag definition even if other methods fail |
| // _resolver.addStrategy(new UnresolvedJSPTagResolvingStrategy()); |
| _changeJob = new ChangeJob(project.getName()); |
| } |
| |
| /** |
| * @return a copy of all tag libs, both with namespaces resolved and without |
| * Changing the returned may has no effect on the registry, however |
| * the containned objects are not copies. |
| */ |
| @Override |
| public Collection<FaceletNamespace> getAllTagLibraries() |
| { |
| boolean setEndRule = false; |
| try { |
| final Set<FaceletNamespace> allTagLibraries = new HashSet<FaceletNamespace>(); |
| |
| if (!_isInitialized) |
| { |
| // preemptive project rule setting here ensures consistent lock ordering |
| // and gives the opportunity for the other thread having the project lock |
| // to finish before we enter synchronization block created with reentrant |
| // lock below |
| // NOTE: it is essential to have _lock.acquire() after project rule start |
| // NOTE: if current thread already has any rule, do not start project rule |
| if(Job.getJobManager().currentRule() == null){ |
| Job.getJobManager().beginRule(_project, null); |
| setEndRule = true; |
| } |
| _lock.acquire(); |
| |
| // double check after sync block if no one else entered "if(!_isInitialized)" |
| if(!_isInitialized){ |
| try |
| { |
| initialize(false); |
| _isInitialized = true; |
| } |
| catch (final JavaModelException e) |
| { |
| FaceletCorePlugin.log("Problem during initialization", e); //$NON-NLS-1$ |
| } |
| catch (final CoreException e) |
| { |
| FaceletCorePlugin.log("Problem during initialization", e); //$NON-NLS-1$ |
| } |
| } |
| }else{ |
| _lock.acquire(); |
| } |
| allTagLibraries.addAll(_nsResolved.values()); |
| allTagLibraries.addAll(_unResolved); |
| return allTagLibraries; |
| } finally { |
| _lock.release(); |
| if (setEndRule){ |
| Job.getJobManager().endRule(_project); |
| } |
| } |
| } |
| |
| private void initialize(boolean fireEvent) throws JavaModelException, CoreException |
| { |
| if (!_project.exists() || !_project.hasNature(JavaCore.NATURE_ID)) |
| { |
| throw new CoreException(new Status(IStatus.ERROR, |
| FaceletCorePlugin.PLUGIN_ID, |
| "Project either does not exists or is not a java project: " //$NON-NLS-1$ |
| + _project)); |
| } |
| |
| final FaceletTagIndex index = FaceletTagIndex.getInstance(_project.getWorkspace()); |
| |
| IProjectTaglibDescriptor tagDesc; |
| try |
| { |
| tagDesc = index.getInstance(_project); |
| } |
| catch (ManagedObjectException e) |
| { |
| throw new CoreException( |
| new Status( |
| IStatus.ERROR, |
| FaceletCorePlugin.PLUGIN_ID, |
| "Error instantiating facelet tag index for project: " + _project.getName(), e)); //$NON-NLS-1$ |
| } |
| |
| if (tagDesc != null) |
| { |
| for (final IFaceletTagRecord taglib : tagDesc.getTagLibraries()) |
| { |
| if (taglib.getURI() != null) |
| initialize(taglib, fireEvent); |
| } |
| |
| _listener = new MyTaglibListener(); |
| tagDesc.addListener(_listener); |
| } |
| } |
| |
| FaceletNamespace initialize(final IFaceletTagRecord tagRecord, |
| final boolean fireEvent) |
| { |
| if (JSFCoreTraceOptions.TRACE_JSPTAGREGISTRY_CHANGES) |
| { |
| FaceletCoreTraceOptions |
| .log("TLDTagRegistry.initialize_TagRecord: Initializing new tld record: " + tagRecord.toString()); //$NON-NLS-1$ |
| } |
| final FaceletNamespace ns = new FaceletNamespace(tagRecord, _resolver); |
| _nsResolved.put(tagRecord.getURI(), ns); |
| |
| if (fireEvent) |
| { |
| fireEvent(new TagRegistryChangeEvent(this, |
| TagRegistryChangeEvent.EventType.ADDED_NAMESPACE, |
| Collections.singletonList(ns))); |
| } |
| return ns; |
| } |
| |
| void remove(final IFaceletTagRecord tagRecord) |
| { |
| final FaceletNamespace ns = _nsResolved.remove(tagRecord.getURI()); |
| |
| if (ns != null) |
| { |
| fireEvent(new TagRegistryChangeEvent(this, |
| TagRegistryChangeEvent.EventType.REMOVED_NAMESPACE, |
| Collections.singletonList(ns))); |
| } |
| } |
| |
| @Override |
| public Namespace getTagLibrary(final String uri) |
| { |
| // TODO: |
| getAllTagLibraries(); |
| return _nsResolved.get(uri); |
| } |
| |
| @Override |
| protected Job getRefreshJob(final boolean flushCaches) |
| { |
| return new Job("Refreshing Facelet tag registry for " + _project.getName()) //$NON-NLS-1$ |
| { |
| @Override |
| protected IStatus run(final IProgressMonitor monitor) |
| { |
| // if (FaceletCoreTraceOptions.TRACE_JSPTAGREGISTRY) |
| // { |
| // JSFCoreTraceOptions.log("FaceletTagRegistry.refresh: start"); //$NON-NLS-1$ |
| // } |
| boolean setEndRule = false; |
| try |
| { |
| if(Job.getJobManager().currentRule() == null){ |
| Job.getJobManager().beginRule(_project, null); |
| setEndRule = true; |
| } |
| _lock.acquire(); |
| if (JSFCoreTraceOptions.TRACE_JSPTAGREGISTRY) |
| { |
| JSFCoreTraceOptions |
| .log("FaceletTagRegistry.refresh: start"); //$NON-NLS-1$ |
| } |
| |
| final List<Namespace> namespaces = new ArrayList( |
| _nsResolved.values()); |
| |
| if (flushCaches) |
| { |
| FaceletTagIndex.getInstance(_project.getWorkspace()).flush(_project); |
| } |
| // if we aren't flushing caches, then check point the |
| // current namespace data, so it isn't lost when we clear |
| // THE NAMESPACES |
| else |
| { |
| checkpoint(); |
| } |
| |
| _nsResolved.clear(); |
| |
| fireEvent(new TagRegistryChangeEvent(FaceletTagRegistry.this, |
| TagRegistryChangeEvent.EventType.REMOVED_NAMESPACE, |
| namespaces)); |
| try |
| { |
| initialize(true); |
| } |
| catch (JavaModelException e) |
| { |
| return new Status(IStatus.ERROR, FaceletCorePlugin.PLUGIN_ID, "Problem refreshing registry", e); //$NON-NLS-1$ |
| } |
| catch (CoreException e) |
| { |
| return new Status(IStatus.ERROR, FaceletCorePlugin.PLUGIN_ID, "Problem refreshing registry", e); //$NON-NLS-1$ |
| } |
| |
| // if (JSFCoreTraceOptions.TRACE_JSPTAGREGISTRY) |
| // { |
| // JSFCoreTraceOptions |
| // .log("TLDTagRegistry.refresh: finished"); |
| // } |
| return Status.OK_STATUS; |
| } finally { |
| _lock.release(); |
| if (setEndRule){ |
| Job.getJobManager().endRule(_project); |
| } |
| } |
| } |
| }; |
| } |
| |
| private class MyTaglibListener extends Listener |
| { |
| @Override |
| public void changed(TaglibChangedEvent event) |
| { |
| switch (event.getChangeType()) |
| { |
| case ADDED: |
| addLibraryOperation(_operationFactory |
| .createAddOperation(event.getNewValue())); |
| break; |
| case CHANGED: |
| addLibraryOperation(_operationFactory |
| .createChangeOperation(event.getNewValue())); |
| break; |
| case REMOVED: |
| addLibraryOperation(_operationFactory |
| .createRemoveOperation(event.getOldValue())); |
| break; |
| } |
| } |
| } |
| |
| private void addLibraryOperation(final LibraryOperation operation) |
| { |
| _changeOperations.add(operation); |
| _changeJob.schedule(); |
| } |
| |
| private class ChangeJob extends Job |
| { |
| private int _rescheduleTime = -1; |
| |
| public ChangeJob(final String projectName) |
| { |
| super("Update job for project " + projectName); //$NON-NLS-1$ |
| // preemptive project rule setting here ensures consistent lock ordering |
| // and gives the opportunity for the other thread having the project lock |
| // to finish before we enter synchronization block created with reentrant |
| // lock below |
| // NOTE: it is essential to have _lock.acquire() after project rule start |
| setRule(_project); |
| } |
| |
| @Override |
| protected IStatus run(final IProgressMonitor monitor) |
| { |
| try |
| { |
| _lock.acquire(); |
| _rescheduleTime = -1; |
| |
| LibraryOperation operation = null; |
| final MultiStatus multiStatus = new MultiStatus( |
| JSFCorePlugin.PLUGIN_ID, 0, "Result of change job", //$NON-NLS-1$ |
| new Throwable()); |
| while ((operation = _changeOperations.poll()) != null) |
| { |
| _rescheduleTime = 10000; // ms |
| |
| operation.run(); |
| multiStatus.add(operation.getResult()); |
| } |
| |
| if (_rescheduleTime >= 0 && !monitor.isCanceled()) |
| { |
| // if any operations were found on this run, reschedule |
| // to run again in 10seconds based on the assumption that |
| // events may be coming in bursts |
| schedule(_rescheduleTime); |
| } |
| |
| return multiStatus; |
| } finally { |
| _lock.release(); |
| } |
| } |
| } |
| |
| @Override |
| protected void doDispose() |
| { |
| if (_listener != null) |
| { |
| FaceletTagIndex index = FaceletTagIndex.getInstance(_project.getWorkspace()); |
| try |
| { |
| IProjectTaglibDescriptor instance = index.getInstance(_project); |
| instance.removeListener(_listener); |
| } |
| catch (ManagedObjectException e) |
| { |
| FaceletCorePlugin |
| .log( |
| "Disposing facelet tag registry for project: " + _project.getName(), e); //$NON-NLS-1$ |
| } |
| |
| _nsResolved.clear(); |
| } |
| } |
| |
| @Override |
| protected void cleanupPersistentState() |
| { |
| // TODO ?? |
| |
| } |
| |
| public void checkpoint() |
| { |
| // TODO ?? |
| |
| } |
| |
| } |