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