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.servicemix.jbi.deployment;
018:
019: import java.io.ByteArrayInputStream;
020: import java.io.ByteArrayOutputStream;
021: import java.io.File;
022: import java.io.FileInputStream;
023: import java.io.InputStream;
024: import java.net.MalformedURLException;
025: import java.net.URL;
026: import java.util.ArrayList;
027: import java.util.List;
028:
029: import javax.xml.namespace.QName;
030: import javax.xml.parsers.DocumentBuilder;
031: import javax.xml.parsers.DocumentBuilderFactory;
032: import javax.xml.transform.stream.StreamSource;
033: import javax.xml.validation.Schema;
034: import javax.xml.validation.SchemaFactory;
035: import javax.xml.validation.Validator;
036:
037: import org.w3c.dom.Document;
038: import org.w3c.dom.DocumentFragment;
039: import org.w3c.dom.Element;
040:
041: import org.xml.sax.ErrorHandler;
042: import org.xml.sax.SAXException;
043: import org.xml.sax.SAXParseException;
044:
045: import org.apache.commons.logging.Log;
046: import org.apache.commons.logging.LogFactory;
047: import org.apache.servicemix.jbi.util.DOMUtil;
048: import org.apache.servicemix.jbi.util.FileUtil;
049:
050: /**
051: * @version $Revision: 359151 $
052: */
053: public final class DescriptorFactory {
054:
055: public static final String DESCRIPTOR_FILE = "META-INF/jbi.xml";
056:
057: /**
058: * JAXP attribute value indicating the XSD schema language.
059: */
060: private static final String XSD_SCHEMA_LANGUAGE = "http://www.w3.org/2001/XMLSchema";
061:
062: private static final Log LOG = LogFactory
063: .getLog(DescriptorFactory.class);
064:
065: private DescriptorFactory() {
066: }
067:
068: /**
069: * Build a jbi descriptor from a file archive
070: *
071: * @param descriptorFile
072: * path to the jbi descriptor, or to the root directory
073: * @return the Descriptor object
074: */
075: public static Descriptor buildDescriptor(File descriptorFile) {
076: if (descriptorFile.isDirectory()) {
077: descriptorFile = new File(descriptorFile, DESCRIPTOR_FILE);
078: }
079: if (descriptorFile.isFile()) {
080: try {
081: return buildDescriptor(descriptorFile.toURL());
082: } catch (MalformedURLException e) {
083: throw new RuntimeException("There is a bug here...", e);
084: }
085: }
086: return null;
087: }
088:
089: /**
090: * Build a jbi descriptor from the specified URL
091: *
092: * @param url
093: * url to the jbi descriptor
094: * @return the Descriptor object
095: */
096: public static Descriptor buildDescriptor(final URL url) {
097: try {
098: // Read descriptor
099: ByteArrayOutputStream baos = new ByteArrayOutputStream();
100: FileUtil.copyInputStream(url.openStream(), baos);
101: // Validate descriptor
102: SchemaFactory schemaFactory = SchemaFactory
103: .newInstance(XSD_SCHEMA_LANGUAGE);
104: Schema schema = schemaFactory
105: .newSchema(DescriptorFactory.class
106: .getResource("/jbi-descriptor.xsd"));
107: Validator validator = schema.newValidator();
108: validator.setErrorHandler(new ErrorHandler() {
109: public void warning(SAXParseException exception)
110: throws SAXException {
111: LOG.debug("Validation warning on " + url + ": "
112: + exception);
113: }
114:
115: public void error(SAXParseException exception)
116: throws SAXException {
117: LOG.info("Validation error on " + url + ": "
118: + exception);
119: }
120:
121: public void fatalError(SAXParseException exception)
122: throws SAXException {
123: throw exception;
124: }
125: });
126: validator.validate(new StreamSource(
127: new ByteArrayInputStream(baos.toByteArray())));
128: // Parse descriptor
129: DocumentBuilderFactory factory = DocumentBuilderFactory
130: .newInstance();
131: factory.setNamespaceAware(true);
132: DocumentBuilder docBuilder = factory.newDocumentBuilder();
133: Document doc = docBuilder.parse(new ByteArrayInputStream(
134: baos.toByteArray()));
135: Element jbi = doc.getDocumentElement();
136: Descriptor desc = new Descriptor();
137: desc.setVersion(Double.parseDouble(getAttribute(jbi,
138: "version")));
139: Element child = DOMUtil.getFirstChildElement(jbi);
140: if ("component".equals(child.getLocalName())) {
141: Component component = parseComponent(child);
142: desc.setComponent(component);
143: } else if ("shared-library".equals(child.getLocalName())) {
144: SharedLibrary sharedLibrary = parseSharedLibrary(child);
145: desc.setSharedLibrary(sharedLibrary);
146: } else if ("service-assembly".equals(child.getLocalName())) {
147: ServiceAssembly serviceAssembly = parseServiceAssembly(child);
148: desc.setServiceAssembly(serviceAssembly);
149: } else if ("services".equals(child.getLocalName())) {
150: Services services = parseServiceUnit(child);
151: desc.setServices(services);
152: }
153: checkDescriptor(desc);
154: return desc;
155: } catch (Exception e) {
156: throw new RuntimeException(e);
157: }
158: }
159:
160: private static Services parseServiceUnit(Element child) {
161: Services services = new Services();
162: services.setBindingComponent(Boolean.valueOf(
163: getAttribute(child, "binding-component"))
164: .booleanValue());
165: List<Provides> provides = new ArrayList<Provides>();
166: List<Consumes> consumes = new ArrayList<Consumes>();
167: for (Element e = DOMUtil.getFirstChildElement(child); e != null; e = DOMUtil
168: .getNextSiblingElement(e)) {
169: if ("provides".equals(e.getLocalName())) {
170: Provides p = new Provides();
171: p.setInterfaceName(readAttributeQName(e,
172: "interface-name"));
173: p.setServiceName(readAttributeQName(e, "service-name"));
174: p.setEndpointName(getAttribute(e, "endpoint-name"));
175: provides.add(p);
176: } else if ("consumes".equals(e.getLocalName())) {
177: Consumes c = new Consumes();
178: c.setInterfaceName(readAttributeQName(e,
179: "interface-name"));
180: c.setServiceName(readAttributeQName(e, "service-name"));
181: c.setEndpointName(getAttribute(e, "endpoint-name"));
182: c.setLinkType(getAttribute(e, "link-type"));
183: consumes.add(c);
184: }
185: }
186: services.setProvides(provides.toArray(new Provides[provides
187: .size()]));
188: services.setConsumes(consumes.toArray(new Consumes[consumes
189: .size()]));
190: return services;
191: }
192:
193: private static ServiceAssembly parseServiceAssembly(Element child) {
194: ServiceAssembly serviceAssembly = new ServiceAssembly();
195: List<ServiceUnit> sus = new ArrayList<ServiceUnit>();
196: for (Element e = DOMUtil.getFirstChildElement(child); e != null; e = DOMUtil
197: .getNextSiblingElement(e)) {
198: if ("identification".equals(e.getLocalName())) {
199: serviceAssembly
200: .setIdentification(readIdentification(e));
201: } else if ("service-unit".equals(e.getLocalName())) {
202: ServiceUnit su = new ServiceUnit();
203: for (Element e2 = DOMUtil.getFirstChildElement(e); e2 != null; e2 = DOMUtil
204: .getNextSiblingElement(e2)) {
205: if ("identification".equals(e2.getLocalName())) {
206: su.setIdentification(readIdentification(e2));
207: } else if ("target".equals(e2.getLocalName())) {
208: Target target = new Target();
209: for (Element e3 = DOMUtil
210: .getFirstChildElement(e2); e3 != null; e3 = DOMUtil
211: .getNextSiblingElement(e3)) {
212: if ("artifacts-zip".equals(e3
213: .getLocalName())) {
214: target.setArtifactsZip(getText(e3));
215: } else if ("component-name".equals(e3
216: .getLocalName())) {
217: target.setComponentName(getText(e3));
218: }
219: }
220: su.setTarget(target);
221: }
222: }
223: sus.add(su);
224: } else if ("connections".equals(e.getLocalName())) {
225: Connections connections = new Connections();
226: List<Connection> cns = new ArrayList<Connection>();
227: for (Element e2 = DOMUtil.getFirstChildElement(e); e2 != null; e2 = DOMUtil
228: .getNextSiblingElement(e2)) {
229: if ("connection".equals(e2.getLocalName())) {
230: Connection cn = new Connection();
231: for (Element e3 = DOMUtil
232: .getFirstChildElement(e2); e3 != null; e3 = DOMUtil
233: .getNextSiblingElement(e3)) {
234: if ("consumer".equals(e3.getLocalName())) {
235: Consumer consumer = new Consumer();
236: consumer
237: .setInterfaceName(readAttributeQName(
238: e3, "interface-name"));
239: consumer
240: .setServiceName(readAttributeQName(
241: e3, "service-name"));
242: consumer.setEndpointName(getAttribute(
243: e3, "endpoint-name"));
244: cn.setConsumer(consumer);
245: } else if ("provider".equals(e3
246: .getLocalName())) {
247: Provider provider = new Provider();
248: provider
249: .setServiceName(readAttributeQName(
250: e3, "service-name"));
251: provider.setEndpointName(getAttribute(
252: e3, "endpoint-name"));
253: cn.setProvider(provider);
254: }
255: }
256: cns.add(cn);
257: }
258: }
259: connections.setConnections(cns
260: .toArray(new Connection[cns.size()]));
261: serviceAssembly.setConnections(connections);
262: }
263: }
264: serviceAssembly.setServiceUnits(sus.toArray(new ServiceUnit[sus
265: .size()]));
266: return serviceAssembly;
267: }
268:
269: private static SharedLibrary parseSharedLibrary(Element child) {
270: SharedLibrary sharedLibrary = new SharedLibrary();
271: sharedLibrary.setClassLoaderDelegation(getAttribute(child,
272: "class-loader-delegation"));
273: sharedLibrary.setVersion(getAttribute(child, "version"));
274: for (Element e = DOMUtil.getFirstChildElement(child); e != null; e = DOMUtil
275: .getNextSiblingElement(e)) {
276: if ("identification".equals(e.getLocalName())) {
277: sharedLibrary.setIdentification(readIdentification(e));
278: } else if ("shared-library-class-path".equals(e
279: .getLocalName())) {
280: ClassPath sharedLibraryClassPath = new ClassPath();
281: List<String> l = new ArrayList<String>();
282: for (Element e2 = DOMUtil.getFirstChildElement(e); e2 != null; e2 = DOMUtil
283: .getNextSiblingElement(e2)) {
284: if ("path-element".equals(e2.getLocalName())) {
285: l.add(getText(e2));
286: }
287: }
288: sharedLibraryClassPath.setPathList(l);
289: sharedLibrary
290: .setSharedLibraryClassPath(sharedLibraryClassPath);
291: }
292: }
293: return sharedLibrary;
294: }
295:
296: private static Component parseComponent(Element child) {
297: Component component = new Component();
298: component.setType(child.getAttribute("type"));
299: component.setComponentClassLoaderDelegation(getAttribute(child,
300: "component-class-loader-delegation"));
301: component.setBootstrapClassLoaderDelegation(getAttribute(child,
302: "bootstrap-class-loader-delegation"));
303: List<SharedLibraryList> sls = new ArrayList<SharedLibraryList>();
304: DocumentFragment ext = null;
305: for (Element e = DOMUtil.getFirstChildElement(child); e != null; e = DOMUtil
306: .getNextSiblingElement(e)) {
307: if ("identification".equals(e.getLocalName())) {
308: component.setIdentification(readIdentification(e));
309: } else if ("component-class-name".equals(e.getLocalName())) {
310: component.setComponentClassName(getText(e));
311: component
312: .setDescription(getAttribute(e, "description"));
313: } else if ("component-class-path".equals(e.getLocalName())) {
314: ClassPath componentClassPath = new ClassPath();
315: List<String> l = new ArrayList<String>();
316: for (Element e2 = DOMUtil.getFirstChildElement(e); e2 != null; e2 = DOMUtil
317: .getNextSiblingElement(e2)) {
318: if ("path-element".equals(e2.getLocalName())) {
319: l.add(getText(e2));
320: }
321: }
322: componentClassPath.setPathList(l);
323: component.setComponentClassPath(componentClassPath);
324: } else if ("bootstrap-class-name".equals(e.getLocalName())) {
325: component.setBootstrapClassName(getText(e));
326: } else if ("bootstrap-class-path".equals(e.getLocalName())) {
327: ClassPath bootstrapClassPath = new ClassPath();
328: List<String> l = new ArrayList<String>();
329: for (Element e2 = DOMUtil.getFirstChildElement(e); e2 != null; e2 = DOMUtil
330: .getNextSiblingElement(e2)) {
331: if ("path-element".equals(e2.getLocalName())) {
332: l.add(getText(e2));
333: }
334: }
335: bootstrapClassPath.setPathList(l);
336: component.setBootstrapClassPath(bootstrapClassPath);
337: } else if ("shared-library".equals(e.getLocalName())) {
338: SharedLibraryList sl = new SharedLibraryList();
339: sl.setName(getText(e));
340: sl.setVersion(getAttribute(e, "version"));
341: sls.add(sl);
342: } else {
343: if (ext == null) {
344: ext = child.getOwnerDocument()
345: .createDocumentFragment();
346: }
347: ext.appendChild(e);
348: }
349: }
350: component.setSharedLibraries(sls
351: .toArray(new SharedLibraryList[sls.size()]));
352: if (ext != null) {
353: InstallationDescriptorExtension descriptorExtension = new InstallationDescriptorExtension();
354: descriptorExtension.setDescriptorExtension(ext);
355: component.setDescriptorExtension(descriptorExtension);
356: }
357: return component;
358: }
359:
360: private static String getAttribute(Element e, String name) {
361: if (e.hasAttribute(name)) {
362: return e.getAttribute(name);
363: } else {
364: return null;
365: }
366: }
367:
368: private static QName readAttributeQName(Element e, String name) {
369: String attr = getAttribute(e, name);
370: if (attr != null) {
371: return DOMUtil.createQName(e, attr);
372: } else {
373: return null;
374: }
375: }
376:
377: private static String getText(Element e) {
378: return DOMUtil.getElementText(e).trim();
379: }
380:
381: private static Identification readIdentification(Element e) {
382: Identification ident = new Identification();
383: for (Element e2 = DOMUtil.getFirstChildElement(e); e2 != null; e2 = DOMUtil
384: .getNextSiblingElement(e2)) {
385: if ("name".equals(e2.getLocalName())) {
386: ident.setName(DOMUtil.getElementText(e2));
387: } else if ("description".equals(e2.getLocalName())) {
388: ident.setDescription(DOMUtil.getElementText(e2));
389: }
390: }
391: return ident;
392: }
393:
394: /**
395: * Check validity of the JBI descriptor
396: *
397: * @param descriptor
398: * the descriptor to check
399: * @throws Exception
400: * if the descriptor is not valid
401: */
402: public static void checkDescriptor(Descriptor descriptor) {
403: List<String> violations = new ArrayList<String>();
404:
405: if (descriptor.getVersion() != 1.0) {
406: violations
407: .add("JBI descriptor version should be set to '1.0' but is "
408: + descriptor.getVersion());
409: }
410:
411: if (descriptor.getComponent() != null) {
412: checkComponent(violations, descriptor.getComponent());
413: } else if (descriptor.getServiceAssembly() != null) {
414: checkServiceAssembly(violations, descriptor
415: .getServiceAssembly());
416: } else if (descriptor.getServices() != null) {
417: checkServiceUnit(violations, descriptor.getServices());
418: } else if (descriptor.getSharedLibrary() != null) {
419: checkSharedLibrary(violations, descriptor
420: .getSharedLibrary());
421: } else {
422: violations
423: .add("The jbi descriptor does not contain any informations");
424: }
425:
426: if (violations.size() > 0) {
427: throw new RuntimeException(
428: "The JBI descriptor is not valid, please correct these violations "
429: + violations.toString());
430: }
431: }
432:
433: /**
434: * Checks that the component is valid
435: *
436: * @param violations
437: * A list of violations that the check can add to
438: *
439: * @param component
440: * The component descriptor that is being checked
441: */
442: private static void checkComponent(List<String> violations,
443: Component component) {
444: if (component.getIdentification() == null) {
445: violations.add("The component has not identification");
446: } else {
447: if (isBlank(component.getIdentification().getName())) {
448: violations.add("The component name is not set");
449: }
450: }
451: if (component.getBootstrapClassName() == null) {
452: violations
453: .add("The component has not defined a boot-strap class name");
454: }
455: if (component.getBootstrapClassPath() == null
456: || component.getBootstrapClassPath().getPathElements() == null) {
457: violations
458: .add("The component has not defined any boot-strap class path elements");
459: }
460: }
461:
462: /**
463: * Checks that the service assembly is valid
464: *
465: * @param violations
466: * A list of violations that the check can add to
467: *
468: * @param serviceAssembly
469: * The service assembly descriptor that is being checked
470: */
471: private static void checkServiceAssembly(List<String> violations,
472: ServiceAssembly serviceAssembly) {
473: if (serviceAssembly.getIdentification() == null) {
474: violations
475: .add("The service assembly has not identification");
476: } else {
477: if (isBlank(serviceAssembly.getIdentification().getName())) {
478: violations.add("The service assembly name is not set");
479: }
480: }
481: }
482:
483: /**
484: * Checks that the service unit is valid
485: *
486: * @param violations
487: * A list of violations that the check can add to
488: *
489: * @param services
490: * The service unit descriptor that is being checked
491: */
492: private static void checkServiceUnit(List<String> violations,
493: Services services) {
494: // TODO Auto-generated method stub
495:
496: }
497:
498: /**
499: * Checks that the shared library is valid
500: *
501: * @param violations
502: * A list of violations that the check can add to
503: *
504: * @param sharedLibrary
505: * The shared library descriptor that is being checked
506: */
507: private static void checkSharedLibrary(List<String> violations,
508: SharedLibrary sharedLibrary) {
509: if (sharedLibrary.getIdentification() == null) {
510: violations.add("The shared library has not identification");
511: } else {
512: if (isBlank(sharedLibrary.getIdentification().getName())) {
513: violations.add("The shared library name is not set");
514: }
515: }
516: }
517:
518: /**
519: * Retrieves the jbi descriptor as a string
520: *
521: * @param descriptorFile
522: * path to the jbi descriptor, or to the root directory
523: * @return the contents of the jbi descriptor
524: */
525: public static String getDescriptorAsText(File descriptorFile) {
526: if (descriptorFile.isDirectory()) {
527: descriptorFile = new File(descriptorFile, DESCRIPTOR_FILE);
528: }
529: if (descriptorFile.isFile()) {
530: try {
531: ByteArrayOutputStream os = new ByteArrayOutputStream();
532: InputStream is = new FileInputStream(descriptorFile);
533: FileUtil.copyInputStream(is, os);
534: return os.toString();
535: } catch (Exception e) {
536: LOG.debug("Error reading jbi descritor: "
537: + descriptorFile, e);
538: }
539: }
540: return null;
541: }
542:
543: /**
544: * <p>Checks if a String is whitespace, empty ("") or null.</p>
545: *
546: * <pre>
547: * StringUtils.isBlank(null) = true
548: * StringUtils.isBlank("") = true
549: * StringUtils.isBlank(" ") = true
550: * StringUtils.isBlank("bob") = false
551: * StringUtils.isBlank(" bob ") = false
552: * </pre>
553: *
554: * @param str the String to check, may be null
555: * @return <code>true</code> if the String is null, empty or whitespace
556: *
557: * Copied from org.apache.commons.lang.StringUtils#isBlanck
558: */
559: private static boolean isBlank(String str) {
560: if (str == null) {
561: return true;
562: }
563: int strLen = str.length();
564: if (strLen == 0) {
565: return true;
566: }
567: for (int i = 0; i < strLen; i++) {
568: if (!(Character.isWhitespace(str.charAt(i)))) {
569: return false;
570: }
571: }
572: return true;
573: }
574:
575: }
|