Commit 289650a0 authored by Frank Bergmann's avatar Frank Bergmann

- import

parent 72cc7ce0
Pipeline #1011 failed with stages
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>PO-Timesheet</name>
<comment>
]project-open[ Time Sheet is a light weight desktop time sheet
logging application written in Java. ]po[ Time Sheet is designed
to work with the ]project-open[ project management on the server
side for managing the project hierarchy, advanced reporting,
billing and financials.
</comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jem.workbench.JavaEMFNature</nature>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.jem.beaninfo.BeanInfoNature</nature>
</natures>
</projectDescription>
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>PO-Timesheet</name>
<comment>
]project-open[ Time Sheet is a light weight desktop time sheet
logging application written in Java. ]po[ Time Sheet is designed
to work with the ]project-open[ project management on the server
side for managing the project hierarchy, advanced reporting,
billing and financials.
</comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jem.workbench.JavaEMFNature</nature>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.jem.beaninfo.BeanInfoNature</nature>
</natures>
</projectDescription>
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Library General
Public License instead of this License.
#--- User account to access ]project-open[ server ---
#Fri May 21 16:01:37 CEST 2010
port=
password=ben
email=bbigboss@tigerpond.com
server=http\://po34demo.project-open.net
#--- Parameters to access a ]project-open[ server ---
#Tue Sep 20 22:53:24 CEST 2016
password=ben
email=bbigboss@tigerpond.com
server=http\://192.168.52.128
;NSIS Modern User Interface version 1.70
;PO-Timesheet Installer Script
;Autors: Stephen Strenn and Frank Bergmann
;--------------------------------
;Include Modern UI
!include "MUI.nsh"
;--------------------------------
;General
;Name and file
Name "PO-Timesheet"
OutFile "PO-Timesheet.exe"
;Default installation folder
InstallDir "$PROGRAMFILES\Project Open\PO-Timesheet"
;Get installation folder from registry if available
InstallDirRegKey HKCU "Software\PO-Timesheet" ""
;--------------------------------
;Interface Settings
!define MUI_ABORTWARNING
!define MUI_HEADERIMAGE "C:\eclipse\PO-Timesheet\images\project-open.149x49.bmp"
!define MUI_HEADERIMAGE_BITMAP_NOSTRETCH
!define MUI_HEADERIMAGE_BITMAP "C:\eclipse\PO-Timesheet\images\project-open.149x49.bmp"
!define MUI_ICON "C:\eclipse\PO-Timesheet\images\setup.ico"
!define MUI_UNICON "C:\eclipse\PO-Timesheet\images\setup.ico"
;--------------------------------
;Pages
!insertmacro MUI_PAGE_LICENSE "C:\eclipse\PO-Timesheet\License.txt"
!insertmacro MUI_PAGE_COMPONENTS
!insertmacro MUI_PAGE_DIRECTORY
!insertmacro MUI_PAGE_INSTFILES
!insertmacro MUI_UNPAGE_CONFIRM
!insertmacro MUI_UNPAGE_INSTFILES
;--------------------------------
;Languages
!insertmacro MUI_LANGUAGE "English"
;--------------------------------
;Installer Sections
Section "PO-Timesheet (required)" SecDummy
SectionIn RO
SetOutPath "$INSTDIR"
File "C:\eclipse\PO-Timesheet.jar"
File "C:\eclipse\PO-Timesheet\images\po-timesheet.ico"
; File "C:\eclipse\PO-Timesheet\swt-win32-3138.dll"
SetOutPath "$INSTDIR\lib"
; File "C:\eclipse\PO-Timesheet\lib\org.eclipse.swt.win32.win32.x86_3.1.0.jar"
SetOutPath "$INSTDIR"
; Write the installation path into the registry
WriteRegStr HKLM SOFTWARE\PO-Timesheet "Install_Dir" "$INSTDIR"
; Write the uninstall keys for Windows
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\PO-Timesheet" "DisplayName" "PO-Timesheet"
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\PO-Timesheet" "UninstallString" '"$INSTDIR\uninstall.exe"'
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\PO-Timesheet" "NoModify" 1
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\PO-Timesheet" "NoRepair" 1
WriteUninstaller "uninstall.exe"
SectionEnd
; Optional section (can be disabled by the user)
Section "Start Menu Shortcuts"
CreateDirectory "$SMPROGRAMS\PO-Timesheet"
CreateShortCut "$SMPROGRAMS\PO-Timesheet\Uninstall.lnk" "$INSTDIR\uninstall.exe" "" "$INSTDIR\uninstall.exe"
CreateShortCut "$SMPROGRAMS\PO-Timesheet\PO-Timesheet.lnk" "$INSTDIR\PO-Timesheet.jar" "" "$INSTDIR\po-timesheet.ico"
SectionEnd
;--------------------------------
;Uninstaller Section
Section "Uninstall"
; Remove registry keys
DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\PO-Timesheet"
DeleteRegKey HKLM SOFTWARE\PO-Timesheet
DeleteRegKey /ifempty HKCU "Software\PO-Timesheet"
; Remove shortcuts
RMDir /r "$SMPROGRAMS\PO-Timesheet"
; Remove directories used
RMDir /r "$INSTDIR"
SectionEnd
Manifest-Version: 1.0
X-COMMENT: Main-Class will be added automatically by build
Manifest-Version: 1.0
Main-Class: org.projectopen.timesheet.TrayIconStarter
Class-Path: PO-Timesheet.jar
package org.projectopen.dynfield;
/**
* This class represents values of a ComboBox.
* In ]project-open[ we identify every object
* uniquely with an integer ID, while object
* names may include duplicates etc.
*
* @author fraber
*
*/
public class ComboBoxValue {
private int id = 0;
private Object object = null;
public ComboBoxValue(Object o, int id) {
this.id = id;
this.object = o;
}
public String toString() { return object.toString(); }
public int getId() { return id; }
public void setId(int id) { this.id = id; }
public Object getObject() { return object; }
public void setObject(Object object) { this.object = object; }
}
package org.projectopen.dynfield;
/*
* Copyright (C) 2010 ]project-open[
*
* This program is free software. You can redistribute it
* and/or modify it under the terms of the GNU General
* Public License as published by the Free Software Foundation;
* either version 2 of the License, or (at your option)
* any later version. This program 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 General Public License for more details.
*
* @author Frank Bergmann (frank.bergmann@project-open.com)
*
*/
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.TreeMap;
import org.projectopen.debug.DomPrinter;
import org.projectopen.debug.Logger;
import org.projectopen.rest.*;
@SuppressWarnings("unchecked")
public class ProjopDynfieldAttribute extends ProjopObject {
private static Hashtable objectTypeAttributes = new Hashtable();
public ProjopDynfieldAttribute() {
super("user");
}
/**
* Returns an ordered list of ProjopDynfieldAttribute objects
* for the given object type.
*
* @param objectType The ]po[ object type to look for
* @return A list of ProjopDynfieldAttribute objects.
*/
public static List getAttributesFromObjectType(String objectType) {
RESTClient rest = RESTClient.defaultInstance();
List attributes = (List)objectTypeAttributes.get(objectType);
if (null == attributes) {
String query = "object_type = '"+objectType+"'";
attributes = rest.fromQuery("im_dynfield_attribute", query);
objectTypeAttributes.put(objectType, attributes);
}
return attributes;
}
public String toString() {
String name = get("username");
if (name == null) { name = super.toString(); }
return name;
}
}
package org.projectopen.dynfield;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.TreeMap;
import org.projectopen.debug.DomPrinter;
import org.projectopen.debug.Logger;
import org.projectopen.rest.ProjopObject;
import org.projectopen.rest.ProjopObjectType;
import org.projectopen.rest.RESTClient;
/*
* Copyright (C) 2010 ]project-open[
*
* This program is free software. You can redistribute it
* and/or modify it under the terms of the GNU General
* Public License as published by the Free Software Foundation;
* either version 2 of the License, or (at your option)
* any later version. This program 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 General Public License for more details.
*
* @author Frank Bergmann (frank.bergmann@project-open.com)
*
*/
public class ProjopDynfieldWidget extends ProjopObject {
public ProjopDynfieldWidget() {
super("user");
}
public String toString() {
String name = get("username");
if (name == null) { name = super.toString(); }
return name;
}
@SuppressWarnings("unchecked")
public static ProjopObject getWidgetFromName(String widgetName) {
// the object type "responsible" for Dynfield Widget
ProjopObjectType dynfieldWidgetType = ProjopObjectType.getObjectType("im_dynfield_widget");
TreeMap instances = dynfieldWidgetType.getInstances();
Iterator iter = instances.values().iterator();
while (iter.hasNext()) {
ProjopObject o = (ProjopObject)iter.next();
String name = o.get("widget_name");
if (widgetName.equals(name)) { return o; }
}
return null;
}
/**
* Retreive the values of a DynField widget.
*
* @param widgetName The name of a DynField Widget
* @return A List of ComboBoxValues representing
* the options for the widget.
*/
public static List<String> restListDynfieldWidgetValues(String widgetName) {
RESTClient rest = RESTClient.defaultInstance();
TreeMap widgets = ProjopObjectType.getObjectType("im_dynfield_widget").getInstances();
Iterator iter = widgets.values().iterator();
ProjopObject widget = null;
while (iter.hasNext()) {
ProjopObject attr = (ProjopObject)iter.next();
if (widgetName.equals(attr.get("widget_name"))) {
widget = attr;
}
}
if (null == widget) {
rest.logMessage(Logger.ERROR, "Dynfield Widgets", "Didn't find widget with name "+widgetName, "");
return null;
}
int widgetId = Integer.parseInt(widget.get("widget_id"));
Document dom = null;
List groups = new ArrayList();
try {
String urlPath = "/intranet-rest/dynfield-widget-values?widget_id="+widgetId+"&format=json";
dom = rest.httpRequest("GET", urlPath, null);
} catch (Exception e) {
e.printStackTrace();
}
// Get the root element
Element docEle = dom.getDocumentElement();
// Iterate through the list of <row> entries,
// adding the data to the list of projects.
NodeList nl = docEle.getElementsByTagName("value");
if(nl != null && nl.getLength() > 0) {
for(int i = 0 ; i < nl.getLength();i++) {
Element el = (Element)nl.item(i);
int type = el.getNodeType();
DomPrinter.walk(el);
// skip everything except for element nodes ("real" nodes).
if (type != Node.ELEMENT_NODE) { continue; }
String value = el.getTextContent();
String key = el.getAttribute("key");
int keyInt = Integer.parseInt(key);
// System.out.println("v="+value+", n="+name+", k="+key);
ComboBoxValue v = new ComboBoxValue(value, keyInt);
groups.add(v);
}
}
rest.logMessage(Logger.INFO, "RESTClient.restListGroups()", "List all groups in the ]po[ system", groups.toString());
return groups;
}
}
package org.projectopen.rest;
/*
* Copyright (C) 2010 ]project-open[
*
* This program is free software. You can redistribute it
* and/or modify it under the terms of the GNU General
* Public License as published by the Free Software Foundation;
* either version 2 of the License, or (at your option)
* any later version. This program 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 General Public License for more details.
*
* @author Frank Bergmann (frank.bergmann@project-open.com)
*
*/
import java.net.URLEncoder;
import java.util.*;
import org.json.simple.JSONObject;
import org.projectopen.debug.DomPrinter;
import org.projectopen.debug.Logger;
import org.w3c.dom.*;
@SuppressWarnings("unchecked")
public class ProjopCategory extends ProjopObject {
private static Hashtable<String,List<ProjopObject>> categoriesPerType = new Hashtable<String,List<ProjopObject>>();
public ProjopCategory() {
super("im_category");
}
/**
* Converts a list of ProjopCategory objects into an
* array of category strings suitable for a combo box.
*
* @return An array of formatted categories, suitable
* to be used in the JComboBox constructor.
*/
public static Object[] comboBoxCategories(String categoryType) {
List<ProjopObject> categories = ProjopCategory.getCategoryObjectsFromType(categoryType);
Iterator<ProjopObject> iter = categories.iterator();
ArrayList<String> categoryStrings = new ArrayList<String>();
while (iter.hasNext()) {
ProjopCategory cat = (ProjopCategory)iter.next();
String s = cat.get("category");
int level = cat.get("tree_sortkey").length() / 8 - 1;
for (int i = 0; i < level; i++) { s = " "+s; }
categoryStrings.add(s);
}
return categoryStrings.toArray();
}
/**
* Get the list of categories for a given category type.
* Example: "Intranet Project Status" returns "Open", "Closed", ...
* @param categoryType The ]po[ category type.
* @return A List of ProjopCategory objects.
*/
public static List<ProjopObject> getCategoryObjectsFromType(String categoryType) {
RESTClient rest = RESTClient.defaultInstance();
// Check the cache
List<ProjopObject> categories = categoriesPerType.get(categoryType);
if (null == categories) {
// Nothing found in the cache - load from REST
List<ProjopObject> categoryObjectList = new ArrayList<ProjopObject>();
String categoryTypeEncoded = null;
try {
categoryTypeEncoded = URLEncoder.encode("'"+categoryType+"'", "UTF-8");
} catch (Exception e) { }
String urlPath = "/intranet-reporting/view?format=json&report_code=rest_category_type&category_type="+categoryTypeEncoded;
categories = rest.fromURL("im_category", urlPath);
rest.logMessage(Logger.INFO, "RESTClient.restListCategories()", "List a category type", categories.toString());
// Write into cache
categoriesPerType.put(categoryType, categories);
}
return categories;
}
/**
* Return the category_id of the input variable.
* The input can either be a
* @param o
* @return
*/
public static int categoryIdFromCategory(String categoryType, Object o) {
RESTClient rest = RESTClient.defaultInstance();
if (null == o || null == categoryType) { return 0; }
if (String.class != o.getClass()) {
rest.logMessage(Logger.ERROR, "GUI", "Calling categoryIdFromCategory with a non-string object", o.toString());
return 0;
}
String searchString = o.toString().trim();
if (null == searchString) { return 0; }
List<ProjopObject> categories = getCategoryObjectsFromType(categoryType);
Iterator<ProjopObject> iter = categories.iterator();
while (iter.hasNext()) {
ProjopObject cat = (ProjopObject)iter.next();
String type = cat.get("category_type");
if (!categoryType.equals(type)) { continue; }
if (!searchString.equals(cat.get("category"))) { continue; }
return cat.getObjectId();
}
return 0;
}
}
package org.projectopen.rest;
import java.util.List;
import org.projectopen.debug.Logger;
/*
* Copyright (C) 2010 ]project-open[
*
* This program is free software. You can redistribute it
* and/or modify it under the terms of the GNU General
* Public License as published by the Free Software Foundation;
* either version 2 of the License, or (at your option)
* any later version. This program 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 General Public License for more details.
*
* @author Frank Bergmann (frank.bergmann@project-open.com)
*
*/
/**
* Represents a ]project-open[ group object.
*/
public class ProjopGroup extends ProjopObject {
public ProjopGroup() {
super("group");
}
public String toString() {
String name = get("group_name");
if (name == null) { name = super.toString(); }
return name;
}
/**
* Query the REST server for the list of available groups in the system.
*
* @return List of ProjopObjects representing the current ]po[ groups.
*/
public List<ProjopObject> fromAll() {
RESTClient rest = RESTClient.defaultInstance();
List<ProjopObject> groups = rest.fromQuery("group", "");
rest.logMessage(Logger.INFO, "RESTClient.restListGroups()", "List all groups in the ]po[ system", groups.toString());
return groups;
}
}
package org.projectopen.rest;
import java.util.List;
import org.json.simple.JSONObject;
/*
* Copyright (C) 2010 ]project-open[
*
* This program is free software. You can redistribute it
* and/or modify it under the terms of the GNU General
* Public License as published by the Free Software Foundation;
* either version 2 of the License, or (at your option)
* any later version. This program 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 General Public License for more details.
*
* @author Frank Bergmann (frank.bergmann@project-open.com)
*
*/
public class ProjopHour extends ProjopObject {
public ProjopHour() {
super("im_hour");
}
public ProjopHour(JSONObject el) {
super("im_hour", el); // ]po[ name of this object type
}
/**
* Query the REST server the hours logged by the current user today.
* @return A list of ProjectHour objects
*/
public static List<ProjopObject> fromToday() {
RESTClient rest = RESTClient.defaultInstance();
String urlPath = "/intranet-reporting/view?format=json&report_code=rest_my_hours";
return rest.fromURL("im_hour", urlPath);
}
}
package org.projectopen.rest;
/*
* Copyright (C) 2010 ]project-open[
*
* This program is free software. You can redistribute it
* and/or modify it under the terms of the GNU General
* Public License as published by the Free Software Foundation;
* either version 2 of the License, or (at your option)
* any later version. This program 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 General Public License for more details.
*
* @author Frank Bergmann (frank.bergmann@project-open.com)
*
*/
import java.util.*;
import org.json.simple.*;
@SuppressWarnings("unchecked")
/*
* A ProjopObject mainly consists of a lists of variables
*/
public class ProjopObject implements Comparable<ProjopObject> {
/**
* Meta-information about the type of the object.
* Examples: "im_project", "im_company", ...
*/
private String objectType;
/**
* Contents of the object, coming directly from the database.
* All values are stored as strings.
*/
private Hashtable<String,String> vars;
protected ProjopObject parentObject = null;
protected Vector<ProjopObject> childObjects = new Vector<ProjopObject>();
/**
* Meta information in order to determine where to store the
* object_id, status_id and type_id information.
*/
private static String[] otypeFieldArray = {"category", "im_hour", "im_project", "user"};
private static String[] idFieldArray = {"category_id", "hour_id", "project_id", "user_id"};
private static String[] nameFieldArray = {"category", "hour_id", "project_name", "username"};
private static String[] statusFieldArray = {"", "", "project_status_id", ""};
private static String[] typeFieldArray = {"", "", "project_type_id", ""};
/**
* Constructor: Every ProjopObject needs an object type.
* @param objectType One of the ]po[ object types
* (im_project, user, ...)
*/
public ProjopObject(String objectType) {
this.objectType = objectType;
vars = new Hashtable<String,String>(20);
}
/**
* Constructor: Create from JSON object.
* @param objectType One of the ]po[ object types (im_project, user, ...)
* @param o JSONObject with a key-value map of Strings
*/
public ProjopObject(String objectType, JSONObject o) {
this.objectType = objectType;
vars = new Hashtable<String,String>(20);
// Write key-value pairs from JSON to Hashmap
if (null == o) { return; }
Iterator<Map.Entry<String,String>> it = o.entrySet().iterator();
while(it.hasNext()) {
Map.Entry<String,String> pair = (Map.Entry<String,String>)it.next();
String value = (String)pair.getValue();
if (value.isEmpty()) continue; // skip empty fields
String key = (String)pair.getKey();
this.set(key, value);
it.remove();
}
}
/**
* Converts an object into a JSON representation.
* @return A JSON string with a sorted list of all fields.
*/
public String toJSONString() {
StringBuffer buf = new StringBuffer();
List<String> l = new ArrayList<String>(vars.keySet());
Collections.sort(l);
Iterator<String> it = l.iterator();
while(it.hasNext()){
String var = it.next();
if (buf.length() > 0) buf.append(", ");
buf.append("\"" + var.toString() +"\": \"" + vars.get(var) + "\"");
}
String result = "{" + buf + "}\n";
return result;
}
public ProjopObject getParent() {
return parentObject;
}
public void setParent(ProjopObject parentObject) {
this.parentObject = parentObject;
}
public void setChildObjects(Vector<ProjopObject> childObjects) {
this.childObjects = childObjects;
}
public Vector<ProjopObject> getChildObjects() {
return childObjects;
}
/*
* Small helper for working with XML in string representation
*/
public static String htmlEncode(String s) {
String result = s;
result = result.replaceAll("&", "&amp;");
result = result.replaceAll("<", "&lt;");
result = result.replaceAll(">", "&gt;");
result = result.replaceAll("\"", "&quot;");
return result;
}
/**
* Fetch a new ProjopObject based on objectType and ID.
*
* @param objectType The ]po[ object type ("im_project", ...)
* @param oid The ]po[ objectId
* @return A ProjopObject with all available fields
*/
public static ProjopObject fromId(String objectType, int oid) {
RESTClient rest = RESTClient.defaultInstance();
ProjopObject o = rest.fromId(objectType, oid);
return o;
}
/**
* Get a value from the object.
*
* @param varName
* @return String value of varName, or an empty string if it doesn't exist.
*/
public String get(String varName) {
if (!(vars.containsKey(varName))) return ""; // Default: return an empty string.
return (String)vars.get(varName);
}
/**
* Get a value from the object and convert to integer.
* Will throw an exception of that fails.
* @param varName
* @return String value of varName, or an empty string if it doesn't exist.
*/
public int getInt(String varName) {
return Integer.parseInt(this.get(varName));
}
/**
* Set a value of the object.
* @param varName
* @param value
*/
public void set(String varName, String value) {
vars.put(varName, value);
}
public String getObjectType() {
return objectType;
}
/**
* Determines the position of an object type in the static list
* of object types.
* @param objectType Object type of the object
* @return The index of the object, or -1 if not supported or found.
*/
private static int getObjectTypeIdx(String objectType) {
int idx = -1;
for (int i = 0; i < otypeFieldArray.length; i++) {
if (otypeFieldArray[i] == objectType) {
idx = i;
break;
}
}
return idx;
}
public static String getObjectIdField(String objectType) {
int idx = getObjectTypeIdx(objectType);
if (idx < 0) return "";
return idFieldArray[idx];
}
/**
* Every ProjopObject should(!) have a field with the object
* type. Whether this is really the case depends on the data
* returned from the ]po[ REST interface, amongst other sources.
* @return the objectId of the object.
*/
public int getObjectId() {
String field = getObjectIdField(this.objectType);
if ("" == field) return 0;
String value = this.get(field);
if (null == value) return 0;
if ("" == value) return 0;
return Integer.parseInt(value);
}
/**
* Every ProjopObject should(!) have a field with the object
* name. Whether this is really the case depends on the data
* returned from the ]po[ REST interface, amongst other sources.
* @return the objectId of the object.
*/
public String getObjectName() {
int idx = getObjectTypeIdx(this.objectType);
if (idx < 0) return "";
String field = nameFieldArray[idx];
String value = this.get(field);
if (null == value) return "";
return value;
}
/**
* Most ProjopObject have a field with a statusId.
* Whether this is really the case depends on the data
* returned from the ]po[ REST interface, amongst other sources.
* @return the statusId of the object.
*/
public int getStatusId() {
int idx = getObjectTypeIdx(this.objectType);
if (idx < 0) return 0;
String field = statusFieldArray[idx];
String value = this.get(field);
if (null == value) return 0;
if ("" == value) return 0;
return Integer.parseInt(value);
}
/**
* Most ProjopObject have a field with a typeId.
* Whether this is really the case depends on the data
* returned from the ]po[ REST interface, amongst other sources.
* @return the typeId of the object.
*/
public int getTypeId() {
int idx = getObjectTypeIdx(this.objectType);
if (idx < 0) return 0;
String field = typeFieldArray[idx];
String value = this.get(field);
if (null == value) return 0;
if ("" == value) return 0;
return Integer.parseInt(value);
}
/**
* @return The raw key-value hash of the object.
*/
public Hashtable<String,String> getVars() { return vars; }
// -----------------------------------------------------------------------
// REST Application Level Methods
// -----------------------------------------------------------------------
/**
* Try to construct an object name from name field, type and objectId.
*/
public String toString() {
String name = this.getObjectName();
String type = this.getObjectType();
int id = this.getObjectId();
if (type != "") { name = name + " (" + type + ")"; }
if (type != "") { name = name + " +" + id; }
return name;
}
/**
* Implementation of Comparable...
*/
public int compareTo(ProjopObject o) {
if (!(o instanceof ProjopObject)) { return -1; };
ProjopObject oObj = (ProjopObject)o;
int typeComp = this.objectType.compareTo(oObj.objectType);
if (0 != typeComp) return typeComp;
// The object type is the same...
return this.getObjectName().compareTo(oObj.getObjectName());
}
/**
* Write object to REST Server:
*
* Accepts a newly setup ProjopObject that doesn't yet contain an objectId.
* It uses the generic "Create" POST operation to create a new object on
* the ]po[ server and stores the returned objectId in the ProjopObject.
*
* Creating new ]po[ objects requires a number of parameters that depend on
* the object type. Please consult the ]po[ documentation for details on
* these required fields.
*
* @param objectType The type of the new object to be created
* @param vars A Hashtable with variable-value pairs to
* be sent to the server
* @return Returns the objectId of the new ]po[ object.
*/
public void persist() {
RESTClient rest = RESTClient.defaultInstance();
rest.createUpdateObject(this);
}
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
ProjopObject o1 = new ProjopObject("im_project");
System.out.println("Retriving all keys from the Hashtable");
Enumeration<String> e = o1.getVars().keys();
while( e.hasMoreElements() ){
System.out.println( e.nextElement() );
}
}
}
package org.projectopen.rest;
/*
* Copyright (C) 2010 ]project-open[
*
* This program is free software. You can redistribute it
* and/or modify it under the terms of the GNU General
* Public License as published by the Free Software Foundation;
* either version 2 of the License, or (at your option)
* any later version. This program 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 General Public License for more details.
*
* @author Frank Bergmann (frank.bergmann@project-open.com)
*
*/
import java.util.*;
public class ProjopObjectType extends ProjopObject {
// A cache for object types, so that every object type
// only exists once in the system.
private static Hashtable<String,ProjopObjectType> objectTypes = new Hashtable<String,ProjopObjectType>();
// Parent object type. "acs_object" is the root of all object
// types. Only acs_objects has NULL as parentObjectType.
private ProjopObjectType parentObjectType = null;
// List of object types that are inheriting from this one.
private Vector<ProjopObjectType> childObjectTypes = new Vector<ProjopObjectType>();
// Sorted list of objects of this object type.
private TreeMap<Integer,ProjopObject> instances = null;
// A sorted vector of children, so that the ObjectBrowser
// can show them as a sorted list.
private Vector<ProjopObjectType> childrenForObjectBrowser = null;
// A list of variables available in the an arbitrary
// first element of this object type.
private ArrayList<String> varList = null;
public ProjopObjectType() {
super("acs_object_type");
}
/**
* Returns an ordered list of all variable names
* available for this object type. The order of the
* variables is arbitrary at the moment. In the future
* this should be replaced by data from DynFields.
* @return
*/
public ArrayList<String> getVarList() {
if (null == varList) {
varList = new ArrayList<String>();
Object firstKey = this.getInstances().firstKey();
Object first = this.instances.get(firstKey);
ProjopObject o = (ProjopObject)first;
Hashtable<String,String> vars = o.getVars();
Enumeration<String> keys = vars.keys();
while (keys.hasMoreElements()) {
String key = (String)keys.nextElement();
varList.add(key);
}
}
return varList;
}
public static Hashtable<String,ProjopObjectType> getObjectTypes() { return objectTypes; }
public static ProjopObjectType getObjectType(String objectType) {
if (null == objectTypes.get(objectType)) {
ProjopObjectType t = new ProjopObjectType();
t.set("object_type",objectType);
objectTypes.put(objectType, t);
}
return (ProjopObjectType)objectTypes.get(objectType);
}
public ProjopObjectType getParentObjectType() { return parentObjectType; }
public void setParentType(ProjopObjectType parentType) { this.parentObjectType = parentType; }
public void setChildTypes(Vector<ProjopObjectType> childTypes) { this.childObjectTypes = childTypes; }
public Vector<ProjopObjectType> getChildObjectTypes() { Collections.sort(childObjectTypes); return childObjectTypes; }
public void set(String varName, String value) {
super.set(varName, value);
// Add this object to the list of object types
// to maintain a global cache
if ("object_type" == varName) {
objectTypes.put(value, this);
}
}
public TreeMap<Integer,ProjopObject> getInstances() {
RESTClient rest = RESTClient.defaultInstance();
if (null == instances) {
// Get the list of instances
List<ProjopObject> is = null;
String objectTypeString = this.get("object_type");
if ("acs_object_type" != objectTypeString) {
is = rest.fromQuery(objectTypeString, "");
}
// Setup the new instanceHash
instances = new TreeMap<Integer,ProjopObject>();
if (null != is) {
Iterator<ProjopObject> e = is.iterator();
while(e.hasNext()) {
ProjopObject o = (ProjopObject)e.next();
Integer i = new Integer(o.getObjectId());
instances.put(i, o);
}
}
}
return instances;
}
/**
* Return an ordered list of children suitable for
* being displayed in the ObjectBrowserTreeModel.
* @return A list of 1) sub-object types and 2)
* of instance objects.
*/
public List<ProjopObjectType> getChildrenForObjectBrowser() {
if (null == childrenForObjectBrowser) {
// clone the child object types
childrenForObjectBrowser = new Vector<ProjopObjectType>(getChildObjectTypes());
}
return childrenForObjectBrowser;
}
public String toString() {
String name = this.get("pretty_name");
if (null == name || "" == name) { name = this.get("object_type"); }
return name;
}
}
package org.projectopen.rest;
/*
* Copyright (C) 2010 ]project-open[
*
* This program is free software. You can redistribute it
* and/or modify it under the terms of the GNU General
* Public License as published by the Free Software Foundation;
* either version 2 of the License, or (at your option)
* any later version. This program 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 General Public License for more details.
*
* @author Frank Bergmann (frank.bergmann@project-open.com)
*
*/
import java.util.*;
import org.json.simple.*;
// @SuppressWarnings("unchecked")
/**
* Class representing a ]po[ Project.
* Values are stored in a key-value list, so the object adapts
* to whatever attributes the REST interface returns.
* Values are stored as strings using TCL string representation,
* independent from the actual type. Convenient...
*/
public class ProjopProject extends ProjopObject {
public ProjopProject() {
super("im_project"); // ]po[ name of this object type
}
public ProjopProject(JSONObject el) {
super("im_project", el); // ]po[ name of this object type
}
public String toString() {
String name = get("project_name");
if (name == null) { name = super.toString(); }
return name;
}
/**
* Query the REST server for a list projects available for
* timesheet logging
*
* @return A list of ProjopHour objects representing the hours
* logged today.
*/
public static List<ProjopObject> myTimesheetProjects() {
RESTClient rest = RESTClient.defaultInstance();
String urlPath = "/intranet-reporting/view?format=json&report_code=rest_my_timesheet_projects";
return rest.fromURL("im_project", urlPath);
}
}
package org.projectopen.rest;
import java.util.List;
import org.projectopen.debug.Logger;
/*
* Copyright (C) 2010 ]project-open[
*
* This program is free software. You can redistribute it
* and/or modify it under the terms of the GNU General
* Public License as published by the Free Software Foundation;
* either version 2 of the License, or (at your option)
* any later version. This program 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 General Public License for more details.
*
* @author Frank Bergmann (frank.bergmann@project-open.com)
*
*/
/**
* Represents a ]project-open[ user object.
*/
public class ProjopUser extends ProjopObject {
public ProjopUser() {
super("user");
}
public String toString() {
String name = get("username");
if (name == null) { name = super.toString(); }
return name;
}
/**
* What is the ]po[ userId of the user that is logged in?
* This function asks the REST server, based on the email
* of the login credentials.
* However, the RESTClient contains a cached version of this
* ID which it gets during intialization, so you probably
* never have to call this function.
*
* @return userId of the user currently logged into the REST interface.
*/
public static int getMyUserIdFromServer() {
int userId = -1;
RESTClient client = RESTClient.defaultInstance();
String email = client.getProperties().getProperty("email");
ProjopObject user = ProjopUser.fromEmail(email);
if (null != user) {
userId = Integer.parseInt(user.get("user_id"));
}
return userId;
}
/**
* Retrieve all information about a ]po[ specific user, identified
* by the user's email (which is unique in ]po[).
*
* @param email The email of a ]po[ user
* @return a ProjopObject of type "user" with everything about the user.
*/
public static ProjopObject fromEmail(String email) {
RESTClient client = RESTClient.defaultInstance();
client.logMessage(Logger.INFO, "RESTClient.restReadUserFromEmail("+email+")", "Query the REST server for a user with the specified email", "");
List<ProjopObject> objects = client.fromQuery("user", "email = '"+email+"'");
if (objects == null) { return null; }
if (objects.isEmpty()) { return null; }
ProjopObject o = objects.get(0);
client.logMessage(Logger.INFO, "RESTClient.restReadUserFromEmail("+email+")", "Query the REST server for a user with the specified email", o.toString());
return o;
}
}
package org.projectopen.rest;
/*
* Copyright (C) 2010 ]project-open[
*
* This program is free software. You can redistribute it
* and/or modify it under the terms of the GNU General
* Public License as published by the Free Software Foundation;
* either version 2 of the License, or (at your option)
* any later version. This program 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 General Public License for more details.
*
* @author Frank Bergmann (frank.bergmann@project-open.com)
*
*/
import java.io.*;
import java.net.*;
import java.util.*;
import java.util.regex.Pattern;
import org.json.simple.*;
import org.json.simple.parser.*;
import org.projectopen.debug.*;
import sun.misc.BASE64Encoder;
// @SuppressWarnings({ "unchecked", "deprecation" })
/**
* Main interface class to the ]project-open[ REST interface.
* This class encapsulates all HTTP communication with the
* server and provides the rest of the application with access
* to the application specific functionality.
*/
public class RESTClient implements Logger {
/**
* Configuration properties including server, email and password.
*/
private Properties properties = null;
/*
* Default instance for singleton pattern.
*/
private static RESTClient defaultInstance = null;
/**
* Link to calling application that needs to implement the logger
* interface to show system messages.
*/
private Logger logger = null;
/**
* UserId of the user currently logged into the REST server.
*/
protected int myUserId = 0;
// REST Version Management
private static String clientRESTVersion = "3.0";
private String serverRESTVersion = null;
// -----------------------------------------------------------------------
//
// -----------------------------------------------------------------------
/**
* Constructor - requires to be started from an applet with
* logging functionality.
*
* @param starterApp
*/
public RESTClient(Logger starterApp) {
super();
logger = starterApp; // Save for logging output
RESTClient.defaultInstance = this;
// Check if we can work with the REST version on the server side
serverRESTVersion = restReadServerRESTVersion();
this.checkRESTVersionCompatibility(serverRESTVersion);
// GET the ID of the login user.
myUserId = ProjopUser.getMyUserIdFromServer();
}
/**
* Return a default RESTClient.
* Most applications in this world will only need one connection
* to a REST server...
* @return A default RESTClient
*/
public static RESTClient defaultInstance () {
if (null == defaultInstance) {
System.out.println("ERROR: default instance not yet initialized, so we can't write out error. Please call RESTClient.setupDefaultInstance(..) first.");
defaultInstance = new RESTClient(null);
}
return defaultInstance;
}
// -----------------------------------------------------------------------
// REST Version Methods
// -----------------------------------------------------------------------
/**
* Determines the server's REST version.
* Major changes in the version digit indicate incompatibilities.
* Minor changes indicated protocol additions.
*
* @return The server's REST protocol version
*/
public String restReadServerRESTVersion() {
JSONObject dom = null;
logMessage(Logger.INFO, "RESTClient.restReadVersion", "Get the server's REST version", "");
try {
String urlPath = "/intranet-rest/version?format=json";
dom = this.httpRequest("GET", urlPath, null);
} catch (Exception e) {
logMessage(Logger.ERROR, "RESTClient.restReadVersion", "Error connecting to server.", e.toString());
return "";
}
if (null == dom) return "";
return ((String)dom.get("version")).trim();
}
/**
* Check if we can deal with the REST version on
* the server.
*/
private void checkRESTVersionCompatibility(String serverRESTVersion) {
// Expect something like: "2.11"
Pattern p = Pattern.compile("\\.");
String serverPieces[] = p.split(serverRESTVersion);
int len = serverPieces.length;
String clientPieces[] = p.split(clientRESTVersion);
int clientPiecesLen = clientPieces.length;
if (clientPiecesLen < len) { len = clientPiecesLen; }
boolean ok = true;
// Check the "major" version.
// Incompatible if different
if ("" == serverPieces[0]) {
this.logMessage(Logger.FATAL, "REST version retreival error", "The server did not provide a major REST version.", "");
System.exit(0);
}
int serverVer = Integer.parseInt(serverPieces[0]);
int clientVer = Integer.parseInt(clientPieces[0]);
if (clientVer != serverVer) { ok = false; }
// Check the "minor" version.
// It's OK if the server version is higher.
if ("" == serverPieces[1]) {
this.logMessage(Logger.FATAL, "REST version retreival error", "The server did not provide a minor REST version.", "");
System.exit(0);
}
serverVer = Integer.parseInt(serverPieces[1]);
clientVer = Integer.parseInt(clientPieces[1]);
if (serverVer < clientVer) { ok = false; }
if (!ok) {
this.logMessage(Logger.FATAL, "REST Protocol Mismatch", "The server doesn't provide the required version of the REST interface.", "REST V"+serverRESTVersion + " incompatible with REST V" + clientRESTVersion);
System.exit(0);
} else {
this.logMessage(Logger.INFO, "REST Protocol Match", "The client and server REST protocol versions are compatible. Server: "+serverRESTVersion + ", Client: "+clientRESTVersion,"");
}
}
// -----------------------------------------------------------------------
// Properties Methods
// -----------------------------------------------------------------------
/**
* Determine the parameters necessary to connect to the
* server. Properties are initialized with the a default
* login to the ]po[ main demo server.
* User can modify these properties and store them in the
* local application folder.
*
* @return A properties object with the configuration.
*/
public Properties getProperties() {
if (properties == null) {
// Setup the "Ben Bigboss" account. Ben is the
// head of the "Tigerpond" ]po[ sample company.
properties = new Properties();
properties.put("server", "http://192.168.52.128/");
properties.put("email", "bbigboss@tigerpond.com");
properties.put("password", "ben");
try {
// now load properties from last invocation
FileInputStream in = new FileInputStream("config.properties");
properties.load(in);
in.close();
} catch (Exception e) {
// ignore if not there
// e.printStackTrace();
}
logMessage(Logger.INFO, "RESTClient.getProperties()", "Loaded properties from 'config.properties'", properties.toString());
}
return properties;
}
/**
* Store modified properties to the local application directory.
*/
public void storeProperties() {
logMessage(Logger.INFO, "RESTClient.storeProperties()", "Storing properties to 'config.properties'", properties.toString());
FileOutputStream out;
try {
out = new FileOutputStream("config.properties");
properties.store(out, "--- Parameters to access a ]project-open[ server ---");
out.close();
} catch (Exception e) {
logMessage(Logger.ERROR, "RESTClient.storeProperties()", "Error storing properties to 'config.properties'", e.toString());
}
}
/**
* Delete all cached information.
* This is necessary when changing the configuration
* or when the next day has started if the application
* is running at night.
*/
public void flushCache() {
properties = null;
// parent: don't touch
// defaultInstance: don't touch
}
/**
* Accepts log messages from the REST interactions
* and GUI elements and logs them 1) to the DebugPanel
* and 2) to the TrayIconStarter, where ERROR and FATAL
* messages are displayed to the user.
*/
public void logMessage(int level, String domain, String message, String details) {
// Always write to System.out
String levelString = "undefined";
switch (level) {
case Logger.DEBUG: levelString = "DEBUG"; break;
case Logger.INFO: levelString = "INFO"; break;
case Logger.WARNING: levelString = "WARNING"; break;
case Logger.ERROR: levelString = "ERROR"; break;
case Logger.FATAL: levelString = "FATAL"; break;
}
System.out.println(levelString + ": " + domain + ": " + message + ": "+details);
if (null != logger) { // pass the log message to the parent, if it's set correctly.
logger.logMessage(level, domain, message, details);
}
}
/**
* Launch a Web browser in the user's desktop with a
* URL including his password for automatic login.
*/
public void restBrowserLogin() {
logMessage(Logger.INFO, "RESTClient.restBrowserLogin()", "Launch a Web browser.", "");
String server = getProperties().getProperty("server");
String email = getProperties().getProperty("email");
String password = getProperties().getProperty("password");
try {
URI uri = new java.net.URI(server + "/intranet/auto-login?email=" + email + "&password="+password + "&url=/intranet-timesheet2/hours/index");
java.awt.Desktop.getDesktop().browse(uri);
} catch (Exception e1) {
System.err.println(e1.getMessage());
}
};
// -----------------------------------------------------------------------
// REST HTTP Level Methods
// -----------------------------------------------------------------------
/**
* The server has returned a reply like this one:
* { success: 'false', message: 'Not authenticated' }
*/
protected void httpError(int httpResponseCode, URL url, JSONObject obj) {
// Extract the information from the XML error text
String message = (String)obj.get("message");
// Format a nice message that will be sent to the user's tray icon
String urlQuery = url.getQuery();
if (urlQuery == null) { urlQuery = ""; } else { urlQuery = "/"+urlQuery; }
String details = "" +
"Status: " + httpResponseCode +
"\nURL: " + url.getPath() + urlQuery;
logMessage(Logger.ERROR, "Application Error", "Server returned: "+message + "\n", details);
}
/**
* Perform a REST HTTP request.
*
* @param method Either "GET" or "POST"
* @param urlPath The path part of the URL including a leading slash,
* for example "/intranet-rest/index"
* @param body Input stream for the XML data to send to the server
* for POST operations. Ignored for GET operations.
* @return A XML document with the parsed REST response.
* Returns NULL in case of an error.
*/
@SuppressWarnings("deprecation")
public JSONObject httpRequest(String method, String urlPath, InputStream body) {
String server = getProperties().getProperty("server");
String email = getProperties().getProperty("email");
String password = getProperties().getProperty("password");
HttpURLConnection connection;
int httpResponseCode = 0;
// Determine the right URL
URL url = null;
try {
url = new URL(server + urlPath);
} catch (MalformedURLException e) {
e.printStackTrace();
}
logMessage(Logger.DEBUG, "HTTP Interaction", "-> " + method + ": " + url, "");
// Open the connection
try {
connection = (HttpURLConnection)url.openConnection();
connection.setRequestMethod(method);
} catch (IOException e1) {
logMessage(Logger.ERROR, " Connection Error", "Unable to connect to server " + server, "");
return null;
}
// Create and write Basic HTTP Authentication header
BASE64Encoder encoder = new BASE64Encoder();
String encodedCredential = encoder.encode( (email + ":" + password).getBytes() );
connection.setRequestProperty("Authorization", "BASIC " + encodedCredential);
// Write body if we're doing POST or PUT
byte buffer[] = new byte[8192];
int read = 0;
if (body != null && method != "DELETE") {
try {
connection.setDoOutput(true);
OutputStream output = connection.getOutputStream();
while ((read = body.read(buffer)) != -1) { output.write(buffer, 0, read); }
} catch (IOException e2) {
logMessage(Logger.ERROR, "Connection Error", "Unable to write to connection to server " + server, "");
return null;
}
}
// do request
try {
connection.connect();
httpResponseCode = connection.getResponseCode();
} catch (IOException e2) {
logMessage(Logger.ERROR, "Connection Error", "Unable to connect to server " + server, "");
return null;
}
logMessage(Logger.DEBUG, "HTTP Interaction", "<- httpResponseCode="+httpResponseCode +" from " + url, "");
/*
* Check for connection errors. In particular, deal with
* application specific errors like insufficient permissions
* etc.
*/
InputStream responseBodyStream = null;
String response = "";
// 200 = HTTP Success.
if (200 == httpResponseCode) {
try {
responseBodyStream = connection.getInputStream();
InputStreamReader reader = new InputStreamReader(responseBodyStream);
BufferedReader br = new BufferedReader(reader);
String line;
while ((line = br.readLine()) != null) {
response += line + "\n";
}
} catch (IOException e) {
logMessage(Logger.ERROR, "Connection Error", "Unable to read from server", "");
return null;
}
} else {
// Any code != 200 means application error
try {
// Take the "ErrorStream" from the connection to check for an error message
responseBodyStream = connection.getErrorStream();
char charBuffer[] = new char[8192];
StringBuilder errorXmlString = new StringBuilder();
Reader in = new InputStreamReader(responseBodyStream, "UTF-8");
do {
read = in.read(charBuffer, 0, buffer.length);
if (read > 0) { errorXmlString.append(charBuffer, 0, read); }
} while (read >= 0);
logMessage(Logger.ERROR, "Application Error", "The REST server reports an application error.", errorXmlString.toString());
responseBodyStream = new StringBufferInputStream(errorXmlString.toString());
} catch (IOException e) {
logMessage(Logger.ERROR, "Connection Error", "Unable to read from server " + server, "");
return null;
}
}
JSONObject dom = null;
try {
JSONParser p = new JSONParser();
dom = (JSONObject)p.parse(response);
} catch (Exception e1) {
logMessage(Logger.ERROR, "Application Error", "Unable to parse the JSON reply from server " + server + " / " + urlPath, response);
return null;
}
// Check if the returned object contains a "success" field
Object success = dom.get("success");
if (success == null) {
logMessage(Logger.ERROR, "Application Error", "JSON reply does not contain a 'success' field: " + server + " / " + urlPath, response);
return null;
}
String successClass = success.getClass().getSimpleName();
switch (successClass) {
case "Boolean":
Boolean successBoolean = (Boolean)success;
if (successBoolean != true) {
logMessage(Logger.ERROR, "Application Error", "JSON request returned a 'false' boolean success status from: " + server + " / " + urlPath, response);
return null;
}
break;
case "String":
String successString = (String)success;
if (!"true".equals(successString)) {
logMessage(Logger.ERROR, "Application Error", "JSON request returned a 'false' string success status from: " + server + " / " + urlPath, response);
return null;
}
break;
default:
logMessage(Logger.ERROR, "Application Error", "JSON request returned a success status of type: " + successClass + " from: " + server + " / " + urlPath, response);
return null;
}
// An error response (responseCode != 200) contains an error
// message that we can pass to the user as a TrayIcon message
if (httpResponseCode != 200) {
httpError(httpResponseCode, url, dom);
dom = null; // Reset the dom to NULL as a
}
return dom;
}
/**
* GET a single object identified by an objectType and an objectId.
*
* Please use this method only in special cases, because retreiving
* each object this way would cause a lot of HTTP traffic and be very
* slow.
*
* @param objectType ]po[ object type ("im_project", "user", "im_hour", ...)
* @param objectId ]po[ object ID
* @return ProjopObject filled with the object's information
* or NULL in case of an error.
*/
public ProjopObject fromId(String objectType, int objectId) {
logMessage(Logger.INFO, "RESTClient.restReadObjectFromId("+objectType+","+objectId+")", "Retreive a specific object", "");
JSONObject dom = null;
try {
String urlPath = "/intranet-rest/"+objectType+"/"+objectId+"?format=json";
dom = this.httpRequest("GET", urlPath, null);
} catch (Exception e) {
logMessage(Logger.ERROR, "RESTClient.readObjectFromId("+objectType+","+objectId+")", "Error reading server data", "");
e.printStackTrace();
return null;
}
// Extract object from. "data" should contain a list with exactly one instance.
if (null == dom) { return null; }
Object data = dom.get("data");
if (!(data instanceof JSONArray)) {
logMessage(Logger.ERROR, "RESTClient.readObjectFromId("+objectType+","+objectId+")", "Server returned invalid data.", data.toString());
return null;
}
JSONArray projectList = (JSONArray)data;
if (projectList.size() == 0) {
logMessage(Logger.ERROR, "RESTClient.readObjectFromId("+objectType+","+objectId+")", "Server returned a list with zero objects", data.toString());
return null;
}
if (projectList.size() > 1) {
logMessage(Logger.WARNING, "RESTClient.readObjectFromId("+objectType+","+objectId+")", "Server returned more than one object, ignoring all but the first one.", data.toString());
}
JSONObject oJSON = (JSONObject)projectList.get(0);
ProjopObject o = new ProjopObject(objectType, oJSON);
// Validity check: Make sure the new object contains an objectId
int oid = o.getObjectId();
if (oid != objectId) {
logMessage(Logger.WARNING, "RESTClient.readObjectFromId("+objectType+","+objectId+")", "Server returned an object with an objectId different from the one requested: "+oid+" != "+objectId, data.toString());
String objectIdField = ProjopObject.getObjectIdField(objectType);
o.set(objectIdField, ""+objectId);
}
// Add the new object to the list of instances of this object type
ProjopObjectType.getObjectType(objectType).getInstances().put(new Integer(objectId), o);
// show a log message
logMessage(Logger.INFO, "RESTClient.readObjectFromId("+objectType+","+objectId+")", "Successfully retreived object", o.toString());
return o;
}
/**
* GET a list of objects. The REST server returns objects always in the same format.
*
* @param objectType ]po[ object type ("im_project", "im_hour", "user", ...)
* @param query A SQL where clause with fields matching the object_type.
* For example, the "user" object defines the fields "email",
* "first_names" etc.
* @return A list of ProjopObject who have matched the SQL query.
*/
public List<ProjopObject> fromURL(String objectType, String urlPath) {
logMessage(Logger.INFO, "RESTClient.readObjectsFromURL("+objectType+","+urlPath+")", "Read objects from REST server", "");
List<ProjopObject> myObjects = new ArrayList<ProjopObject>();
JSONObject dom = httpRequest("GET", urlPath, null);
if (dom == null) { return null; }
Object data = dom.get("data");
if (!(data instanceof JSONArray)) {
logMessage(Logger.ERROR, "RESTClient.readObjectsFromURL("+objectType+","+urlPath+")", "Server returned an invalid list of objects.", data.toString());
}
// Iterate through the list of <row> entries, adding the data to the list
JSONArray dataJSON = (JSONArray)data;
for (int i = 0; i < dataJSON.size(); i++) {
JSONObject el = (JSONObject)dataJSON.get(i);
ProjopObject p = new ProjopObject(objectType, el);
myObjects.add(p);
}
logMessage(Logger.INFO, "RESTClient.readObjectsFromURL("+objectType+","+urlPath+")", "Successfully retreived "+myObjects.size()+" objects from REST server", "");
return myObjects;
}
/**
* Create or update an object on the REST Server:
*
* Accepts a newly setup ProjopObject that doesn't yet contain an objectId.
* It uses the generic "Create" POST operation to create a new object on
* the ]po[ server and stores the returned objectId in the ProjopObject.
*
* Creating new ]po[ objects requires a number of parameters that depend on
* the object type. Please consult the ]po[ documentation for details on
* these required fields.
*
* @param objectType The type of the new object to be created
* @param vars A Hashtable with variable-value pairs to
* be sent to the server
* @return Returns the objectId of the new ]po[ object.
*/
public void createUpdateObject(ProjopObject o) {
// Convert into a string that can be passed directly to the ]po[ REST interface.
String json = o.toJSONString(); // Convert into marshalling format
String objectType = o.getObjectType();
String urlPath = "/intranet-rest/"+objectType; // Determine the URL for the REST request.
// Check if we are going to "create" or "update"
// Create uses the generic urlPath, update ads the objectId.
// These are standard REST conventions.
int objectId = o.getObjectId();
if (0 != objectId) {
urlPath = urlPath + "/" + objectId;
}
// Perform the actual HTTP request to update/create the object in ]project-open[
JSONObject response = null;
try {
StringBuffer buf = new StringBuffer(json);
ByteArrayInputStream bis = new ByteArrayInputStream(buf.toString().getBytes("UTF-8"));
response = this.httpRequest("POST", urlPath, bis);
} catch (Exception e) {
e.printStackTrace();
}
// Check the returned value. We'll need to extract the object_id and it to the object.
if (response == null) {
this.logMessage(Logger.ERROR, "Application Error", "Problems saving an object "+o.getObjectName(), "The server returned an empty reply");
return;
}
// Check if the returned object contains a "success" field
Object success = response.get("success");
if (null == success) {
this.logMessage(Logger.ERROR, "Application Error", "Server returned JSON without 'success' field from: " + urlPath, response.toString());
return;
}
String successClass = success.getClass().getSimpleName();
switch (successClass) {
case "Boolean":
Boolean successBoolean = (Boolean)success;
if (successBoolean != true) {
this.logMessage(Logger.ERROR, "Application Error", "JSON request returned a 'false' boolean success status from: " + urlPath, response.toString());
return;
}
break;
case "String":
String successString = (String)success;
if (!"true".equals(successString)) {
this.logMessage(Logger.ERROR, "Application Error", "JSON request returned a 'false' string success status from: " + urlPath, response.toString());
return;
}
break;
default:
this.logMessage(Logger.ERROR, "Application Error", "JSON request returned a success status of type: " + successClass + " from: " + urlPath, response.toString());
return;
}
if (0 == objectId) {
// We tried to create a new object.
// Check if the JSON return data include an object_id field (which they should in case of a create)
Object objectIdString = response.get("object_id");
this.logMessage(Logger.INFO, "Received object_id", "CREATE: Received object_id from server.", ""+objectIdString);
if (objectIdString instanceof Integer) {
String idField = ProjopObject.getObjectIdField(o.getObjectType());
objectId = Integer.parseInt(""+objectIdString);
o.set(idField, ""+objectId);
this.logMessage(Logger.INFO, "CREATE", "Successfully created the object #"+objectId + " via " + urlPath, "");
} else {
this.logMessage(Logger.ERROR, "Application Error", "CREATE: Received bad objectId from server: " + urlPath, ""+objectId);
}
} else {
this.logMessage(Logger.INFO, "UPDATE", "Successfully updated the object #"+objectId + " via " + urlPath, "");
}
return;
}
/**
* Generic "list" request towards the REST server that will return a list of objects
* according to the specified SQL query string.
* @param objectType ]po[ object type ("im_project", "im_hour", "user", ...)
* @param query A SQL where clause with fields matching the object_type.
* For example, the "user" object defines the fields "email",
* "first_names" etc.
* @return A list of ProjopObject who have matched the SQL query.
*/
public List<ProjopObject> fromQuery(String objectType, String query) {
logMessage(Logger.INFO, "RESTClient.readObjectsFromQuery("+objectType+","+query+")", "Query REST server for objects matching the SQL where clause", "");
String encodedQuery = null;
try {
encodedQuery = URLEncoder.encode(query, "UTF-8");
} catch (Exception e) {
logMessage(Logger.ERROR, "RESTClient.readObjectsFromQuery("+objectType+","+query+")", "Error encoding query", query);
e.printStackTrace();
return null;
}
// http://demo.project-open.net/intranet-rest/user?format=json&query=username%20like%20'sys%'
String urlPath = "/intranet-rest/" + objectType + "?format=json&query="+encodedQuery;
List<ProjopObject> list = this.fromURL(objectType, urlPath);
logMessage(Logger.INFO, "RESTClient.readObjectsFromQuery("+objectType+","+query+")", "Query REST server for objects matching the SQL where clause", list.toString());
return list;
}
public int getMyUserId() { return myUserId; }
}
/**
*
*/
package org.projectopen.serverstatus;
import java.awt.*;
import java.util.*;
import javax.swing.*;
import org.projectopen.debug.*;
/**
* @author fraber
*
*/
public class ServicePanel extends JPanel implements Logger {
/**
*
*/
private static final long serialVersionUID = 6360295344991781929L;
private static Logger logger = null;
public ServicePanel(Map map) {
// Setup the panel
super();
this.setLayout(new FlowLayout(FlowLayout.CENTER, 7, 7)); // alignment, horizontal gap and vertical gap
// Set transparent background
this.setOpaque(false);
// Extract the interesting values from the map
String serviceName = map.containsKey("service_name") ? (String)map.get("service_name") : "";
String toolTipText = map.containsKey("service_tooltip") ? (String)map.get("service_tooltip") : "";
String installStatus = map.containsKey("installed_status") ? (String)map.get("installed_status") : "clear";
String processStatus = map.containsKey("process_status") ? (String)map.get("process_status") : "clear";
String portStatus = map.containsKey("port_status") ? (String)map.get("port_status") : "clear";
String logStatus = map.containsKey("log_status") ? (String)map.get("log_status") : "clear";
// calculate service status as a summary of individual states
String serviceStatus = "green";
if ("orange" == installStatus | "orange" == processStatus | "orange" == portStatus | "orange" == logStatus) { serviceStatus = "orange"; }
if ("red" == installStatus | "red" == processStatus | "red" == portStatus | "red" == logStatus) { serviceStatus = "red"; }
// Status + Service Description
ImageIcon imageIcon = new ImageIcon(ServicesPanel.class.getResource("/org/projectopen/serverstatus/" + serviceStatus + ".100.gif"));
JLabel serivceLabel = new JLabel(imageIcon);
serivceLabel.setToolTipText(toolTipText);
serivceLabel.setText(serviceName);
serivceLabel.setHorizontalTextPosition(JLabel.CENTER);
serivceLabel.setVerticalTextPosition(JLabel.CENTER);
this.add(serivceLabel);
// Install Status
imageIcon = new ImageIcon(ServicesPanel.class.getResource("/org/projectopen/serverstatus/bb_" + installStatus + ".gif"));
JLabel installLabel = new JLabel(imageIcon);
this.add(installLabel);
// Process Status
imageIcon = new ImageIcon(ServicesPanel.class.getResource("/org/projectopen/serverstatus/bb_" + processStatus + ".gif"));
JLabel processLabel = new JLabel(imageIcon);
this.add(processLabel);
// Port Status
imageIcon = new ImageIcon(ServicesPanel.class.getResource("/org/projectopen/serverstatus/bb_" + portStatus + ".gif"));
JLabel portLabel = new JLabel(imageIcon);
this.add(portLabel);
// log Status
imageIcon = new ImageIcon(ServicesPanel.class.getResource("/org/projectopen/serverstatus/bb_" + logStatus + ".gif"));
JLabel logLabel = new JLabel(imageIcon);
this.add(logLabel);
}
/* (non-Javadoc)
* @see org.projectopen.debug.Logger#logMessage(int, java.lang.String, java.lang.String, java.lang.String)
*/
@Override
public void logMessage(int level, String domain, String message,
String details) {
// TODO Auto-generated method stub
}
/**
* @param args
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
public static void main(String[] args) {
HashMap map = new HashMap();
map.put("service_name", "AOLserver");
map.put("service_status", "green");
map.put("port_status", "yellow");
map.put("process_status", "green");
map.put("port_status", "yellow");
// Launch a frame with the panel
JFrame frame = new JFrame("Single Service Panel");
frame.setBounds(0, 0, 400, 300);
JPanel p = new ServicePanel(map);
frame.getContentPane().add(p);
frame.setVisible(true);
}
}
package org.projectopen.serverstatus;
import java.io.*;
import java.net.*;
import java.util.*;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import org.projectopen.debug.*;
@SuppressWarnings({"unchecked", "rawtypes"})
public class ServicesPanel extends JPanel {
/**
*
*/
private static final long serialVersionUID = 6360295344991781939L;
private static Logger logger = null;
/*
* The status of the ServicesPanel
* Contains available ports, processes, services etc.
* which get updated gradually after a refresh.
*/
private Map ports = new HashMap();
private Map processes = new HashMap();
private Map services = new HashMap();
private Image backgroundImage = new ImageIcon(ServicesPanel.class.getResource("/org/projectopen/serverstatus/background.png")).getImage();
// Action Icons
final ImageIcon startIconBlue = new ImageIcon(ServicesPanel.class.getResource("/org/projectopen/serverstatus/control_play_blue.png"));
final ImageIcon startIconGrey = new ImageIcon(ServicesPanel.class.getResource("/org/projectopen/serverstatus/control_play.png"));
final ImageIcon stopIconBlue = new ImageIcon(ServicesPanel.class.getResource("/org/projectopen/serverstatus/control_stop_blue.png"));
final ImageIcon stopIconGrey = new ImageIcon(ServicesPanel.class.getResource("/org/projectopen/serverstatus/control_stop.png"));
final ImageIcon reloadIcon = new ImageIcon(ServicesPanel.class.getResource("/org/projectopen/serverstatus/arrow_refresh.png"));
private static final String aolserverServiceName = "po-projop";
private static final Integer aolserverPort = new Integer(8000);
private static final String aolserverProcess = "nsd.exe";
private HashMap aolserverMap;
private String aolserverInstalledColor;
private String aolserverPortColor;
private String aolserverProcessColor;
private String aolserverLogColor;
private static final String postgresServiceName = "postgresql-9.2";
private static final Integer postgresPort = new Integer(5432);
private static final String postgresProcess = "postgres.exe";
private HashMap postgresMap;
private String postgresInstalledColor;
private String postgresPortColor;
private String postgresProcessColor;
private String postgresLogColor;
private static final String eximServiceName = "exim";
private static final Integer eximPort = new Integer(25);
private static final String eximProcess = "exim.exe";
private HashMap eximMap = new HashMap();
private String eximInstalledColor;
private String eximPortColor;
private String eximProcessColor;
private String eximLogColor;
/**
* Overwritten paint method in order to add a background image.
*/
public void paintComponent(Graphics g) {
g.drawImage(backgroundImage, 0, 0, null); //no need for ImageObserver here
}
/**
* Constructor
*/
public ServicesPanel(Logger l) {
// Store the passed logger as the default logger
logger = l;
// Set 400 x 300 size
this.setPreferredSize(new Dimension(380,280));
// Setup the grid bag layout of the container
GridBagLayout gridBagLayout = new GridBagLayout();
gridBagLayout.columnWidths = new int[]{10, 80, 5, 5, 5, 100};
gridBagLayout.rowHeights = new int[]{80, 0, 0, 0, 0, 0};
gridBagLayout.columnWeights = new double[]{0.0, 0.0, 0.0, 0.0, 0.0, Double.MIN_VALUE};
gridBagLayout.rowWeights = new double[]{0.0, 0.0, 0.0, 0.0, 0.0, Double.MIN_VALUE};
setLayout(gridBagLayout);
// Initial setup of the panel
// with "clear" status
updateDisplay();
class UpdateWorker extends SwingWorker {
protected String doInBackground() throws Exception {
ports = SystemInterface.busyPorts(logger);
processes = SystemInterface.activePocesses(logger);
services = SystemInterface.installedServices(logger);
return "ready";
}
protected void done() {
try {
updateDisplay();
// JOptionPane.showMessageDialog(null, get());
} catch (Exception e) {
e.printStackTrace();
}
}
}
// Start immediately
new UpdateWorker().execute();
// Schedule a new update every 3 seconds or so.
int delay = 4000;
ActionListener taskPerformer = new ActionListener() {
public void actionPerformed(ActionEvent evt) {
logger.logMessage(Logger.INFO, "Timer", "Starting", "");
new UpdateWorker().execute();
}
};
javax.swing.Timer timer = new javax.swing.Timer(delay, taskPerformer);
timer.setRepeats(true);
timer.start();
}
/*
* Update the display
*/
private void updateDisplay() {
/* **********************************************************************************
* AOLserver Status
***********************************************************************************/
aolserverMap = new HashMap();
aolserverInstalledColor = services.containsKey(aolserverServiceName) ? "green" : "red";
aolserverPortColor = ports.containsKey(aolserverPort) ? "green" : "red";
aolserverProcessColor = processes.containsKey(aolserverProcess) ? "green" : "red";
aolserverLogColor = "clear";
if (ports.isEmpty()) {
aolserverInstalledColor = "clear";
aolserverPortColor = "clear";
aolserverProcessColor = "clear";
aolserverLogColor = "clear";
}
aolserverMap.put("service_name", "AOLserver");
aolserverMap.put("service_status", "green");
aolserverMap.put("installed_status", aolserverInstalledColor);
aolserverMap.put("port_status", aolserverPortColor);
aolserverMap.put("process_status", aolserverProcessColor);
aolserverMap.put("log_status", "clear");
aolserverMap.put("service_tooltip", "AOLserver (]project-open[ application server)");
/***********************************************************************************
* PostgreSQL Status
***********************************************************************************/
postgresMap = new HashMap();
postgresInstalledColor = services.containsKey(postgresServiceName) ? "green" : "red";
postgresPortColor = ports.containsKey(postgresPort) ? "green" : "red";
postgresProcessColor = processes.containsKey(postgresProcess) ? "green" : "red";
postgresLogColor = "clear";
if (ports.isEmpty()) {
postgresInstalledColor = "clear";
postgresPortColor = "clear";
postgresProcessColor = "clear";
postgresLogColor = "clear";
}
postgresMap.put("service_name", "PostgreSQL");
postgresMap.put("service_status", "green");
postgresMap.put("installed_status", postgresInstalledColor);
postgresMap.put("port_status", postgresPortColor);
postgresMap.put("process_status", postgresProcessColor);
postgresMap.put("log_status", "clear");
postgresMap.put("service_tooltip", "PostgreSQL (]project-open[ database server)");
/***********************************************************************************
* Exim Status
***********************************************************************************/
eximMap = new HashMap();
eximInstalledColor = services.containsKey(eximServiceName) ? "green" : "red";
eximPortColor = ports.containsKey(eximPort) ? "green" : "red";
eximProcessColor = processes.containsKey(eximProcess) ? "green" : "red";
eximLogColor = "clear";
if (ports.isEmpty()) {
eximInstalledColor = "clear";
eximPortColor = "clear";
eximProcessColor = "clear";
eximLogColor = "clear";
}
eximMap.put("service_name", "Exim");
eximMap.put("service_status", "yellow");
eximMap.put("installed_status", eximInstalledColor);
eximMap.put("port_status", eximPortColor);
eximMap.put("process_status", eximProcessColor);
eximMap.put("log_status", "clear");
eximMap.put("service_tooltip", "Exim (]project-open[ mail server)");
// Update the actual GUI elements
updateGUI();
}
/*
* Update the display
*/
private void updateGUI() {
this.removeAll();
// Placeholder on the top
JPanel placeholderPanel = new JPanel();
placeholderPanel.setPreferredSize(new Dimension(10,80));
placeholderPanel.setOpaque(false); // Set transparent
GridBagConstraints gbc4 = new GridBagConstraints();
gbc4.gridx = 0;
gbc4.gridy = 0;
add(placeholderPanel, gbc4);
/* **********************************************************************************
* AOLserver
***********************************************************************************/
JPanel aolserverPanel = new ServicePanel(aolserverMap);
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 1;
gbc.gridy = 1;
add(aolserverPanel, gbc);
// Start Button
JButton btnAOLserverStart = new JButton(startIconBlue);
btnAOLserverStart.setBorder(BorderFactory.createEmptyBorder());
btnAOLserverStart.setContentAreaFilled(false);
btnAOLserverStart.setToolTipText("Start AOLserver (]project-open[ application server)");
btnAOLserverStart.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
SystemInterface.startService(logger, aolserverServiceName);
}
});
GridBagConstraints gbc_btnAOLserverStart = new GridBagConstraints();
gbc_btnAOLserverStart.insets = new Insets(5, 0, 5, 0);
gbc_btnAOLserverStart.gridx = 2;
gbc_btnAOLserverStart.gridy = 1;
add(btnAOLserverStart, gbc_btnAOLserverStart);
btnAOLserverStart.setEnabled(true);
// Stop Button
JButton btnAOLserverstop = new JButton(stopIconBlue);
btnAOLserverstop.setBorder(BorderFactory.createEmptyBorder());
btnAOLserverstop.setContentAreaFilled(false);
btnAOLserverstop.setToolTipText("Stop AOLserver (]project-open[ application server)");
btnAOLserverstop.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
SystemInterface.stopService(logger, aolserverServiceName);
}
});
GridBagConstraints gbc_btnAOLserverstop = new GridBagConstraints();
gbc_btnAOLserverstop.insets = new Insets(5, 0, 5, 0);
gbc_btnAOLserverstop.gridx = 3;
gbc_btnAOLserverstop.gridy = 1;
add(btnAOLserverstop, gbc_btnAOLserverstop);
btnAOLserverstop.setEnabled(true);
/***********************************************************************************
* PostgreSQL Status
***********************************************************************************/
JPanel postgresPanel = new ServicePanel(postgresMap);
GridBagConstraints gbc2 = new GridBagConstraints();
gbc2.gridx = 1;
gbc2.gridy = 2;
add(postgresPanel, gbc2);
// Start Button
JButton btnpostgresStart = new JButton(startIconBlue);
btnpostgresStart.setBorder(BorderFactory.createEmptyBorder());
btnpostgresStart.setContentAreaFilled(false);
btnpostgresStart.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
SystemInterface.startService(logger, postgresServiceName);
}
});
GridBagConstraints gbc_btnpostgresStart = new GridBagConstraints();
gbc_btnpostgresStart.insets = new Insets(5, 0, 5, 0);
gbc_btnpostgresStart.gridx = 2;
gbc_btnpostgresStart.gridy = 2;
add(btnpostgresStart, gbc_btnpostgresStart);
btnpostgresStart.setEnabled(true);
// Stop Button
JButton btnpostgresstop = new JButton(stopIconBlue);
btnpostgresstop.setBorder(BorderFactory.createEmptyBorder());
btnpostgresstop.setContentAreaFilled(false);
btnpostgresstop.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
SystemInterface.stopService(logger, postgresServiceName);
}
});
GridBagConstraints gbc_btnpostgresstop = new GridBagConstraints();
gbc_btnpostgresstop.insets = new Insets(5, 0, 5, 0);
gbc_btnpostgresstop.gridx = 3;
gbc_btnpostgresstop.gridy = 2;
add(btnpostgresstop, gbc_btnpostgresstop);
btnpostgresstop.setEnabled(true);
/***********************************************************************************
* Exim Status
***********************************************************************************/
/*
JPanel eximPanel = new ServicePanel(eximMap);
GridBagConstraints gbc3 = new GridBagConstraints();
gbc3.gridx = 1;
gbc3.gridy = 3;
add(eximPanel, gbc3);
// Start Button
JButton btneximStart = new JButton(startIconBlue);
btneximStart.setBorder(BorderFactory.createEmptyBorder());
btneximStart.setContentAreaFilled(false);
btneximStart.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
SystemInterface.startService(logger, eximServiceName);
}
});
GridBagConstraints gbc_btneximStart = new GridBagConstraints();
gbc_btneximStart.insets = new Insets(5, 0, 5, 0);
gbc_btneximStart.gridx = 2;
gbc_btneximStart.gridy = 3;
add(btneximStart, gbc_btneximStart);
btneximStart.setEnabled(true);
// Stop Button
JButton btneximstop = new JButton(stopIconBlue);
btneximstop.setBorder(BorderFactory.createEmptyBorder());
btneximstop.setContentAreaFilled(false);
btneximstop.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
SystemInterface.stopService(logger, eximServiceName);
}
});
GridBagConstraints gbc_btneximstop = new GridBagConstraints();
gbc_btneximstop.insets = new Insets(5, 0, 5, 0);
gbc_btneximstop.gridx = 3;
gbc_btneximstop.gridy = 3;
add(btneximstop, gbc_btneximstop);
btneximstop.setEnabled(true);
*/
/***********************************************************************************
* JLabel with the bottom text.
***********************************************************************************/
final JTextArea textArea = new JTextArea("You are running the ]po[ 5.0 community edition:\n" +
" Based on 32 bit code,\n" +
" suitable for small and medium enterprises,\n" +
" without support and \n" +
" without enterprise packages.\n" +
"Please contact info@project-open.com for pro & enterprise editions\n" +
"");
textArea.setEditable(false);
textArea.setBounds(10, 152, 456, 255);
// textArea.setBorder(border);
textArea.setLineWrap(true);
textArea.setForeground(new Color(0,0,153));
Font font = new Font("Arial", Font.PLAIN, 12);
textArea.setFont(font);
textArea.setOpaque(false);
GridBagConstraints gbc_messageLabel = new GridBagConstraints();
gbc_messageLabel.insets = new Insets(5,5,5,5);
gbc_messageLabel.gridx = 1;
gbc_messageLabel.gridy = 4;
gbc_messageLabel.gridwidth = 4;
add(textArea, gbc_messageLabel);
/***********************************************************************************
* Redraw the panel
***********************************************************************************/
// tell the component to repaint
this.revalidate();
this.repaint();
}
}
package org.projectopen.serverstatus;
import java.io.*;
import java.net.*;
import java.util.*;
import org.projectopen.debug.Logger;
import org.projectopen.debug.SystemLogger;
import org.projectopen.winservice.*;
public class SystemInterface {
public static int startService(Logger logger, String serviceName) {
String cmd = "sc start " + serviceName;
WinClient cli = new WinClient();
cli.exec(cmd);
if (0 != cli.getReturnCode()) {
logger.logMessage(Logger.ERROR, cmd, cli.getServerResponse(), "");
}
cli.close();
/*
String line = null;
Process process = null;
int returnCode;
int logLevel = Logger.INFO;
StringBuffer errorMessageBuffer = new StringBuffer();
try {
process = Runtime.getRuntime().exec(cmd);
BufferedReader input = new BufferedReader(new InputStreamReader(process.getInputStream()), 13107200);
while ((line = input.readLine()) != null) {
if (0 == line.length()) { continue; }
errorMessageBuffer.append(line).append("\n");
}
try {
returnCode = process.waitFor();
if (returnCode > 0) { logLevel = Logger.ERROR; }
logger.logMessage(logLevel, cmd, errorMessageBuffer.toString(), "");
} catch (InterruptedException e) {
e.printStackTrace();
}
} catch (Exception e) {
e.printStackTrace(System.err);
}
*/
return 0;
}
public static int stopService(Logger logger, String serviceName) {
String cmd = "sc stop " + serviceName;
WinClient cli = new WinClient();
cli.exec(cmd);
if (0 != cli.getReturnCode()) {
logger.logMessage(Logger.ERROR, cmd, cli.getServerResponse(), "");
}
cli.close();
/*
String line = null;
Process process = null;
int returnCode;
int logLevel = Logger.INFO;
StringBuffer errorMessageBuffer = new StringBuffer();
try {
process = Runtime.getRuntime().exec(cmd);
BufferedReader input = new BufferedReader(new InputStreamReader(process.getInputStream()), 13107200);
while ((line = input.readLine()) != null) {
if (0 == line.length()) { continue; }
errorMessageBuffer.append(line).append("\n");
}
try {
returnCode = process.waitFor();
if (returnCode > 0) { logLevel = Logger.ERROR; }
logger.logMessage(logLevel, cmd, errorMessageBuffer.toString(), "");
} catch (InterruptedException e) {
e.printStackTrace();
}
} catch (Exception e) {
e.printStackTrace(System.err);
}
*/
return 0;
}
/*
* Get the list of ports that are busy on this machine.
* Parses the output of "netstat -ano".
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public static HashMap busyPorts(Logger logger) {
final String cmd = "netstat -ano";
HashMap result = new HashMap();
Process process = null;
try {
/* Run netstat - produces an output like this:
* Active Connections
*
* Proto Local Address Foreign Address State PID
* TCP 0.0.0.0:80 0.0.0.0:0 LISTENING 4308
* TCP 0.0.0.0:135 0.0.0.0:0 LISTENING 1072
* TCP 0.0.0.0:443 0.0.0.0:0 LISTENING 4308
*/
process = Runtime.getRuntime().exec(cmd);
} catch (Exception e) {
e.printStackTrace(System.err);
}
// Parse the netstat output line by line
InputStream stream = process.getInputStream();
java.util.Scanner s = new java.util.Scanner(stream).useDelimiter("\\r\\n");
while (s.hasNext()) {
String str = s.next();
// logger.logMessage(Logger.INFO, "netstat", str, "");
String[] strings = str.split("\\s+");
/* Only get active if there are 5 elements:
* 0 - empty string (leading spaces)
* 1 - Proto
* 2 - Local Address
* 3 - Foreign Address
* 4 - State
* 5- PID
*/
// Make sure there are all 6 pieces
if (6 != strings.length) { continue; }
// Protocol = TCL
if (!strings[1].equals("TCP")) { continue; }
// Foreign address is "0.0.0.0:0"
if (!strings[3].equals("0.0.0.0:0")) { continue; }
// Get the port number from #2
String[] ip_port = strings[2].split(":");
Integer port = new Integer(ip_port[1]);
result.put(port, port);
}
return result;
}
/*
* Get the list of services installed on this machine.
* Parses the output of "sc query
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public static HashMap installedServices(Logger logger) {
final String cmd = "sc queryex state= all";
HashMap result = new HashMap();
Process process = null;
String serviceName = null;
String displayName = null;
String state = null;
String key, value;
try {
/* Run sc produces an output like this:
*
* SERVICE_NAME: AOLserver-projop
* DISPLAY_NAME: ]po[ AOLserver Projop
* TYPE : 10 WIN32_OWN_PROCESS
* STATE : 1 STOPPED
* WIN32_EXIT_CODE : 1067 (0x42b)
* SERVICE_EXIT_CODE : 0 (0x0)
* CHECKPOINT : 0x0
* WAIT_HINT : 0x0
* PID : 0
* FLAGS :
*/
process = Runtime.getRuntime().exec(cmd);
} catch (Exception e) {
e.printStackTrace(System.err);
}
// Parse the netstat output line by line
InputStream stream = process.getInputStream();
java.util.Scanner s = new java.util.Scanner(stream).useDelimiter("\\r\\n");
int lineCount = 0;
while (s.hasNext()) {
String str = s.next().trim().toLowerCase();
// logger.logMessage(Logger.INFO, "netstat", lineCount + " : " + str, "");
String[] strings = str.split(":");
// reset the line counter after an empty line
if ("".equals(str)) {
lineCount = 0;
continue;
}
// Make sure there are all 6 pieces
if (2 != strings.length) { continue; }
key = strings[0].trim();
value = strings[1].trim();
// line 0 => service name,
// line 1 => display name
// line 3 => status
if (0 == lineCount) { serviceName = value; }
if (1 == lineCount) { displayName = value; }
if (3 == lineCount) {
state = value.substring(0, 1);
// Write into result
result.put(serviceName, state);
logger.logMessage(Logger.INFO, "netstat", serviceName + " -> " + state, "");
}
lineCount++;
}
return result;
}
/*
* Get the list of processes running on this machine.
* Parses the output of "tasklist /V /FO CSV".
*/
public static HashMap activePocesses(Logger logger) {
final String cmd = "tasklist /V /FO CSV";
HashMap result = new HashMap();
Process process = null;
try {
/* Run tasklist /V /FO CSV - produces an output like this:
* "Image Name","PID","Session Name","Session#","Mem Usage","Status","User Name","CPU Time","Window Title"
* "System Idle Process","0","Services","0","24 K","Unknown","NT AUTHORITY\SYSTEM","40:31:37","N/A"
* "System","4","Services","0","328 K","Unknown","N/A","0:05:56","N/A"
* "smss.exe","488","Services","0","120 K","Unknown","N/A","0:00:01","N/A"
*/
process = Runtime.getRuntime().exec(cmd);
} catch (Exception e) {
e.printStackTrace(System.err);
}
// Parse the output line by line
InputStream stream = process.getInputStream();
java.util.Scanner s = new java.util.Scanner(stream).useDelimiter("\\r\\n");
while (s.hasNext()) {
String str = s.next();
// logger.logMessage(Logger.INFO, "tasklist", str, "");
String[] strings = str.split(",");
// Make sure there are all 9 columns
if (9 > strings.length) { continue; }
// Get the port number from #2
String image = strings[0].replaceAll("\"", "");
result.put(image, image);
}
return result;
}
/*
* Where is the log file located for AOLserver?
*/
public static String aolServerLogDir() {
String aolDir = System.getenv("AOLDIR");
String[] pieces = aolDir.split("\\\\");
String logDir = pieces[0] + "/" + pieces[1] + "/servers/projop/log";
return logDir;
}
/*
* Get the list of processes running on this machine.
* Parses the output of "tasklist /V /FO CSV".
*/
public static HashMap parseAolserverLogfile(Logger logger) {
String aolLogDir = aolServerLogDir();
String logFile = aolLogDir + "/" + "error.log";
BufferedReader br = null;
try {
br = new BufferedReader(new FileReader(logFile));
String line;
int ctr = 0;
while ((line = br.readLine()) != null) {
// process the line.
System.out.println(line);
ctr = ctr + 1;
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
br.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
/**
* Main Procedure
*
* Test some of the system interfaces
*/
public static void main(String[] args) {
Logger logger = new SystemLogger();
HashMap installedProcesses = installedServices(logger);
//HashMap aolIssues = parseAolserverLogfile(logger);
/*
HashMap ports = SystemInterface.busyPorts(logger);
HashMap processes = SystemInterface.activePocesses(logger);
*/
}
}
package org.projectopen.serverstatus;
/*
* Copyright (C) 2010 ]project-open[
*
* This program is free software. You can redistribute it
* and/or modify it under the terms of the GNU General
* Public License as published by the Free Software Foundation;
* either version 2 of the License, or (at your option)
* any later version. This program 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 General Public License for more details.
*
* @author Frank Bergmann (frank.bergmann@project-open.com)
*
*/
import java.awt.*;
import java.awt.TrayIcon.MessageType;
import java.awt.event.*;
import java.net.URL;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.UIManager;
import javax.swing.table.TableColumn;
import org.projectopen.debug.DebugPanel;
import org.projectopen.debug.Logger;
@SuppressWarnings("deprecation")
/**
* Starts the ]po[-Timesheet application as a "Tray Icon".
* This class encapsulates user facing and GUI activities.
* - Setup a TrayIcon in the system tray
* - Setup the menu for the TrayIcon
* - Handle menu events for:
* - Setup tool tips for the tray icon
* - Configuration panel - Edit properties
* - Time sheet log panel - log hours
* - Browser login - open a browser window and perform a login
*/
public class TrayIconServiceStarter implements Logger {
static TrayIcon trayIcon = null;
public static JFrame debugFrame;
public static DebugPanel debugPanel = null;
public static JFrame servicesFrame = null;
public static ServicesPanel servicesPanel = null;
static int mouseX, mouseY;
//Optionally set the look and feel.
private static boolean useSystemLookAndFeel = true;
/**
* Provide the rest of the application with a way
* to communicate error messages to the user.
*/
public static void trayIconError(TrayIcon.MessageType errorType, String title, String message) {
TrayIcon trayIcon = TrayIconServiceStarter.getTrayIcon();
trayIcon.displayMessage(title, message, errorType);
}
private static TrayIcon getTrayIcon() {
return trayIcon;
}
/**
* Launch a panel that shows services status information.
*/
private void launchServicesPanel(boolean visible) {
if (servicesFrame == null) {
servicesFrame = new JFrame("Services");
servicesFrame.add(new ServicesPanel(this));
servicesFrame.pack();
Dimension dim = servicesFrame.getSize();
servicesFrame.setLocation(mouseX - dim.width, mouseY-dim.height);
// servicesFrame.setPreferredSize(new Dimension(400,300));
}
servicesFrame.setVisible(visible);
servicesFrame.setResizable(false);
};
/**
* Launch a panel that shows debugging information.
*/
private void launchDebugPanel(boolean visible) {
if (debugFrame == null) {
debugFrame = new JFrame("Debug");
debugFrame.add(debugPanel);
debugFrame.pack();
Dimension dim = debugFrame.getSize();
debugFrame.setLocation(mouseX - dim.width, mouseY-dim.height);
}
debugFrame.setVisible(visible);
};
public TrayIconServiceStarter() {
if (SystemTray.isSupported()) {
// create a new panel and store locally for logging,
// even before launching the debug panel GUI.
debugPanel = new DebugPanel();
// log the application start
this.logMessage(Logger.INFO, "Application", "Started", "");
MouseListener mouseListener = new MouseListener() {
public void mouseClicked(MouseEvent e) {
// System.out.println("Tray Icon - Mouse clicked!" + e.getButton());
// remember the mouse position
mouseX = e.getX();
mouseY = e.getY();
// Take action depending on the button clicked
switch (e.getButton()) {
case 1: {
//Create and set up the window.
launchServicesPanel(true);
}
}
}
public void mouseEntered(MouseEvent e) {
// System.out.println("Tray Icon - Mouse entered!");
}
public void mouseExited(MouseEvent e) {
// System.out.println("Tray Icon - Mouse exited!");
}
public void mousePressed(MouseEvent e) {
// System.out.println("Tray Icon - Mouse pressed!");
}
public void mouseReleased(MouseEvent e) {
// System.out.println("Tray Icon - Mouse released!");
}
};
// Exit the Tray application
ActionListener exitListener = new ActionListener() {
public void actionPerformed(ActionEvent e) {
System.out.println("Exiting...");
System.exit(0);
}
};
// Show configuration frame
ActionListener debugListener = new ActionListener() {
public void actionPerformed(ActionEvent e) {
launchDebugPanel(true);
}
};
// Show configuration frame
ActionListener servicesListener = new ActionListener() {
public void actionPerformed(ActionEvent e) {
launchServicesPanel(true);
}
};
// Build the TrayIcon menu
PopupMenu popup = new PopupMenu();
MenuItem servicesItem = new MenuItem("Services");
servicesItem.addActionListener(servicesListener);
popup.add(servicesItem);
MenuItem debugItem = new MenuItem("Debug");
debugItem.addActionListener(debugListener);
popup.add(debugItem);
MenuItem exitItem = new MenuItem("Exit");
exitItem.addActionListener(exitListener);
popup.add(exitItem);
ActionListener actionListener = new ActionListener() {
public void actionPerformed(ActionEvent e) {
/*
* Double-click on the tray icon - do nothing
*
trayIcon.displayMessage("Action Event",
"An Action Event Has Been Peformed!",
TrayIcon.MessageType.INFO);
Image updatedImage = Toolkit.getDefaultToolkit().getImage("images/tray.gif");
trayIcon.setImage(updatedImage);
trayIcon.setImageAutoSize(true);
*/
}
};
// get the image stored together with the classes
String imagePath = "/org/projectopen/timesheet/po-icon.gif";
URL fileLocation = getClass().getResource(imagePath);
Image image = Toolkit.getDefaultToolkit().getImage(fileLocation);
// java.net.URL imageURL = TrayIconStarter.class.getResource("images/tray.gif");
// Image image = Toolkit.getDefaultToolkit().getImage(imagePath);
String imageStr = "null";
if (image != null) { imageStr = image.getSource().toString(); }
this.logMessage(Logger.INFO, "Application", "Loading image from "+imagePath, imageStr);
trayIcon = new TrayIcon(image, "]project-open[ Services Panel", popup);
trayIcon.setImageAutoSize(true);
trayIcon.addActionListener(actionListener);
trayIcon.addMouseListener(mouseListener);
SystemTray tray = SystemTray.getSystemTray();
try {
tray.add(trayIcon);
} catch (AWTException e) {
System.err.println("TrayIcon could not be added.");
}
// Launch a message in the Tray Icon to remind users to click here
TrayIcon ti = TrayIconServiceStarter.getTrayIcon();
ti.displayMessage("]project-open[ Services Panel", "Please click here in order to check and modify the status of the ]po[ server.", TrayIcon.MessageType.INFO);
} else {
System.err.println("System tray is currently not supported.");
}
}
/**
* Start the time sheet logging application.
* @param args the command line arguments
*/
public static void main(String[] args)
{
// Use Look and Feel?
if (useSystemLookAndFeel) {
try {
UIManager.setLookAndFeel(
UIManager.getSystemLookAndFeelClassName());
} catch (Exception ex) {
System.err.println("Couldn't use system look and feel.");
}
}
// Start the main application
new TrayIconServiceStarter();
// Do we need to cleanup after closing the application?
// this code could go here...
}
/**
* Accepts log messages from the RESTClient and
* displays them to the user as a TrayIcon message
* if they are of level ERROR or higher.
*/
public void logMessage(int level, String domain, String message, String details) {
// send message to debugging panel
if (null != debugPanel) {
debugPanel.logMessage(level, domain, message, details);
}
// Only show ERROR and FATAL to the user as a TrayIcon message
if (level >= Logger.ERROR) {
MessageType errorType = TrayIcon.MessageType.ERROR;
switch (level) {
case Logger.DEBUG: errorType = TrayIcon.MessageType.INFO;
case Logger.INFO: errorType = TrayIcon.MessageType.INFO;
case Logger.WARNING: errorType = TrayIcon.MessageType.WARNING;
case Logger.ERROR: errorType = TrayIcon.MessageType.ERROR;
case Logger.FATAL: errorType = TrayIcon.MessageType.ERROR;
}
TrayIcon trayIcon = TrayIconServiceStarter.getTrayIcon();
trayIcon.displayMessage(domain, message, errorType);
}
}
}
/**
*
*/
/**
* @author fraber
*
*/
package org.projectopen.serverstatus;
\ No newline at end of file
package org.projectopen.winservice;
import java.io.*;
import java.net.*;
public class WinClient {
private Socket socket = null;
private PrintWriter out = null;
private BufferedReader in = null;
private boolean connectionOpen = false;
private String serverResponse = null;
private String serverReturnCodeString = null;
private int serverReturnCode = 0;
public WinClient() {
try {
socket = new Socket("localhost", WinService.winServiceSocket);
out = new PrintWriter(socket.getOutputStream(), true);
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
connectionOpen = true;
} catch (UnknownHostException e) {
System.err.println("Unable to get name for 'localhost'");
} catch (IOException e) {
System.err.println("Unable to connect to 'localhost' with port " + WinService.winServiceSocket);
}
}
/**
* Destructor: Close the connection if still open.
*/
protected void finalize() throws Throwable {
this.close();
}
/**
* Close the connection if open
*/
public void close() {
if (connectionOpen) {
try {
out.close();
in.close();
socket.close();
} catch (Exception e) {
System.err.println("Error closing connection");
System.exit(1);
}
}
}
public boolean isOpen() { return connectionOpen; }
public PrintWriter getOut() { return out; }
public BufferedReader getIn() { return in; }
public String getServerResponse() { return serverResponse; }
public int getReturnCode() {
int colonPos = serverReturnCodeString.indexOf(":");
String returnCodeString = serverReturnCodeString.substring(colonPos+2);
return new Integer(returnCodeString).intValue();
}
/**
* Execute a command on the server side
*/
public String exec(String cmdLine) {
StringBuffer output = new StringBuffer();
String line = null;
// return an error indicator if not open
if (!connectionOpen) { return null; }
try {
// Send input server
this.getOut().println(cmdLine);
// Wait until the reply gets ready
do {
try {
Thread.sleep(10);
} catch(InterruptedException e) { }
} while (!"".equals(cmdLine) && !this.getIn().ready());
// Read until all text has been read
int ctr = 0;
while (this.getIn().ready()) {
line = in.readLine();
if (0 == ctr) {
serverReturnCodeString = line;
} else {
output.append(line).append("\n");
}
ctr++;
}
} catch (IOException e) { }
// Return the result
serverResponse = output.toString();
return serverResponse;
}
/**
* Testing function with manual client.
* @throws IOException
*/
public static void main(String[] args) throws IOException {
BufferedReader stdIn = new BufferedReader(new InputStreamReader(System.in));
WinClient client = new WinClient();
String userInput;
do {
// Get one line of input
userInput = stdIn.readLine();
String output = client.exec(userInput);
int returnCode = client.getReturnCode();
System.out.println("Return Code = " + returnCode);
System.out.println("Output = " + output);
} while (userInput != null && !"".equals(userInput));
client.close();
stdIn.close();
}
}
package org.projectopen.winservice;
import java.net.*;
import java.io.*;
import java.security.*;
/**
* Windows service listening on port 7999 for command lines.
* The service executes these command lines if they comply
* with the patters of permissible lines.
*
* This service is designed to be run with "elevated" admin
* permission in order to start and stop the ]po[ services
* "aolserver-projop" and "postgresql-8.4". This way we can
* run the po-service-monitor.jar application with user
* permissions and interacting with the TrayIcon while executing
* certain admin actions for starting and stopping Win
* services.
*
* @author frank.bergmann@project-open.com
*
*/
public class WinService {
public static int winServiceSocket = 7999;
/**
* Main server routine:
*
* Listen for incoming connections and execute them
* in the same thread. We don't need to spawn new threads
* for occasionally executing a command string.
*/
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = null;
boolean running = true;
try {
serverSocket = new ServerSocket(winServiceSocket);
Socket socket;
while(running) {
socket = serverSocket.accept();
ConnectionServer connection = new ConnectionServer(socket);
Thread t = new Thread(connection);
t.start();
}
} catch (IOException e) {
System.out.println("IOException on socket listen: " + e);
e.printStackTrace();
}
serverSocket.close();
}
}
class ConnectionServer implements Runnable {
private Socket connection;
WinServiceProtocol protocol = new WinServiceProtocol();
ConnectionServer(Socket serverConnection) {
this.connection = serverConnection;
}
public void run () {
String input = "";
String output = "";
try {
// Get input from the client
DataInputStream in = new DataInputStream (connection.getInputStream());
PrintStream out = new PrintStream(connection.getOutputStream());
// Read exactly one line
input = in.readLine();
while (input != null && !"".equals(input)) {
System.out.println("Overall message is:" + input);
// Process protocol
output = protocol.process(input);
// Now write to the client
out.println(output);
// read the next line
input = in.readLine();
}
// close the connection
connection.close();
} catch (IOException ioe) {
System.out.println("IOException on socket listen: " + ioe);
ioe.printStackTrace();
}
}
}
package org.projectopen.winservice;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.projectopen.debug.Logger;
/**
* Simple protocol implementation for executing command lines
* with elevated Windows permissions.
*
* The ]po[ Service Manager needs this background services
* in order to start or stop ]po[ services while operating
* as a non-elevated TrayIcon program with user level permissions.
*
* @author frank.bergmann@project-open.com
*
*/
public class WinServiceProtocol {
/**
* Main and only action of the Protocol:
* Check if the command line adheres to security constraints,
* execute and send the command line output back to the client.
* @param cmdLine A command line to execute
* @return The output of the command line
*/
public String process(String cmdLine) {
if (cmdLine.isEmpty()) { return "Return Code: 0"; }
Pattern p = Pattern.compile("^sc .*");
Matcher m = p.matcher(cmdLine);
if (!m.find()) {
System.out.println("Security: Somebody tries to fiddle with system security: cmdLine=" + cmdLine);
return "Return Code: -1";
}
String line = null;
Process process = null;
int returnCode = -1;
StringBuffer errorMessageBuffer = new StringBuffer();
try {
process = Runtime.getRuntime().exec(cmdLine);
BufferedReader input = new BufferedReader(new InputStreamReader(process.getInputStream()), 13107200);
while ((line = input.readLine()) != null) {
if (0 == line.length()) { continue; }
errorMessageBuffer.append(line).append("\n");
}
try {
returnCode = process.waitFor();
} catch (InterruptedException e) {
e.printStackTrace();
}
} catch (Exception e) {
e.printStackTrace(System.err);
}
return "Return Code: " + returnCode + "\n" + errorMessageBuffer;
}
}
/**
*
*/
/**
* @author fraber
*
*/
package org.projectopen.winservice;
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment