001 /**
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017 package org.apache.camel.spring.handler;
018
019 import java.lang.reflect.Method;
020 import java.util.HashMap;
021 import java.util.HashSet;
022 import java.util.Map;
023 import java.util.Set;
024
025 import javax.xml.bind.Binder;
026 import javax.xml.bind.JAXBContext;
027 import javax.xml.bind.JAXBException;
028
029 import org.w3c.dom.Document;
030 import org.w3c.dom.Element;
031 import org.w3c.dom.Node;
032 import org.w3c.dom.NodeList;
033
034 import org.apache.camel.builder.xml.Namespaces;
035 import org.apache.camel.core.xml.CamelJMXAgentDefinition;
036 import org.apache.camel.core.xml.CamelPropertyPlaceholderDefinition;
037 import org.apache.camel.impl.DefaultCamelContextNameStrategy;
038 import org.apache.camel.model.FromDefinition;
039 import org.apache.camel.model.SendDefinition;
040 import org.apache.camel.spi.CamelContextNameStrategy;
041 import org.apache.camel.spi.NamespaceAware;
042 import org.apache.camel.spring.CamelBeanPostProcessor;
043 import org.apache.camel.spring.CamelConsumerTemplateFactoryBean;
044 import org.apache.camel.spring.CamelContextFactoryBean;
045 import org.apache.camel.spring.CamelEndpointFactoryBean;
046 import org.apache.camel.spring.CamelProducerTemplateFactoryBean;
047 import org.apache.camel.spring.CamelRedeliveryPolicyFactoryBean;
048 import org.apache.camel.spring.CamelRouteContextFactoryBean;
049 import org.apache.camel.spring.CamelThreadPoolFactoryBean;
050 import org.apache.camel.spring.remoting.CamelProxyFactoryBean;
051 import org.apache.camel.spring.remoting.CamelServiceExporter;
052 import org.apache.camel.util.ObjectHelper;
053 import org.apache.camel.view.ModelFileGenerator;
054 import org.slf4j.Logger;
055 import org.slf4j.LoggerFactory;
056 import org.springframework.beans.factory.BeanCreationException;
057 import org.springframework.beans.factory.BeanDefinitionStoreException;
058 import org.springframework.beans.factory.config.BeanDefinition;
059 import org.springframework.beans.factory.config.RuntimeBeanReference;
060 import org.springframework.beans.factory.parsing.BeanComponentDefinition;
061 import org.springframework.beans.factory.support.BeanDefinitionBuilder;
062 import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
063 import org.springframework.beans.factory.xml.ParserContext;
064
065
066 /**
067 * Camel namespace for the spring XML configuration file.
068 */
069 public class CamelNamespaceHandler extends NamespaceHandlerSupport {
070 private static final String SPRING_NS = "http://camel.apache.org/schema/spring";
071 private static final Logger LOG = LoggerFactory.getLogger(CamelNamespaceHandler.class);
072 protected BeanDefinitionParser endpointParser = new BeanDefinitionParser(CamelEndpointFactoryBean.class, false);
073 protected BeanDefinitionParser beanPostProcessorParser = new BeanDefinitionParser(CamelBeanPostProcessor.class, false);
074 protected Set<String> parserElementNames = new HashSet<String>();
075 private JAXBContext jaxbContext;
076 private Map<String, BeanDefinitionParser> parserMap = new HashMap<String, BeanDefinitionParser>();
077 private Map<String, BeanDefinition> autoRegisterMap = new HashMap<String, BeanDefinition>();
078
079 public static void renameNamespaceRecursive(Node node) {
080 if (node.getNodeType() == Node.ELEMENT_NODE) {
081 Document doc = node.getOwnerDocument();
082 if (node.getNamespaceURI().startsWith(SPRING_NS + "/v")) {
083 doc.renameNode(node, SPRING_NS, node.getNodeName());
084 }
085 }
086 NodeList list = node.getChildNodes();
087 for (int i = 0; i < list.getLength(); ++i) {
088 renameNamespaceRecursive(list.item(i));
089 }
090 }
091
092 public ModelFileGenerator createModelFileGenerator() throws JAXBException {
093 return new ModelFileGenerator(getJaxbContext());
094 }
095
096 public void init() {
097 // register routeContext parser
098 registerParser("routeContext", new RouteContextDefinitionParser());
099
100 addBeanDefinitionParser("proxy", CamelProxyFactoryBean.class, true, false);
101 addBeanDefinitionParser("template", CamelProducerTemplateFactoryBean.class, true, false);
102 addBeanDefinitionParser("consumerTemplate", CamelConsumerTemplateFactoryBean.class, true, false);
103 addBeanDefinitionParser("export", CamelServiceExporter.class, true, false);
104 addBeanDefinitionParser("endpoint", CamelEndpointFactoryBean.class, true, false);
105 addBeanDefinitionParser("threadPool", CamelThreadPoolFactoryBean.class, true, true);
106 addBeanDefinitionParser("redeliveryPolicyProfile", CamelRedeliveryPolicyFactoryBean.class, true, true);
107
108 // jmx agent and property placeholder cannot be used outside of the camel context
109 addBeanDefinitionParser("jmxAgent", CamelJMXAgentDefinition.class, false, false);
110 addBeanDefinitionParser("propertyPlaceholder", CamelPropertyPlaceholderDefinition.class, false, false);
111
112 // errorhandler could be the sub element of camelContext or defined outside camelContext
113 BeanDefinitionParser errorHandlerParser = new ErrorHandlerDefinitionParser();
114 registerParser("errorHandler", errorHandlerParser);
115 parserMap.put("errorHandler", errorHandlerParser);
116
117 // camel context
118 boolean osgi = false;
119 Class cl = CamelContextFactoryBean.class;
120 try {
121 Class<?> c = Class.forName("org.apache.camel.osgi.Activator");
122 Method mth = c.getDeclaredMethod("getBundle");
123 Object bundle = mth.invoke(null);
124 if (bundle != null) {
125 cl = Class.forName("org.apache.camel.osgi.CamelContextFactoryBean");
126 osgi = true;
127 }
128 } catch (Throwable t) {
129 // not running with camel-osgi so we fallback to the regular factory bean
130 LOG.trace("Cannot find class so assuming not running in OSGi container: " + t.getMessage());
131 }
132 if (osgi) {
133 LOG.info("OSGi environment detected.");
134 } else {
135 LOG.info("OSGi environment not detected.");
136 }
137 if (LOG.isDebugEnabled()) {
138 LOG.debug("Using " + cl.getCanonicalName() + " as CamelContextBeanDefinitionParser");
139 }
140 registerParser("camelContext", new CamelContextBeanDefinitionParser(cl));
141 }
142
143 private void addBeanDefinitionParser(String elementName, Class<?> type, boolean register, boolean assignId) {
144 BeanDefinitionParser parser = new BeanDefinitionParser(type, assignId);
145 if (register) {
146 registerParser(elementName, parser);
147 }
148 parserMap.put(elementName, parser);
149 }
150
151 protected void registerParser(String name, org.springframework.beans.factory.xml.BeanDefinitionParser parser) {
152 parserElementNames.add(name);
153 registerBeanDefinitionParser(name, parser);
154 }
155
156 protected Object parseUsingJaxb(Element element, ParserContext parserContext, Binder<Node> binder) {
157 try {
158 return binder.unmarshal(element);
159 } catch (JAXBException e) {
160 throw new BeanDefinitionStoreException("Failed to parse JAXB element", e);
161 }
162 }
163
164 public JAXBContext getJaxbContext() throws JAXBException {
165 if (jaxbContext == null) {
166 jaxbContext = createJaxbContext();
167 }
168 return jaxbContext;
169 }
170
171 protected JAXBContext createJaxbContext() throws JAXBException {
172 StringBuilder packages = new StringBuilder();
173 for (Class cl : getJaxbPackages()) {
174 if (packages.length() > 0) {
175 packages.append(":");
176 }
177 packages.append(cl.getName().substring(0, cl.getName().lastIndexOf('.')));
178 }
179 return JAXBContext.newInstance(packages.toString(), getClass().getClassLoader());
180 }
181
182 protected Set<Class> getJaxbPackages() {
183 Set<Class> classes = new HashSet<Class>();
184 classes.add(org.apache.camel.spring.CamelContextFactoryBean.class);
185 classes.add(CamelJMXAgentDefinition.class);
186 classes.add(org.apache.camel.ExchangePattern.class);
187 classes.add(org.apache.camel.model.RouteDefinition.class);
188 classes.add(org.apache.camel.model.config.StreamResequencerConfig.class);
189 classes.add(org.apache.camel.model.dataformat.DataFormatsDefinition.class);
190 classes.add(org.apache.camel.model.language.ExpressionDefinition.class);
191 classes.add(org.apache.camel.model.loadbalancer.RoundRobinLoadBalancerDefinition.class);
192 return classes;
193 }
194
195 protected class RouteContextDefinitionParser extends BeanDefinitionParser {
196
197 public RouteContextDefinitionParser() {
198 super(CamelRouteContextFactoryBean.class, false);
199 }
200
201 @Override
202 protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
203 renameNamespaceRecursive(element);
204 super.doParse(element, parserContext, builder);
205
206 // now lets parse the routes with JAXB
207 Binder<Node> binder;
208 try {
209 binder = getJaxbContext().createBinder();
210 } catch (JAXBException e) {
211 throw new BeanDefinitionStoreException("Failed to create the JAXB binder", e);
212 }
213 Object value = parseUsingJaxb(element, parserContext, binder);
214
215 if (value instanceof CamelRouteContextFactoryBean) {
216 CamelRouteContextFactoryBean factoryBean = (CamelRouteContextFactoryBean) value;
217 builder.addPropertyValue("routes", factoryBean.getRoutes());
218 }
219 }
220 }
221
222
223 protected class CamelContextBeanDefinitionParser extends BeanDefinitionParser {
224
225 public CamelContextBeanDefinitionParser(Class type) {
226 super(type, false);
227 }
228
229 @Override
230 protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
231 renameNamespaceRecursive(element);
232 super.doParse(element, parserContext, builder);
233
234 String contextId = element.getAttribute("id");
235 boolean implicitId = false;
236
237 // lets avoid folks having to explicitly give an ID to a camel context
238 if (ObjectHelper.isEmpty(contextId)) {
239 // if no explicit id was set then use a default auto generated name
240 CamelContextNameStrategy strategy = new DefaultCamelContextNameStrategy();
241 contextId = strategy.getName();
242 element.setAttribute("id", contextId);
243 implicitId = true;
244 }
245
246 // now lets parse the routes with JAXB
247 Binder<Node> binder;
248 try {
249 binder = getJaxbContext().createBinder();
250 } catch (JAXBException e) {
251 throw new BeanDefinitionStoreException("Failed to create the JAXB binder", e);
252 }
253 Object value = parseUsingJaxb(element, parserContext, binder);
254
255 if (value instanceof CamelContextFactoryBean) {
256 // set the property value with the JAXB parsed value
257 CamelContextFactoryBean factoryBean = (CamelContextFactoryBean) value;
258 builder.addPropertyValue("id", contextId);
259 builder.addPropertyValue("implicitId", implicitId);
260 builder.addPropertyValue("routes", factoryBean.getRoutes());
261 builder.addPropertyValue("intercepts", factoryBean.getIntercepts());
262 builder.addPropertyValue("interceptFroms", factoryBean.getInterceptFroms());
263 builder.addPropertyValue("interceptSendToEndpoints", factoryBean.getInterceptSendToEndpoints());
264 builder.addPropertyValue("dataFormats", factoryBean.getDataFormats());
265 builder.addPropertyValue("onCompletions", factoryBean.getOnCompletions());
266 builder.addPropertyValue("onExceptions", factoryBean.getOnExceptions());
267 builder.addPropertyValue("builderRefs", factoryBean.getBuilderRefs());
268 builder.addPropertyValue("routeRefs", factoryBean.getRouteRefs());
269 builder.addPropertyValue("properties", factoryBean.getProperties());
270 builder.addPropertyValue("packageScan", factoryBean.getPackageScan());
271 builder.addPropertyValue("contextScan", factoryBean.getContextScan());
272 if (factoryBean.getPackages().length > 0) {
273 builder.addPropertyValue("packages", factoryBean.getPackages());
274 }
275 builder.addPropertyValue("camelPropertyPlaceholder", factoryBean.getCamelPropertyPlaceholder());
276 builder.addPropertyValue("camelJMXAgent", factoryBean.getCamelJMXAgent());
277 builder.addPropertyValue("threadPoolProfiles", factoryBean.getThreadPoolProfiles());
278 // add any depends-on
279 addDependsOn(factoryBean, builder);
280 }
281
282 NodeList list = element.getChildNodes();
283 int size = list.getLength();
284 for (int i = 0; i < size; i++) {
285 Node child = list.item(i);
286 if (child instanceof Element) {
287 Element childElement = (Element) child;
288 String localName = child.getLocalName();
289 if (localName.equals("endpoint")) {
290 registerEndpoint(childElement, parserContext, contextId);
291 } else if (localName.equals("routeBuilder")) {
292 addDependsOnToRouteBuilder(childElement, parserContext, contextId);
293 } else {
294 BeanDefinitionParser parser = parserMap.get(localName);
295 if (parser != null) {
296 BeanDefinition definition = parser.parse(childElement, parserContext);
297 String id = childElement.getAttribute("id");
298 if (ObjectHelper.isNotEmpty(id)) {
299 parserContext.registerComponent(new BeanComponentDefinition(definition, id));
300 // set the templates with the camel context
301 if (localName.equals("template") || localName.equals("consumerTemplate")
302 || localName.equals("proxy") || localName.equals("export")) {
303 // set the camel context
304 definition.getPropertyValues().addPropertyValue("camelContext", new RuntimeBeanReference(contextId));
305 }
306 }
307 }
308 }
309 }
310 }
311
312 // register as endpoint defined indirectly in the routes by from/to types having id explicit set
313 registerEndpointsWithIdsDefinedInFromOrToTypes(element, parserContext, contextId, binder);
314
315 // register templates if not already defined
316 registerTemplates(element, parserContext, contextId);
317
318 // lets inject the namespaces into any namespace aware POJOs
319 injectNamespaces(element, binder);
320
321 // inject bean post processor so we can support @Produce etc.
322 // no bean processor element so lets create it by our self
323 injectBeanPostProcessor(element, parserContext, contextId, builder);
324 }
325 }
326
327 protected void addDependsOn(CamelContextFactoryBean factoryBean, BeanDefinitionBuilder builder) {
328 String dependsOn = factoryBean.getDependsOn();
329 if (ObjectHelper.isNotEmpty(dependsOn)) {
330 // comma, whitespace and semi colon is valid separators in Spring depends-on
331 String[] depends = dependsOn.split(",|;|\\s");
332 if (depends == null) {
333 throw new IllegalArgumentException("Cannot separate depends-on, was: " + dependsOn);
334 } else {
335 for (String depend : depends) {
336 depend = depend.trim();
337 if (LOG.isDebugEnabled()) {
338 LOG.debug("Adding dependsOn " + depend + " to CamelContext(" + factoryBean.getId() + ")");
339 }
340 builder.addDependsOn(depend);
341 }
342 }
343 }
344 }
345
346 private void addDependsOnToRouteBuilder(Element childElement, ParserContext parserContext, String contextId) {
347 // setting the depends-on explicitly is required since Spring 3.0
348 String routeBuilderName = childElement.getAttribute("ref");
349 if (ObjectHelper.isNotEmpty(routeBuilderName)) {
350 // set depends-on to the context for a routeBuilder bean
351 try {
352 BeanDefinition definition = parserContext.getRegistry().getBeanDefinition(routeBuilderName);
353 Method getDependsOn = definition.getClass().getMethod("getDependsOn", new Class[]{});
354 String[] dependsOn = (String[])getDependsOn.invoke(definition);
355 if (dependsOn == null || dependsOn.length == 0) {
356 dependsOn = new String[]{contextId};
357 } else {
358 String[] temp = new String[dependsOn.length + 1];
359 System.arraycopy(dependsOn, 0, temp, 0, dependsOn.length);
360 temp[dependsOn.length] = contextId;
361 dependsOn = temp;
362 }
363 Method method = definition.getClass().getMethod("setDependsOn", String[].class);
364 method.invoke(definition, (Object)dependsOn);
365 } catch (Exception e) {
366 // Do nothing here
367 }
368 }
369 }
370
371 protected void injectNamespaces(Element element, Binder<Node> binder) {
372 NodeList list = element.getChildNodes();
373 Namespaces namespaces = null;
374 int size = list.getLength();
375 for (int i = 0; i < size; i++) {
376 Node child = list.item(i);
377 if (child instanceof Element) {
378 Element childElement = (Element) child;
379 Object object = binder.getJAXBNode(child);
380 if (object instanceof NamespaceAware) {
381 NamespaceAware namespaceAware = (NamespaceAware) object;
382 if (namespaces == null) {
383 namespaces = new Namespaces(element);
384 }
385 namespaces.configure(namespaceAware);
386 }
387 injectNamespaces(childElement, binder);
388 }
389 }
390 }
391
392 protected void injectBeanPostProcessor(Element element, ParserContext parserContext, String contextId, BeanDefinitionBuilder builder) {
393 Element childElement = element.getOwnerDocument().createElement("beanPostProcessor");
394 element.appendChild(childElement);
395
396 String beanPostProcessorId = contextId + ":beanPostProcessor";
397 childElement.setAttribute("id", beanPostProcessorId);
398 BeanDefinition definition = beanPostProcessorParser.parse(childElement, parserContext);
399 // only register to camel context id as a String. Then we can look it up later
400 // otherwise we get a circular reference in spring and it will not allow custom bean post processing
401 // see more at CAMEL-1663
402 definition.getPropertyValues().addPropertyValue("camelId", contextId);
403 builder.addPropertyReference("beanPostProcessor", beanPostProcessorId);
404 }
405
406 /**
407 * Used for auto registering endpoints from the <tt>from</tt> or <tt>to</tt> DSL if they have an id attribute set
408 */
409 protected void registerEndpointsWithIdsDefinedInFromOrToTypes(Element element, ParserContext parserContext, String contextId, Binder<Node> binder) {
410 NodeList list = element.getChildNodes();
411 int size = list.getLength();
412 for (int i = 0; i < size; i++) {
413 Node child = list.item(i);
414 if (child instanceof Element) {
415 Element childElement = (Element) child;
416 Object object = binder.getJAXBNode(child);
417 // we only want from/to types to be registered as endpoints
418 if (object instanceof FromDefinition || object instanceof SendDefinition) {
419 registerEndpoint(childElement, parserContext, contextId);
420 }
421 // recursive
422 registerEndpointsWithIdsDefinedInFromOrToTypes(childElement, parserContext, contextId, binder);
423 }
424 }
425 }
426
427 /**
428 * Used for auto registering producer and consumer templates if not already defined in XML.
429 */
430 protected void registerTemplates(Element element, ParserContext parserContext, String contextId) {
431 boolean template = false;
432 boolean consumerTemplate = false;
433
434 NodeList list = element.getChildNodes();
435 int size = list.getLength();
436 for (int i = 0; i < size; i++) {
437 Node child = list.item(i);
438 if (child instanceof Element) {
439 Element childElement = (Element) child;
440 String localName = childElement.getLocalName();
441 if ("template".equals(localName)) {
442 template = true;
443 } else if ("consumerTemplate".equals(localName)) {
444 consumerTemplate = true;
445 }
446 }
447 }
448
449 if (!template) {
450 // either we have not used template before or we have auto registered it already and therefore we
451 // need it to allow to do it so it can remove the existing auto registered as there is now a clash id
452 // since we have multiple camel contexts
453 boolean existing = autoRegisterMap.get("template") != null;
454 boolean inUse = false;
455 try {
456 inUse = parserContext.getRegistry().isBeanNameInUse("template");
457 } catch (BeanCreationException e) {
458 // Spring Eclipse Tooling may throw an exception when you edit the Spring XML online in Eclipse
459 // when the isBeanNameInUse method is invoked, so ignore this and continue (CAMEL-2739)
460 LOG.debug("Error checking isBeanNameInUse(template). This exception will be ignored", e);
461 }
462 if (!inUse || existing) {
463 String id = "template";
464 // auto create a template
465 Element templateElement = element.getOwnerDocument().createElement("template");
466 templateElement.setAttribute("id", id);
467 BeanDefinitionParser parser = parserMap.get("template");
468 BeanDefinition definition = parser.parse(templateElement, parserContext);
469
470 // auto register it
471 autoRegisterBeanDefinition(id, definition, parserContext, contextId);
472 }
473 }
474
475 if (!consumerTemplate) {
476 // either we have not used template before or we have auto registered it already and therefore we
477 // need it to allow to do it so it can remove the existing auto registered as there is now a clash id
478 // since we have multiple camel contexts
479 boolean existing = autoRegisterMap.get("consumerTemplate") != null;
480 boolean inUse = false;
481 try {
482 inUse = parserContext.getRegistry().isBeanNameInUse("consumerTemplate");
483 } catch (BeanCreationException e) {
484 // Spring Eclipse Tooling may throw an exception when you edit the Spring XML online in Eclipse
485 // when the isBeanNameInUse method is invoked, so ignore this and continue (CAMEL-2739)
486 LOG.debug("Error checking isBeanNameInUse(consumerTemplate). This exception will be ignored", e);
487 }
488 if (!inUse || existing) {
489 String id = "consumerTemplate";
490 // auto create a template
491 Element templateElement = element.getOwnerDocument().createElement("consumerTemplate");
492 templateElement.setAttribute("id", id);
493 BeanDefinitionParser parser = parserMap.get("consumerTemplate");
494 BeanDefinition definition = parser.parse(templateElement, parserContext);
495
496 // auto register it
497 autoRegisterBeanDefinition(id, definition, parserContext, contextId);
498 }
499 }
500
501 }
502
503 private void autoRegisterBeanDefinition(String id, BeanDefinition definition, ParserContext parserContext, String contextId) {
504 // it is a bit cumbersome to work with the spring bean definition parser
505 // as we kinda need to eagerly register the bean definition on the parser context
506 // and then later we might find out that we should not have done that in case we have multiple camel contexts
507 // that would have a id clash by auto registering the same bean definition with the same id such as a producer template
508
509 // see if we have already auto registered this id
510 BeanDefinition existing = autoRegisterMap.get(id);
511 if (existing == null) {
512 // no then add it to the map and register it
513 autoRegisterMap.put(id, definition);
514 parserContext.registerComponent(new BeanComponentDefinition(definition, id));
515 if (LOG.isDebugEnabled()) {
516 LOG.debug("Registered default: " + definition.getBeanClassName() + " with id: " + id + " on camel context: " + contextId);
517 }
518 } else {
519 // ups we have already registered it before with same id, but on another camel context
520 // this is not good so we need to remove all traces of this auto registering.
521 // end user must manually add the needed XML elements and provide unique ids access all camel context himself.
522 if (LOG.isDebugEnabled()) {
523 LOG.debug("Unregistered default: " + definition.getBeanClassName() + " with id: " + id
524 + " as we have multiple camel contexts and they must use unique ids."
525 + " You must define the definition in the XML file manually to avoid id clashes when using multiple camel contexts");
526 }
527
528 parserContext.getRegistry().removeBeanDefinition(id);
529 }
530 }
531
532 private void registerEndpoint(Element childElement, ParserContext parserContext, String contextId) {
533 String id = childElement.getAttribute("id");
534 // must have an id to be registered
535 if (ObjectHelper.isNotEmpty(id)) {
536 BeanDefinition definition = endpointParser.parse(childElement, parserContext);
537 definition.getPropertyValues().addPropertyValue("camelContext", new RuntimeBeanReference(contextId));
538 // Need to add this dependency of CamelContext for Spring 3.0
539 try {
540 Method method = definition.getClass().getMethod("setDependsOn", String[].class);
541 method.invoke(definition, (Object) new String[]{contextId});
542 } catch (Exception e) {
543 // Do nothing here
544 }
545 parserContext.registerBeanComponent(new BeanComponentDefinition(definition, id));
546 }
547 }
548
549 }