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