Browse Source

Fully implement AccountManager, overview UI now supports multiple accounts

master
Chris Smith 15 years ago
parent
commit
83624154e6

+ 90
- 0
src/uk/co/md87/evetool/Account.java View File

@@ -0,0 +1,90 @@
1
+/*
2
+ * Copyright (c) 2009 Chris Smith
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in
12
+ * all copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+package uk.co.md87.evetool;
24
+
25
+import uk.co.md87.evetool.api.EveApi;
26
+
27
+/**
28
+ * Represents one user account, with a user ID and an API key.
29
+ *
30
+ * @author chris
31
+ */
32
+public class Account {
33
+
34
+    /** The user ID for this account. */
35
+    private final int id;
36
+
37
+    /** The API key for this account. */
38
+    private final String key;
39
+
40
+    /**
41
+     * Creates a new account with the specified details.
42
+     *
43
+     * @param id The user ID for the account
44
+     * @param key The API key for the account
45
+     */
46
+    public Account(final int id, final String key) {
47
+        this.id = id;
48
+        this.key = key;
49
+    }
50
+
51
+    /**
52
+     * Retrieves the user ID associated with this account.
53
+     *
54
+     * @return This account's user ID
55
+     */
56
+    public int getId() {
57
+        return id;
58
+    }
59
+
60
+    /**
61
+     * Retrieves the API key associated with this account.
62
+     *
63
+     * @return This account's API key
64
+     */
65
+    public String getKey() {
66
+        return key;
67
+    }
68
+
69
+    /**
70
+     * Uses the specified API factory to create a new instance of the EVE Api
71
+     * and initialises its ID and Key settings to those of this account.
72
+     *
73
+     * @param factory The API factory to use
74
+     * @return An API instance for this account
75
+     */
76
+    public EveApi getApi(final ApiFactory factory) {
77
+        final EveApi api = factory.getApi();
78
+        api.setUserID(id);
79
+        api.setApiKey(key);
80
+
81
+        return api;
82
+    }
83
+
84
+    /** {@inheritDoc} */
85
+    @Override
86
+    public String toString() {
87
+        return "[" + id + " - " + key.substring(0, 6) + "...]";
88
+    }
89
+
90
+}

+ 138
- 0
src/uk/co/md87/evetool/AccountManager.java View File

@@ -23,6 +23,14 @@
23 23
 package uk.co.md87.evetool;
24 24
 
25 25
 import java.sql.Connection;
26
+import java.sql.PreparedStatement;
27
+import java.sql.ResultSet;
28
+import java.sql.SQLException;
29
+import java.sql.Statement;
30
+import java.util.ArrayList;
31
+import java.util.List;
32
+import java.util.logging.Level;
33
+import java.util.logging.Logger;
26 34
 
27 35
 /**
28 36
  * Manages accounts that have been added to the tool.
@@ -31,11 +39,141 @@ import java.sql.Connection;
31 39
  */
32 40
 public class AccountManager {
33 41
 
42
+    /** Logger to use for this class. */
43
+    private static final Logger LOGGER = Logger.getLogger(AccountManager.class.getName());
44
+
45
+    /** A list of known accounts. */
46
+    private final List<Account> accounts = new ArrayList<Account>();
47
+
48
+    /** A list of listeners. */
49
+    private final List<AccountListener> listeners = new ArrayList<AccountListener>();
50
+
34 51
     /** The SQL connection to use. */
35 52
     private final Connection conn;
36 53
 
54
+    /** The SQL statement to use to insert new accounts. */
55
+    private PreparedStatement insertStm;
56
+
57
+    /**
58
+     * Creates a new account manager which will use the specified DB connection.
59
+     *
60
+     * @param conn The database connection to use
61
+     */
37 62
     public AccountManager(final Connection conn) {
38 63
         this.conn = conn;
64
+
65
+        try {
66
+            this.insertStm = conn.prepareStatement("INSERT INTO accounts " +
67
+                    "(account_userid, account_key) VALUES (?, ?)");
68
+        } catch (SQLException ex) {
69
+            LOGGER.log(Level.SEVERE, "Unable to prepare account statement", ex);
70
+        }
71
+
72
+        loadAccounts();
73
+    }
74
+
75
+    /**
76
+     * Retrieves a list of all known accounts.
77
+     * 
78
+     * @return A list of known accounts
79
+     */
80
+    public List<Account> getAccounts() {
81
+        return new ArrayList<Account>(accounts);
82
+    }
83
+
84
+    /**
85
+     * Retrieves a list of all known accounts, and registers the specified
86
+     * {@link AccountListener}. Any account added before, during or after this
87
+     * method call is guaranteed to either be present in the returned list, or
88
+     * have an accountAdded listener fired for it.
89
+     *
90
+     * @param listener The listener to be added
91
+     * @return A list of known accounts
92
+     */
93
+    public synchronized List<Account> getAccounts(final AccountListener listener) {
94
+        addListener(listener);
95
+        
96
+        return new ArrayList<Account>(accounts);
97
+    }
98
+
99
+    /**
100
+     * Adds a new account with the specified details.
101
+     * 
102
+     * @param id The user ID of the account owner
103
+     * @param key The API key for the account
104
+     */
105
+    public void addAccount(final int id, final String key) {
106
+        try {
107
+            insertStm.setInt(1, id);
108
+            insertStm.setString(2, key);
109
+            insertStm.execute();
110
+
111
+            addAccount(new Account(id, key));
112
+        } catch (SQLException ex) {
113
+            LOGGER.log(Level.SEVERE, "Unable to add account", ex);
114
+        }
39 115
     }
40 116
 
117
+    /**
118
+     * Registers the specified listener with this account manager.
119
+     *
120
+     * @param listener The listener to be registered
121
+     */
122
+    public void addListener(final AccountListener listener) {
123
+        synchronized (listeners) {
124
+            listeners.add(listener);
125
+        }
126
+    }
127
+
128
+    /**
129
+     * Adds an account to the list of known accounts and fires the
130
+     * appropriate listeners.
131
+     *
132
+     * @param account The account to be added
133
+     */
134
+    protected synchronized void addAccount(final Account account) {
135
+        LOGGER.log(Level.FINE, "Adding account: " + account);
136
+
137
+        accounts.add(account);
138
+
139
+        synchronized (listeners) {
140
+            for (AccountListener listener : listeners) {
141
+                listener.accountAdded(account);
142
+            }
143
+        }
144
+    }
145
+
146
+    /**
147
+     * Loads all accounts in the database.
148
+     */
149
+    protected void loadAccounts() {
150
+        try {
151
+            final Statement statement = conn.createStatement();
152
+            final ResultSet set = statement.executeQuery("SELECT account_userid, "
153
+                    + "account_key FROM accounts");
154
+
155
+            while (set.next()) {
156
+                final int id = set.getInt("ACCOUNT_USERID");
157
+                final String key = set.getString("ACCOUNT_KEY");
158
+                addAccount(new Account(id, key));
159
+            }
160
+        } catch (SQLException ex) {
161
+            LOGGER.log(Level.SEVERE, "Error loading accounts", ex);
162
+        }
163
+    }
164
+
165
+    /**
166
+     * Interfaces implemented by objects who wish to be notified when accounts
167
+     * are modified.
168
+     */
169
+    public static interface AccountListener {
170
+
171
+        /**
172
+         * Called when a new account has been added.
173
+         *
174
+         * @param account The account that was added.
175
+         */
176
+        void accountAdded(final Account account);
177
+
178
+    }
41 179
 }

+ 1
- 0
src/uk/co/md87/evetool/ApiFactory.java View File

@@ -34,6 +34,7 @@ import uk.co.md87.evetool.api.EveApi;
34 34
  * Factory class to create instances of the {@link EveApi} class. Uses an
35 35
  * embedded Derby database server by default.
36 36
  *
37
+ * TODO: Make non-static
37 38
  * @author chris
38 39
  */
39 40
 public class ApiFactory {

+ 10
- 6
src/uk/co/md87/evetool/Main.java View File

@@ -52,13 +52,17 @@ public class Main {
52 52
         initLogging();
53 53
         readVersion();
54 54
         initTables();
55
-        
56
-        final EveApi api = ApiFactory.getApi();
57
-        api.setApiKey("yaISaqXrSnaQPnRSFi4ODeWjSzWu2gNq1h6F0tVevtSGr5dzoEkZ6YrzHeBzzgNg");
58
-        api.setUserID("403848");
59
-        api.setCharID("113499922");
60 55
 
61
-        new MainWindow(api).setVisible(true);
56
+        final AccountManager manager = new AccountManager(ApiFactory.getConnection());
57
+
58
+        if (manager.getAccounts().isEmpty()) {
59
+            // TODO: Remove me before release!
60
+
61
+            manager.addAccount(403848,
62
+                    "yaISaqXrSnaQPnRSFi4ODeWjSzWu2gNq1h6F0tVevtSGr5dzoEkZ6YrzHeBzzgNg");
63
+        }
64
+        
65
+        new MainWindow(manager, new ApiFactory()).setVisible(true);
62 66
     }
63 67
 
64 68
     /**

+ 5
- 5
src/uk/co/md87/evetool/api/EveApi.java View File

@@ -60,10 +60,10 @@ public class EveApi {
60 60
     private final ApiDownloader downloader;
61 61
     
62 62
     /** The client's user ID, if specified. */
63
-    private String userID;
63
+    private int userID;
64 64
 
65 65
     /** The client's character ID, if specified. */
66
-    private String charID;
66
+    private int charID;
67 67
 
68 68
     /** The client's API key, if specified. */
69 69
     private String apiKey;
@@ -77,7 +77,7 @@ public class EveApi {
77 77
     public EveApi(final Connection sqlConnection) {
78 78
         this.conn = sqlConnection;
79 79
         
80
-        new TableCreator(conn, "../db/", TABLES).checkTables();
80
+        new TableCreator(conn, "../db/", TABLES).checkTables(); // TODO: Only do this once?
81 81
 
82 82
         this.downloader = new ApiDownloader(new DBCache(conn), new ApiParser());
83 83
     }
@@ -97,7 +97,7 @@ public class EveApi {
97 97
      *
98 98
      * @param charID The user's chosen character ID
99 99
      */
100
-    public void setCharID(final String charID) {
100
+    public void setCharID(final int charID) {
101 101
         this.charID = charID;
102 102
         downloader.setCharID(charID);
103 103
     }
@@ -107,7 +107,7 @@ public class EveApi {
107 107
      *
108 108
      * @param userID The user's user ID
109 109
      */
110
-    public void setUserID(final String userID) {
110
+    public void setUserID(final int userID) {
111 111
         this.userID = userID;
112 112
         downloader.setUserID(userID);
113 113
     }

+ 10
- 10
src/uk/co/md87/evetool/api/io/ApiDownloader.java View File

@@ -48,8 +48,8 @@ public class ApiDownloader {
48 48
     private final ApiCache cache;
49 49
     private final ApiParser parser;
50 50
 
51
-    private String userID = null;
52
-    private String charID = null;
51
+    private int userID = -1;
52
+    private int charID = -1;
53 53
     private String apiKey = null;
54 54
 
55 55
     public ApiDownloader(final ApiCache cache, final ApiParser parser) {
@@ -58,29 +58,29 @@ public class ApiDownloader {
58 58
     }
59 59
 
60 60
     public ApiDownloader(final ApiCache cache, final ApiParser parser,
61
-            final String userID, final String apiKey) {
61
+            final int userID, final String apiKey) {
62 62
         this(cache, parser);
63 63
         this.userID = userID;
64 64
         this.apiKey = apiKey;
65 65
     }
66 66
 
67 67
     public ApiDownloader(final ApiCache cache, final ApiParser parser,
68
-            final String userID, final String apiKey, final String charID) {
68
+            final int userID, final String apiKey, final int charID) {
69 69
         this(cache, parser, userID, apiKey);
70 70
         this.charID = charID;
71 71
     }
72 72
 
73 73
     protected void addArgs(final Map<String, String> args) {
74
-        if (userID != null) {
75
-            args.put("userID", userID);
74
+        if (userID != -1) {
75
+            args.put("userID", String.valueOf(userID));
76 76
         }
77 77
 
78 78
         if (apiKey != null) {
79 79
             args.put("apiKey", apiKey);
80 80
         }
81 81
 
82
-        if (charID != null) {
83
-            args.put("characterID", charID);
82
+        if (charID != -1) {
83
+            args.put("characterID", String.valueOf(charID));
84 84
         }
85 85
     }
86 86
 
@@ -130,11 +130,11 @@ public class ApiDownloader {
130 130
         this.apiKey = apiKey;
131 131
     }
132 132
 
133
-    public void setCharID(String charID) {
133
+    public void setCharID(int charID) {
134 134
         this.charID = charID;
135 135
     }
136 136
 
137
-    public void setUserID(String userID) {
137
+    public void setUserID(int userID) {
138 138
         this.userID = userID;
139 139
     }
140 140
 

+ 4
- 3
src/uk/co/md87/evetool/ui/ContentPanel.java View File

@@ -26,7 +26,8 @@ import java.awt.CardLayout;
26 26
 import javax.swing.JLabel;
27 27
 import javax.swing.JPanel;
28 28
 
29
-import uk.co.md87.evetool.api.EveApi;
29
+import uk.co.md87.evetool.AccountManager;
30
+import uk.co.md87.evetool.ApiFactory;
30 31
 import uk.co.md87.evetool.ui.pages.OverviewPage;
31 32
 
32 33
 /**
@@ -35,11 +36,11 @@ import uk.co.md87.evetool.ui.pages.OverviewPage;
35 36
  */
36 37
 public class ContentPanel extends JPanel {
37 38
 
38
-    public ContentPanel(final EveApi api) {
39
+    public ContentPanel(final AccountManager manager, final ApiFactory factory) {
39 40
         super(new CardLayout());
40 41
 
41 42
         //setBackground(Color.BLUE);
42
-        add(new OverviewPage(api), "push, grow");
43
+        add(new OverviewPage(manager, factory), "push, grow");
43 44
         add(new JLabel("Main content2", JLabel.CENTER), "push, grow");
44 45
     }
45 46
 

+ 8
- 5
src/uk/co/md87/evetool/ui/MainWindow.java View File

@@ -31,7 +31,8 @@ import javax.swing.JFrame;
31 31
 
32 32
 import javax.swing.UIManager;
33 33
 import net.miginfocom.swing.MigLayout;
34
-import uk.co.md87.evetool.api.EveApi;
34
+import uk.co.md87.evetool.AccountManager;
35
+import uk.co.md87.evetool.ApiFactory;
35 36
 
36 37
 /**
37 38
  *
@@ -40,14 +41,16 @@ import uk.co.md87.evetool.api.EveApi;
40 41
  */
41 42
 public class MainWindow extends JFrame {
42 43
 
43
-    private final EveApi api;
44
+    private final AccountManager manager;
45
+    private final ApiFactory factory;
44 46
 
45
-    public MainWindow(final EveApi api) {
47
+    public MainWindow(final AccountManager manager, final ApiFactory factory) {
46 48
         super("EVE Tool - Initialising...");
47 49
 
48 50
         UIManager.put("swing.boldMetal", false);
49 51
 
50
-        this.api = api;
52
+        this.factory = factory;
53
+        this.manager = manager;
51 54
         
52 55
         setLayout(new MigLayout("insets 0, fill, wrap 2", "[]0[fill,grow]",
53 56
                 "[fill,grow]0[]"));
@@ -65,7 +68,7 @@ public class MainWindow extends JFrame {
65 68
 
66 69
     protected void addComponents() {
67 70
         add(new MenuPanel(), "width 201!");
68
-        add(new ContentPanel(api));
71
+        add(new ContentPanel(manager, factory));
69 72
         add(new StatusPanel(), "growx, span, height 30!");
70 73
     }
71 74
 

+ 33
- 13
src/uk/co/md87/evetool/ui/pages/OverviewPage.java View File

@@ -28,8 +28,12 @@ import java.util.Map;
28 28
 import javax.swing.JLabel;
29 29
 import javax.swing.JPanel;
30 30
 
31
+import javax.swing.SwingUtilities;
31 32
 import net.miginfocom.swing.MigLayout;
32 33
 
34
+import uk.co.md87.evetool.Account;
35
+import uk.co.md87.evetool.AccountManager;
36
+import uk.co.md87.evetool.ApiFactory;
33 37
 import uk.co.md87.evetool.api.EveApi;
34 38
 import uk.co.md87.evetool.ui.workers.AccountUpdateWorker;
35 39
 
@@ -38,31 +42,47 @@ import uk.co.md87.evetool.ui.workers.AccountUpdateWorker;
38 42
  * TODO: Document OverviewPage
39 43
  * @author chris
40 44
  */
41
-public class OverviewPage extends JPanel {
45
+public class OverviewPage extends JPanel implements AccountManager.AccountListener {
42 46
 
43
-    private final EveApi api;
44
-    private final Map<String, JPanel> panels = new HashMap<String, JPanel>();
47
+    private final ApiFactory factory;
48
+    private final Map<Account, EveApi> apis = new HashMap<Account, EveApi>();
49
+    private final Map<Account, JPanel> panels = new HashMap<Account, JPanel>();
45 50
 
46
-    public OverviewPage(final EveApi api) {
47
-        this.api = api;
51
+    public OverviewPage(final AccountManager manager, final ApiFactory factory) {
52
+        this.factory = factory;
48 53
 
49 54
         setLayout(new MigLayout("fillx"));
50
-        add(new JLabel("Account 1 - 403848"), "span, wrap");
55
+
56
+        for (Account account : manager.getAccounts(this)) {
57
+            addAccount(account);
58
+        }
59
+    }
60
+
61
+    protected void addAccount(final Account account) {
62
+        // TODO: Number them or reformat or something
63
+        add(new JLabel("Account N - " + account.getId()), "span, wrap");
51 64
 
52 65
         final JPanel panel = new JPanel(new MigLayout(" fillx", "[|fill,grow|fill,grow]"));
53 66
         panel.add(new JLabel("Loading..."), "span");
54
-        panels.put("Account 1", panel);
55 67
         add(panel, "growx, wrap");
56 68
 
57
-        new AccountUpdateWorker("Account 1",this).execute();
69
+        panels.put(account, panel);
70
+        apis.put(account, account.getApi(factory));
71
+        
72
+        new AccountUpdateWorker(apis.get(account), panel).execute();
58 73
     }
59 74
 
60
-    public EveApi getApi() {
61
-        return api;
62
-    }
75
+    /** {@inheritDoc} */
76
+    @Override
77
+    public void accountAdded(final Account account) {
78
+        SwingUtilities.invokeLater(new Runnable() {
63 79
 
64
-    public Map<String, JPanel> getPanels() {
65
-        return panels;
80
+            /** {@inheritDoc} */
81
+            @Override
82
+            public void run() {
83
+                addAccount(account);
84
+            }
85
+        });
66 86
     }
67 87
 
68 88
 }

+ 19
- 21
src/uk/co/md87/evetool/ui/workers/AccountUpdateWorker.java View File

@@ -27,10 +27,12 @@ import java.util.logging.Level;
27 27
 import java.util.logging.Logger;
28 28
 
29 29
 import javax.swing.JLabel;
30
+import javax.swing.JPanel;
30 31
 import javax.swing.JSeparator;
31 32
 import javax.swing.SwingWorker;
32 33
 
33 34
 import uk.co.md87.evetool.api.ApiResponse;
35
+import uk.co.md87.evetool.api.EveApi;
34 36
 import uk.co.md87.evetool.api.wrappers.CharacterList;
35 37
 import uk.co.md87.evetool.api.wrappers.data.BasicCharInfo;
36 38
 import uk.co.md87.evetool.ui.pages.OverviewPage;
@@ -42,20 +44,21 @@ import uk.co.md87.evetool.ui.pages.OverviewPage;
42 44
  */
43 45
 public class AccountUpdateWorker extends SwingWorker<ApiResponse<CharacterList>, Object> {
44 46
 
45
-    private final String account;
46
-    private final OverviewPage overview;
47
+    private final EveApi api;
48
+    private final JPanel target;
47 49
 
48
-    public AccountUpdateWorker(final String account, OverviewPage overview) {
50
+    public AccountUpdateWorker(final EveApi api, final JPanel panel) {
49 51
         super();
50
-        this.overview = overview;
51
-        this.account = account;
52
+        
53
+        this.api = api;
54
+        this.target = panel;
52 55
     }
53 56
 
54 57
     /** {@inheritDoc} */
55 58
     @Override
56 59
     protected ApiResponse<CharacterList> doInBackground() throws Exception {
57 60
         Logger.getLogger(OverviewPage.class.getName()).log(Level.FINEST, "doInBackground()");
58
-        return overview.getApi().getCharacterList();
61
+        return api.getCharacterList();
59 62
     }
60 63
 
61 64
     @Override
@@ -65,14 +68,14 @@ public class AccountUpdateWorker extends SwingWorker<ApiResponse<CharacterList>,
65 68
             final ApiResponse<CharacterList> res = get();
66 69
             System.out.println(Thread.currentThread().getName());
67 70
             if (res.wasSuccessful()) {
68
-                overview.getPanels().get(account).removeAll();
71
+                target.removeAll();
69 72
                 boolean first = true;
70 73
 
71 74
                 for (BasicCharInfo character : res.getResult()) {
72 75
                     if (first) {
73 76
                         first = false;
74 77
                     } else {
75
-                        overview.getPanels().get(account).add(new JSeparator(),
78
+                        target.add(new JSeparator(),
76 79
                                 "span, growx, pushx, gaptop 5, gapbottom 5");
77 80
                     }
78 81
 
@@ -80,25 +83,20 @@ public class AccountUpdateWorker extends SwingWorker<ApiResponse<CharacterList>,
80 83
                     final JLabel nameLabel = new JLabel(character.getName());
81 84
                     nameLabel.setFont(nameLabel.getFont().deriveFont(Font.BOLD));
82 85
                     
83
-                    overview.getPanels().get(account)
84
-                            .add(portrait, "spany 2, height 64!, width 64!");
85
-                    overview.getPanels().get(account)
86
-                            .add(nameLabel, "height 20!");
87
-                    overview.getPanels().get(account)
88
-                            .add(new JLabel(character.getCorp().getName(), JLabel.RIGHT), "wrap");
89
-                    overview.getPanels().get(account)
90
-                            .add(new JLabel("Training XXXX to level 5 " +
86
+                    target.add(portrait, "spany 2, height 64!, width 64!");
87
+                    target.add(nameLabel, "height 20!");
88
+                    target.add(new JLabel(character.getCorp().getName(), JLabel.RIGHT), "wrap");
89
+                    target.add(new JLabel("Training XXXX to level 5 " +
91 90
                             "(3 years 1 second...)"), "gaptop 20, height 20!");
92
-                    overview.getPanels().get(account)
93
-                            .add(new JLabel("1,333,337 ISK", JLabel.RIGHT), "wrap");
91
+                    target.add(new JLabel("1,333,337 ISK", JLabel.RIGHT), "wrap");
94 92
 
95 93
                     new PortraitLoaderWorker(character.getId(), portrait, 64).execute();
96 94
                 }
97 95
             } else {
98
-                overview.getPanels().get(account).removeAll();
99
-                overview.getPanels().get(account).add(new JLabel("Error!: " + res.getError()));
96
+                target.removeAll();
97
+                target.add(new JLabel("Error!: " + res.getError()));
100 98
             }
101
-            overview.getPanels().get(account).revalidate();
99
+            target.revalidate();
102 100
         } catch (Exception ex) {
103 101
             Logger.getLogger(OverviewPage.class.getName()).log(Level.SEVERE, null, ex);
104 102
         }

Loading…
Cancel
Save