1
2
3
4
5
6
7
8
9
10
11
12
13 package com.eviware.soapui.impl.rest.panels.request.inspectors.schema;
14
15 import java.awt.BorderLayout;
16 import java.awt.event.ActionEvent;
17 import java.awt.event.ActionListener;
18 import java.awt.event.ItemEvent;
19 import java.awt.event.ItemListener;
20 import java.beans.PropertyChangeEvent;
21 import java.beans.PropertyChangeListener;
22 import java.net.URL;
23 import java.util.ArrayList;
24 import java.util.Collections;
25 import java.util.List;
26
27 import javax.swing.AbstractAction;
28 import javax.swing.JButton;
29 import javax.swing.JCheckBox;
30 import javax.swing.JComponent;
31 import javax.swing.JList;
32 import javax.swing.JPanel;
33 import javax.swing.JScrollPane;
34 import javax.swing.JSplitPane;
35 import javax.swing.JTabbedPane;
36 import javax.swing.ListSelectionModel;
37 import javax.swing.event.ListSelectionEvent;
38 import javax.swing.event.ListSelectionListener;
39 import javax.xml.namespace.QName;
40
41 import org.apache.xmlbeans.XmlException;
42 import org.apache.xmlbeans.XmlObject;
43 import org.apache.xmlbeans.XmlOptions;
44
45 import com.eviware.soapui.impl.rest.RestRequest;
46 import com.eviware.soapui.impl.rest.RestService;
47 import com.eviware.soapui.impl.settings.XmlBeansSettingsImpl;
48 import com.eviware.soapui.impl.wadl.inference.ConflictHandler;
49 import com.eviware.soapui.impl.wsdl.submit.transports.http.HttpResponse;
50 import com.eviware.soapui.impl.wsdl.submit.transports.jms.JMSResponse;
51 import com.eviware.soapui.model.iface.Submit;
52 import com.eviware.soapui.model.iface.SubmitContext;
53 import com.eviware.soapui.model.iface.SubmitListener;
54 import com.eviware.soapui.model.iface.Submit.Status;
55 import com.eviware.soapui.support.UISupport;
56 import com.eviware.soapui.support.components.JXToolBar;
57 import com.eviware.soapui.support.editor.EditorView;
58 import com.eviware.soapui.support.editor.inspectors.AbstractXmlInspector;
59 import com.eviware.soapui.support.editor.views.xml.raw.RawXmlEditorFactory;
60 import com.eviware.soapui.support.editor.xml.XmlDocument;
61 import com.eviware.soapui.support.log.JLogList;
62 import com.eviware.soapui.support.xml.JXEditTextArea;
63 import com.eviware.soapui.support.xml.XmlUtils;
64
65 /***
66 * @author Dain.Nilsson
67 */
68 public class InferredSchemaInspector extends AbstractXmlInspector implements SubmitListener
69 {
70 private SchemaTabs tabs;
71 private RestService service;
72 private RestRequest request;
73 private Handler handler;
74 private Thread thread;
75
76 protected InferredSchemaInspector(RestRequest request)
77 {
78 super("Schema", "Inferred Schema", true, InferredSchemaInspectorFactory.INSPECTOR_ID);
79 service = request.getResource().getService();
80 this.request = request;
81
82 request.addSubmitListener(this);
83 }
84
85 public JComponent getComponent()
86 {
87 if (tabs == null)
88 {
89 tabs = new SchemaTabs();
90 InferredSchemaManager.addPropertyChangeListener(service, tabs);
91 }
92
93 return tabs;
94 }
95
96 @Override
97 public boolean isEnabledFor(EditorView<XmlDocument> view)
98 {
99 return !view.getViewId().equals(RawXmlEditorFactory.VIEW_ID);
100 }
101
102 public void afterSubmit(Submit submit, SubmitContext context)
103 {
104 if (submit.getResponse() == null)
105 return;
106 HttpResponse httpResponse = (HttpResponse) submit.getResponse();
107 String content = httpResponse.getContentAsXml();
108 if (content == null || content.equals("<xml/>"))
109 return;
110 XmlObject xml;
111 try
112 {
113 URL url = httpResponse.getURL();
114 String defaultNamespace = null;
115 if (url != null)
116 {
117 defaultNamespace = url.getProtocol() + "://" + url.getHost();
118 }
119 else
120 {
121 if (httpResponse instanceof JMSResponse)
122 {
123 defaultNamespace = ((JMSResponse) httpResponse).getEndpoint();
124 }
125 }
126 XmlOptions options = new XmlOptions().setLoadSubstituteNamespaces(Collections.singletonMap("",
127 defaultNamespace));
128 xml = XmlObject.Factory.parse(content, options);
129 }
130 catch (XmlException e)
131 {
132 e.printStackTrace();
133 return;
134 }
135 if (!submit.getStatus().equals(Status.CANCELED)
136 && !InferredSchemaManager.getInferredSchema(service).validate(xml))
137 {
138 setTitle("Schema (conflicts)");
139 if (thread != null && thread.isAlive())
140 {
141 handler.kill();
142 try
143 {
144 thread.join();
145 }
146 catch (InterruptedException e)
147 {
148 e.printStackTrace();
149 }
150 }
151 handler = new Handler(tabs, xml);
152 thread = new Thread(handler);
153 thread.start();
154 }
155 }
156
157 public boolean beforeSubmit(Submit submit, SubmitContext context)
158 {
159 return true;
160 }
161
162 public void release()
163 {
164 InferredSchemaManager.removePropertyChangeListener(service, tabs);
165 }
166
167 @SuppressWarnings("serial")
168 private class SchemaTabs extends JTabbedPane implements ActionListener, PropertyChangeListener,
169 ListSelectionListener
170 {
171 private JLogList log;
172 private JPanel conflicts;
173 private JButton resolveButton;
174 private JCheckBox auto;
175 private Handler handler;
176 private JXEditTextArea xsd;
177 private JList schemaList;
178 public static final String AUTO_INFER_SCHEMAS = "AutoInferSchemas";
179 public static final String NO_NAMESPACE = "<no namespace>";
180
181 public SchemaTabs()
182 {
183 super();
184 conflicts = new JPanel();
185 conflicts.setLayout(new BorderLayout());
186 auto = new JCheckBox("Auto-Resolve");
187 auto.setToolTipText("Automatically modify inferred schema from received Responses");
188 auto.setOpaque(false);
189 UISupport.setFixedSize(auto, 120, 20);
190 XmlBeansSettingsImpl settings = getRequest().getSettings();
191 if (settings.isSet(AUTO_INFER_SCHEMAS))
192 {
193 auto.setSelected(settings.getBoolean(AUTO_INFER_SCHEMAS));
194 }
195 auto.addItemListener(new ItemListener()
196 {
197 public void itemStateChanged(ItemEvent e)
198 {
199 getRequest().getSettings().setBoolean(AUTO_INFER_SCHEMAS, auto.isSelected());
200 }
201 });
202 resolveButton = new JButton("Resolve conflicts");
203 resolveButton.setEnabled(false);
204 resolveButton.setActionCommand("resolve");
205 resolveButton.addActionListener(this);
206
207 JXToolBar toolbar = UISupport.createToolbar();
208 toolbar.addFixed(auto);
209 toolbar.addFixed(resolveButton);
210
211 log = new JLogList("Schema log");
212 conflicts.add(toolbar, BorderLayout.NORTH);
213 conflicts.add(log, BorderLayout.CENTER);
214 addTab("Conflicts", conflicts);
215
216 schemaList = new JList(InferredSchemaManager.getInferredSchema(service).getNamespaces());
217 schemaList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
218 schemaList.addListSelectionListener(this);
219
220 toolbar = UISupport.createToolbar();
221 toolbar.addFixed(UISupport.createToolbarButton(new RemoveNamespaceAction()));
222
223 JPanel listPanel = new JPanel();
224 listPanel.setLayout(new BorderLayout());
225 listPanel.add(toolbar, BorderLayout.NORTH);
226 listPanel.add(new JScrollPane(schemaList), BorderLayout.CENTER);
227 xsd = JXEditTextArea.createXmlEditor(false);
228 xsd.setEditable(false);
229 update();
230 addTab("Schemas", new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, listPanel, new JScrollPane(xsd)));
231 }
232
233 public synchronized boolean awaitButton(Handler handler)
234 {
235 if (auto.isSelected())
236 return false;
237 resolveButton.setEnabled(true);
238 this.handler = handler;
239 return true;
240 }
241
242 public synchronized void actionPerformed(ActionEvent e)
243 {
244 if (e.getActionCommand().equals("resolve"))
245 {
246 resolveButton.setEnabled(false);
247 handler.go();
248 }
249 else if (e.getActionCommand().equals("save"))
250 {
251 InferredSchemaManager.save(service);
252 }
253 }
254
255 public void propertyChange(PropertyChangeEvent evt)
256 {
257 update();
258 }
259
260 public void update()
261 {
262 String[] namespaces = InferredSchemaManager.getInferredSchema(service).getNamespaces();
263 for (int i = 0; i < namespaces.length; i++)
264 if (namespaces[i].equals(""))
265 namespaces[i] = NO_NAMESPACE;
266 schemaList.setListData(namespaces);
267 if (schemaList.isSelectionEmpty())
268 {
269 xsd.setText("");
270 }
271 else
272 {
273 xsd.setText(XmlUtils.prettyPrintXml(InferredSchemaManager.getInferredSchema(service).getXsdForNamespace(
274 (String) schemaList.getSelectedValue())));
275 xsd.scrollTo(0, 0);
276 }
277 }
278
279 public void logln(String line)
280 {
281 log.addLine(line);
282 }
283
284 public void valueChanged(ListSelectionEvent e)
285 {
286 if (e.getValueIsAdjusting() == false)
287 {
288 if (!schemaList.isSelectionEmpty())
289 {
290 String namespace = (String) schemaList.getSelectedValue();
291 if (namespace.equals(NO_NAMESPACE))
292 namespace = "";
293 xsd.setText(XmlUtils.prettyPrintXml(InferredSchemaManager.getInferredSchema(service).getXsdForNamespace(
294 namespace)));
295 xsd.scrollTo(0, 0);
296 }
297 }
298 }
299
300 private class RemoveNamespaceAction extends AbstractAction
301 {
302 private RemoveNamespaceAction()
303 {
304 putValue(SMALL_ICON, UISupport.createImageIcon("/remove_property.gif"));
305 putValue(SHORT_DESCRIPTION, "Removes selected inferred namespace definition");
306 }
307
308 public void actionPerformed(ActionEvent e)
309 {
310 if (!schemaList.isSelectionEmpty())
311 {
312 String ns = (String) schemaList.getSelectedValue();
313 if (UISupport.confirm("Remove inferred namespace '" + ns + "'?", "Remove namespace"))
314 {
315 if (ns.equals(NO_NAMESPACE))
316 ns = "";
317 InferredSchemaManager.deleteNamespace(service, ns);
318 }
319 }
320 }
321 }
322 }
323
324 public class Handler implements ConflictHandler, Runnable
325 {
326 private SchemaTabs panel;
327 private XmlObject xml;
328 private List<String> paths;
329 private boolean yesToAll = false;
330 private boolean kill = false;
331
332 public Handler(SchemaTabs panel, XmlObject xml)
333 {
334 this.panel = panel;
335 this.xml = xml;
336 paths = new ArrayList<String>();
337 }
338
339 public synchronized void run()
340 {
341 try
342 {
343 if (panel.awaitButton(this))
344 {
345 try
346 {
347 wait();
348 }
349 catch (InterruptedException e)
350 {
351 e.printStackTrace();
352 }
353 }
354 else
355 yesToAll = true;
356 if (kill)
357 return;
358 InferredSchemaManager.getInferredSchema(service).learningValidate(xml, this);
359 panel.update();
360 setTitle("Schema");
361 InferredSchemaManager.save(service);
362 }
363 catch (XmlException e)
364 {
365 setTitle("Schema (invalid)");
366 }
367 }
368
369 public synchronized void go()
370 {
371 notifyAll();
372 }
373
374 public synchronized void kill()
375 {
376 kill = true;
377 notifyAll();
378 }
379
380 public boolean callback(Event event, Type type, QName name, String path, String message)
381 {
382
383
384 StringBuilder s = new StringBuilder(message).append(" ");
385 if (event == Event.CREATION)
386 {
387 paths.add(path);
388 s.append("Create ");
389 }
390 else if (event == Event.MODIFICATION)
391 {
392 paths.add(path);
393 s.append("Modify ");
394 }
395 if (type == Type.ELEMENT)
396 s.append("element '");
397 else if (type == Type.ATTRIBUTE)
398 s.append("attribute '");
399 else if (type == Type.TYPE)
400 s.append("type '");
401 s.append(name.getLocalPart()).append("' in namespace '").append(name.getNamespaceURI()).append("' at path ")
402 .append(path).append("?");
403 if (!yesToAll)
404 {
405 int choice = UISupport.yesYesToAllOrNo(s.toString(), "Conflict");
406 if (choice == 2)
407 {
408 panel.logln(s.append(" FAIL").toString());
409 return false;
410 }
411 else if (choice == 1)
412 yesToAll = true;
413 }
414 panel.logln(s.append(" OK").toString());
415 return true;
416 }
417
418 }
419
420 public RestRequest getRequest()
421 {
422 return request;
423 }
424 }