| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| package org.apache.ace.deployment.verifier.ui; |
| |
| import java.io.ByteArrayOutputStream; |
| import java.io.Closeable; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.PrintStream; |
| import java.net.URL; |
| import java.net.URLConnection; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.Map; |
| import java.util.StringTokenizer; |
| import java.util.jar.Attributes; |
| import java.util.jar.JarInputStream; |
| import java.util.jar.Manifest; |
| |
| import org.apache.ace.client.repository.RepositoryObject; |
| import org.apache.ace.client.repository.object.DeploymentArtifact; |
| import org.apache.ace.client.repository.object.DeploymentVersionObject; |
| import org.apache.ace.client.repository.repository.DeploymentVersionRepository; |
| import org.apache.ace.client.repository.stateful.StatefulTargetObject; |
| import org.apache.ace.connectionfactory.ConnectionFactory; |
| import org.apache.ace.deployment.verifier.VerifierService; |
| import org.apache.ace.deployment.verifier.VerifierService.VerifyEnvironment; |
| import org.apache.ace.deployment.verifier.VerifierService.VerifyReporter; |
| import org.apache.ace.webui.UIExtensionFactory; |
| import org.osgi.framework.Constants; |
| import org.osgi.framework.wiring.BundleCapability; |
| import org.osgi.framework.wiring.BundleRequirement; |
| import org.osgi.framework.wiring.BundleRevision; |
| import org.osgi.service.log.LogEntry; |
| |
| import com.vaadin.data.Property; |
| import com.vaadin.data.Property.ValueChangeEvent; |
| import com.vaadin.ui.Button; |
| import com.vaadin.ui.Button.ClickEvent; |
| import com.vaadin.ui.Component; |
| import com.vaadin.ui.Label; |
| import com.vaadin.ui.Panel; |
| import com.vaadin.ui.PopupView; |
| import com.vaadin.ui.TextArea; |
| import com.vaadin.ui.VerticalLayout; |
| |
| public class ACEVerifierExtension implements UIExtensionFactory { |
| |
| /** |
| * |
| */ |
| final class ManifestArea extends VerticalLayout implements Property.ValueChangeListener, Button.ClickListener { |
| private final String m_id; |
| private final TextArea m_editor; |
| private final Label m_plainText; |
| private final StatefulTargetObject m_object; |
| private final PopupView m_popup; |
| |
| public ManifestArea(String id, String initialText, StatefulTargetObject object) { |
| setWidth("100%"); |
| |
| m_id = id; |
| m_object = object; |
| |
| m_editor = new TextArea(null, initialText); |
| m_editor.setRows(15); |
| m_editor.addListener(this); |
| m_editor.setImmediate(true); |
| m_editor.setWidth("100%"); |
| m_editor.setHeight("70%"); |
| |
| m_plainText = new Label(); |
| m_plainText.setContentMode(Label.CONTENT_XHTML); |
| m_plainText.setImmediate(true); |
| m_plainText.setSizeFull(); |
| |
| Panel panel = new Panel(); |
| panel.setCaption("Verification result"); |
| panel.getContent().addComponent(m_plainText); |
| panel.setWidth("800px"); |
| panel.setHeight("300px"); |
| |
| m_popup = new PopupView("Result", panel); |
| m_popup.setCaption("Verification result"); |
| m_popup.setHideOnMouseOut(false); |
| m_popup.setVisible(false); |
| |
| Button verify = new Button("Verify", this); |
| |
| addComponent(m_editor); |
| addComponent(verify); |
| addComponent(m_popup); |
| } |
| |
| public void buttonClick(ClickEvent event) { |
| if (m_popup.isPopupVisible()) { |
| m_popup.setPopupVisible(false); |
| } |
| |
| String output; |
| try { |
| String manifest = m_editor.getValue().toString(); |
| output = verify(m_id, manifest); |
| } |
| catch (Exception e) { |
| ByteArrayOutputStream baos = new ByteArrayOutputStream(); |
| e.printStackTrace(new PrintStream(baos)); |
| output = baos.toString(); |
| } |
| |
| m_plainText.setValue(output); |
| |
| m_popup.setVisible(true); |
| m_popup.setPopupVisible(true); |
| } |
| |
| public void valueChange(ValueChangeEvent event) { |
| String text = (String) m_editor.getValue(); |
| |
| if (text != null) { |
| m_object.addAttribute("manifest", text); |
| } |
| } |
| } |
| |
| // Injected by Dependency Manager |
| private volatile VerifierService m_verifier; |
| private volatile DeploymentVersionRepository m_repo; |
| private volatile ConnectionFactory m_connectionFactory; |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public Component create(Map<String, Object> context) { |
| StatefulTargetObject target = getRepositoryObjectFromContext(context); |
| |
| Component content = new Label("This target is not yet registered, so it can not verify anything."); |
| if (target.isRegistered()) { |
| content = new ManifestArea(target.getID(), getManifest(target), target); |
| } |
| |
| VerticalLayout result = new VerticalLayout(); |
| result.setMargin(true); |
| result.setCaption("Verify/resolve"); |
| result.addComponent(content); |
| |
| return result; |
| } |
| |
| /** |
| * Performs the actual verification. |
| */ |
| final String verify(String targetId, String manifestText) throws Exception { |
| DeploymentVersionObject version = m_repo.getMostRecentDeploymentVersion(targetId); |
| if (version == null) { |
| return "No deployment version available to verify."; |
| } |
| |
| VerificationResult result = new VerificationResult(); |
| Map<String, String> manifestMap = getManifestEntries(manifestText); |
| |
| VerifyEnvironment env = createVerifyEnvironment(manifestMap, result); |
| |
| // Add the main entry... |
| result.addBundle(env, manifestMap); |
| |
| processArtifacts(version.getDeploymentArtifacts(), env, result); |
| |
| StringBuilder sb = new StringBuilder(); |
| if (result.hasCustomizers()) { |
| if (!result.allCustomizerMatch()) { |
| sb.append("<p><b>Not all bundle customizers match!</b><br/>"); |
| sb.append("Provided = ").append(result.getCustomizers().toString()).append("<br/>"); |
| sb.append("Required = ").append(result.getProcessors().toString()).append(".</p>"); |
| } |
| else { |
| sb.append("<p>All bundle customizers match!</p>"); |
| } |
| } |
| |
| boolean resolves = env.verifyResolve(result.getBundles(), null, null); |
| if (resolves) { |
| sb.append("<p>Deployment package resolves.<br/>"); |
| } |
| else { |
| sb.append("<p>Deployment package does <b>not</b> resolve!<br/>"); |
| } |
| |
| sb.append("Details:<br/>"); |
| sb.append(result.toString()).append("</p>"); |
| |
| return sb.toString(); |
| } |
| |
| /** |
| * Quietly closes a given {@link Closeable}. |
| * |
| * @param closeable |
| * the closeable to close, can be <code>null</code>. |
| */ |
| private void closeQuietly(Closeable closeable) { |
| if (closeable != null) { |
| try { |
| closeable.close(); |
| } |
| catch (IOException ex) { |
| // Ignore quietly... |
| } |
| } |
| } |
| |
| /** |
| * Factory method to create a suitable {@link VerifyEnvironment} instance. |
| * |
| * @param manifest |
| * the manifest to use; |
| * @param verifyResult |
| * the verification result to use. |
| * @return a new {@link VerifyEnvironment} instance, never <code>null</code>. |
| */ |
| @SuppressWarnings("deprecation") |
| private VerifyEnvironment createVerifyEnvironment(Map<String, String> manifest, final VerificationResult verifyResult) { |
| String ee = manifest.get(Constants.FRAMEWORK_EXECUTIONENVIRONMENT); |
| if (ee == null) { |
| ee = VerifierService.EE_1_6; |
| } |
| |
| Map<String, String> envMap = Collections.singletonMap(Constants.FRAMEWORK_EXECUTIONENVIRONMENT, ee); |
| |
| VerifyEnvironment env = m_verifier.createEnvironment(envMap, new VerifyReporter() { |
| public void reportException(Exception ex) { |
| ex.printStackTrace(verifyResult.m_out); |
| } |
| |
| public void reportLog(LogEntry logEntry) { |
| verifyResult.m_out.printf("Log (%l): [%s] %s", logEntry.getTime(), logEntry.getLevel(), logEntry.getMessage()); |
| |
| Throwable ex = logEntry.getException(); |
| if (ex != null) { |
| ex.printStackTrace(verifyResult.m_out); |
| } |
| } |
| |
| public void reportWire(BundleRevision importer, BundleRequirement requirement, BundleRevision exporter, |
| BundleCapability capability) { |
| verifyResult.m_out.println("<tt>WIRE: " + requirement + " -> " + capability + "</tt><br/>"); |
| } |
| }); |
| return env; |
| } |
| |
| /** |
| * Returns a "static"/hardcoded manifest. |
| * |
| * @return a manifest, never <code>null</code>. |
| */ |
| private String defineStaticManifest() { |
| // @formatter:off |
| return Constants.BUNDLE_MANIFESTVERSION + ": 2\n" + |
| Constants.BUNDLE_SYMBOLICNAME + ": org.apache.felix.framework\n" + |
| Constants.EXPORT_PACKAGE + ": " + VerifierService.SYSTEM_PACKAGES + "," + VerifierService.JRE_1_6_PACKAGES + "," + |
| "org.osgi.service.cm; version=1.2," + |
| "org.osgi.service.metatype; version=1.1.1," + |
| "org.osgi.service.cm; version=1.3.0," + |
| "org.osgi.service.deploymentadmin.spi; version=1.0.1," + |
| "org.osgi.service.deploymentadmin; version=1.1.0\n"; |
| // @formatter:on |
| } |
| |
| /** |
| * Returns the manifest for a given repository object. |
| * <p> |
| * In case the given repository object does not provide a manifest, this method will return a hard-coded manifest. |
| * </p> |
| * |
| * @param object |
| * the repository object to get the manifest for, cannot be <code>null</code>. |
| * @return a manifest, never <code>null</code>. |
| */ |
| private String getManifest(RepositoryObject object) { |
| String manifest = object.getAttribute("manifest"); |
| if (manifest == null) { |
| manifest = defineStaticManifest(); |
| } |
| return manifest; |
| } |
| |
| /** |
| * Converts a given {@link Attributes} into a map. |
| * |
| * @param attributes |
| * the attributes to convert, cannot be <code>null</code>. |
| * @return a manifest map, never <code>null</code>. |
| */ |
| private Map<String, String> getManifestEntries(final Manifest manifest) { |
| Attributes attributes = manifest.getMainAttributes(); |
| |
| Map<String, String> entries = new HashMap<>(); |
| for (Map.Entry<Object, Object> entry : attributes.entrySet()) { |
| entries.put(entry.getKey().toString(), entry.getValue().toString()); |
| } |
| return entries; |
| } |
| |
| /** |
| * @param manifestText |
| * @return |
| */ |
| private Map<String, String> getManifestEntries(String manifestText) { |
| StringTokenizer tok = new StringTokenizer(manifestText, ":\n"); |
| |
| Map<String, String> manMap = new HashMap<>(); |
| while (tok.hasMoreTokens()) { |
| manMap.put(tok.nextToken(), tok.nextToken()); |
| } |
| return manMap; |
| } |
| |
| private StatefulTargetObject getRepositoryObjectFromContext(Map<String, Object> context) { |
| return (StatefulTargetObject) context.get("object"); |
| } |
| |
| /** |
| * Processes all artifacts. |
| * |
| * @param artifacts |
| * the artifacts to process. |
| * @param env |
| * the environment to use; |
| * @param verifyResult |
| * the verification result, cannot be <code>null</code>. |
| */ |
| private void processArtifacts(DeploymentArtifact[] artifacts, VerifyEnvironment env, VerificationResult verifyResult) { |
| String dir; |
| for (DeploymentArtifact artifact : artifacts) { |
| if (artifact.getDirective(Constants.BUNDLE_SYMBOLICNAME) != null) { |
| processBundle(artifact, env, verifyResult); |
| } |
| else if ((dir = artifact.getDirective("Resource-Processor")) != null) { |
| verifyResult.addProcessor(dir); |
| } |
| } |
| } |
| |
| /** |
| * Processes a single bundle. |
| * |
| * @param bundle |
| * the bundle to process; |
| * @param env |
| * the environment to use; |
| * @param verifyResult |
| * the verification result, cannot be <code>null</code>. |
| */ |
| private void processBundle(DeploymentArtifact bundle, VerifyEnvironment env, VerificationResult verifyResult) { |
| InputStream is = null; |
| JarInputStream jis = null; |
| |
| try { |
| is = getBundleContents(bundle.getUrl()); |
| jis = new JarInputStream(is, false /* verify */); |
| |
| Map<String, String> manifest = getManifestEntries(jis.getManifest()); |
| |
| verifyResult.addBundle(env, manifest); |
| |
| if (manifest.get("DeploymentPackage-Customizer") != null) { |
| String typeString = manifest.get("Deployment-ProvidesResourceProcessor"); |
| if (typeString != null) { |
| String[] types = typeString.split(","); |
| for (String type : types) { |
| verifyResult.addCustomizer(type); |
| } |
| } |
| } |
| } |
| catch (Exception ex) { |
| ex.printStackTrace(verifyResult.m_out); |
| } |
| finally { |
| closeQuietly(is); |
| closeQuietly(jis); |
| } |
| } |
| |
| /** |
| * @param url |
| * the remote URL to connect to, cannot be <code>null</code>. |
| * @return an {@link InputStream} to the remote URL, never <code>null</code>. |
| * @throws IOException |
| * in case of I/O problems opening the remote connection. |
| */ |
| private InputStream getBundleContents(String url) throws IOException { |
| URLConnection conn = m_connectionFactory.createConnection(new URL(url)); |
| return conn.getInputStream(); |
| } |
| } |