View Javadoc

1   /*
2    *  soapUI, copyright (C) 2004-2010 eviware.com
3    *
4    *  soapUI is free software; you can redistribute it and/or modify it under the
5    *  terms of version 2.1 of the GNU Lesser General Public License as published by
6    *  the Free Software Foundation.
7    *
8    *  soapUI is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
9    *  even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
10   *  See the GNU Lesser General Public License for more details at gnu.org.
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 			// if(paths.contains(path)) return true;
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 }