/* * SoapUI, Copyright (C) 2004-2016 SmartBear Software * * Licensed under the EUPL, Version 1.1 or - as soon as they will be approved by the European Commission - subsequent * versions of the EUPL (the "Licence"); * You may not use this work except in compliance with the Licence. * You may obtain a copy of the Licence at: * * http://ec.europa.eu/idabc/eupl * * Unless required by applicable law or agreed to in writing, software distributed under the Licence is * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either * express or implied. See the Licence for the specific language governing permissions and limitations * under the Licence. */ package com.eviware.soapui.impl.rest.panels.request.inspectors.schema; import com.eviware.soapui.impl.rest.RestRequest; import com.eviware.soapui.impl.rest.RestService; import com.eviware.soapui.impl.settings.XmlBeansSettingsImpl; import com.eviware.soapui.impl.wadl.inference.ConflictHandler; import com.eviware.soapui.impl.wsdl.submit.transports.http.HttpResponse; import com.eviware.soapui.impl.wsdl.submit.transports.jms.JMSResponse; import com.eviware.soapui.model.iface.Submit; import com.eviware.soapui.model.iface.Submit.Status; import com.eviware.soapui.model.iface.SubmitContext; import com.eviware.soapui.model.iface.SubmitListener; import com.eviware.soapui.support.UISupport; import com.eviware.soapui.support.components.JXToolBar; import com.eviware.soapui.support.editor.EditorView; import com.eviware.soapui.support.editor.inspectors.AbstractXmlInspector; import com.eviware.soapui.support.editor.views.xml.raw.RawXmlEditorFactory; import com.eviware.soapui.support.editor.xml.XmlDocument; import com.eviware.soapui.support.log.JLogList; import com.eviware.soapui.support.xml.SyntaxEditorUtil; import com.eviware.soapui.support.xml.XmlUtils; import org.apache.xmlbeans.XmlException; import org.apache.xmlbeans.XmlObject; import org.apache.xmlbeans.XmlOptions; import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea; import javax.swing.AbstractAction; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JComponent; import javax.swing.JList; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JSplitPane; import javax.swing.JTabbedPane; import javax.swing.ListSelectionModel; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import javax.xml.namespace.QName; import java.awt.BorderLayout; import java.awt.Rectangle; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.net.URL; import java.util.ArrayList; import java.util.Collections; import java.util.List; /** * @author Dain.Nilsson */ public class InferredSchemaInspector extends AbstractXmlInspector implements SubmitListener { private SchemaTabs tabs; private RestService service; private RestRequest request; private Handler handler; private Thread thread; protected InferredSchemaInspector(RestRequest request) { super("Schema", "Inferred Schema", true, InferredSchemaInspectorFactory.INSPECTOR_ID); service = request.getResource().getService(); this.request = request; request.addSubmitListener(this); } public JComponent getComponent() { if (tabs == null) { tabs = new SchemaTabs(); InferredSchemaManager.addPropertyChangeListener(service, tabs); } return tabs; } @Override public boolean isEnabledFor(EditorView<XmlDocument> view) { return !view.getViewId().equals(RawXmlEditorFactory.VIEW_ID); } public void afterSubmit(Submit submit, SubmitContext context) { if (submit.getResponse() == null) { return; } HttpResponse httpResponse = (HttpResponse) submit.getResponse(); String content = httpResponse.getContentAsXml(); if (content == null || content.equals("<xml/>")) { return; } XmlObject xml; try { URL url = httpResponse.getURL(); String defaultNamespace = null; if (url != null) { defaultNamespace = url.getProtocol() + "://" + url.getHost(); } else { if (httpResponse instanceof JMSResponse) { defaultNamespace = ((JMSResponse) httpResponse).getEndpoint(); } } XmlOptions options = new XmlOptions().setLoadSubstituteNamespaces(Collections.singletonMap("", defaultNamespace)); // xml = XmlObject.Factory.parse( content, options ); xml = XmlUtils.createXmlObject(content, options); } catch (XmlException e) { e.printStackTrace(); return; } if (!submit.getStatus().equals(Status.CANCELED) && !InferredSchemaManager.getInferredSchema(service).validate(xml)) { setTitle("Schema (conflicts)"); if (thread != null && thread.isAlive()) { handler.kill(); try { thread.join(); } catch (InterruptedException e) { e.printStackTrace(); } } handler = new Handler(tabs, xml); thread = new Thread(handler); thread.start(); } } public boolean beforeSubmit(Submit submit, SubmitContext context) { return true; } public void release() { super.release(); request.removeSubmitListener(this); InferredSchemaManager.removePropertyChangeListener(service, tabs); if (thread != null && thread.isAlive()) { handler.kill(); } } @SuppressWarnings("serial") private class SchemaTabs extends JTabbedPane implements ActionListener, PropertyChangeListener, ListSelectionListener { private JLogList log; private JPanel conflicts; private JButton resolveButton; private JCheckBox auto; private Handler handler; private RSyntaxTextArea xsd; private JList schemaList; public static final String AUTO_INFER_SCHEMAS = "AutoInferSchemas"; public static final String NO_NAMESPACE = "<no namespace>"; public SchemaTabs() { super(); conflicts = new JPanel(); conflicts.setLayout(new BorderLayout()); auto = new JCheckBox("Auto-Resolve"); auto.setToolTipText("Automatically modify inferred schema from received Responses"); auto.setOpaque(false); UISupport.setFixedSize(auto, 120, 20); XmlBeansSettingsImpl settings = getRequest().getSettings(); if (settings.isSet(AUTO_INFER_SCHEMAS)) { auto.setSelected(settings.getBoolean(AUTO_INFER_SCHEMAS)); } auto.addItemListener(new ItemListener() { public void itemStateChanged(ItemEvent e) { getRequest().getSettings().setBoolean(AUTO_INFER_SCHEMAS, auto.isSelected()); } }); resolveButton = new JButton("Resolve conflicts"); resolveButton.setEnabled(false); resolveButton.setActionCommand("resolve"); resolveButton.addActionListener(this); JXToolBar toolbar = UISupport.createToolbar(); toolbar.addFixed(auto); toolbar.addFixed(resolveButton); log = new JLogList("Schema log"); conflicts.add(toolbar, BorderLayout.NORTH); conflicts.add(log, BorderLayout.CENTER); addTab("Conflicts", conflicts); schemaList = new JList(InferredSchemaManager.getInferredSchema(service).getNamespaces()); schemaList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); schemaList.addListSelectionListener(this); toolbar = UISupport.createToolbar(); toolbar.addFixed(UISupport.createToolbarButton(new RemoveNamespaceAction())); JPanel listPanel = new JPanel(); listPanel.setLayout(new BorderLayout()); listPanel.add(toolbar, BorderLayout.NORTH); listPanel.add(new JScrollPane(schemaList), BorderLayout.CENTER); xsd = SyntaxEditorUtil.createDefaultXmlSyntaxTextArea(); xsd.setEditable(false); update(); addTab("Schemas", new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, listPanel, new JScrollPane(xsd))); } public synchronized boolean awaitButton(Handler handler) { if (auto.isSelected()) { return false; } resolveButton.setEnabled(true); this.handler = handler; return true; } public synchronized void actionPerformed(ActionEvent e) { if (e.getActionCommand().equals("resolve")) { resolveButton.setEnabled(false); handler.go(); } else if (e.getActionCommand().equals("save")) { InferredSchemaManager.save(service); } } public void propertyChange(PropertyChangeEvent evt) { update(); } public void update() { String[] namespaces = InferredSchemaManager.getInferredSchema(service).getNamespaces(); for (int i = 0; i < namespaces.length; i++) { if (namespaces[i].equals("")) { namespaces[i] = NO_NAMESPACE; } } schemaList.setListData(namespaces); if (schemaList.isSelectionEmpty()) { xsd.setText(""); } else { xsd.setText(XmlUtils.prettyPrintXml(InferredSchemaManager.getInferredSchema(service) .getXsdForNamespace((String) schemaList.getSelectedValue()))); xsd.setCaretPosition(0); xsd.scrollRectToVisible(new Rectangle(0, 0, (int) (getSize().getWidth()), (int) (getSize() .getHeight()))); } } public void logln(String line) { log.addLine(line); } public void valueChanged(ListSelectionEvent e) { if (e.getValueIsAdjusting() == false) { if (!schemaList.isSelectionEmpty()) { String namespace = (String) schemaList.getSelectedValue(); if (namespace.equals(NO_NAMESPACE)) { namespace = ""; } xsd.setText(XmlUtils.prettyPrintXml(InferredSchemaManager.getInferredSchema(service) .getXsdForNamespace(namespace))); xsd.setCaretPosition(0); xsd.scrollRectToVisible(new Rectangle(0, 0, (int) (getSize().getWidth()), (int) (getSize() .getHeight()))); } } } private class RemoveNamespaceAction extends AbstractAction { private RemoveNamespaceAction() { putValue(SMALL_ICON, UISupport.createImageIcon("/delete.png")); putValue(SHORT_DESCRIPTION, "Removes selected inferred namespace definition"); } public void actionPerformed(ActionEvent e) { if (!schemaList.isSelectionEmpty()) { String ns = (String) schemaList.getSelectedValue(); if (UISupport.confirm("Remove inferred namespace '" + ns + "'?", "Remove namespace")) { if (ns.equals(NO_NAMESPACE)) { ns = ""; } InferredSchemaManager.deleteNamespace(service, ns); } } } } } public class Handler implements ConflictHandler, Runnable { private SchemaTabs panel; private XmlObject xml; private List<String> paths; private boolean yesToAll = false; private boolean kill = false; public Handler(SchemaTabs panel, XmlObject xml) { this.panel = panel; this.xml = xml; paths = new ArrayList<String>(); } public synchronized void run() { try { if (panel.awaitButton(this)) { try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } else { yesToAll = true; } if (kill) { return; } InferredSchemaManager.getInferredSchema(service).learningValidate(xml, this); panel.update(); setTitle("Schema"); InferredSchemaManager.save(service); } catch (XmlException e) { setTitle("Schema (invalid)"); } } public synchronized void go() { notifyAll(); } public synchronized void kill() { kill = true; notifyAll(); } public boolean callback(Event event, Type type, QName name, String path, String message) { // if(paths.contains(path)) return true; StringBuilder s = new StringBuilder(message).append(" "); if (event == Event.CREATION) { paths.add(path); s.append("Create "); } else if (event == Event.MODIFICATION) { paths.add(path); s.append("Modify "); } if (type == Type.ELEMENT) { s.append("element '"); } else if (type == Type.ATTRIBUTE) { s.append("attribute '"); } else if (type == Type.TYPE) { s.append("type '"); } s.append(name.getLocalPart()).append("' in namespace '").append(name.getNamespaceURI()) .append("' at path ").append(path).append("?"); if (!yesToAll) { int choice = UISupport.yesYesToAllOrNo(s.toString(), "Conflict"); if (choice == 2) { panel.logln(s.append(" FAIL").toString()); return false; } else if (choice == 1) { yesToAll = true; } } panel.logln(s.append(" OK").toString()); return true; } } public RestRequest getRequest() { return request; } }