package org.pentaho.reporting.ui.datasources.cda;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dialog;
import java.awt.Frame;
import java.awt.HeadlessException;
import java.awt.event.ActionEvent;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.Collections;
import java.util.Map;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.table.TableModel;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.auth.AuthScope;
import org.apache.commons.httpclient.cookie.CookiePolicy;
import org.apache.commons.httpclient.methods.GetMethod;
import org.pentaho.reporting.engine.classic.core.AbstractReportDefinition;
import org.pentaho.reporting.engine.classic.core.ClassicEngineBoot;
import org.pentaho.reporting.engine.classic.core.DataFactory;
import org.pentaho.reporting.engine.classic.core.DefaultReportEnvironment;
import org.pentaho.reporting.engine.classic.core.MasterReport;
import org.pentaho.reporting.engine.classic.core.ReportDataFactoryException;
import org.pentaho.reporting.engine.classic.core.ReportEnvironment;
import org.pentaho.reporting.engine.classic.core.designtime.DesignTimeContext;
import org.pentaho.reporting.engine.classic.core.designtime.DesignTimeUtil;
import org.pentaho.reporting.engine.classic.core.modules.gui.commonswing.ExceptionDialog;
import org.pentaho.reporting.engine.classic.core.modules.gui.commonswing.SwingUtil;
import org.pentaho.reporting.engine.classic.core.util.ReportParameterValues;
import org.pentaho.reporting.engine.classic.core.util.TypedTableModel;
import org.pentaho.reporting.engine.classic.extensions.datasources.cda.CdaDataFactory;
import org.pentaho.reporting.engine.classic.extensions.datasources.cda.CdaResponseParser;
import org.pentaho.reporting.libraries.base.config.Configuration;
import org.pentaho.reporting.libraries.base.util.StringUtils;
import org.pentaho.reporting.libraries.designtime.swing.CommonDialog;
import org.pentaho.reporting.libraries.designtime.swing.LibSwingUtil;
import org.pentaho.reporting.libraries.designtime.swing.SmartComboBox;
import org.pentaho.reporting.libraries.designtime.swing.VerticalLayout;
import org.pentaho.reporting.libraries.designtime.swing.background.CancelEvent;
import org.pentaho.reporting.libraries.designtime.swing.background.DataPreviewDialog;
import org.pentaho.reporting.libraries.designtime.swing.background.PreviewWorker;
import org.pentaho.reporting.libraries.formula.util.URLEncoder;
import org.pentaho.reporting.libraries.resourceloader.ResourceKey;
/**
* Todo: Document me!
* <p/>
* Date: 17.12.10
* Time: 19:00
*
* @author Thomas Morgner.
*/
public class CdaDataSourceEditor extends CommonDialog
{
private class FetchAction extends AbstractAction
{
private FetchAction()
{
putValue(Action.NAME, "Fetch");
}
public void actionPerformed(final ActionEvent e)
{
try
{
final TypedTableModel model = fetchData("listQueries", Collections.EMPTY_MAP);
queriesTableModel.clear();
for (int i = 0; i < model.getRowCount(); i++)
{
final String query = (String) model.getValueAt(i, 0);
final String name = (String) model.getValueAt(i, 1);
if (StringUtils.isEmpty(name))
{
queriesTableModel.addQuery("Anonymous Query #" + i, query);
}
else
queriesTableModel.addQuery(name, query);
}
}
catch (ReportDataFactoryException e1)
{
e1.printStackTrace();
}
}
}
private class PreviewAction extends AbstractAction
{
private PreviewAction()
{
putValue(Action.NAME, "Preview");
}
public void actionPerformed(final ActionEvent aEvt)
{
final int selectedRow = queriesTable.getSelectedRow();
if (selectedRow == -1)
{
return;
}
try
{
final CdaDataFactory dataFactory = produceDataFactory();
final AbstractReportDefinition report = context.getReport();
final MasterReport masterReport = DesignTimeUtil.getMasterReport(report);
final Configuration configuration;
final ResourceKey contentBase;
final ReportEnvironment reportEnvironment;
if (masterReport == null)
{
contentBase = null;
configuration = ClassicEngineBoot.getInstance().getGlobalConfig();
reportEnvironment = new DefaultReportEnvironment(configuration);
}
else
{
contentBase = masterReport.getContentBase();
configuration = masterReport.getConfiguration();
reportEnvironment = masterReport.getReportEnvironment();
}
dataFactory.initialize(configuration,
report.getResourceManager(), contentBase, MasterReport.computeAndInitResourceBundleFactory
(report.getResourceBundleFactory(), reportEnvironment));
final DataPreviewDialog previewDialog = new DataPreviewDialog(CdaDataSourceEditor.this);
final CdaPreviewWorker worker = new CdaPreviewWorker(dataFactory, queriesTableModel.getName(selectedRow));
previewDialog.showData(worker);
final ReportDataFactoryException factoryException = worker.getException();
if (factoryException != null)
{
ExceptionDialog.showExceptionDialog(CdaDataSourceEditor.this, "Error",
"An Error Occured during preview", factoryException);
}
}
catch (Exception e)
{
ExceptionDialog.showExceptionDialog(CdaDataSourceEditor.this, "Error",
"An Error Occured during preview", e);
}
}
}
private static class CdaPreviewWorker implements PreviewWorker
{
private CdaDataFactory dataFactory;
private TableModel resultTableModel;
private ReportDataFactoryException exception;
private String query;
private CdaPreviewWorker(final CdaDataFactory dataFactory,
final String query)
{
if (dataFactory == null)
{
throw new NullPointerException();
}
this.query = query;
this.dataFactory = dataFactory;
}
public ReportDataFactoryException getException()
{
return exception;
}
public TableModel getResultTableModel()
{
return resultTableModel;
}
public void close()
{
}
/**
* Requests that the thread stop processing as soon as possible.
*/
public void cancelProcessing(final CancelEvent event)
{
dataFactory.cancelRunningQuery();
}
/**
* When an object implementing interface <code>Runnable</code> is used
* to create a thread, starting the thread causes the object's
* <code>run</code> method to be called in that separately executing
* thread.
* <p/>
* The general contract of the method <code>run</code> is that it may
* take any action whatsoever.
*
* @see Thread#run()
*/
public void run()
{
try
{
dataFactory.open();
resultTableModel = dataFactory.queryData(query, new ReportParameterValues());
dataFactory.close();
}
catch (ReportDataFactoryException e)
{
exception = e;
}
}
}
private JTextField baseUrl;
private JComboBox baseUrlField;
private JTextField solution;
private JTextField path;
private JTextField file;
private JTextField username;
private JTextField password;
private QueriesTableModel queriesTableModel;
private GetMethod httpCall;
private HttpClient client;
private DesignTimeContext context;
private JTable queriesTable;
public CdaDataSourceEditor(final DesignTimeContext context)
{
init(context);
}
public CdaDataSourceEditor(final DesignTimeContext context, final Frame owner)
throws HeadlessException
{
super(owner);
init(context);
}
public CdaDataSourceEditor(final DesignTimeContext context, final Dialog owner)
throws HeadlessException
{
super(owner);
init(context);
}
private void init(final DesignTimeContext context)
{
this.context = context;
this.queriesTableModel = new QueriesTableModel();
queriesTable = new JTable(queriesTableModel);
baseUrl = new JTextField();
baseUrlField = new SmartComboBox();
baseUrlField.setEditable(true);
final DefaultComboBoxModel comboBoxModel = new DefaultComboBoxModel(context.getDataSchemaModel().getColumnNames());
comboBoxModel.insertElementAt(null, 0);
comboBoxModel.setSelectedItem(null);
baseUrlField.setModel(comboBoxModel);
solution = new JTextField();
path = new JTextField();
file = new JTextField();
username = new JTextField();
password = new JTextField();
super.init();
LibSwingUtil.centerDialogInParent(this);
}
protected Component createContentPane()
{
final JPanel fetchQueriesPanel = new JPanel();
fetchQueriesPanel.setLayout(new BorderLayout());
fetchQueriesPanel.add(new JLabel("Fetch Queries"), BorderLayout.CENTER);
fetchQueriesPanel.add(new JButton(new FetchAction()), BorderLayout.EAST);
final JPanel previewPanel = new JPanel(new BorderLayout());
previewPanel.add(new JButton(new PreviewAction()), BorderLayout.EAST);
final JPanel panel = new JPanel();
panel.setLayout(new VerticalLayout(5, VerticalLayout.BOTH));
panel.add(new JLabel("BaseURL"));
panel.add(baseUrl);
panel.add(new JLabel("BaseURL-Field"));
panel.add(baseUrlField);
panel.add(new JLabel("Username"));
panel.add(username);
panel.add(new JLabel("Password"));
panel.add(password);
panel.add(new JLabel("Solution"));
panel.add(solution);
panel.add(new JLabel("Path"));
panel.add(path);
panel.add(new JLabel("File"));
panel.add(file);
panel.add(fetchQueriesPanel);
final JPanel cpanel = new JPanel();
cpanel.setLayout(new BorderLayout());
cpanel.add(panel, BorderLayout.NORTH);
cpanel.add(new JScrollPane(queriesTable), BorderLayout.CENTER);
cpanel.add(previewPanel, BorderLayout.SOUTH);
return cpanel;
}
public DataFactory performConfiguration(final CdaDataFactory input, final String queryName)
{
if (input != null)
{
baseUrl.setText(input.getBaseUrl());
baseUrlField.setSelectedItem(input.getBaseUrlField());
password.setText(input.getPassword());
username.setText(input.getUsername());
file.setText(input.getFile());
solution.setText(input.getSolution());
path.setText(input.getPath());
queriesTableModel.clear();
final String[] queryNames = input.getQueryNames();
for (int i = 0; i < queryNames.length; i++)
{
final String name = queryNames[i];
queriesTableModel.addQuery(name, input.getQuery(name));
}
}
if (performEdit() == false)
{
return null;
}
return produceDataFactory();
}
private CdaDataFactory produceDataFactory()
{
final CdaDataFactory dataFactory = new CdaDataFactory();
dataFactory.setBaseUrl(baseUrl.getText());
dataFactory.setBaseUrlField((String) baseUrlField.getSelectedItem());
dataFactory.setPassword(password.getText());
dataFactory.setUsername(username.getText());
dataFactory.setFile(file.getText());
dataFactory.setPath(path.getText());
dataFactory.setSolution(solution.getText());
for (int i = 0; i < queriesTableModel.getRowCount(); i++)
{
dataFactory.setQuery(queriesTableModel.getName(i), queriesTableModel.getQuery(i));
}
return dataFactory;
}
private TypedTableModel fetchData(final String method,
final Map<String, String> extraParameter) throws ReportDataFactoryException
{
if (StringUtils.isEmpty(baseUrl.getText(), true))
{
throw new ReportDataFactoryException("Base URL is null");
}
try
{
final StringBuilder url = new StringBuilder();
url.append(baseUrl.getText());
url.append("/content/cda/");
url.append(method);
url.append("?");
url.append("outputType=xml");
url.append("&solution=");
url.append(encodeParameter(solution.getText()));
url.append("&path=");
url.append(encodeParameter(path.getText()));
url.append("&file=");
url.append(encodeParameter(file.getText()));
for (final Map.Entry<String, String> entry : extraParameter.entrySet())
{
final String key = encodeParameter(entry.getKey());
if (StringUtils.isEmpty(key))
{
continue;
}
url.append("&");
url.append(key);
url.append("=");
url.append(encodeParameter(entry.getValue()));
}
httpCall = new GetMethod(url.toString());
final HttpClient client = getHttpClient();
final int status = client.executeMethod(httpCall);
if (status != 200)
{
throw new ReportDataFactoryException("Failed to retrieve data: " + httpCall.getStatusLine());
}
final InputStream responseBody = httpCall.getResponseBodyAsStream();
return CdaResponseParser.performParse(responseBody);
}
catch (UnsupportedEncodingException use)
{
throw new ReportDataFactoryException("Failed to encode parameter", use);
}
catch (Exception e)
{
throw new ReportDataFactoryException("Failed to send request", e);
}
finally
{
httpCall = null;
}
}
private HttpClient getHttpClient()
{
if (client == null)
{
client = new HttpClient();
client.getParams().setCookiePolicy(CookiePolicy.BROWSER_COMPATIBILITY);
client.getParams().setAuthenticationPreemptive(true);
}
client.getState().setCredentials(AuthScope.ANY, CdaDataFactory.getCredentials(username.getText(), password.getText()));
return client;
}
private String getURLEncoding()
{
return ClassicEngineBoot.getInstance().getGlobalConfig().getConfigProperty
("org.pentaho.reporting.engine.classic.core.URLEncoding");
}
private String encodeParameter(final String value) throws UnsupportedEncodingException
{
if (StringUtils.isEmpty(value))
{
return "";
}
return URLEncoder.encode(value, getURLEncoding());
}
}