001 // Copyright 2004, 2005 The Apache Software Foundation 002 // 003 // Licensed under the Apache License, Version 2.0 (the "License"); 004 // you may not use this file except in compliance with the License. 005 // You may obtain a copy of the License at 006 // 007 // http://www.apache.org/licenses/LICENSE-2.0 008 // 009 // Unless required by applicable law or agreed to in writing, software 010 // distributed under the License is distributed on an "AS IS" BASIS, 011 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 012 // See the License for the specific language governing permissions and 013 // limitations under the License. 014 015 package org.apache.hivemind.impl; 016 017 import java.util.ArrayList; 018 import java.util.Collections; 019 import java.util.List; 020 import java.util.Map; 021 022 import org.apache.commons.logging.Log; 023 import org.apache.commons.logging.LogFactory; 024 import org.apache.hivemind.*; 025 import org.apache.hivemind.ApplicationRuntimeException; 026 import org.apache.hivemind.Occurances; 027 import org.apache.hivemind.internal.ConfigurationPoint; 028 import org.apache.hivemind.internal.Contribution; 029 import org.apache.hivemind.schema.Schema; 030 import org.apache.hivemind.util.ToStringBuilder; 031 032 /** 033 * Implementation of the {@link org.apache.hivemind.internal.ConfigurationPoint} interface; a 034 * container for {@link org.apache.hivemind.internal.Contribution}s. 035 * 036 * @author Howard Lewis Ship 037 */ 038 public final class ConfigurationPointImpl extends AbstractExtensionPoint implements 039 ConfigurationPoint 040 { 041 private static final Log LOG = LogFactory.getLog(ConfigurationPointImpl.class); 042 043 /** 044 * The cached elements for the extension point (if caching is enabled). 045 */ 046 private List _elements; 047 048 private List _elementsProxy; 049 050 private Map _mappedElements; 051 052 private Map _mappedElementsProxy; 053 054 private boolean _canElementsBeMapped = false; 055 056 private Occurances _expectedCount; 057 058 private List _contributions; 059 060 private boolean _building; 061 062 private Schema _contributionsSchema; 063 064 private ShutdownCoordinator _shutdownCoordinator; 065 066 protected void extendDescription(ToStringBuilder builder) 067 { 068 builder.append("expectedCount", _expectedCount); 069 builder.append("contributions", _contributions); 070 builder.append("schema", _contributionsSchema); 071 } 072 073 /** 074 * Returns the number of contributions; it is expected that each top-level 075 * {@link org.apache.hivemind.Element} in each {@link Contribution} will convert to one element 076 * instance; the value returned is the total number of top-level elements in all contributed 077 * Extensions. 078 */ 079 public int getContributionCount() 080 { 081 if (_contributions == null) 082 return 0; 083 084 int total = 0; 085 086 int count = _contributions.size(); 087 for (int i = 0; i < count; i++) 088 { 089 Contribution c = (Contribution) _contributions.get(i); 090 total += c.getElements().size(); 091 } 092 093 return total; 094 } 095 096 public void addContribution(Contribution c) 097 { 098 if (_contributions == null) 099 _contributions = new ArrayList(); 100 101 _contributions.add(c); 102 } 103 104 public Occurances getExpectedCount() 105 { 106 return _expectedCount; 107 } 108 109 public void setExpectedCount(Occurances occurances) 110 { 111 _expectedCount = occurances; 112 } 113 114 /** 115 * Returns the contributed elements as an unmodifiable {@link List}. Internally, a proxy to the 116 * real list is returned, such that the real list may not be constructed until actually needed. 117 */ 118 public synchronized List getElements() 119 { 120 if (_elements != null) 121 return _elements; 122 123 if (_elementsProxy == null) 124 { 125 ElementsProxyList outerProxy = new ElementsProxyList(); 126 127 new ElementsInnerProxyList(this, outerProxy); 128 129 _shutdownCoordinator.addRegistryShutdownListener(outerProxy); 130 131 _elementsProxy = outerProxy; 132 } 133 134 return _elementsProxy; 135 } 136 137 public boolean areElementsMappable() 138 { 139 return _canElementsBeMapped; 140 } 141 142 /** 143 * Returns the contributed elements as an unmodifiable {@link Map}. Internally, a proxy to the 144 * real map is returned, such that the real map may not be constructed until actually needed. 145 */ 146 public synchronized Map getElementsAsMap() 147 { 148 if (!areElementsMappable()) 149 throw new ApplicationRuntimeException(ImplMessages.unableToMapConfiguration(this)); 150 151 if (_mappedElements != null) 152 return _mappedElements; 153 154 if (_mappedElementsProxy == null) 155 { 156 ElementsProxyMap outerProxy = new ElementsProxyMap(); 157 158 new ElementsInnerProxyMap(this, outerProxy); 159 160 _shutdownCoordinator.addRegistryShutdownListener(outerProxy); 161 162 _mappedElementsProxy = outerProxy; 163 } 164 165 return _mappedElementsProxy; 166 } 167 168 /** 169 * Invoked by {@link ElementsInnerProxyList} when the actual list is needed. Returns the List 170 * (which is modifiable, but that's OK because ElementsInnerProxyList is unmodifiable) created 171 * by calling {@link #processContributionElements()}. 172 */ 173 synchronized List constructElements() 174 { 175 // It's nice to have this protection, but (unlike services), you 176 // would really have to go out of your way to provoke 177 // a recursive configuration. 178 179 if (_building) 180 throw new ApplicationRuntimeException(ImplMessages 181 .recursiveConfiguration(getExtensionPointId())); 182 183 try 184 { 185 if (_elements == null) 186 { 187 _building = true; 188 189 processContributionElements(); 190 } 191 192 // Now that we have the real list, we don't need the proxy anymore, either. 193 194 _elementsProxy = null; 195 196 return _elements; 197 } 198 finally 199 { 200 _building = false; 201 } 202 } 203 204 /** 205 * Analoguously to {@link #constructElements()} this method will be called by 206 * {@link ElementsInnerProxyMap} to construct the actual map. 207 */ 208 synchronized Map constructMapElements() 209 { 210 // It's nice to have this protection, but (unlike services), you 211 // would really have to go out of your way to provoke 212 // a recursive configuration. 213 214 if (_building) 215 throw new ApplicationRuntimeException(ImplMessages 216 .recursiveConfiguration(getExtensionPointId())); 217 218 try 219 { 220 if (_mappedElements == null) 221 { 222 _building = true; 223 224 processContributionElements(); 225 } 226 227 // Now that we have the real map, we don't need the proxy anymore, either. 228 229 _mappedElementsProxy = null; 230 231 return _mappedElements; 232 } 233 finally 234 { 235 _building = false; 236 } 237 } 238 239 /** 240 * Processes the contribution elements using the 241 * {@link org.apache.hivemind.schema.SchemaProcessor}. The processed contributions will be 242 * stored as an immutable list (in {@link #_elements}) and as an immutable map (in 243 * {@link #_mappedElements}) if applicable (see {@link #areElementsMappable()}). 244 */ 245 private void processContributionElements() 246 { 247 if (LOG.isDebugEnabled()) 248 LOG.debug("Constructing extension point " + getExtensionPointId()); 249 250 if (_contributions == null) 251 { 252 _elements = Collections.EMPTY_LIST; 253 _mappedElements = Collections.EMPTY_MAP; 254 255 return; 256 } 257 258 SchemaProcessorImpl processor = new SchemaProcessorImpl(getErrorLog(), _contributionsSchema); 259 260 int count = _contributions.size(); 261 262 try 263 { 264 for (int i = 0; i < count; i++) 265 { 266 Contribution extension = (Contribution) _contributions.get(i); 267 268 processor.process(extension.getElements(), extension.getContributingModule()); 269 } 270 } 271 catch (Exception ex) 272 { 273 throw new ApplicationRuntimeException(ImplMessages.unableToConstructConfiguration( 274 getExtensionPointId(), 275 ex), ex); 276 } 277 278 if (areElementsMappable()) 279 _mappedElements = Collections.unmodifiableMap(processor.getMappedElements()); 280 281 _elements = Collections.unmodifiableList(processor.getElements()); 282 283 // After constructing the result, if the result 284 // will be cached, then there's no need to keep 285 // the schema and extensions (used to build the 286 // result); it can all be released to the GC. 287 288 _contributionsSchema = null; 289 _contributions = null; 290 } 291 292 public Schema getSchema() 293 { 294 return _contributionsSchema; 295 } 296 297 public void setContributionsSchema(Schema schema) 298 { 299 _contributionsSchema = schema; 300 301 _canElementsBeMapped = _contributionsSchema != null 302 && _contributionsSchema.canInstancesBeKeyed(); 303 } 304 305 public Schema getContributionsSchema() 306 { 307 return _contributionsSchema; 308 } 309 310 public void setShutdownCoordinator(ShutdownCoordinator coordinator) 311 { 312 _shutdownCoordinator = coordinator; 313 } 314 315 }