org.joty.workstation.app.Application Maven / Gradle / Ivy
Show all versions of joty-workstation Show documentation
/*
Copyright (c) 2013-2017, Stefano Pizzocaro. All rights reserved. Use is subject to license terms.
This file is part of Joty 2.0 Workstation.
Joty 2.0 Workstation is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Joty 2.0 Workstation is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Joty 2.0 Workstation. If not, see .
*/
package org.joty.workstation.app;
import java.awt.Component;
import java.awt.Container;
import java.awt.Cursor;
import java.awt.Desktop;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Image;
import java.awt.Point;
import java.awt.Toolkit;
import java.awt.Window;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.ClipboardOwner;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.StringSelection;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DragSource;
import java.awt.dnd.DragSourceContext;
import java.awt.dnd.DragSourceDragEvent;
import java.awt.dnd.DragSourceDropEvent;
import java.awt.dnd.DragSourceEvent;
import java.awt.dnd.DragSourceListener;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowEvent;
import java.awt.image.BufferedImage;
import java.beans.Beans;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.URI;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Random;
import java.util.Set;
import java.util.Stack;
import java.util.Vector;
import javax.jnlp.BasicService;
import javax.jnlp.ServiceManager;
import javax.jnlp.UnavailableServiceException;
import javax.swing.ImageIcon;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.UIManager;
import javax.swing.text.MaskFormatter;
import javax.xml.bind.DatatypeConverter;
import org.joty.access.Accessor;
import org.joty.access.BirtManager;
import org.joty.access.DbManager;
import org.joty.access.Instantiator;
import org.joty.access.Logger;
import org.joty.access.MethodExecutor;
import org.joty.access.PostStatement;
import org.joty.app.Common;
import org.joty.app.JotyApplication;
import org.joty.app.JotyException;
import org.joty.app.LiteralsCollection;
import org.joty.app.LiteralsCollection.LiteralStructParams;
import org.joty.common.ApplMessenger;
import org.joty.common.BasicPostStatement;
import org.joty.common.BasicPostStatement.Item;
import org.joty.common.BasicPostStatement.ReturnedValueItem;
import org.joty.common.CaselessStringKeyMap;
import org.joty.common.ConfigFile;
import org.joty.common.ConfigFile.ConfigException;
import org.joty.common.ErrorCarrier;
import org.joty.common.ICommon;
import org.joty.common.JotyMessenger;
import org.joty.common.JotyTypes;
import org.joty.common.LangLiteralRetCodeMapper;
import org.joty.common.ParamContext;
import org.joty.common.ReportManager;
import org.joty.common.Utilities;
import org.joty.common.Utilities.Stocker;
import org.joty.common.XmlTextEncoder;
import org.joty.data.JotyDate;
import org.joty.data.SearchQueryBuilderFront;
import org.joty.data.WrappedField;
import org.joty.web.AbstractWebClient;
import org.joty.web.AbstractWebClient.DocumentDescriptor;
import org.joty.workstation.authorization.ChangePasswordDialog;
import org.joty.workstation.authorization.LoginDialog;
import org.joty.workstation.authorization.UsersPanel;
import org.joty.workstation.data.JotyDB;
import org.joty.workstation.data.JotyDataBuffer;
import org.joty.workstation.data.WField;
import org.joty.workstation.data.WResultSet;
import org.joty.workstation.gui.AboutDialog;
import org.joty.workstation.gui.AppOptionsDialog;
import org.joty.workstation.gui.DataAccessDialog;
import org.joty.workstation.gui.DataAccessPanel;
import org.joty.workstation.gui.DescrTerm;
import org.joty.workstation.gui.InfoDialog;
import org.joty.workstation.gui.JotyDialog;
import org.joty.workstation.gui.JotyFrame;
import org.joty.workstation.gui.JotyTextField;
import org.joty.workstation.gui.Panel;
import org.joty.workstation.gui.Table.JotyJTable;
import org.joty.workstation.gui.Term;
import org.joty.workstation.gui.TermContainerPanel;
import org.joty.workstation.web.WebClient;
/**
* {@code Application} provides initialization of the main functionalities:
* detects the mode of running (desktop or web), manages the user's
* authentication, builds the asset of the application by loading data from the
* configuration file/s and from the user's home directory, loads the language
* vector depending on the choice made by the user, loads rarely changing data
* into memory from the database and keeps them in {@link LiteralStruct} objects.
*
*
* Furthermore it exposes a large set of methods for presenting the user with framework
* and application messages and for getting simple input, keeps track of the currently
* opened dialogs and of the state of the application main frame.
*
*
* Offers methods for accessing the database server by hiding all is needed to
* manage different assets derived by running in desktop or web mode and in
* 'accessor' or 'non accessor' mode.
*
*/
public class Application implements ClipboardOwner, ApplMessenger, JotyApplication {
/**
* Provides a way to concentrate on the execution body of a transaction
* implementing the exec method.
*/
public abstract class JotyTransaction {
public boolean success;
public JotyTransaction() {
this(false);
}
public JotyTransaction(boolean insideExternalTrans) {
if (!insideExternalTrans)
beginTrans();
try {
success = exec();
if (!insideExternalTrans)
if (success)
commitTrans();
else
rollbackTrans();
} catch (JotyException e) {
success = false;
if (!insideExternalTrans)
try {
rollbackTrans();
} catch (JotyException e1) {}
}
}
public abstract boolean exec() throws JotyException;
}
/**
* {@code LangActionListener} implements the selection of the language. An
* instance of this class is passed to {@code addItemToMenu} method.
*
* @see Application#addItemToMenu(JComponent, String, ActionListener)
*/
class LangActionListener implements ActionListener {
String m_lang;
LangActionListener(String lang) {
m_lang = lang;
}
@Override
public void actionPerformed(ActionEvent evt) {
JCheckBoxMenuItem langItem = (JCheckBoxMenuItem) evt.getSource();
boolean newState = langItem.isSelected();
m_applicationPreferences.put("language", m_lang);
informationMsg(String.format(m_common.jotyLang("OptionAcquired"), m_common.jotyLang("LangChoice")));
langItem.setSelected(!newState);
}
}
public class LiteralStruct extends LiteralsCollection{
public LiteralStruct(JotyMessenger jotyMessangerInstance) {
super(jotyMessangerInstance);
m_termsSet = new HashSet();
}
public Set m_termsSet;
public void updateTerms() {
for (DescrTerm term : m_termsSet)
term.reloadDescrList();
}
}
/**
* It is the invocation target of the proxy class defined in the
* {@code manageMacOSenvironment} method. The proxy is defined as the
* dispatcher of calls to the com.apple.eawt.ApplicationListener, there
* instantiated through reflection. It receives the calls to the listener
* methods and specifically manages the call to the {@code handleAbout}
* method, in order to address the locally defined method {@link
* #showAboutDialog()}. Secondarily it invokes the {@code setHandled} method
* of the {@code ApplicationEvent} object received in {@code args[0]}.
*
* @see #manageMacOSenvironment()
* @see #showAboutDialog()
*
*/
public class MacAppListenerInvocationHandler implements java.lang.reflect.InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getName().equals("handleAbout"))
showAboutDialog();
Method setHandledMethod = args[0].getClass().getDeclaredMethod("setHandled", new Class[] { boolean.class });
setHandledMethod.invoke(args[0], new Object[] { Boolean.valueOf(true) });
return null;
}
}
/**
* An instance of this class is provided as the default
* {@code DragSourceListener} for the JVM in order to have the correct
* behavior of the cursor during dragging operations. For this it uses the
* {@code insideDataDialogs} method to manage also the cursor when it is
* over graphical objects that are not part of the Joty application.
*
* @see Application#insideDataDialogs
*/
class MyDragSourceListener implements DragSourceListener {
private void checkLocation(DragSourceEvent e) {
m_insideDataDialogs = insideDataDialogs(e.getLocation());
}
@Override
public void dragDropEnd(DragSourceDropEvent dsde) {}
@Override
public void dragEnter(DragSourceDragEvent dsde) {
dsde.getDragSourceContext().setCursor(DragSource.DefaultCopyNoDrop);
}
@Override
public void dragExit(DragSourceEvent dse) {
dse.getDragSourceContext().setCursor(DragSource.DefaultCopyNoDrop);
}
@Override
public void dragOver(DragSourceDragEvent dsde) {
checkLocation(dsde);
setCursor(dsde);
}
@Override
public void dropActionChanged(DragSourceDragEvent dsde) {}
private void setCursor(DragSourceDragEvent dsde) {
DragSourceContext context = dsde.getDragSourceContext();
int action = dsde.getDropAction();
if (action == DnDConstants.ACTION_NONE || !m_insideDataDialogs)
context.setCursor(DragSource.DefaultCopyNoDrop);
else
switch (action) {
case DnDConstants.ACTION_MOVE:
context.setCursor(m_dragDrainTriggerOn ? m_drainDropCursor : DragSource.DefaultMoveDrop);
break;
case DnDConstants.ACTION_COPY:
context.setCursor(DragSource.DefaultCopyDrop);
break;
case DnDConstants.ACTION_LINK:
context.setCursor(DragSource.DefaultLinkDrop);
break;
}
if (m_DnDdrainIn && (!m_currDnDsourceIsDrainEnabled || !m_dragDrainTriggerOn))
context.setCursor(DragSource.DefaultCopyNoDrop);
}
}
public interface PasswordValidator {
boolean validate(String password);
}
/**
*
* It is the vehicle for values identified by the selection made by the user
* in the context of the opening of identity selector dialog. The selection
* in general, beyond the id value of the selected identity, identifies also
* fields of interest owning to the record just isolated. This set of fields
* takes definition during the call of
* {@code TermContainerPanel.acquireSelectedValueFrom}. It uses convenient
* data structures for keeping association between database fields and their
* values, furthermore it maps, when needed, TermContainerPanel Term-s on
* the corresponding database fields
*
* @see TermContainerPanel#acquireSelectedValueFrom
*/
public class ValuesContainer {
Vector fields;
CaselessStringKeyMap terms2fieldsMap;
CaselessStringKeyMap values;
public ValuesContainer() {
fields = new Vector();
values = new CaselessStringKeyMap(Application.this);
values.setOverWritable();
terms2fieldsMap = new CaselessStringKeyMap(Application.this);
}
public void add(String termName, String fieldName) {
fields.add(fieldName);
terms2fieldsMap.put(termName, fieldName);
}
public void clear() {
fields.removeAllElements();
values.clear();
terms2fieldsMap.clear();
}
public Vector fieldNames() {
return fields;
}
public WrappedField getValue(String termName) {
return values.get(terms2fieldsMap.get(termName));
}
public boolean isEmpty() {
return fields.size() == 0;
}
public void putValue(String fieldName, WrappedField value) {
values.put(fieldName, value);
}
}
public class ClauseContribution implements SearchQueryBuilderFront.TermContributor{
@Override
public String sqlValueExpr(WrappedField term) {
return ((Term) term).sqlValueExpr();
}
@Override
public String getOperator(WrappedField term, String matchOperator) {
JComboBox opCombo = ((Term) term).m_operatorsCombo;
String operator;
if (opCombo == null)
operator = matchOperator;
else {
String cmbOperator = (String) opCombo.getItemAt(opCombo.getSelectedIndex());
operator = cmbOperator.length() == 0 ? matchOperator : cmbOperator;
}
return operator;
}
}
private static boolean m_exclamationIconMissing;
private static boolean m_exclamationIconChecked;
public static JMenuItem addItemToMenu(JComponent menu, String string, ActionListener actionListener) {
return addItemToMenu(menu, string, actionListener, null);
}
public static JMenuItem addItemToMenu(JComponent menu, String string, ActionListener actionListener, JMenuItem instancedItem) {
JMenuItem menuItem = instancedItem == null ? new JMenuItem() : instancedItem;
menuItem.setText(string);
menu.add(menuItem);
if (actionListener != null)
menuItem.addActionListener(actionListener);
return menuItem;
}
public static JMenuItem addItemToMenu(JComponent menu, String string, JMenuItem instancedItem) {
return addItemToMenu(menu, string, null, instancedItem);
}
public static JMenuItem addLangItemToMenu(JComponent menu, String string, ActionListener actionListener) {
return addItemToMenu(menu, m_app.m_common.jotyLang(string), actionListener);
}
public static JMenuItem addLangItemToMenu(JComponent menu, String string, ActionListener actionListener, JMenuItem instancedItem) {
return addItemToMenu(menu, m_app.m_common.jotyLang(string), actionListener, instancedItem);
}
public static JMenuItem addLangItemToMenu(JComponent menu, String string, JMenuItem instancedItem) {
return addLangItemToMenu(menu, string, null, instancedItem);
}
public static void appInit() {
setFrontMostContainer(null);
}
public static boolean beginWaitPossibleCursor() {
return m_app == null ? false : m_app.setWaitCursor(true);
}
public static void center(Window window, int width, int height) {
if (!m_settingBound) {
m_settingBound = true;
Point handle = centeredWindowHandle(width, height);
window.setLocation(handle.x, handle.y);
m_settingBound = false;
}
}
/**
* Uses its overridden flavor to get the top-left corner for centering,
* within the screen, the window the dimensions of which are passed as
* parameters.
*
* @return top-left corner coordinates.
*/
public static Point centeredWindowHandle(int width, int height) {
Toolkit tk = Toolkit.getDefaultToolkit();
Dimension screenSize = tk.getScreenSize();
return centeredWindowHandle(width, height, screenSize.width, screenSize.height);
}
/**
* Returns the top-left corner position of a sized window located inside a
* sized container such that the window is centered within the container
*
* @return the Point object containing the coordinates that the top-left
* corner
*
*/
public static Point centeredWindowHandle(int windowWidth, int windowHeight, int containerWidth, int containerHeght) {
Point handle = new Point();
handle.x = (containerWidth - windowWidth) / 2;
handle.y = (containerHeght - windowHeight) / 2;
return handle;
}
/**
* Checks if design mode is on and creates the log file if it still doesn't
* exist. It also sets the framework with a reference to the container
* possibly used in communication to the user during the design
* session.
*/
public static void checkWBE(Container container) {
if (Beans.isDesignTime()) {
if (m_app == null) {
m_app = new Application();
Application.m_debug = false;
}
try {
Files.deleteIfExists(Paths.get(m_app.m_JotyDesignLog));
} catch (IOException e) {
}
Logger.appendToLog(m_app.m_JotyDesignLog, "", false, null, true);
if (container != null)
setFrontMostContainer(container.getParent());
}
}
public static void endWaitPossibleCursor() {
if (m_app != null)
m_app.setWaitCursor(false);
}
private static String formatMessage(String text, Object[] objects) {
return String.format(text, objects);
}
/**
* Returns the container {@code JotyDialog}
*
* @param component
* the operating component
* @return the JotyDialog
* @see JotyDialog
*/
public static JotyDialog getDialog(Component component) {
Container retVal = component.getParent();
while (retVal != null && !(retVal instanceof JotyDialog))
retVal = retVal.getParent();
return (JotyDialog) (retVal == null ? m_app.m_definingDialog : retVal);
}
/**
* See its override
* {@link #getInputFromUser(Container , String , Object[], Object)}
*/
public static String getInputFromUser(Container container, String text) {
return getInputFromUser(container, text, null, null);
}
/**
* Opens the standard input dialog to get one choice from the user. The
* choice can be made by simple text or by a value selected from a pull-down
* list.
*
* @param container
* the container in which the input will open
* @param text
* the text presented to the user
* @param selectionValues
* (optional) an array of string composing the list of values
* that will be presented in the pull down-list
* @param initialSelectionValue
* (optional) the default selection for the pull-down list
* @return the text inputed by the user. If the choice was made by the
* pull-down list, it is the text corresponding to the item
* selected.
*
* @see Application#getWrapAction(Container)
* @see JotyFrame#setAsFloatingBar(boolean)
*/
public static String getInputFromUser(Container container, String text, Object[] selectionValues, Object initialSelectionValue) {
String retVal = null;
boolean wrapAction = getWrapAction(container);
Container actualContainer = wrapAction && !m_app.m_macOs ? m_app.m_frame : container instanceof JotyDialog ? ((JotyDialog) container).getContentPane() : container;
ImageIcon icon = m_app.imageIcon("Userinput.png");
if (actualContainer instanceof JotyFrame)
actualContainer = ((JotyFrame) actualContainer).getContentPane();
retVal = (String) JOptionPane.showInputDialog(actualContainer, text, joptionPaneAppName(), JOptionPane.PLAIN_MESSAGE, icon, selectionValues, initialSelectionValue);
if (wrapAction)
m_app.m_frame.setAsFloatingBar(true);
return retVal;
}
/**
* It is the prologue for any message to the user, for determining whether,
* before opening the message, the main frame is to be restored to the
* centered position and to its default size.
*
* @param container
* the container in which the message will live
* @return true if the main frame has been restored
*
* @see JotyFrame#setAsFloatingBar(boolean)
*/
private static boolean getWrapAction(Container container) {
boolean wrapAction = (container == m_app.m_frame || container == null && !(m_frontMostContainer instanceof JotyDialog)) && m_app.m_mntmSetFrame != null && m_app.m_mntmSetFrame.isSelected();
if (wrapAction)
m_app.m_frame.setAsFloatingBar(false);
return wrapAction;
}
public static void informationMsg(Container container, String text) {
message(JOptionPane.INFORMATION_MESSAGE, container, text, 0, null);
}
public static void informationMsg(String text) {
informationMsg(null, text);
}
/**
* Presents informational message to he user by means of an optionally
* parameterized format string.
*
* @param text
* the format string
* @param objects
* the parameters for the string
*
*/
public static void informationMsg(String text, Object[] objects) {
informationMsg(formatMessage(text, objects));
}
/**
* This method is used for presenting a message to the developer of the Joty
* application. It is thought to help the testing activity.
*
* @param object
* the class object of interest in the context (typically
* 'this').
* @param text
* the message body.
*/
public void JotyMsg(Object object, String text) {
m_app.m_common.resetRemoteTransactionBuilding();
if (!m_exclamationIconMissing && ! m_exclamationIconChecked) {
m_exclamationIconChecked = true;
try {
if (m_app.getClass().getClassLoader().getResource("res/Exclamation.png") == null)
m_exclamationIconMissing = true;
} catch (Throwable th) {
m_exclamationIconMissing = true;
}
}
warningMsg(m_app.onTopDialog() != null && m_app.onTopDialog().isModal() ? m_frontMostContainer : m_app.m_frame, (object == null ? "" : (object.getClass().getName() + " : " + "")) + text, "Joty");
}
public static void langInformationMsg(String literal) {
langInformationMsg(literal, null);
}
/**
* Like {@link #informationMsg(String , Object[])} but, instead of text, it
* accepts the literal that identifies the actual text within the
* {@code jotyLang.xml} file.
*
* @param literal
* the identifying literal.
* @param objects
* (optional) if not null it is the list of the parameters for
* the text format template, as the actual text can be.
*/
public static void langInformationMsg(String literal, Object[] objects) {
informationMsg(formatMessage(Application.m_common.jotyLang(literal), objects));
}
public static void langWarningMsg(String literal) {
langWarningMsg(literal, null);
}
/**
* Like {@link #langInformationMsg(String , Object[])} but for a warning
* message.
*
*/
public static void langWarningMsg(String literal, Object[] objects) {
warningMsg(formatMessage(Application.m_common.jotyLang(literal), objects));
}
public static boolean langYesNoQuestion(String literal) {
return langYesNoQuestion(literal, null);
}
public static boolean langYesNoQuestion(String literal, Container container) {
return langYesNoQuestion(literal, container, null);
}
/**
* Like {@link #langInformationMsg(String , Object[])} but for presenting a
* question and getting an answer from the user.
*
*/
public static boolean langYesNoQuestion(String literal, Container container, Object[] objects) {
m_askingToQuit = literal.compareTo("WantExitApp") == 0;
boolean retVal = yesNoQuestion(formatMessage(Application.m_common.jotyLang(literal), objects), container);
if (m_askingToQuit)
m_askingToQuit = false;
return retVal;
}
private static String joptionPaneAppName() {
return m_app.m_name + (System.getProperty("os.name").toLowerCase().compareTo("linux") == 0 ? " ." : "");
}
/**
* It is the core method for messages to the user made by the framework. It
* relies on the Java Swing class {@code JOptionPane}. It invokes
* {@code getWrapAction} to eventually restore temporarily the main frame to
* its default asset in order to present the message in the context of the
* overall application.
*
* @return zero a part from the case in which it serves calls made by
* {@link #yesNoQuestion(String)} method and its callers. In this
* case it returns what returned by
* {@code JOptionPane.showConfirmDialog}.
*
* @see Application#getWrapAction(Container)
* @see JotyFrame#setAsFloatingBar(boolean)
*/
private static int message(int type, Container container, String text, int option, String caption) {
boolean wrapAction = getWrapAction(container);
int retVal = 0;
String appName = joptionPaneAppName();
Container context = container != null ? container : (m_frontMostContainer instanceof JotyDialog ? m_frontMostContainer : m_app.m_frame);
switch (type) {
case JOptionPane.QUESTION_MESSAGE:
retVal = JOptionPane.showConfirmDialog(context, text, appName, option, type, m_app.imageIcon("Question.png"));
break;
case JOptionPane.INFORMATION_MESSAGE:
JOptionPane.showMessageDialog(context, text, appName, type, m_app.imageIcon("Information.png"));
break;
case JOptionPane.WARNING_MESSAGE:
JOptionPane.showMessageDialog(context,
text + (m_exclamationIconMissing ? " + Missing : res/Exclamation.png !" : ""),
caption,
type,
m_exclamationIconMissing ? null : m_app.imageIcon("Exclamation.png"));
break;
}
if (wrapAction && (!m_askingToQuit || retVal != 0))
m_app.m_frame.setAsFloatingBar(true);
return retVal;
}
public static void warningMsg(Container container, String text, String caption) {
message(JOptionPane.WARNING_MESSAGE, container, text, 0, caption);
}
public static void warningMsg(String text) {
warningMsg(text, m_app == null ? "Joty" : joptionPaneAppName());
}
public static void warningMsg(String text, Object[] objects) {
warningMsg(formatMessage(text, objects));
}
public static void warningMsg(String text, String caption) {
warningMsg(null, text, caption == null ? joptionPaneAppName() : caption);
}
public static int yesNoCancelQuestion(Container container, String text, int option) {
return message(JOptionPane.QUESTION_MESSAGE, container, text, option, null);
}
public static int yesNoCancelQuestion(String text) {
return yesNoCancelQuestion(text, JOptionPane.YES_NO_CANCEL_OPTION);
}
public static int yesNoCancelQuestion(String text, int option) {
return yesNoCancelQuestion(null, text, option);
}
public static boolean yesNoQuestion(String text) {
return yesNoQuestion(text, (Container) null);
}
public static boolean yesNoQuestion(String text, Container container) {
return yesNoCancelQuestion(container, text, JOptionPane.YES_NO_OPTION) == 0;
}
public static boolean yesNoQuestion(String text, Object[] objects) {
return yesNoQuestion(formatMessage(text, objects));
}
/**
* Saves the bytes received as parameter in a temporary file. Then open this
* file;
*
* @param bytes
* the source buffer of bytes
* @param fileExt
* the file name extension
*
* @see org.joty.app.Common#saveBytesAsFile(byte[], String, String, boolean)
* @see #openUri(String, boolean)
*/
public static void openDocumentFromBytes(byte[] bytes, String fileExt) {
m_app.beginWaitCursor();
String tempDir = System.getProperty("java.io.tmpdir");
String fileName = "JotyTemp" + String.valueOf(m_app.m_random.nextInt()) + "." + fileExt;
m_common.saveBytesAsFile(bytes, tempDir, fileName, true);
Application.m_app.openUri(tempDir + "/" + fileName, false);
m_app.endWaitCursor();
}
/**
* Opens a resource from the local file system or from the web.
*
* @param uri
* the uri of the resource.
* @param webLocator
* if true the resource is considered coming from the web and the
* default Internet browser is used.
*/
public void openUri(String uri, boolean webLocator) {
String exceptionMsg = null;
Desktop desktop = null;
if (!Desktop.isDesktopSupported())
exceptionMsg = "Desktop is not supported !";
if (exceptionMsg == null) {
desktop = Desktop.getDesktop();
if (!desktop.isSupported(Desktop.Action.BROWSE))
exceptionMsg = "Desktop doesn't support the browse action !";
}
if (exceptionMsg == null) {
try {
if (webLocator)
desktop.browse(new URI(uri));
else
desktop.open(new File(uri));
} catch (Exception e) {
Logger.exceptionToHostLog(e);
}
} else {
Logger.appendToHostLog(exceptionMsg);
}
}
public static void setFrontMostContainer(Container container) {
m_frontMostContainer = container == null ? (m_app == null ? null : m_app.m_frame.getContentPane()) : container;
}
public static void setLocation(Window window, Point location) {
m_settingBound = true;
window.setLocation(location);
m_settingBound = false;
}
public boolean setWaitCursor(boolean set) {
boolean oldIsWaitCursor = m_frontMostContainer.getCursor().equals(waitCursor);
m_frontMostContainer.setCursor(set ? waitCursor : defCursor);
return oldIsWaitCursor;
}
public static void writeOnDeskLog(String descr, String valStr) {
if (m_deskTracing && m_tracing) {
String outStr = descr + " : " + valStr;
Logger.appendToHostLog(outStr);
if (m_debug) {
String msg = outStr + "\n\nDo you want entering debugging ?\n(Click on 'Cancel' not to be asked again)";
if (yesNoCancelQuestion(msg) != JOptionPane.NO_OPTION)
if (yesNoQuestion("Do you want to turn logging off (until an activation is reached on code execution) ?"))
m_tracing = false;
}
}
}
public PasswordValidator m_passwordValidator;
public boolean m_webMode;
public boolean m_accessorMode;
public JotyFrame m_frame;
public String m_name;
public JotyDB m_db;
public String m_versionString;
public String m_startPath;
/**
* Associates a name of a LiteralStruct object with a map that holds
* association between id values and LiteralStruct object names. It is used,
* indeed, by a {@code GridTerm} class, to support the identification
* of a LiteralStruct instance depending on the literal selection made on a
* 'first level' LiteralStruct object.
*/
public CaselessStringKeyMap> m_2L_literalMap;
public CaselessStringKeyMap m_openedDialogs;
public Stack m_activationStack;
public static Application m_app;
public boolean m_oldLoggingActivation;
public WebClient m_webClient;
public static Clipboard m_clipboard;
public JotyDialog m_definingDialog;
public Random m_random;
public ConfigFile m_configExtension;
public DbManager m_dbManager;
public Accessor m_accessor;
public ErrorCarrier m_errorCarrier;
public Vector m_iconImages;
public boolean m_committedClose;
public JMenuBar m_menuBar;
public boolean m_alreadyCertDeletionOffered;
public String m_JotyDesignLog = System.getProperty("user.home") + "/JotyDesignLog.log";
public String m_userHomeDataDir = "JotyData";
public String m_ksPath = "/sec";
public String m_ksFileName = "ks";
public String m_JotyDeskLog;
public static boolean m_deskTracing;
public static boolean m_debug = true;
public ReportManager m_reportManager;
public Stocker m_userRoles;
public JMenu m_windowsMenu;
public JCheckBoxMenuItem m_mntmSetFrame;
public JCheckBoxMenuItem m_mntmEnableTooltips;
public Map m_windowsLocations;
public Map m_dialogMainSortInfos;
public Map m_applicationPreferences;
public String m_openDlgCurrentDir;
public JotyJTable m_currDnDjtable;
public boolean m_currDnDsourceIsDrainEnabled;
public boolean m_DnDdrainIn;
public boolean m_currDnDcanDrop;
public boolean m_dragDrainTriggerOn;
public boolean m_dragDrainDropped;
public boolean m_insideDataDialogs;
public boolean m_dialogOpeningAsValueSelector;
public long m_justSelectedValue;
public ValuesContainer m_valuesContainer;
public CaselessStringKeyMap> m_refreshMap;
public String m_msgDataDefExpectedAsEntry = "Data definitions are expected to be defined as entries in the accessor !";
public ParamContext m_paramContext;
public boolean m_exitByMenu;
/** If true the {@code Accessor} object lives within the Joty Server instead of in the Application object. */
public boolean m_remoteAccessor;
public boolean m_dialogsDesignedOnMac;
public Vector m_returnedValues;
public Dimension m_screenSize;
public ParamContext m_webTransPrmContext;
public boolean m_dialogsAreToBeForcedOnTop;
public ImageIcon m_appLogo;
public ImageIcon m_jotyLogo;
public String m_applicationLink;
public String m_author;
public String m_copyrightYears;
public boolean m_macOs;
public int m_applicationDefaultFontSize;
private int m_returnedValuesAvailablePos;
private InfoDialog m_infoDialog;
private DragSourceListener m_dragSourceListener = new MyDragSourceListener();
private Cursor m_drainDropCursor;
private String m_dbmsSessionPreset;
private CaselessStringKeyMap m_reportsRolePerms;
protected String m_configuredUser;
protected static Object m_macOSXapp;
protected JMenu m_toolsMenu;
protected JMenu m_fileMenu;
protected JMenu m_viewMenu;
protected JMenu m_authMenu;
protected JMenu m_helpMenu;
int m_fullOptions;
/** holds the mapping between {@code JotyDataBuffer} objects and their names */
CaselessStringKeyMap m_setMap;
String m_dataSourceTerm;
String m_serverUrl;
long m_userID;
boolean m_xmlLogging;
boolean m_bChargeIcon;
boolean m_dbAuthOnly;
Utilities m_utils;
int m_iReTryCount;
int m_iReTryMax;
MethodExecutor m_methodExecutor;
final String m_windowsLocationsFile = "WindowsLocations";
final String m_dialogMainSortInfosFile = "DialogMainSortInfos";
final String m_applicationPreferencesFile = "ApplicationPreferences";
private BirtManager m_BirtManager;
/**
* see {@link LangLiteralRetCodeMapper}
*/
private LangLiteralRetCodeMapper m_langLiteralRetCodeMapper;
private String m_defaultBirtRender;
private String m_workStationFontSize;
public boolean m_connected;
static boolean m_tracing;
public static final Cursor defCursor = Cursor.getDefaultCursor();
public static final Cursor waitCursor = Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR);
public static final String INTEGER_MASK_VALID_CHARS = "0123456789";
public static final String MSG_ListNotDef = "Main ScrollListPane Object not defined !";
public static boolean m_settingBound = false;
public static Container m_frontMostContainer;
public static Common m_common;
/**
* It keeps the detected intention of the user to exit. Its truth is used
* for avoiding main frame restoring during exiting.
*/
private static boolean m_askingToQuit;
/**
* It is the only constructor. Part of the initialization of the Joty
* application happens here.
*
* It performs the detection of the mode of running (Web/desktop client) and
* creates operational java objects (among them the ReportManager) and creates the main frame and the menu
* bar.
*
* It detects the platform OS.
*
* If the running mode is desktop client it performs the jdbc initialization.
* Further initialization occurs in the {@link #init(String, String, String, String)} method.
*
* @see ReportManager
*/
public Application() {
Utilities.setMessanger(this);
m_common = new Common(this);
m_app = this;
Logger.m_app = m_app;
Logger.appInit();
Logger.setHostLogName("JotyDeskLog", "JotyLogs");
m_db = new JotyDB();
getClipboard();
m_common.m_configuration = new ConfigFile(Application.this, "Joty.xml", false, null, true);
m_webMode = m_common.m_configuration.m_document == null;
m_common.m_commitExit = false;
m_common.m_literalStructMap = new CaselessStringKeyMap(this);
m_2L_literalMap = new CaselessStringKeyMap>(this);
m_setMap = new CaselessStringKeyMap(Application.this);
m_openedDialogs = new CaselessStringKeyMap(Application.this);
m_activationStack = new Stack();
m_reportsRolePerms = new CaselessStringKeyMap(Application.this);
m_userRoles = Utilities.m_me.new Stocker();
m_valuesContainer = new ValuesContainer();
m_refreshMap = new CaselessStringKeyMap>(Application.this);
m_paramContext = new ParamContext(this);
m_tracing = false;
if (!m_webMode) {
try {
DriverManager.registerDriver((Driver) Class.forName(m_common.getConfStr("jdbcDriverClass")).newInstance());
} catch (Exception e) {
jotyMessage("jdbc initialization failure !");
Logger.exceptionToHostLog(e);
}
}
m_xmlLogging = false;
m_startPath = "";
m_configuredUser = "";
m_iReTryMax = 3;
m_random = new Random();
m_webClient = null;
m_committedClose = false;
m_alreadyCertDeletionOffered = false;
m_passwordValidator = null;
m_reportManager = new ReportManager();
String os = System.getProperty("os.name").toLowerCase();
m_dialogsAreToBeForcedOnTop = os.compareTo("linux") == 0;
m_macOs = os.compareTo("mac os x") == 0;
if (m_macOs)
System.setProperty("apple.laf.useScreenMenuBar", "true");
m_frame = new JotyFrame();
m_menuBar = new JMenuBar();
m_frame.setJMenuBar(m_menuBar);
m_frame.setDefaultRectDim(1024, 640);
DragSource.getDefaultDragSource().addDragSourceListener(m_dragSourceListener);
if (!designTime())
m_drainDropCursor = Toolkit.getDefaultToolkit().createCustomCursor(image("trashcan.gif"), new Point(15, 0), "trashcan");
m_common.m_dataReLoad = false;
m_screenSize = Toolkit.getDefaultToolkit().getScreenSize();
}
/**
* may be overridden for implementing actions upon initialization failure.
*/
protected void abortJobs() {}
/**
* Creates a {@code BasicPostStatement} object hosting the name and the
* parameters of a method that will be invoked outside of the client
* context.
*
* Typically it prepares a server-side call of the method, in the web
* running mode of the client, whichever is the task of the method (either a
* wrapper for a dbms store procedure or anything else to be executed
* 'far').
*
* the definition of the method must exists in the application
* {@code Accessor} object.
*
* @param method
* the method name
* @param returnedValuePos
* the position of the first returned value (among output
* parameters and method return value) in the array of the values
* returned by the invocation context - the {@code Accessor}
* object - living either on the client side - in desktop client
* mode - or on the Joty server side - in web mode - The position
* is intended within the entirety of the set of values returned
* in a transaction in which the method invocation participates.
* @param returnedValuesQty
* integer value that, if positive, represents the quantity of
* the output parameters values, if negative, represents again
* the same quantity plus the method return value
* @return the created BasicPostStatement object
*
* @see Accessor
* @see BasicPostStatement
*/
public BasicPostStatement accessorMethodPostStatement(String method, Integer returnedValuePos, Integer returnedValuesQty) {
if (!m_webMode && !inDbTransaction())
clearReturnStatus();
BasicPostStatement postStatement = new BasicPostStatement(this);
postStatement.setMethod(method, returnedValuePos, returnedValuesQty);
return postStatement;
}
public boolean accessorExecute(String literal) {
return accessorExecute(literal, m_paramContext);
}
/**
* creates a {@code BasicPostStatement} hosting an sql statement and a
* substitution literal (for dbms names obfuscation purposes) and executes
* it in terms of the Joty technology.
*
* @param literal
* the literal the substitution value of which is define in the
* {@code Accessor} object of the application.
* @param paramContext
* (optional) the {@code ParamContext} object (if missing the
* {@code m_paramContext} member will be used.
* @return true in web mode and the actual execution result in desktop
* client mode. mode.
*/
public boolean accessorExecute(String literal, ParamContext paramContext) {
return m_db.executeSQL(literal, null, accessorPostStatement(null, -1, null, paramContext));
}
/**
* It is the core method for the creation of a {@code BasicPostStatement} object.
*
* It exposes parameters for identifying a specific Accessor definition
* context.
*
* If {@code dialog} is not null and the DataAccesorDialog object supports
* several modes along with be opened the BasicPostStatement.m_method of the
* creating object is assigned the value of the string value for the current
* opening mode
*
* (note that on the client side, dialog opening modes are referred as
* object instance values - typically they are enumeration values - on the
* server side, however, they are referred as string values: here the
* conversion takes place).
*
* In a Joty transaction this method is called typically several times. In
* web mode it keeps track of the current ParamContext object in the
* {@link Application#m_webTransPrmContext} member; on every call the method
* checks if ParamContext object changes and at each new different object
* encountered the method resets the dirty status of the
* {@code ContextParameter} objects contained in it, such that the
* ParamContext object is ready for new value assignments to its
* ContextParam objects and the WebClient will compose, in the JotyRequest,
* only the context parameters that has been assigned during the current
* Joty transaction definition session.
*
*
* @param dialog
* the {@code DataAccessDialog} object the Accessor context of
* which is to be addressed (actually a DialogDataDef object).
* @param panelIdx
* the ordinal position of the {@code DataAccessPanel} object
* the Accessor sub-context of which is to be addressed (actually
* a DataDef object) (it must be equal to -1 if {@code dialog} is
* null).
* @param termName
* the name of the {@code GridTerm} object within the
* DataAccessPanel, the Accessor sub-context of which is to be
* addressed (actually a DataDef object), or the name of a
* Accessor context-less {@code DataDef} object (see
* {@link Accessor#m_statementDefs}) hosting an sql statement,
* or, even, a literal residing in
* {@link Accessor#m_literalSubsts} map that will be used for
* dbms table name obfuscation acting on sql statement specified
* later, during further definition of the BasicPostStatement object.
*
* @param prmParamContext
* the {@code ParamContext} object the {@code ContextParameter}
* objects of which need to be built in the creating object as
* {@code Item} objects. These items will be used by the Accessor
* object (see
* {@link Accessor#setPostStatement(PostStatement, boolean)}) to
* re-create there the ParamContext object, no matter which its living
* location could be (either the server or the client side).
*
* Actually currently an hybrid solution is adopted that seems to
* support successfully the most needs: within a transaction
* definition many ParamContext instances may be identified by
* means of calls to this method, however, on the Accessor side a
* merge of all ContextParam objects of all ParanContext objects
* encountered is performed upon an only ParamContext map there
* managed.
* @return the created PostStatement object
*
* @see org.joty.access.PostStatement
* @see org.joty.common.BasicPostStatement.Item
* @see DataAccessDialog
* @see DataAccessPanel
* @see org.joty.access.Accessor
* @see org.joty.access.Accessor.DataDef
* @see org.joty.access.Accessor.DialogDataDef
* @see org.joty.server.JotyServer#dbExecute()
*/
public PostStatement accessorPostStatement(JotyDialog dialog, int panelIdx, String termName, ParamContext prmParamContext) {
return accessorPostStatement(dialog == null ? null : dialog.getClass().getName(),
panelIdx,
termName,
prmParamContext == null ? (dialog == null ? null : dialog.m_callContext) : prmParamContext,
dialog != null && dialog.getMode() != null ? dialog.getMode().toString() : null);
}
/**
* It does the job for {@code accessorPostStatement(JotyDialog, int, String, ParamContext)}
* but allows also to be called without the living instance of the {@code JotyDialog}, addressing
* freely a resource within the implemented {@code Accessor} object, indeed.
* @see accessorPostStatement(JotyDialog, int, String, ParamContext)}
*/
public PostStatement accessorPostStatement(String dialogName, int panelIdx, String termName, ParamContext paramContext, String mode) {
if (!m_webMode && !inDbTransaction())
clearReturnStatus();
PostStatement postStatement = new PostStatement(this);
postStatement.setDataDefCoordinates(dialogName, panelIdx, termName);
if (mode != null)
postStatement.m_method = mode;
if (paramContext != null) {
if (m_webMode && m_webClient.buildingRemoteTransaction() || !m_webMode && inDbTransaction()) {
if (paramContext != m_webTransPrmContext) {
if (m_webTransPrmContext == null)
m_webTransPrmContext = paramContext;
paramContext.setDirty(false);
}
}
postStatement.addItemsFromParamContext(paramContext);
}
return postStatement;
}
/**
* Creates a {@code PostStatement} object for a literal substitution to be
* operated on an sql statement provided later to the object
*
* @param literalName the literal for the substitution
* @return the object created
*
* @see #openAccessorSubstWResultSet
*/
public PostStatement createLiteralSubstPostStatement(String literalName) {
return accessorPostStatement(null, -1, literalName, null);
}
/**
* One of the methods providing the duality of the framework behavior, it
* returns the currently available position within the vector of long values
* returned to the caller, vector that holds the invoked method return value
* or the value of one of its output parameters.
*/
@Override
public int returnedValuesAvailablePos() {
return m_webMode ? m_webClient.returnedValuesAvailablePos() : m_returnedValuesAvailablePos + 1;
}
/**
* Opens a WResultSet object through a BasicPostStatement object in which may be
* programmed a literal substitution to occur for the place holder of the
* database table name conveniently prepared in the selector sql statement.
*
* @param tabLiteral
* the table name or the literal used by the {@code Accessor}
* object to return the actual table name.
* @param sql
* the selector sql statement
*
* @return true on success
* @see WResultSet
* @see #openDbWResultSetByPostStatement(PostStatement)
*/
public WResultSet openAccessorSubstWResultSet(String tabLiteral, String sql) {
PostStatement postStatement = createLiteralSubstPostStatement(tabLiteral);
postStatement.m_sql = sql;
return openDbWResultSetByPostStatement(postStatement);
}
/** see {@link #openAccessorWResultSet(String, Panel, ParamContext)} */
public WResultSet openAccessorWResultSet(String literal) {
return openAccessorWResultSet(literal, null, null);
}
/** see {@link #openAccessorWResultSet(String, Panel, ParamContext)} */
public WResultSet openAccessorWResultSet(String literal, Panel panel) {
return openAccessorWResultSet(literal, panel, null);
}
/** see {@link #openAccessorWResultSet(String, Panel, ParamContext)} */
public WResultSet openAccessorWResultSet(String literal, ParamContext paramContext) {
return openAccessorWResultSet(literal, null, paramContext);
}
/**
* Flexible method to open a {@code WresultSet} object my means of the
* {@code Accessor} object by the use of a {@code BasicPostStatement} instance
* conveniently prepared.
*
* @param literal
* possible name of the statement the body of which is stored
* inside the Accessor object
* @param panel
* possible {@code Panel} object used either for identifying the
* parameters context or for locating the main statements for its
* access to the database
* @param paramContext
* parameters context explicitly passed by the caller.
* @return the {@code WResultSet} object
*
* @see #openDbWResultSetByPostStatement
* @see Panel#createContextPostStatement()
* @see #accessorPostStatement(JotyDialog, int, String, ParamContext)
*
*/
public WResultSet openAccessorWResultSet(String literal, Panel panel, ParamContext paramContext) {
PostStatement postStatement = panel != null ? panel.createContextPostStatement() : accessorPostStatement(null, -1, literal, paramContext);
postStatement.m_sql = literal;
return openDbWResultSetByPostStatement(postStatement);
}
/**
* Provides different behaviors for opening a WResutSet object depending on
* the execution context being in Accessor mode and being in web mode. It is
* one of the core methods by which the framework can offer the invariance
* of interface to the developer, whether the application runs in web mode
* or not.
*
* @param postStatement
* the prepared PostStatement object
* @return the instantiated object
*/
public WResultSet openDbWResultSetByPostStatement(PostStatement postStatement) {
String query = null;
boolean byPostStatement = m_accessorMode || m_common.m_applicationScopeAccessorMode;
if (m_accessor != null) {
if (byPostStatement) {
m_accessor.setPostStatement(postStatement);
query = m_accessor.getQueryFromPostStatement();
}
}
if (!byPostStatement)
query = postStatement.m_sql;
if (m_errorCarrier.m_exceptionMsg.length() > 0)
jotyWarning(m_errorCarrier.m_exceptionMsg.toString());
WResultSet rs = new WResultSet(null, query);
if (rs.open(m_accessor != null && m_webMode || !byPostStatement ? null : postStatement))
return rs;
else
return null;
}
/**
* It is a dual behavior method: it manages user authentication on the dbms
* in both the Joty modes.
*
* Then it delegates the {@code Accessor} object for performing the
* following tasks: - manages validation of credentials accessing a map
* stored in the database table - checks if password is expired - checks if
* password must be changed
*
* @return true if authentication completes successfully.
* @throws SQLException
*
* @see #webAuthentication()
* @see #dbmsAuthentication()
* @see #verifyLogin()
* @see #doFurtherJobs()
*
*/
boolean acquireConnection() throws SQLException {
beginWaitCursor();
boolean retVal = true;
if (m_webMode) {
retVal = webAuthentication();
retVal = retVal && m_common.m_webSessionOn;
} else
retVal = dbmsAuthentication();
endWaitCursor();
if (retVal) {
verificationProlog();
retVal = verifyLogin();
if (retVal)
retVal = doFurtherJobs();
else
m_common.m_webSessionOn = false;
}
return retVal;
}
/**
* Enable a role to launch a report.
*
* @param reportName
* @param roleName
*
* @see #launchReport(String, String)
*/
protected void enableRoleToReport(String reportName, String roleName) {
if (m_reportsRolePerms.get(reportName) == null)
m_reportsRolePerms.put(reportName, Utilities.m_me.new Stocker());
m_reportsRolePerms.get(reportName).add(roleName);
}
public void addToolTipRowToComponent(JComponent comp, String text) {
if (m_toolTipsEnabled()) {
String currentText = comp.getToolTipText();
String newText;
newText = currentText == null ? "
" : currentText.substring(0, currentText.indexOf("")) + "
";
comp.setToolTipText(newText + text + "