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 super.release();
165
166 request.removeSubmitListener( this );
167 InferredSchemaManager.removePropertyChangeListener( service, tabs );
168
169 if( thread != null && thread.isAlive() )
170 {
171 handler.kill();
172 }
173 }
174
175 @SuppressWarnings( "serial" )
176 private class SchemaTabs extends JTabbedPane implements ActionListener, PropertyChangeListener,
177 ListSelectionListener
178 {
179 private JLogList log;
180 private JPanel conflicts;
181 private JButton resolveButton;
182 private JCheckBox auto;
183 private Handler handler;
184 private JXEditTextArea xsd;
185 private JList schemaList;
186 public static final String AUTO_INFER_SCHEMAS = "AutoInferSchemas";
187 public static final String NO_NAMESPACE = "<no namespace>";
188
189 public SchemaTabs()
190 {
191 super();
192 conflicts = new JPanel();
193 conflicts.setLayout( new BorderLayout() );
194 auto = new JCheckBox( "Auto-Resolve" );
195 auto.setToolTipText( "Automatically modify inferred schema from received Responses" );
196 auto.setOpaque( false );
197 UISupport.setFixedSize( auto, 120, 20 );
198 XmlBeansSettingsImpl settings = getRequest().getSettings();
199 if( settings.isSet( AUTO_INFER_SCHEMAS ) )
200 {
201 auto.setSelected( settings.getBoolean( AUTO_INFER_SCHEMAS ) );
202 }
203 auto.addItemListener( new ItemListener()
204 {
205 public void itemStateChanged( ItemEvent e )
206 {
207 getRequest().getSettings().setBoolean( AUTO_INFER_SCHEMAS, auto.isSelected() );
208 }
209 } );
210 resolveButton = new JButton( "Resolve conflicts" );
211 resolveButton.setEnabled( false );
212 resolveButton.setActionCommand( "resolve" );
213 resolveButton.addActionListener( this );
214
215 JXToolBar toolbar = UISupport.createToolbar();
216 toolbar.addFixed( auto );
217 toolbar.addFixed( resolveButton );
218
219 log = new JLogList( "Schema log" );
220 conflicts.add( toolbar, BorderLayout.NORTH );
221 conflicts.add( log, BorderLayout.CENTER );
222 addTab( "Conflicts", conflicts );
223
224 schemaList = new JList( InferredSchemaManager.getInferredSchema( service ).getNamespaces() );
225 schemaList.setSelectionMode( ListSelectionModel.SINGLE_SELECTION );
226 schemaList.addListSelectionListener( this );
227
228 toolbar = UISupport.createToolbar();
229 toolbar.addFixed( UISupport.createToolbarButton( new RemoveNamespaceAction() ) );
230
231 JPanel listPanel = new JPanel();
232 listPanel.setLayout( new BorderLayout() );
233 listPanel.add( toolbar, BorderLayout.NORTH );
234 listPanel.add( new JScrollPane( schemaList ), BorderLayout.CENTER );
235 xsd = JXEditTextArea.createXmlEditor( false );
236 xsd.setEditable( false );
237 update();
238 addTab( "Schemas", new JSplitPane( JSplitPane.HORIZONTAL_SPLIT, listPanel, new JScrollPane( xsd ) ) );
239 }
240
241 public synchronized boolean awaitButton( Handler handler )
242 {
243 if( auto.isSelected() )
244 return false;
245 resolveButton.setEnabled( true );
246 this.handler = handler;
247 return true;
248 }
249
250 public synchronized void actionPerformed( ActionEvent e )
251 {
252 if( e.getActionCommand().equals( "resolve" ) )
253 {
254 resolveButton.setEnabled( false );
255 handler.go();
256 }
257 else if( e.getActionCommand().equals( "save" ) )
258 {
259 InferredSchemaManager.save( service );
260 }
261 }
262
263 public void propertyChange( PropertyChangeEvent evt )
264 {
265 update();
266 }
267
268 public void update()
269 {
270 String[] namespaces = InferredSchemaManager.getInferredSchema( service ).getNamespaces();
271 for( int i = 0; i < namespaces.length; i++ )
272 if( namespaces[i].equals( "" ) )
273 namespaces[i] = NO_NAMESPACE;
274 schemaList.setListData( namespaces );
275 if( schemaList.isSelectionEmpty() )
276 {
277 xsd.setText( "" );
278 }
279 else
280 {
281 xsd.setText( XmlUtils.prettyPrintXml( InferredSchemaManager.getInferredSchema( service )
282 .getXsdForNamespace( ( String )schemaList.getSelectedValue() ) ) );
283 xsd.scrollTo( 0, 0 );
284 }
285 }
286
287 public void logln( String line )
288 {
289 log.addLine( line );
290 }
291
292 public void valueChanged( ListSelectionEvent e )
293 {
294 if( e.getValueIsAdjusting() == false )
295 {
296 if( !schemaList.isSelectionEmpty() )
297 {
298 String namespace = ( String )schemaList.getSelectedValue();
299 if( namespace.equals( NO_NAMESPACE ) )
300 namespace = "";
301 xsd.setText( XmlUtils.prettyPrintXml( InferredSchemaManager.getInferredSchema( service )
302 .getXsdForNamespace( namespace ) ) );
303 xsd.scrollTo( 0, 0 );
304 }
305 }
306 }
307
308 private class RemoveNamespaceAction extends AbstractAction
309 {
310 private RemoveNamespaceAction()
311 {
312 putValue( SMALL_ICON, UISupport.createImageIcon( "/remove_property.gif" ) );
313 putValue( SHORT_DESCRIPTION, "Removes selected inferred namespace definition" );
314 }
315
316 public void actionPerformed( ActionEvent e )
317 {
318 if( !schemaList.isSelectionEmpty() )
319 {
320 String ns = ( String )schemaList.getSelectedValue();
321 if( UISupport.confirm( "Remove inferred namespace '" + ns + "'?", "Remove namespace" ) )
322 {
323 if( ns.equals( NO_NAMESPACE ) )
324 ns = "";
325 InferredSchemaManager.deleteNamespace( service, ns );
326 }
327 }
328 }
329 }
330 }
331
332 public class Handler implements ConflictHandler, Runnable
333 {
334 private SchemaTabs panel;
335 private XmlObject xml;
336 private List<String> paths;
337 private boolean yesToAll = false;
338 private boolean kill = false;
339
340 public Handler( SchemaTabs panel, XmlObject xml )
341 {
342 this.panel = panel;
343 this.xml = xml;
344 paths = new ArrayList<String>();
345 }
346
347 public synchronized void run()
348 {
349 try
350 {
351 if( panel.awaitButton( this ) )
352 {
353 try
354 {
355 wait();
356 }
357 catch( InterruptedException e )
358 {
359 e.printStackTrace();
360 }
361 }
362 else
363 yesToAll = true;
364 if( kill )
365 return;
366 InferredSchemaManager.getInferredSchema( service ).learningValidate( xml, this );
367 panel.update();
368 setTitle( "Schema" );
369 InferredSchemaManager.save( service );
370 }
371 catch( XmlException e )
372 {
373 setTitle( "Schema (invalid)" );
374 }
375 }
376
377 public synchronized void go()
378 {
379 notifyAll();
380 }
381
382 public synchronized void kill()
383 {
384 kill = true;
385 notifyAll();
386 }
387
388 public boolean callback( Event event, Type type, QName name, String path, String message )
389 {
390
391
392 StringBuilder s = new StringBuilder( message ).append( " " );
393 if( event == Event.CREATION )
394 {
395 paths.add( path );
396 s.append( "Create " );
397 }
398 else if( event == Event.MODIFICATION )
399 {
400 paths.add( path );
401 s.append( "Modify " );
402 }
403 if( type == Type.ELEMENT )
404 s.append( "element '" );
405 else if( type == Type.ATTRIBUTE )
406 s.append( "attribute '" );
407 else if( type == Type.TYPE )
408 s.append( "type '" );
409 s.append( name.getLocalPart() ).append( "' in namespace '" ).append( name.getNamespaceURI() ).append(
410 "' at path " ).append( path ).append( "?" );
411 if( !yesToAll )
412 {
413 int choice = UISupport.yesYesToAllOrNo( s.toString(), "Conflict" );
414 if( choice == 2 )
415 {
416 panel.logln( s.append( " FAIL" ).toString() );
417 return false;
418 }
419 else if( choice == 1 )
420 yesToAll = true;
421 }
422 panel.logln( s.append( " OK" ).toString() );
423 return true;
424 }
425
426 }
427
428 public RestRequest getRequest()
429 {
430 return request;
431 }
432 }