0

When I typed in my question, similar questions came up, like this one [ How can I communicate Servlet from Swing ], but I looked at it, and didn't find the answer I'm looking for, so here is my question.

I'm developing a web app for internal call center, it uses a Java application [ CCT : call center technology ] to call asterisk server to make calls, this CCT has lots of servlets to generate html web pages so caller agents can access web pages and make calls. In order to better develop and debug, I added a Swing GUI panel to it, so when it runs on my development PC, it will open a window with lots of buttons, each represents a caller agent with their setting info, so I can click and look at the details. It runs fine and I've copies the code to a test server, when it runs there I can remote into it and see the Swing panel with JButtons representing each caller agent.

But the problem is, when this Java app runs on production server, it will be run as a Windows service [ so I was told by my boss ], and there will be no display, so the Swing panel won't be seen, therefore I thought about Java web Start, with it I can have a web page with a "WebStartLaunchButton" so when I click that button it will let me download a Jar file which will start a Swing App. But then I realize the Jar file will be downloaded onto my local PC, and run from the PC, it will have no knowledge of the servlet setting/info, it is independent, it has nothing to do with the call center web app sevlet, and won't be able to show caller agents info. Am I right, is there a way to have a Swing app run on the server and see [ have access to ] it from my PC ?

Here are more details, from the servlet there is a class like this :

public class DialerService extends Service implements Runnable
{
  private boolean running = false;
  private Thread thread;

  private CallManager cm = null;
  private AgentDialListManager adlm = null;            // <-- My Swing program
  private DataAccessManager dam = null;
  ...

  public void run()
  {
    adlm = new AgentDialListManager();                 // <-- My Swing program
    ...
  }
  ...

  private CallData getNextDial(SessionManager sm, SessionData session)
  {
    CallData nextDial = null;

    AgentData agent = session.getAgentData();
    if (agent != null)
    {
      SystemSettingsData settings = dam.getSystemSettings();
      if (settings.isNextDialBufferOn())
      {
        nextDial = adlm.getNextNumber(dam,session);   // <-- My Swing program : Get a number from self-managed agent dial list
      }
      ...
    }
  }
  ...

}

Here is my main Swing program [ other related classes not shown ] :

package com.amerisave.cct.call;

import com.amerisave.cct.data.DataAccessManager;
import com.amerisave.cct.session.AgentData;
import com.amerisave.cct.session.SessionData;
import com.orderlysoftware.orderlycalls.OrderlyCalls;
...
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.JTextArea;
import javax.swing.SwingConstants;
...

public class AgentDialListManager extends JPanel implements Runnable
{
  public static final long serialVersionUID = 26362862L;
  static Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
  static int W = 1136, H = 800, updateInterval = 1000;
  HashMap<Integer,AgentDialListRunner> agentDialListMap = null;
  HashMap<Integer,JFrame> Agent_Panel_Frame_Map = null;
  AgentData agent;
  Insets anInset = new Insets(0,0,0,0);
  AgentDialListRunner adlr;
  JPanel agentPanel = new JPanel(),Log_Panel=new JPanel(new FlowLayout(1,0,0));
...
  static JTabbedPane Tabbed_Pane;
  static JTextArea Log_TextArea = new JTextArea(Get_System_Properties());
  String hostAddress,hostName,dialerServerInfo="";
  List dialerServerList = null;
  Thread agentDialListManagerThread;

  AgentDialListManager()
  {
    if (!agentDialListManager_Exists_B)
    {
      agentDialListMap = new HashMap();
      Agent_Panel_Frame_Map = new HashMap();
      shutdown = false;
      if (showGUI_B) Init();
      start();
      agentDialListManager_Exists_B = true;
    }
  }

  void Init()
  {
    setLayout(new FlowLayout(1,0,1));
    Tabbed_Pane=new JTabbedPane();
    Tabbed_Pane.setDoubleBuffered(true);
    Tabbed_Pane.setPreferredSize(new Dimension(W - 20,H - 42));
    Tabbed_Pane.setAutoscrolls(true);
    Tabbed_Pane.setFont(new Font("Times New Roman",0,16));
    Tabbed_Pane.setForeground(new Color(0,0,230));
    add(Tabbed_Pane);

    agentPanel.setBorder(new TitledBorder(new EtchedBorder(EtchedBorder.RAISED,Color.white,new Color(134,134,134))," Agents",TitledBorder.CENTER,TitledBorder.TOP,new Font("Times New Roman",0,15),new Color(0,60,198)));
    agentPanel.setPreferredSize(new Dimension(W - 20,H - 42));
    Tabbed_Pane.addTab("Agents",null,agentPanel,"");

    JPanel bottomPanel = new JPanel(new FlowLayout(1,0,0));

    JPanel skipRatioPanel = new JPanel(new FlowLayout(1,0,0));
    skipRatioPanel.setBorder(new EtchedBorder(EtchedBorder.RAISED,new Color(0,188,250),new Color(134,134,134)));
    skipRatioPanel.setPreferredSize(new Dimension(330,30));
    bottomPanel.add(skipRatioPanel);

...

    Tabbed_Pane.addTab("Log",null,Log_Panel,"");

    setPreferredSize(new Dimension(W,H));

    SwingUtilities.invokeLater(new Runnable()
    {
      public void run()
      {
        createAndShowGUI();
      }
    });
  }

  CallData getNextNumber(DataAccessManager dam,SessionData session)
  {
    CallData nextDial = null;
    if (agentDialListMap != null)
    {
      agent = session.getAgentData();
      int nEmployeeId = agent.nEmployeeId;

      if (!agentDialListMap.containsKey(nEmployeeId))
      {
        adlr = new AgentDialListRunner(nEmployeeId,dam);
        agentDialListMap.put(nEmployeeId,adlr);
        agentCount = agentDialListMap.size();
        agentPanel.setBorder(new TitledBorder(new EtchedBorder(EtchedBorder.RAISED,Color.white,new Color(134,134,134)),agentCount + " Agent" + (agentCount > 1 ? "s" : ""),TitledBorder.CENTER,TitledBorder.TOP,new Font("Times New Roman",0,15),new Color(0,60,198)));
        addAgent(adlr.id,adlr,agent);
      }
      adlr = agentDialListMap.get(nEmployeeId);
      nextDial = adlr.getNextNumber();
    }
    return nextDial;
  }

  void createAndShowGUI()
  {
    JFrame frame = new JFrame("AgentDialListManager [  Java = " + System.getProperty("java.version") + "  |  jetty.home = "+System.getProperty("jetty.home")+"  ]   dialerServerInfo = " + dialerServerInfo+"   [ hostAddress = " + hostAddress+"   hostName = " + hostName+" ]");
    frame.add(this);

    frame.addWindowListener(new WindowAdapter()
    {
      public void windowActivated(WindowEvent e)  { }
      public void windowClosed(WindowEvent e) { }
      public void windowClosing(WindowEvent e) { if (runFromMain_B) System.exit(0); }
      public void windowDeactivated(WindowEvent e) { }
      public void windowDeiconified(WindowEvent e) { repaint(); }
      public void windowGainedFocus(WindowEvent e) { repaint(); }
      public void windowIconified(WindowEvent e) { }
      public void windowLostFocus(WindowEvent e) { }
      public void windowOpening(WindowEvent e) { repaint(); }
      public void windowOpened(WindowEvent e) { }
      public void windowResized(WindowEvent e) { repaint(); }
      public void windowStateChanged(WindowEvent e) { repaint(); }
    });

    frame.pack();
    if (runFromMain_B) frame.setLocationRelativeTo(null);
    else frame.setBounds(displayCount==3?-1728:0,displayCount==3?0:26,this.getWidth(),this.getHeight() + 43);
    frame.setVisible(true);
  }

  public void run()
  {
    try
    {
      while (!shutdown)
      {
        Thread.sleep(sleepTime);
        AgentDialListRunner.averageTimeToMoreNumbers = 0;
        for (Map.Entry<Integer,AgentDialListRunner> mapElement : agentDialListMap.entrySet())
        {
          int nEmployeeId = (int)mapElement.getKey();
          AgentDialListRunner adlr = (AgentDialListRunner)mapElement.getValue();
          AgentDialListRunner.averageTimeToMoreNumbers += adlr.averageTimeInSecondsBeforeGettingMoreNumbers;
        }
        agentCount = agentDialListMap.size();
        if (agentCount > 0)
        {
          AgentDialListRunner.averageTimeToMoreNumbers /= agentCount;
       averageTimeToMoreNumbersValueLabel.setText(AgentDialListRunner.averageTimeToMoreNumbers + " Sec.");
        }
      }
    }
    catch (Exception e)
    {
      log.error("Exception in runner thread:", e);
    }
  }

  public void start()
  {
    stop();
    if (agentDialListManagerThread == null)
    {
      agentDialListManagerThread = new Thread(this);
      agentDialListManagerThread.setPriority(Thread.NORM_PRIORITY);
      agentDialListManagerThread.start();
    }
  }

  public void stop()
  {
    if (agentDialListManagerThread != null) agentDialListManagerThread = null;
  }
...

  public static void main(String[] args)
  {
    runFromMain_B = true;
    new AgentDialListManager();
  }
}

The GUI when the server starts looks like the following, on the left side there is a window showing all active caller agents [ only showing one on my development PC ], when the agent button is clicked, another panel shows up on the right side displaying the numbers in the buffer for the agent to call, mouse over each number, customer info for that number will be displayed in tooltip of the button :

enter image description here

Hope these details will make the question more clear.

Frank
  • 30,590
  • 58
  • 161
  • 244
  • The swing app will never run on the server. It will always run on the client, so the question boils down to "how can a swing app communicate with a server", which a lot of the duplicates deal with. Webstart won't give you any advantages, as it's just a mechanism to get the jar file. You'll have to either build a communication channel between the server and the swing app, or a web interface on the application itself. – Kayaman Dec 11 '19 at 15:26
  • Thanks for the insight, but there would be a lot of work involved for a swing app to talk to a servlet, including getting related servlet class objects [ run-time instances ], the best way is to have it run from the application on the server and the client just use a browser to look at and interact with it [ clicking buttons to see agent details ] like when I run it on the test server. So I question the statement "swing app will never run on the server", is it accurate ? Because it worked on the test server, it is a server none the less, right ? – Frank Dec 11 '19 at 16:13
  • I don't understand what you mean by"getting servlet class objects". You would communicate with the server through HTTP or some other protocol, there would be no sharing of classes. Yes, technically you can run a swing app on a server, but since they are usually headless, it's not like it's the best choice around. I still don't see any choice between either making the swing app communicate with the server in a way chosen by you *or* building the interface into the webapp itself, unless it has something very swing specific. Of course if you already have the swing app, it's a full rewrite. – Kayaman Dec 11 '19 at 19:15
  • I said "getting servlet class objects" because the Swing app is actually part of the servlet, it creates a buffer of phone numbers for each caller agent, and supply each agent with next number to dial and if the buffer gets low, it will call DB to get more numbers, in the Swing app, each number is a JButton, when clicked it will show more info of the customer, and in order to get the numbers for the buffer, a lot of related classes needs to be included into the Swing app so it can have access to the properties of those classes, therefore the Swing app IS NOT independent from the servlet app. – Frank Dec 11 '19 at 19:49
  • The Swing app is independent, because Swing apps don't run in servlet containers. So even if they were running on the same computer, they'd at least be running in their own JVMs. At that point you need some form of communication, whether it be through IPC, sockets, database or any other scheme, since you can't just call `myServlet.myMethod()` from your swing app. Technically **it is** possible to run a GUI program on a headless server, e.g. in *NIX you can export `DISPLAY` to your own computer. But from a maintenance POV I would definitely build a web UI instead. – Kayaman Dec 12 '19 at 07:50
  • I wonder if anyone has any experience with "Webswing" ? From what I read on their site [ https://www.webswing.org/company ], it seems to be the solution I was looking for ? What do you think, can it solve my problem ? – Frank Dec 12 '19 at 14:04
  • I don't know. I still don't understand your whole situation, so maybe. It's also possible you'll end up with 2 servers running, and they still can't communicate with each other, because you haven't managed to explain: how does the Swing app communicate with the servlets? Because they do need to communicate somehow, otherwise you're claiming you have a magical architecture. I mean you're saying they're "together", which as per my last comment can't be true, yet you've never said anything about how an action in the Swing app affects the server (or vice versa). – Kayaman Dec 12 '19 at 14:21
  • I understand you're looking for a quick way out, but you made a bad choice when you introduced a Swing application to interface with a web app (in the year 2019). It's theoretically possible that I've horribly misunderstood you, and it's not a big issue after all, but so far what you've told seems *suspicious* at the least. Not the kind of industry standard architecture you would expect. – Kayaman Dec 12 '19 at 14:42
  • YES I agree with you, it's not a standard way of doing things, and because I'm a creative person [ got 2 patents if you are interested in checking them out ], I'm trying to achieve things normally other people won't think of, so I came up with the idea of running Swing with servlet, because it seems to me easier to develop a swing app than html/JSP, therefore I'm experimenting with this feature, what if it works out as I wished [ wishful thinking eh ^_^ ?! ], won't that be nice ? Anyway, I've added more details to the original question, hope maybe someone will see what I mean & find solution. – Frank Dec 12 '19 at 15:37

1 Answers1

2

I was trying to understand for quite a while how your application works. I think what you are doing is that you have a servlet which is handling an http request AND as a part of the request handling you start a thread which opens up a Swing application. Is this correct?

If so, this is a bad design. Servlet is for serving web content. If you start a Swing app, it runs in the same JVM and you can actually access same backend resources as in the servlet but the problem is that the content that servlet serves is transported over the network to the client. While this can work on your local machine (where the server machine is also the client machine) it will not work on a normal client-server architecture, where server is a different machine than the client. Here the client receives the response (html) but has no access to the Swing app running on server machine.

Another problem with this design is how would you bind the swing application to the correct client? Image 100 users access the servlet, does it open 100 instances of the Swing application? As Kayaman said, you would need some kind of another connection to client's machine to access the Swing application. This is not trivial and not worth doing.

I am actually from Webswing team, but this is not the right use case for Webswing. Webswing is for standalone Swing/JavaFX applications that user would normally start up from desktop, not from a servlet.

I think the best choice for you is to implement the logic from you Swing app into an HTML page served by the servlet, probably use some AJAX calls on the buttons, etc. There is really a lot of possibilities how to access backend from webpage in a very responsive and native-like way.

Branislav Kuliha
  • 149
  • 2
  • 10
  • Hello, thanks for looking into my question and try to explain the situation, yes, you understood the first part correctly, but the Swing app is not for all users to access from client, it's only for a few who are admins monitoring the services running on the server, so it mostly for my boss, only he [ maybe another sys admin ] might have access to see the Swing app, and interact with it, and our servlet is running Jetty [ I notice Webswing also uses Jetty ], so naturally I would hope Webswing can make this happen. – Frank Jan 18 '20 at 16:58
  • Because if I write html + javascript + servlet to make it happen, I feel like much more needs to be done, than simply write it in Swing which I'm very familiar with. But if you, from Webswing team, say this can't happen, then I'll take your word for it and find the html + javascript + servlet path. – Frank Jan 18 '20 at 16:59