James Moger
2012-02-16 3cc6e2de29a0fa33dd585e938e1614a6dd5f9755
commit | author | age
19c634 1 /*
JM 2  * Copyright 2011 gitblit.com.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.gitblit.client;
17
18 import java.awt.BorderLayout;
f14f76 19 import java.awt.Cursor;
841651 20 import java.awt.Dimension;
19c634 21 import java.awt.EventQueue;
ee25c8 22 import java.awt.Point;
19c634 23 import java.awt.event.ActionEvent;
JM 24 import java.awt.event.ActionListener;
25 import java.awt.event.KeyEvent;
ee25c8 26 import java.awt.event.WindowAdapter;
JM 27 import java.awt.event.WindowEvent;
bab9c9 28 import java.io.BufferedReader;
ee25c8 29 import java.io.File;
bab9c9 30 import java.io.FileReader;
JM 31 import java.io.FileWriter;
19c634 32 import java.io.IOException;
2a99c3 33 import java.net.ConnectException;
ee25c8 34 import java.text.MessageFormat;
f14f76 35 import java.text.SimpleDateFormat;
JM 36 import java.util.ArrayList;
37 import java.util.Collections;
38 import java.util.Comparator;
39 import java.util.Date;
bab9c9 40 import java.util.HashMap;
ee25c8 41 import java.util.LinkedHashMap;
f14f76 42 import java.util.List;
JM 43 import java.util.Locale;
ee25c8 44 import java.util.Map;
JM 45 import java.util.Set;
f14f76 46 import java.util.TimeZone;
19c634 47
841651 48 import javax.swing.ImageIcon;
19c634 49 import javax.swing.JFrame;
841651 50 import javax.swing.JMenu;
JM 51 import javax.swing.JMenuBar;
52 import javax.swing.JMenuItem;
19c634 53 import javax.swing.JOptionPane;
JM 54 import javax.swing.JPanel;
55 import javax.swing.JTabbedPane;
841651 56 import javax.swing.KeyStroke;
f14f76 57 import javax.swing.SwingWorker;
841651 58 import javax.swing.UIManager;
19c634 59
ee25c8 60 import org.eclipse.jgit.errors.ConfigInvalidException;
JM 61 import org.eclipse.jgit.lib.StoredConfig;
62 import org.eclipse.jgit.storage.file.FileBasedConfig;
63 import org.eclipse.jgit.util.FS;
64
19c634 65 import com.gitblit.Constants;
8b7636 66 import com.gitblit.GitBlitException.ForbiddenException;
17820f 67 import com.gitblit.models.FeedModel;
38688b 68 import com.gitblit.utils.Base64;
19c634 69 import com.gitblit.utils.StringUtils;
JM 70
841651 71 /**
ee25c8 72  * Gitblit Manager issues JSON RPC requests to a Gitblit server.
841651 73  * 
JM 74  * @author James Moger
75  * 
76  */
f14f76 77 public class GitblitManager extends JFrame implements RegistrationsDialog.RegistrationListener {
19c634 78
JM 79     private static final long serialVersionUID = 1L;
c25a1d 80     private static final String SERVER = "server";
JM 81     private static final String FEED = "feed";
f14f76 82     private final SimpleDateFormat dateFormat;
19c634 83     private JTabbedPane serverTabs;
ee25c8 84     private File configFile = new File(System.getProperty("user.home"), ".gitblit/config");
841651 85
ee25c8 86     private Map<String, GitblitRegistration> registrations = new LinkedHashMap<String, GitblitRegistration>();
841651 87     private JMenu recentMenu;
f14f76 88     private int maxRecentCount = 5;
19c634 89
a7a9f7 90     private GitblitManager() {
19c634 91         super();
f14f76 92         dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US);
JM 93         dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
19c634 94     }
JM 95
96     private void initialize() {
97         setContentPane(getCenterPanel());
841651 98         setIconImage(new ImageIcon(getClass().getResource("/gitblt-favicon.png")).getImage());
a7a9f7 99         setTitle("Gitblit Manager v" + Constants.VERSION + " (" + Constants.VERSION_DATE + ")");
19c634 100         setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
ee25c8 101         addWindowListener(new WindowAdapter() {
JM 102             @Override
103             public void windowClosing(WindowEvent event) {
104                 saveSizeAndPosition();
105             }
3f5a93 106
68413a 107             @Override
JM 108             public void windowOpened(WindowEvent event) {
109                 manageRegistrations();
110             }
ee25c8 111         });
JM 112
113         setSizeAndPosition();
114         loadRegistrations();
115         rebuildRecentMenu();
116     }
117
118     private void setSizeAndPosition() {
119         String sz = null;
120         String pos = null;
121         try {
122             StoredConfig config = getConfig();
123             sz = config.getString("ui", null, "size");
124             pos = config.getString("ui", null, "position");
125         } catch (Throwable t) {
126             t.printStackTrace();
127         }
128
129         // try to restore saved window size
130         if (StringUtils.isEmpty(sz)) {
131             setSize(850, 500);
132         } else {
133             String[] chunks = sz.split("x");
134             int width = Integer.parseInt(chunks[0]);
135             int height = Integer.parseInt(chunks[1]);
136             setSize(width, height);
137         }
138
139         // try to restore saved window position
140         if (StringUtils.isEmpty(pos)) {
141             setLocationRelativeTo(null);
142         } else {
143             String[] chunks = pos.split(",");
144             int x = Integer.parseInt(chunks[0]);
145             int y = Integer.parseInt(chunks[1]);
146             setLocation(x, y);
147         }
148     }
149
150     private void saveSizeAndPosition() {
151         try {
152             // save window size and position
153             StoredConfig config = getConfig();
154             Dimension sz = GitblitManager.this.getSize();
155             config.setString("ui", null, "size",
156                     MessageFormat.format("{0,number,0}x{1,number,0}", sz.width, sz.height));
157             Point pos = GitblitManager.this.getLocationOnScreen();
158             config.setString("ui", null, "position",
159                     MessageFormat.format("{0,number,0},{1,number,0}", pos.x, pos.y));
160             config.save();
161         } catch (Throwable t) {
162             Utils.showException(GitblitManager.this, t);
163         }
19c634 164     }
JM 165
841651 166     private JMenuBar setupMenu() {
JM 167         JMenuBar menuBar = new JMenuBar();
b7f591 168         JMenu serversMenu = new JMenu(Translation.get("gb.servers"));
19c634 169         menuBar.add(serversMenu);
b7f591 170         recentMenu = new JMenu(Translation.get("gb.recent"));
841651 171         serversMenu.add(recentMenu);
f14f76 172
JM 173         JMenuItem manage = new JMenuItem(Translation.get("gb.manage") + "...");
174         manage.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_M, KeyEvent.CTRL_DOWN_MASK, false));
175         manage.addActionListener(new ActionListener() {
176             public void actionPerformed(ActionEvent event) {
177                 manageRegistrations();
178             }
179         });
180         serversMenu.add(manage);
181
841651 182         return menuBar;
19c634 183     }
JM 184
185     private JPanel getCenterPanel() {
186         serverTabs = new JTabbedPane(JTabbedPane.TOP);
841651 187         JMenuBar menubar = setupMenu();
19c634 188         JPanel panel = new JPanel(new BorderLayout());
841651 189         panel.add(menubar, BorderLayout.NORTH);
19c634 190         panel.add(serverTabs, BorderLayout.CENTER);
JM 191         return panel;
192     }
193
f14f76 194     private void manageRegistrations() {
JM 195         RegistrationsDialog dialog = new RegistrationsDialog(new ArrayList<GitblitRegistration>(
196                 registrations.values()), this);
197         dialog.setLocationRelativeTo(GitblitManager.this);
198         dialog.setVisible(true);
199     }
200
201     @Override
3f5a93 202     public void login(GitblitRegistration reg) {
JM 203         if (!reg.savePassword && (reg.password == null || reg.password.length == 0)) {
204             // prompt for password
205             EditRegistrationDialog dialog = new EditRegistrationDialog(this, reg, true);
206             dialog.setLocationRelativeTo(GitblitManager.this);
207             dialog.setVisible(true);
ac7f17 208             GitblitRegistration newReg = dialog.getRegistration();
JM 209             if (newReg == null) {
3f5a93 210                 // user canceled
JM 211                 return;
f14f76 212             }
ac7f17 213             // preserve feeds
17820f 214             newReg.feeds.addAll(reg.feeds);
JM 215
ac7f17 216             // use new reg
JM 217             reg = newReg;
f14f76 218         }
4cac0d 219
3f5a93 220         // login
f14f76 221         setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
3f5a93 222         final GitblitRegistration registration = reg;
4cac0d 223         final GitblitPanel panel = new GitblitPanel(registration, this);
f14f76 224         SwingWorker<Boolean, Void> worker = new SwingWorker<Boolean, Void>() {
JM 225
226             @Override
227             protected Boolean doInBackground() throws IOException {
228                 panel.login();
229                 return true;
230             }
231
232             @Override
233             protected void done() {
234                 try {
235                     boolean success = get();
3f5a93 236                     serverTabs.addTab(registration.name, panel);
f14f76 237                     int idx = serverTabs.getTabCount() - 1;
JM 238                     serverTabs.setSelectedIndex(idx);
3f5a93 239                     serverTabs.setTabComponentAt(idx, new ClosableTabComponent(registration.name,
JM 240                             null, serverTabs, panel));
241                     registration.lastLogin = new Date();
242                     saveRegistration(registration.name, registration);
243                     registrations.put(registration.name, registration);
f14f76 244                     rebuildRecentMenu();
3f5a93 245                     if (!registration.savePassword) {
JM 246                         // clear password
247                         registration.password = null;
248                     }
f14f76 249                 } catch (Throwable t) {
2a99c3 250                     Throwable cause = t.getCause();
JM 251                     if (cause instanceof ConnectException) {
252                         JOptionPane.showMessageDialog(GitblitManager.this, cause.getMessage(),
253                                 Translation.get("gb.error"), JOptionPane.ERROR_MESSAGE);
8b7636 254                     } else if (cause instanceof ForbiddenException) {
JM 255                         JOptionPane
256                                 .showMessageDialog(
257                                         GitblitManager.this,
258                                         "This Gitblit server does not allow RPC Management or Administration",
259                                         Translation.get("gb.error"), JOptionPane.ERROR_MESSAGE);
2a99c3 260                     } else {
JM 261                         Utils.showException(GitblitManager.this, t);
262                     }
f14f76 263                 } finally {
JM 264                     setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
265                 }
266             }
267         };
268         worker.execute();
19c634 269     }
JM 270
841651 271     private void rebuildRecentMenu() {
JM 272         recentMenu.removeAll();
ee25c8 273         ImageIcon icon = new ImageIcon(getClass().getResource("/gitblt-favicon.png"));
f14f76 274         List<GitblitRegistration> list = new ArrayList<GitblitRegistration>(registrations.values());
JM 275         Collections.sort(list, new Comparator<GitblitRegistration>() {
276             @Override
277             public int compare(GitblitRegistration o1, GitblitRegistration o2) {
278                 return o2.lastLogin.compareTo(o1.lastLogin);
279             }
280         });
281         if (list.size() > maxRecentCount) {
282             list = list.subList(0, maxRecentCount);
283         }
a8cb5d 284         for (int i = 0; i < list.size(); i++) {
JM 285             final GitblitRegistration reg = list.get(i);
ee25c8 286             JMenuItem item = new JMenuItem(reg.name, icon);
a8cb5d 287             item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_1 + i, KeyEvent.CTRL_DOWN_MASK,
JM 288                     false));
841651 289             item.addActionListener(new ActionListener() {
JM 290                 public void actionPerformed(ActionEvent e) {
f14f76 291                     login(reg);
841651 292                 }
JM 293             });
294             recentMenu.add(item);
295         }
296     }
297
ee25c8 298     private void loadRegistrations() {
JM 299         try {
300             StoredConfig config = getConfig();
c25a1d 301             Set<String> servers = config.getSubsections(SERVER);
ee25c8 302             for (String server : servers) {
3f5a93 303                 Date lastLogin = new Date(0);
c25a1d 304                 String date = config.getString(SERVER, server, "lastLogin");
3f5a93 305                 if (!StringUtils.isEmpty(date)) {
JM 306                     lastLogin = dateFormat.parse(date);
307                 }
c25a1d 308                 String url = config.getString(SERVER, server, "url");
JM 309                 String account = config.getString(SERVER, server, "account");
5ae0b7 310                 char[] password;
c25a1d 311                 String pw = config.getString(SERVER, server, "password");
5ae0b7 312                 if (StringUtils.isEmpty(pw)) {
JM 313                     password = new char[0];
314                 } else {
315                     password = new String(Base64.decode(pw)).toCharArray();
316                 }
bab9c9 317                 GitblitRegistration reg = new GitblitRegistration(server, url, account, password) {
JM 318                     private static final long serialVersionUID = 1L;
319
320                     protected void cacheFeeds() {
321                         writeFeedCache(this);
322                     }
323                 };
c25a1d 324                 String[] feeds = config.getStringList(SERVER, server, FEED);
4cac0d 325                 if (feeds != null) {
17820f 326                     // deserialize the field definitions
JM 327                     for (String definition : feeds) {
328                         FeedModel feed = new FeedModel(definition);
329                         reg.feeds.add(feed);
330                     }
4cac0d 331                 }
f14f76 332                 reg.lastLogin = lastLogin;
bab9c9 333                 loadFeedCache(reg);
ee25c8 334                 registrations.put(reg.name, reg);
JM 335             }
336         } catch (Throwable t) {
337             Utils.showException(GitblitManager.this, t);
338         }
339     }
340
3f5a93 341     @Override
JM 342     public boolean saveRegistration(String name, GitblitRegistration reg) {
ee25c8 343         try {
JM 344             StoredConfig config = getConfig();
3f5a93 345             if (!StringUtils.isEmpty(name) && !name.equals(reg.name)) {
JM 346                 // delete old registration
347                 registrations.remove(name);
c25a1d 348                 config.unsetSection(SERVER, name);
3f5a93 349             }
JM 350
351             // update registration
c25a1d 352             config.setString(SERVER, reg.name, "url", reg.url);
JM 353             config.setString(SERVER, reg.name, "account", reg.account);
3f5a93 354             if (reg.savePassword) {
c25a1d 355                 config.setString(SERVER, reg.name, "password",
3f5a93 356                         Base64.encodeBytes(new String(reg.password).getBytes("UTF-8")));
JM 357             } else {
c25a1d 358                 config.setString(SERVER, reg.name, "password", "");
3f5a93 359             }
JM 360             if (reg.lastLogin != null) {
c25a1d 361                 config.setString(SERVER, reg.name, "lastLogin", dateFormat.format(reg.lastLogin));
3f5a93 362             }
17820f 363             // serialize the feed definitions
JM 364             List<String> definitions = new ArrayList<String>();
365             for (FeedModel feed : reg.feeds) {
366                 definitions.add(feed.toString());
367             }
368             if (definitions.size() > 0) {
c25a1d 369                 config.setStringList(SERVER, reg.name, FEED, definitions);
4cac0d 370             }
ee25c8 371             config.save();
3f5a93 372             return true;
ee25c8 373         } catch (Throwable t) {
JM 374             Utils.showException(GitblitManager.this, t);
375         }
3f5a93 376         return false;
ee25c8 377     }
JM 378
3f5a93 379     @Override
f14f76 380     public boolean deleteRegistrations(List<GitblitRegistration> list) {
JM 381         boolean success = false;
382         try {
383             StoredConfig config = getConfig();
384             for (GitblitRegistration reg : list) {
c25a1d 385                 config.unsetSection(SERVER, reg.name);
a8cb5d 386                 registrations.remove(reg.name);
f14f76 387             }
JM 388             config.save();
389             success = true;
390         } catch (Throwable t) {
391             Utils.showException(GitblitManager.this, t);
392         }
393         return success;
394     }
395
ee25c8 396     private StoredConfig getConfig() throws IOException, ConfigInvalidException {
JM 397         FileBasedConfig config = new FileBasedConfig(configFile, FS.detect());
398         config.load();
399         return config;
400     }
401
bab9c9 402     private void loadFeedCache(GitblitRegistration reg) {
609a16 403         File feedCache = new File(configFile.getParentFile(), StringUtils.getSHA1(reg.toString())
bab9c9 404                 + ".cache");
JM 405         if (!feedCache.exists()) {
406             // no cache for this registration
407             return;
408         }
409         try {
410             BufferedReader reader = new BufferedReader(new FileReader(feedCache));
411             Map<String, Date> cache = new HashMap<String, Date>();
412             SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
413             String line = null;
414             while ((line = reader.readLine()) != null) {
415                 String[] kvp = line.split("=");
416                 cache.put(kvp[0], df.parse(kvp[1]));
417             }
418             reader.close();
419             for (FeedModel feed : reg.feeds) {
420                 String name = feed.toString();
421                 if (cache.containsKey(name)) {
422                     feed.currentRefreshDate = cache.get(name);
423                 }
424             }
425         } catch (Exception e) {
426             Utils.showException(GitblitManager.this, e);
427         }
428     }
429
430     private void writeFeedCache(GitblitRegistration reg) {
431         try {
609a16 432             File feedCache = new File(configFile.getParentFile(), StringUtils.getSHA1(reg
JM 433                     .toString()) + ".cache");
bab9c9 434             FileWriter writer = new FileWriter(feedCache);
JM 435             for (FeedModel feed : reg.feeds) {
436                 writer.append(MessageFormat.format("{0}={1,date,yyyy-MM-dd'T'HH:mm:ss}\n",
437                         feed.toString(), feed.currentRefreshDate));
438             }
439             writer.close();
440         } catch (Exception e) {
441             Utils.showException(GitblitManager.this, e);
442         }
443     }
444
19c634 445     public static void main(String[] args) {
JM 446         EventQueue.invokeLater(new Runnable() {
447             public void run() {
841651 448                 try {
JM 449                     UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
450                 } catch (Exception e) {
451                 }
a7a9f7 452                 GitblitManager frame = new GitblitManager();
19c634 453                 frame.initialize();
JM 454                 frame.setVisible(true);
455             }
456         });
457     }
458 }