diff --git a/cave/build/static/common/cave/etc/gfe/userPython/procedures/CheckTandTd.py b/cave/build/static/common/cave/etc/gfe/userPython/procedures/CheckTandTd.py index faf5d3a8ed..97eea54aea 100644 --- a/cave/build/static/common/cave/etc/gfe/userPython/procedures/CheckTandTd.py +++ b/cave/build/static/common/cave/etc/gfe/userPython/procedures/CheckTandTd.py @@ -261,8 +261,7 @@ class Procedure (SmartScript.SmartScript): if checkOnly: self.createGrid(MODEL, "MaxLessThanMin", "SCALAR", mask.astype('float32'), - maxTR, minAllowedValue=0.0, maxAllowedValue= 1.0, - units="", descriptiveName="") + maxTR, minAllowedValue=0.0, maxAllowedValue= 1.0) else: # force the change if maxTR in maxTLocks: msg = "Can't modify MaxT grid at " + str(maxTR) + \ diff --git a/cave/com.raytheon.uf.viz.gisdatastore/src/com/raytheon/uf/viz/gisdatastore/ui/LineStyleDialog.java b/cave/com.raytheon.uf.viz.gisdatastore/src/com/raytheon/uf/viz/gisdatastore/ui/LineStyleDialog.java index a8a78c2af3..a80f70a46e 100644 --- a/cave/com.raytheon.uf.viz.gisdatastore/src/com/raytheon/uf/viz/gisdatastore/ui/LineStyleDialog.java +++ b/cave/com.raytheon.uf.viz.gisdatastore/src/com/raytheon/uf/viz/gisdatastore/ui/LineStyleDialog.java @@ -21,6 +21,8 @@ package com.raytheon.uf.viz.gisdatastore.ui; import org.eclipse.jface.dialogs.Dialog; import org.eclipse.swt.SWT; +import org.eclipse.swt.events.DisposeEvent; +import org.eclipse.swt.events.DisposeListener; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.graphics.GC; @@ -47,6 +49,7 @@ import com.raytheon.uf.viz.core.IGraphicsTarget.LineStyle; * Date Ticket# Engineer Description * ------------ ---------- ----------- -------------------------- * Nov 27, 2012 randerso Initial creation + * Apr 9, 2013 #1860 randerso Fix image disposed issued on Windows * * * @@ -87,7 +90,7 @@ public class LineStyleDialog extends Dialog { continue; } TableItem item = new TableItem(table, SWT.NONE); - Image image = new Image(d, 128, 10); + final Image image = new Image(d, 128, 10); Rectangle bounds = image.getBounds(); int[] dashes = ls.getSWTLineStyle(); GC gc = new GC(image); @@ -98,7 +101,14 @@ public class LineStyleDialog extends Dialog { gc.dispose(); item.setImage(image); - image.dispose(); + item.addDisposeListener(new DisposeListener() { + + @Override + public void widgetDisposed(DisposeEvent e) { + image.dispose(); + } + }); + item.setData(ls); if (ls.equals(this.style)) { diff --git a/cave/com.raytheon.uf.viz.gisdatastore/src/com/raytheon/uf/viz/gisdatastore/ui/LineWidthDialog.java b/cave/com.raytheon.uf.viz.gisdatastore/src/com/raytheon/uf/viz/gisdatastore/ui/LineWidthDialog.java index 4324a567ef..4e5c21aca5 100644 --- a/cave/com.raytheon.uf.viz.gisdatastore/src/com/raytheon/uf/viz/gisdatastore/ui/LineWidthDialog.java +++ b/cave/com.raytheon.uf.viz.gisdatastore/src/com/raytheon/uf/viz/gisdatastore/ui/LineWidthDialog.java @@ -21,6 +21,8 @@ package com.raytheon.uf.viz.gisdatastore.ui; import org.eclipse.jface.dialogs.Dialog; import org.eclipse.swt.SWT; +import org.eclipse.swt.events.DisposeEvent; +import org.eclipse.swt.events.DisposeListener; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.graphics.GC; @@ -45,6 +47,7 @@ import org.eclipse.swt.widgets.TableItem; * Date Ticket# Engineer Description * ------------ ---------- ----------- -------------------------- * Nov 27, 2012 randerso Initial creation + * Apr 9, 2013 #1860 randerso Fix image disposed issued on Windows * * * @@ -88,7 +91,7 @@ public class LineWidthDialog extends Dialog { for (int w = min; w <= max; w++) { TableItem item = new TableItem(table, SWT.NONE); - Image image = new Image(d, 128, 10); + final Image image = new Image(d, 128, 10); Rectangle bounds = image.getBounds(); GC gc = new GC(image); gc.fillRectangle(bounds); @@ -98,7 +101,13 @@ public class LineWidthDialog extends Dialog { gc.dispose(); item.setImage(image); - image.dispose(); + item.addDisposeListener(new DisposeListener() { + + @Override + public void widgetDisposed(DisposeEvent e) { + image.dispose(); + } + }); item.setData(w); if (w == this.width) { diff --git a/cave/com.raytheon.uf.viz.monitor.scan/src/com/raytheon/uf/viz/monitor/scan/resource/ScanResource.java b/cave/com.raytheon.uf.viz.monitor.scan/src/com/raytheon/uf/viz/monitor/scan/resource/ScanResource.java index eccee8f0e6..7fba88a56e 100644 --- a/cave/com.raytheon.uf.viz.monitor.scan/src/com/raytheon/uf/viz/monitor/scan/resource/ScanResource.java +++ b/cave/com.raytheon.uf.viz.monitor.scan/src/com/raytheon/uf/viz/monitor/scan/resource/ScanResource.java @@ -82,6 +82,7 @@ import com.vividsolutions.jts.geom.Coordinate; * Jul 24 2012 12996 Xiaochuan Compare with MidVal() * Feb 28, 2013 1731 bsteffen Allow ScanResource to work better with * D2DTimeMatcher. + * Apr 02, 2013 1731 mpduff Fix problem with DMD updates. * * * @@ -1108,7 +1109,8 @@ public class ScanResource extends try { if (!getScan().getTimeOrderedKeys(getScan(), newrecord.getType(), resourceData.icao).contains( - newrecord.getDataTime().getRefTime())) { + newrecord.getDataTime().getRefTime()) + || newrecord.getType().equals("DMD")) { newrecord = resourceData.populateRecord(newrecord); @@ -1118,11 +1120,12 @@ public class ScanResource extends getScan().setTableData(resourceData.icao, newrecord.getTableData(), - /* TODO: This should be the volume scan time, - * but {Radar,Scan}Record.getVolScanTime is actually - * the radar product generation time. + /* + * TODO: This should be the volume scan time, but + * {Radar,Scan}Record.getVolScanTime is actually the + * radar product generation time. */ - newrecord.getDataTime().getRefTime(), + newrecord.getDataTime().getRefTime(), newrecord.getTilt(), newrecord.getDataTime().getRefTime(), newrecord.getType()); diff --git a/cave/com.raytheon.uf.viz.ui.menus/src/com/raytheon/uf/viz/ui/menus/widgets/tearoff/MenuItemComposite.java b/cave/com.raytheon.uf.viz.ui.menus/src/com/raytheon/uf/viz/ui/menus/widgets/tearoff/MenuItemComposite.java index 2cb401a303..c21d7f2262 100644 --- a/cave/com.raytheon.uf.viz.ui.menus/src/com/raytheon/uf/viz/ui/menus/widgets/tearoff/MenuItemComposite.java +++ b/cave/com.raytheon.uf.viz.ui.menus/src/com/raytheon/uf/viz/ui/menus/widgets/tearoff/MenuItemComposite.java @@ -24,8 +24,6 @@ import java.util.Collections; import java.util.List; import org.eclipse.swt.SWT; -import org.eclipse.swt.events.MenuEvent; -import org.eclipse.swt.events.MenuListener; import org.eclipse.swt.events.MouseAdapter; import org.eclipse.swt.events.MouseEvent; import org.eclipse.swt.events.MouseTrackAdapter; @@ -46,6 +44,8 @@ import org.eclipse.swt.widgets.Listener; import org.eclipse.swt.widgets.Menu; import org.eclipse.swt.widgets.MenuItem; +import com.raytheon.uf.viz.ui.menus.widgets.tearoff.TearOffMenuDialog.MenuPathElement; + /** * Holds the information for all the menu items in the dialog * @@ -56,6 +56,7 @@ import org.eclipse.swt.widgets.MenuItem; * Date Ticket# Engineer Description * ------------ ---------- ----------- -------------------------- * Sep 15, 2011 mnash Initial creation + * Apr 10, 2013 DR 15185 D. Friedman Preserve tear-offs over perspective switches. * * * @@ -63,7 +64,7 @@ import org.eclipse.swt.widgets.MenuItem; * @version 1.0 */ -public class MenuItemComposite extends Composite implements MenuListener { +public class MenuItemComposite extends Composite { private boolean separator = false; @@ -74,6 +75,8 @@ public class MenuItemComposite extends Composite implements MenuListener { // backing data for executing listeners private MenuItem item; + private MenuPathElement itemPath; + private Image arrow = null; private Image highlightedArrow = null; @@ -84,8 +87,6 @@ public class MenuItemComposite extends Composite implements MenuListener { private List myPath; - private Menu topMostParent; - /** * @param parent * @param style @@ -108,14 +109,13 @@ public class MenuItemComposite extends Composite implements MenuListener { do { myPath.add(toAdd.getText()); toAdd = parent.getParentItem(); - topMostParent = parent; parent = parent.getParentMenu(); } while (parent.getParentMenu() != null); Collections.reverse(myPath); - topMostParent.addMenuListener(this); item = it; + itemPath = new MenuPathElement(it); String[] labels = item.getText().split("\t"); // handle for a separator menu item @@ -189,6 +189,8 @@ public class MenuItemComposite extends Composite implements MenuListener { * */ private void addItemListeners() { + if (item == null) + return; if (updateListener != null) { item.addListener(SWT.Modify, updateListener); } @@ -209,11 +211,11 @@ public class MenuItemComposite extends Composite implements MenuListener { for (Control comp : firstItem.getParent().getParent() .getChildren()) { MenuItemComposite composite = (MenuItemComposite) comp; - if (composite.item.getText().equals( + if (composite.getItem().getText().equals( ((MenuItem) e.widget).getText())) { if (composite.firstItem instanceof Button) { ((Button) composite.firstItem) - .setSelection(composite.item + .setSelection(composite.getItem() .getSelection()); } } else { @@ -233,7 +235,7 @@ public class MenuItemComposite extends Composite implements MenuListener { @Override public void handleEvent(Event event) { if (secondItem != null && !secondItem.isDisposed()) { - if (item == event.data) { + if (getItem() == event.data) { if (((MenuItem) event.data).getText().split("\t").length > 1) { ((Label) secondItem) .setText(((MenuItem) event.data).getText() @@ -377,6 +379,8 @@ public class MenuItemComposite extends Composite implements MenuListener { MouseAdapter mouseAdapter = new MouseAdapter() { @Override public void mouseDown(MouseEvent e) { + MenuItem item = getItem(); + if (item.getMenu() != null) { // This is item opens a submenu, get the y offset based on // the location of the click @@ -420,7 +424,7 @@ public class MenuItemComposite extends Composite implements MenuListener { for (int i = 0; i < siblings.length; i++) { final MenuItemComposite mic = (MenuItemComposite) siblings[i]; if (mic.separator == false - && mic.item.getStyle() == SWT.RADIO) { + && mic.getItem().getStyle() == SWT.RADIO) { try { MenuItemComposite parent = null; // check whether a Label is clicked or a @@ -434,16 +438,16 @@ public class MenuItemComposite extends Composite implements MenuListener { // check that the radio groups match if (mic.getData("radioGroup").equals( parent.getData("radioGroup"))) { - if (!parent.item + if (!parent.getItem() .getText() .replaceAll("&", "") - .equals(mic.item.getText().replaceAll( + .equals(mic.getItem().getText().replaceAll( "&", ""))) { - mic.item.setSelection(false); + mic.getItem().setSelection(false); ((Button) mic.firstItem) .setSelection(false); } else { - mic.item.setSelection(true); + mic.getItem().setSelection(true); ((Button) mic.firstItem).setSelection(true); } } @@ -476,10 +480,6 @@ public class MenuItemComposite extends Composite implements MenuListener { } } - if (topMostParent != null) { - topMostParent.removeMenuListener(this); - } - super.dispose(); } @@ -489,67 +489,40 @@ public class MenuItemComposite extends Composite implements MenuListener { } } - /* - * (non-Javadoc) - * - * @see - * org.eclipse.swt.events.MenuListener#menuHidden(org.eclipse.swt.events - * .MenuEvent) - */ - @Override - public void menuHidden(MenuEvent e) { - if (item.isDisposed() == false) { - return; - } + private MenuItem getItem() { + MenuItem item = getItemIfAvailable(); + if (item == null) + throw new IllegalStateException( + String.format("Could not find target of tear-off menu item \"%s\"", + itemPath.getName())); + return item; + } - // At some point we may need to check against the index as well but that - // is difficult because we don't know if the tear off menu item will/was - // in the menu when we were created/now so our index could be off by one - // very easily making it unreliable - Menu menu = topMostParent; - MenuItem myItem = null; - String last = myPath.get(myPath.size() - 1); - for (String path : myPath) { - if (menu != null) { - MenuItem[] items = menu.getItems(); - for (int i = 0; i < items.length; ++i) { - MenuItem item = items[i]; - if (path.equals(item.getText())) { - if (path == last) { - myItem = item; - } else { - menu = item.getMenu(); - - if (menu != null && menu.getItemCount() == 0) { - // Have to manually fill menu - for (Listener listener : menu - .getListeners(SWT.Show)) { - Event event = new Event(); - event.type = SWT.Show; - event.widget = menu; - listener.handleEvent(event); - } - } - } - break; - } - } - } - } - if (myItem != null && myItem.isDisposed() == false) { - item = myItem; + private MenuItem getItemIfAvailable() { + if (item == null || item.isDisposed()) { + item = findItem(); addItemListeners(); } + return item; } - /* - * (non-Javadoc) - * - * @see - * org.eclipse.swt.events.MenuListener#menuShown(org.eclipse.swt.events. - * MenuEvent) - */ - @Override - public void menuShown(MenuEvent e) { + private MenuItem findItem() { + Menu menu = getTargetMenu(); + if (menu != null) + return TearOffMenuDialog.findItem(menu, itemPath); + else + return null; } + + private Menu getTargetMenu() { + return getDialog().getTargetMenu(); + } + + private TearOffMenuDialog getDialog() { + return (TearOffMenuDialog) getShell().getData(); + } + + public void reconnect() { + getItemIfAvailable(); + } } \ No newline at end of file diff --git a/cave/com.raytheon.uf.viz.ui.menus/src/com/raytheon/uf/viz/ui/menus/widgets/tearoff/PopupMenu.java b/cave/com.raytheon.uf.viz.ui.menus/src/com/raytheon/uf/viz/ui/menus/widgets/tearoff/PopupMenu.java index 6743112002..0a8ce369d7 100644 --- a/cave/com.raytheon.uf.viz.ui.menus/src/com/raytheon/uf/viz/ui/menus/widgets/tearoff/PopupMenu.java +++ b/cave/com.raytheon.uf.viz.ui.menus/src/com/raytheon/uf/viz/ui/menus/widgets/tearoff/PopupMenu.java @@ -27,6 +27,7 @@ import org.eclipse.swt.widgets.Listener; import org.eclipse.swt.widgets.Menu; import org.eclipse.swt.widgets.MenuItem; import org.eclipse.swt.widgets.Shell; +import org.eclipse.ui.IEditorPart; import com.raytheon.viz.ui.EditorUtil; import com.raytheon.viz.ui.editor.AbstractEditor; @@ -41,6 +42,7 @@ import com.raytheon.viz.ui.editor.AbstractEditor; * Date Ticket# Engineer Description * ------------ ---------- ----------- -------------------------- * Dec 5, 2011 mnash Initial creation + * Apr 10, 2013 DR 15185 D. Friedman Do not assume there is an active editor. * * * @@ -88,7 +90,10 @@ public class PopupMenu { mItem.addListener(SWT.Selection, new Listener() { @Override public void handleEvent(Event event) { - ((AbstractEditor) EditorUtil.getActiveEditor()).refresh(); + IEditorPart editor = EditorUtil.getActiveEditor(); + if (editor instanceof AbstractEditor) { + ((AbstractEditor) editor).refresh(); + } } }); diff --git a/cave/com.raytheon.uf.viz.ui.menus/src/com/raytheon/uf/viz/ui/menus/widgets/tearoff/TearOffMenuDialog.java b/cave/com.raytheon.uf.viz.ui.menus/src/com/raytheon/uf/viz/ui/menus/widgets/tearoff/TearOffMenuDialog.java index 07dee9696b..ab03d8368d 100644 --- a/cave/com.raytheon.uf.viz.ui.menus/src/com/raytheon/uf/viz/ui/menus/widgets/tearoff/TearOffMenuDialog.java +++ b/cave/com.raytheon.uf.viz.ui.menus/src/com/raytheon/uf/viz/ui/menus/widgets/tearoff/TearOffMenuDialog.java @@ -19,6 +19,12 @@ **/ package com.raytheon.uf.viz.ui.menus.widgets.tearoff; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.eclipse.jface.action.ContributionItem; +import org.eclipse.jface.action.MenuManager; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.ScrolledComposite; import org.eclipse.swt.graphics.Point; @@ -53,6 +59,7 @@ import com.raytheon.viz.ui.dialogs.CaveSWTDialog; * ------------ ---------- ----------- -------------------------- * Sep 14, 2011 mnash Initial creation * Jan 09, 2013 1442 rferrel Add Simulated Time Change Listener. + * Apr 10, 2013 DR 15185 D. Friedman Preserve tear-offs over perspective switches. * * * @@ -62,7 +69,9 @@ import com.raytheon.viz.ui.dialogs.CaveSWTDialog; public class TearOffMenuDialog extends CaveSWTDialog { - private MenuItem[] items; + private MenuPathElement[] menuPath; + + private Menu menu; private ScrolledComposite scrolledComp; @@ -80,8 +89,9 @@ public class TearOffMenuDialog extends CaveSWTDialog { public TearOffMenuDialog(Menu menu) { super(VizWorkbenchManager.getInstance().getCurrentWindow().getShell(), SWT.DIALOG_TRIM | SWT.RESIZE, CAVE.DO_NOT_BLOCK); + this.menuPath = getMenuPath(menu); + this.menu = menu; String text = menu.getParentItem().getText(); - this.items = menu.getItems(); // handle for the & that makes key bindings setText(text.replace("&", "")); @@ -89,6 +99,7 @@ public class TearOffMenuDialog extends CaveSWTDialog { @Override protected void initializeComponents(final Shell shell) { + shell.setData(this); // allow for scrolling if necessary scrolledComp = new ScrolledComposite(shell, SWT.V_SCROLL); GridData gd = new GridData(SWT.FILL, SWT.FILL, true, true); @@ -106,6 +117,7 @@ public class TearOffMenuDialog extends CaveSWTDialog { // go through menu items and build MenuItemComposite for each item, // which handles all the selection and color of the "MenuItem" in the // dialog + MenuItem[] items = getTargetMenu().getItems(); int radioGroup = 0; for (int i = 1; i < items.length; i++) { MenuItem item = items[i]; @@ -140,11 +152,10 @@ public class TearOffMenuDialog extends CaveSWTDialog { int y = point.y; shell.setLocation(x, y); - // close the dialog on perspective change - shell.addListener(SWT.Hide, new Listener() { + shell.addListener(SWT.Show, new Listener() { @Override public void handleEvent(Event event) { - close(); + updateItems(); } }); } @@ -204,12 +215,156 @@ public class TearOffMenuDialog extends CaveSWTDialog { * Force update of item's display. */ private void updateItems() { - // items[0] is the tear off object and is not in the dialog's display. - for (int index = 1; index < items.length; ++index) { - MenuItem item = items[index]; + Menu menu = getMenuIfAvailable(); + if (menu == null) + return; + for (MenuItemComposite mic : getMenuItemComposites()) + mic.reconnect(); + for (MenuItem item : menu.getItems()) { if (item.getData() instanceof BundleContributionItem) { ((BundleContributionItem) item.getData()).refreshText(); } } } + + private List getMenuItemComposites() { + List result = new ArrayList(); + for (Control c : fullComp.getChildren()) { + if (c instanceof MenuItemComposite) + result.add((MenuItemComposite) c); + } + return result; + } + + /** + * Return the portion of a menu item's title that should not change over + * time + * + */ + private static String getCleanMenuItemText(String text) { + int pos = text.indexOf('\t'); + if (pos >= 0) + return text.substring(0, pos); + else + return text; + } + + private Menu getMenuIfAvailable() { + if (menu == null || menu.isDisposed()) { + menu = findMenu(); + } + return menu; + } + + /*package*/ Menu getTargetMenu() { + Menu menu = getMenuIfAvailable(); + if (menu == null) { + throw new IllegalStateException( + String.format("Tear-off menu %s is not available", shell.getText())); + } + if (menu.getItems().length == 0) + tryToFillMenu(menu); + return menu; + } + + private void tryToFillMenu(Menu menu) { + /* + * Menu may not have been created so call listeners. This still does + * not work if all of the menu items need the workbench window to be + * active in order to be enabled. + */ + + Shell shell = this.shell.getParent().getShell(); + shell.setActive(); + while (shell.getDisplay().readAndDispatch()) { + // nothing + } + + Event event = new Event(); + event.type = SWT.Show; + menu.notifyListeners(SWT.Show, event); + event = new Event(); + event.type = SWT.Hide; + menu.notifyListeners(SWT.Hide, event); + } + + private Menu findMenu() { + /* NOTE: Assuming shell.getParent().getShell() is the workbench window. */ + Menu container = shell.getParent().getShell().getMenuBar(); + MenuPathElement lastPathElement = null; + for (int i = 0; i < menuPath.length; ++i) { + MenuItem mi = findItem(container, menuPath[i]); + if (mi == null) + return null; + Menu mim = mi.getMenu(); + if (mim == null) + throw new IllegalStateException(String.format( + "Could not get target menu \"%s\" in %s", + menuPath[i].getName(), lastPathElement != null ? + '"' + lastPathElement.getName() + '"' : "menu bar")); + tryToFillMenu(mim); + container = mim; + lastPathElement = menuPath[i]; + } + return container; + } + + /** + * Identifies a specific item in an SWT menu. It has been observed that + * associated data of a menu item maintains the same identity during a CAVE + * session even if the MenuItem is recreated. However, the associated + * data is not always unique. Menu item text is used to differentiate. + */ + static class MenuPathElement { + Object data; + String cleanText; + public MenuPathElement(MenuItem item) { + data = item.getData(); + cleanText = getCleanMenuItemText(item.getText()); + } + public int getMatchLevel(MenuItem item) { + int level = 0; + if (item.getData() == data) + ++level; + if (cleanText.equals(item.getText())) + ++level; + return level; + } + public String getName() { + if (cleanText != null && cleanText.length() > 0) + return cleanText; + Object value = data; + if (value instanceof MenuManager) + value = ((MenuManager) value).getId(); + else if (value instanceof ContributionItem) + value = ((ContributionItem) value).getId(); + return String.valueOf(value); + } + } + + /*package*/ static MenuItem findItem(Menu menu, MenuPathElement pe) { + MenuItem best = null; + int bestLevel = 0; + for (MenuItem item : menu.getItems()) { + int matchLevel = pe.getMatchLevel(item); + if (matchLevel > bestLevel) { + bestLevel = matchLevel; + best = item; + } + } + return best; + } + + private static MenuPathElement[] getMenuPath(Menu menu) { + ArrayList data = new ArrayList(); + while (menu != null) { + MenuItem mi = menu.getParentItem(); + if (mi == null) + break; + data.add(new MenuPathElement(mi)); + menu = menu.getParentMenu(); + } + Collections.reverse(data); + return data.toArray(new MenuPathElement[data.size()]); + } } diff --git a/cave/com.raytheon.uf.viz.ui.menus/src/com/raytheon/uf/viz/ui/menus/widgets/tearoff/TearOffMenuListener.java b/cave/com.raytheon.uf.viz.ui.menus/src/com/raytheon/uf/viz/ui/menus/widgets/tearoff/TearOffMenuListener.java index 529d26777a..f486955279 100644 --- a/cave/com.raytheon.uf.viz.ui.menus/src/com/raytheon/uf/viz/ui/menus/widgets/tearoff/TearOffMenuListener.java +++ b/cave/com.raytheon.uf.viz.ui.menus/src/com/raytheon/uf/viz/ui/menus/widgets/tearoff/TearOffMenuListener.java @@ -39,6 +39,11 @@ import org.eclipse.swt.widgets.Listener; import org.eclipse.swt.widgets.Menu; import org.eclipse.swt.widgets.MenuItem; +import com.raytheon.uf.common.status.IUFStatusHandler; +import com.raytheon.uf.common.status.UFStatus; +import com.raytheon.uf.common.status.UFStatus.Priority; +import com.raytheon.viz.ui.VizWorkbenchManager; + /** * Menu listener that adds item to menu which will open dialog which is the menu * @@ -49,6 +54,7 @@ import org.eclipse.swt.widgets.MenuItem; * Date Ticket# Engineer Description * ------------ ---------- ----------- -------------------------- * Sep 14, 2011 mschenke Initial creation + * Apr 10, 2013 DR 15185 D. Friedman Preserve tear-offs over perspective switches. * * * @@ -66,6 +72,9 @@ public class TearOffMenuListener implements IMenuListener2 { public static final String TEAROFF_PREFERENCE_ID = "tearoffmenus"; + private static final IUFStatusHandler statusHandler = UFStatus + .getHandler(TearOffMenuListener.class); + private static boolean enabled; static { final IPreferenceStore store = com.raytheon.uf.viz.core.Activator @@ -94,7 +103,7 @@ public class TearOffMenuListener implements IMenuListener2 { @Override public void menuAboutToShow(final IMenuManager manager) { register(manager.getItems(), this); - if (openDialogs.contains(getKey(manager)) == false) { + if (openDialogs.contains(getPerspectiveKey(manager)) == false) { // We need to add our item to be first so we need to remove others // then add ourself IContributionItem[] items = manager.getItems(); @@ -112,7 +121,7 @@ public class TearOffMenuListener implements IMenuListener2 { */ @Override public void menuAboutToHide(IMenuManager manager) { - if (openDialogs.contains(getKey(manager)) == false) { + if (openDialogs.contains(getPerspectiveKey(manager)) == false) { manager.remove(ID); manager.remove(ACTION_ID); unregister(manager.getItems(), this); @@ -137,6 +146,16 @@ public class TearOffMenuListener implements IMenuListener2 { } } + private Object getPerspectiveKey(IMenuManager manager) { + String perspectiveId = ""; + try { + perspectiveId = VizWorkbenchManager.getInstance().getCurrentWindow().getActivePage().getPerspective().getId(); + } catch (Exception e) { + statusHandler.handle(Priority.EVENTA, "Failed to get current perspective ID", e); + } + return perspectiveId + "::" + getKey(manager); + } + private static Object getKey(IMenuManager manager) { Object key = manager; if (manager.getId() != null) { @@ -215,17 +234,18 @@ public class TearOffMenuListener implements IMenuListener2 { */ @Override public void run() { + final Object key = getPerspectiveKey(manager); TearOffMenuDialog dialog = new TearOffMenuDialog(menu); dialog.addListener(SWT.Dispose, new Listener() { @Override public void handleEvent(Event event) { - openDialogs.remove(getKey(manager)); + openDialogs.remove(key); manager.remove(ID); manager.remove(ACTION_ID); unregister(manager.getItems(), TearOffMenuListener.this); } }); - openDialogs.add(getKey(manager)); + openDialogs.add(key); register(manager.getItems(), TearOffMenuListener.this); dialog.open(); } diff --git a/cave/com.raytheon.viz.awipstools/src/com/raytheon/viz/awipstools/ui/action/TimeOfArrivalAction.java b/cave/com.raytheon.viz.awipstools/src/com/raytheon/viz/awipstools/ui/action/TimeOfArrivalAction.java index d638221c72..210f5b5986 100644 --- a/cave/com.raytheon.viz.awipstools/src/com/raytheon/viz/awipstools/ui/action/TimeOfArrivalAction.java +++ b/cave/com.raytheon.viz.awipstools/src/com/raytheon/viz/awipstools/ui/action/TimeOfArrivalAction.java @@ -19,9 +19,12 @@ **/ package com.raytheon.viz.awipstools.ui.action; +import java.util.List; + +import com.raytheon.uf.viz.core.IDisplayPane; +import com.raytheon.uf.viz.core.VizApp; import com.raytheon.uf.viz.core.drawables.IDescriptor; import com.raytheon.uf.viz.core.exception.VizException; -import com.raytheon.uf.viz.core.rsc.AbstractVizResource.ResourceStatus; import com.raytheon.uf.viz.core.rsc.LoadProperties; import com.raytheon.uf.viz.core.rsc.tools.AwipsToolsResourceData; import com.raytheon.uf.viz.core.rsc.tools.action.AbstractMapToolAction; @@ -35,6 +38,7 @@ import com.raytheon.viz.awipstools.ui.layer.TimeOfArrivalLayer; * Date Ticket# Engineer Description * ------------ ---------- ----------- -------------------------- * 07DEC2007 #496 Eric Babin Initial Creation. + * Apr 12 2013 DR 16032 D. Friedman Make it work in multiple panes. * * * @@ -45,10 +49,6 @@ import com.raytheon.viz.awipstools.ui.layer.TimeOfArrivalLayer; public class TimeOfArrivalAction extends AbstractMapToolAction { - private TimeOfArrivalLayer layer = null; - - private AwipsToolsResourceData data = null; - /* * (non-Javadoc) * @@ -57,22 +57,40 @@ public class TimeOfArrivalAction extends */ @Override protected AwipsToolsResourceData getResourceData() { - if (data == null) { - data = new AwipsToolsResourceData( + return new AwipsToolsResourceData( TimeOfArrivalLayer.NAME, TimeOfArrivalLayer.class); - } - return data; } @Override protected TimeOfArrivalLayer getResource(LoadProperties loadProperties, IDescriptor descriptor) throws VizException { - if (layer == null || layer.getStatus() == ResourceStatus.DISPOSED) { - layer = super.getResource(loadProperties, descriptor); - } else { - layer.reopenDialog(); - } + TimeOfArrivalLayer layer = getExistingResource(); + if (layer == null) + return super.getResource(loadProperties, descriptor); + + VizApp.runAsync( new Runnable() { + @Override + public void run() { + TimeOfArrivalLayer layer = getExistingResource(); + if (layer != null) { + layer.makeEditableAndReopenDialog(); + } + } + }); return layer; } + private TimeOfArrivalLayer getExistingResource() { + IDisplayPane[] panes = getSelectedPanes(); + if (panes != null && panes.length > 0) { + List layers = null; + layers = panes[0].getDescriptor().getResourceList() + .getResourcesByTypeAsType(TimeOfArrivalLayer.class); + if (layers.size() > 0) { + return layers.get(0); + } + } + return null; + } + } diff --git a/cave/com.raytheon.viz.awipstools/src/com/raytheon/viz/awipstools/ui/layer/TimeOfArrivalLayer.java b/cave/com.raytheon.viz.awipstools/src/com/raytheon/viz/awipstools/ui/layer/TimeOfArrivalLayer.java index 4e98e5378a..6f80d3f216 100644 --- a/cave/com.raytheon.viz.awipstools/src/com/raytheon/viz/awipstools/ui/layer/TimeOfArrivalLayer.java +++ b/cave/com.raytheon.viz.awipstools/src/com/raytheon/viz/awipstools/ui/layer/TimeOfArrivalLayer.java @@ -71,6 +71,7 @@ import com.raytheon.viz.awipstools.common.stormtrack.StormTrackUIManager; import com.raytheon.viz.awipstools.ui.dialog.TimeOfArrivalDialog; import com.raytheon.viz.core.rsc.jts.JTSCompiler; import com.raytheon.viz.ui.VizWorkbenchManager; +import com.raytheon.viz.ui.input.EditableManager; import com.raytheon.viz.ui.input.InputAdapter; import com.vividsolutions.jts.geom.Coordinate; import com.vividsolutions.jts.geom.GeometryFactory; @@ -98,6 +99,7 @@ import com.vividsolutions.jts.geom.GeometryFactory; * left-to-right if there is not enough room * for the text to the left of the point. * 15Mar2013 15693 mgamazaychikov Added magnification capability. + * Apr 12 2013 DR 16032 D. Friedman Make it work in multiple panes. * * * @author mschenke @@ -256,7 +258,6 @@ public class TimeOfArrivalLayer extends AbstractStormTrackResource { this.pdProps.setMaxDisplayWidth(TimeOfArrivalLayer.PD_MAX_WIDTH); timeFormat.setTimeZone(TimeZone.getTimeZone("GMT")); - reopenDialog(); leadState = new LeadTimeState(); shell = VizWorkbenchManager.getInstance().getCurrentWindow().getShell(); @@ -272,6 +273,7 @@ public class TimeOfArrivalLayer extends AbstractStormTrackResource { if (container != null) { container.registerMouseHandler(adapter); } + reopenDialog(); } @Override @@ -565,20 +567,20 @@ public class TimeOfArrivalLayer extends AbstractStormTrackResource { */ public void reopenDialog() { // Open the dialog - if (dialog == null || dialog.getShell() == null - || dialog.getShell().isDisposed()) { - VizApp.runAsync(new Runnable() { + VizApp.runAsync(new Runnable() { - @Override - public void run() { + @Override + public void run() { + if (dialog == null || dialog.getShell() == null + || dialog.getShell().isDisposed()) { dialog = new TimeOfArrivalDialog(VizWorkbenchManager .getInstance().getCurrentWindow().getShell(), TimeOfArrivalLayer.this); dialog.setBlockOnOpen(false); dialog.open(); } - }); - } + } + }); } private void updateLeadTimeState() { @@ -678,4 +680,9 @@ public class TimeOfArrivalLayer extends AbstractStormTrackResource { } leadState.changed = false; } + + public void makeEditableAndReopenDialog() { + EditableManager.makeEditable(this, true); + reopenDialog(); + } } diff --git a/cave/com.raytheon.viz.core/src/com/raytheon/viz/core/topo/TopoResource.java b/cave/com.raytheon.viz.core/src/com/raytheon/viz/core/topo/TopoResource.java index 5f69a7a141..554858ca07 100644 --- a/cave/com.raytheon.viz.core/src/com/raytheon/viz/core/topo/TopoResource.java +++ b/cave/com.raytheon.viz.core/src/com/raytheon/viz/core/topo/TopoResource.java @@ -69,6 +69,7 @@ import com.raytheon.viz.core.drawables.ColorMapParameterFactory; * Date Ticket# Engineer Description * ------------ ---------- ----------- -------------------------- * Feb 14, 2007 chammack Initial Creation. + * Apr 03, 2013 1562 mschenke Fix for custom colormaps * * * @@ -140,10 +141,9 @@ public class TopoResource extends ColorMapParameters existing = getCapability(ColorMapCapability.class) .getColorMapParameters(); if (existing != null) { - PersistedParameters params = existing.getPersisted(); - if (params != null) { - parameters.setColorMapMin(params.getColorMapMin()); - parameters.setColorMapMax(params.getColorMapMax()); + PersistedParameters persisted = existing.getPersisted(); + if (persisted != null) { + parameters.applyPersistedParameters(persisted); } if (existing.getColorMap() != null) { diff --git a/cave/com.raytheon.viz.gfe/src/com/raytheon/viz/gfe/core/internal/AbstractParmManager.java b/cave/com.raytheon.viz.gfe/src/com/raytheon/viz/gfe/core/internal/AbstractParmManager.java index fd4eb09274..abaf9325c4 100644 --- a/cave/com.raytheon.viz.gfe/src/com/raytheon/viz/gfe/core/internal/AbstractParmManager.java +++ b/cave/com.raytheon.viz.gfe/src/com/raytheon/viz/gfe/core/internal/AbstractParmManager.java @@ -1,19 +1,19 @@ /** * This software was developed and / or modified by Raytheon Company, * pursuant to Contract DG133W-05-CQ-1067 with the US Government. - * + * * U.S. EXPORT CONTROLLED TECHNICAL DATA * This software product contains export-restricted data whose * export/transfer/disclosure is restricted by U.S. law. Dissemination * to non-U.S. persons whether in the United States or abroad requires * an export license or other authorization. - * + * * Contractor Name: Raytheon Company * Contractor Address: 6825 Pine Street, Suite 340 * Mail Stop B8 * Omaha, NE 68106 * 402.291.0100 - * + * * See the AWIPS II Master Rights File ("Master Rights File.pdf") for * further licensing information. **/ @@ -80,13 +80,13 @@ import com.raytheon.viz.gfe.core.parm.vcparm.VCModuleJobPool; /** * Implements common parm manager functionality shared between concrete and mock * implementations. - * + * *
  * SOFTWARE HISTORY
  * Date         Ticket#    Engineer    Description
  * ------------ ---------- ----------- --------------------------
  * 03/26/2008              chammack    Split non-mock code from MockParmManager
- * 02/23/2012    #346      dgilling    Dispose of VCParms from this class's 
+ * 02/23/2012    #346      dgilling    Dispose of VCParms from this class's
  *                                     dispose method.
  * 02/23/2012    #346      dgilling    Ensure all Parms are disposed when calling
  *                                     dispose method.
@@ -101,9 +101,11 @@ import com.raytheon.viz.gfe.core.parm.vcparm.VCModuleJobPool;
  * 01/22/2013    #1515     dgilling    Increase default size of VCModule thread pool
  *                                     to decrease UI hang-ups waiting for results.
  * 03/20/2013    #1774     randerso    Code cleanup
- * 
+ * 04/11/2013    16028     ryu         Fixed setParmsRemoveISCDeps() to not remove
+ *                                     modified parms.
+ *
  * 
- * + * * @author chammack * @version 1.0 */ @@ -419,7 +421,7 @@ public abstract class AbstractParmManager implements IParmManager { /* * (non-Javadoc) - * + * * @see com.raytheon.viz.gfe.core.IParmManager#dispose() */ @Override @@ -491,14 +493,14 @@ public abstract class AbstractParmManager implements IParmManager { /** * Return the DataManager - * + * * @return the dataManager */ protected abstract DataManager getDataManager(); /* * (non-Javadoc) - * + * * @see java.lang.Object#finalize() */ @Override @@ -509,7 +511,7 @@ public abstract class AbstractParmManager implements IParmManager { /** * Recalculate the system time range using the total time span of all * displayed parms and their locks - * + * * @return the system time range */ protected TimeRange recalcSystemTimeRange() { @@ -571,7 +573,7 @@ public abstract class AbstractParmManager implements IParmManager { /* * (non-Javadoc) - * + * * @see com.raytheon.viz.gfe.core.parm.IParmManager#getLockedParms() */ @Override @@ -593,7 +595,7 @@ public abstract class AbstractParmManager implements IParmManager { /* * (non-Javadoc) - * + * * @see * com.raytheon.viz.gfe.core.parm.IParmManager#getParm(com.raytheon.viz. * gfe.core.parm.ParmID) @@ -615,7 +617,7 @@ public abstract class AbstractParmManager implements IParmManager { /* * (non-Javadoc) - * + * * @see com.raytheon.viz.gfe.core.parm.IParmManager#getUndisplayedParms() */ @Override @@ -630,7 +632,7 @@ public abstract class AbstractParmManager implements IParmManager { /* * (non-Javadoc) - * + * * @see com.raytheon.viz.gfe.core.IParmManager#getSelectedParms() */ @Override @@ -653,7 +655,7 @@ public abstract class AbstractParmManager implements IParmManager { /* * (non-Javadoc) - * + * * @see com.raytheon.viz.gfe.core.IParmManager#getModifiedParms() */ @Override @@ -676,7 +678,7 @@ public abstract class AbstractParmManager implements IParmManager { /** * Returns a matching parm * (creates if necessary) for the given expression * and database id. - * + * * @param dbid * the database * @param exprName @@ -810,7 +812,7 @@ public abstract class AbstractParmManager implements IParmManager { * Helper function for setParms. Takes the toBeLoaded and * removeParms lists, calculates non-visible ISC dependencies, and then * returns the updated lists through the calling arguments. - * + * * @param toBeLoaded * @param removeParms */ @@ -822,7 +824,8 @@ public abstract class AbstractParmManager implements IParmManager { List depParms = dependentParms(removeList.get(i), true); for (ParmID pid : depParms) { int index = pivdIndex(toBeLoaded, pid); - if ((index != -1) && (!toBeLoaded.get(index).isVisible())) { + if ((index != -1) && (!toBeLoaded.get(index).isVisible()) + && (!getParm(toBeLoaded.get(index).getParmID()).isModified())) { removeList.add(toBeLoaded.get(index).getParmID()); toBeLoaded.remove(index); } @@ -840,7 +843,7 @@ public abstract class AbstractParmManager implements IParmManager { * Helper function for setParms. Takes the toBeLoaded, * addedParms, removeParms, and modParms lists, calculates dependencies, and * then returns the updated lists through the calling arguments. - * + * * @param toBeLoaded * @param addParms * @param removeParms @@ -935,7 +938,7 @@ public abstract class AbstractParmManager implements IParmManager { /** * Actual parm creation mechanism - * + * * @param pid * parm id * @param mutableParm @@ -948,20 +951,20 @@ public abstract class AbstractParmManager implements IParmManager { boolean mutableParm, boolean displayable) throws GFEServerException; /** - * + * * Command to create/remove parms based on ParmID. For additions, the Map * contains the ParmID and visibility. - * + * * implementation --------------------------------------------------------- * Note: addParms, removeParms is modified within this routine, thus they * are not passed in as const references. - * + * * Routine converts the ParmIDs into Parms*. Special cases for VCParms, * since they need to load other parms possibly. Thus the input add and * remove may not result in the same parms being created and destroyed. * ------ * --------------------------------------------------------------------- - * + * * @param addParms * @param removeParms */ @@ -1084,7 +1087,7 @@ public abstract class AbstractParmManager implements IParmManager { /** * Command to create/remove parms based on ParmID. - * + * * @param addParms * the parms to add * @param removeParms @@ -1098,7 +1101,7 @@ public abstract class AbstractParmManager implements IParmManager { /* * (non-Javadoc) - * + * * @see * com.raytheon.viz.gfe.core.IParmManager#getParmInExpr(java.lang.String, * boolean, com.raytheon.viz.gfe.core.parm.Parm) @@ -1150,7 +1153,7 @@ public abstract class AbstractParmManager implements IParmManager { /* * (non-Javadoc) - * + * * @see com.raytheon.viz.gfe.core.parm.IParmManager#getDisplayedParms() */ @Override @@ -1171,7 +1174,7 @@ public abstract class AbstractParmManager implements IParmManager { /* * (non-Javadoc) - * + * * @see * com.raytheon.viz.gfe.core.IParmManager#setDisplayedParms(com.raytheon * .edex.plugin.gfe.db.objects.ParmID[]) @@ -1275,7 +1278,7 @@ public abstract class AbstractParmManager implements IParmManager { /* * (non-Javadoc) - * + * * @see * com.raytheon.viz.gfe.core.msgs.IParmIDChangedListener#parmIDChanged(com * .raytheon.viz.gfe.core.parm.Parm, @@ -1291,7 +1294,7 @@ public abstract class AbstractParmManager implements IParmManager { /* * (non-Javadoc) - * + * * @seecom.raytheon.viz.gfe.core.msgs.IParmInventoryChangedListener# * parmInventoryChanged(com.raytheon.viz.gfe.core.parm.Parm, * com.raytheon.uf.common.time.TimeRange) @@ -1315,7 +1318,7 @@ public abstract class AbstractParmManager implements IParmManager { /* * (non-Javadoc) - * + * * @see * com.raytheon.viz.gfe.core.msgs.ILockTableChangedListener#lockTableChanged * (com.raytheon.viz.gfe.core.parm.Parm, @@ -1333,7 +1336,7 @@ public abstract class AbstractParmManager implements IParmManager { /* * (non-Javadoc) - * + * * @see * com.raytheon.viz.gfe.core.IParmManager#addDisplayedParmListChangedListener * (com.raytheon.viz.gfe.core.msgs.IDisplayedParmListChangedListener) @@ -1346,7 +1349,7 @@ public abstract class AbstractParmManager implements IParmManager { /* * (non-Javadoc) - * + * * @see * com.raytheon.viz.gfe.core.IParmManager#removeDisplayedParmListChangedListener * (com.raytheon.viz.gfe.core.msgs.IDisplayedParmListChangedListener) @@ -1359,7 +1362,7 @@ public abstract class AbstractParmManager implements IParmManager { /* * (non-Javadoc) - * + * * @see * com.raytheon.viz.gfe.core.IParmManager#addParmListChangedListener(com * .raytheon.viz.gfe.core.msgs.IParmListChangedListener) @@ -1372,7 +1375,7 @@ public abstract class AbstractParmManager implements IParmManager { /* * (non-Javadoc) - * + * * @see * com.raytheon.viz.gfe.core.IParmManager#removeParmListChangedListener( * com.raytheon.viz.gfe.core.msgs.IParmListChangedListener) @@ -1384,7 +1387,7 @@ public abstract class AbstractParmManager implements IParmManager { /* * (non-Javadoc) - * + * * @see * com.raytheon.viz.gfe.core.IParmManager#addParmIDChangedListener(com.raytheon * .viz.gfe.core.msgs.IParmIDChangedListener) @@ -1396,7 +1399,7 @@ public abstract class AbstractParmManager implements IParmManager { /* * (non-Javadoc) - * + * * @see * com.raytheon.viz.gfe.core.IParmManager#removeParmIDChangedListener(com * .raytheon.viz.gfe.core.msgs.IParmIDChangedListener) @@ -1408,7 +1411,7 @@ public abstract class AbstractParmManager implements IParmManager { /* * (non-Javadoc) - * + * * @see * com.raytheon.viz.gfe.core.IParmManager#addSystemTimeRangeChangedListener * (com.raytheon.viz.gfe.core.msgs.ISystemTimeRangeChangedListener) @@ -1421,7 +1424,7 @@ public abstract class AbstractParmManager implements IParmManager { /* * (non-Javadoc) - * + * * @see * com.raytheon.viz.gfe.core.IParmManager#removeSystemTimeRangeChangedListener * (com.raytheon.viz.gfe.core.msgs.ISystemTimeRangeChangedListener) @@ -1434,7 +1437,7 @@ public abstract class AbstractParmManager implements IParmManager { /* * (non-Javadoc) - * + * * @see * com.raytheon.viz.gfe.core.IParmManager#addAvailableSourcesChangedListener * (com.raytheon.viz.gfe.core.msgs.IAvailableSourcesChangedListener) @@ -1447,7 +1450,7 @@ public abstract class AbstractParmManager implements IParmManager { /* * (non-Javadoc) - * + * * @see * com.raytheon.viz.gfe.core.IParmManager#addNewModelAvailableListener(com * .raytheon.viz.gfe.core.msgs.INewModelAvailableListener) @@ -1459,7 +1462,7 @@ public abstract class AbstractParmManager implements IParmManager { /* * (non-Javadoc) - * + * * @see * com.raytheon.viz.gfe.core.IParmManager#removeAvailableSourcesChangedListener * (com.raytheon.viz.gfe.core.msgs.IAvailableSourcesChangedListener) @@ -1472,7 +1475,7 @@ public abstract class AbstractParmManager implements IParmManager { /* * (non-Javadoc) - * + * * @see * com.raytheon.viz.gfe.core.IParmManager#removeNewModelAvailableListener * (com.raytheon.viz.gfe.core.msgs.INewModelAvailableListener) @@ -1485,7 +1488,7 @@ public abstract class AbstractParmManager implements IParmManager { /** * Fire the displayed parm list changed listener - * + * * @param parms * complete list of parms * @param adds @@ -1512,7 +1515,7 @@ public abstract class AbstractParmManager implements IParmManager { /** * Fire the ParmID changed event. - * + * * @param parm * The parm which had its ParmID change * @param newParmId @@ -1535,7 +1538,7 @@ public abstract class AbstractParmManager implements IParmManager { /** * Fire the parm list changed listener - * + * * @param parms * complete list of parms * @param adds @@ -1561,7 +1564,7 @@ public abstract class AbstractParmManager implements IParmManager { /** * Fire the system time range changed listener - * + * * @param systemTimeRange * new system time range */ @@ -1583,7 +1586,7 @@ public abstract class AbstractParmManager implements IParmManager { /** * Fire the available sources changed event. - * + * * @param inventory * The complete inventory * @param deletions @@ -1612,7 +1615,7 @@ public abstract class AbstractParmManager implements IParmManager { /** * Fire the new model available event. - * + * * @param additions * The DatabaseID of the newly-available model */ @@ -1633,7 +1636,7 @@ public abstract class AbstractParmManager implements IParmManager { /** * Return a list of ParmIDs for a list of Parms - * + * * @param parms * @return */ @@ -1649,7 +1652,7 @@ public abstract class AbstractParmManager implements IParmManager { /* * (non-Javadoc) - * + * * @see * com.raytheon.viz.gfe.core.IParmManager#getParms(com.raytheon.uf.common * .dataplugin.gfe.db.objects.ParmID[]) @@ -1667,7 +1670,7 @@ public abstract class AbstractParmManager implements IParmManager { /** * Return a list of Parms for a list of ParmIDs with nulls in place of parms * that are not loaded. - * + * * @param parmIDs * @return */ @@ -1685,7 +1688,7 @@ public abstract class AbstractParmManager implements IParmManager { /* * (non-Javadoc) - * + * * @see com.raytheon.viz.gfe.core.IParmManager#getAllAvailableParms() */ @Override @@ -1701,7 +1704,7 @@ public abstract class AbstractParmManager implements IParmManager { /* * (non-Javadoc) - * + * * @see * com.raytheon.viz.gfe.core.IParmManager#setParmDisplayable(com.raytheon * .viz.gfe.core.parm.Parm, boolean) @@ -1727,7 +1730,7 @@ public abstract class AbstractParmManager implements IParmManager { /* * (non-Javadoc) - * + * * @see com.raytheon.viz.gfe.core.IParmManager#deallocateUnusedGrids(int) */ @Override @@ -1794,7 +1797,7 @@ public abstract class AbstractParmManager implements IParmManager { /* * (non-Javadoc) - * + * * @see com.raytheon.viz.gfe.core.IParmManager#getProductDB() */ @Override @@ -1805,7 +1808,7 @@ public abstract class AbstractParmManager implements IParmManager { /** * Filters out a complete list of databaseIDs to those only allowed by the * dbCatagories in the gfeConfig. Sorts the final list. - * + * * @param dbIds * The list of DatabaseIDs to filter * @return A sorted list of DatabseIDs that are GRID types and match the @@ -1832,7 +1835,7 @@ public abstract class AbstractParmManager implements IParmManager { * mutable model, plus all other databases identified by the database * categories specified in the gfeConfig. The databases are filtered by * projection also, since the GFE can only handle one projection. - * + * * @return A filtered list of available databases. */ private List getDatabaseInventory() { @@ -1852,7 +1855,7 @@ public abstract class AbstractParmManager implements IParmManager { * This function is called when the list of available database has changed. * The list of available parms is updated based on the list of additions and * deletions. - * + * * @param deletions * The items being removed from the inventory * @param additions @@ -1886,7 +1889,7 @@ public abstract class AbstractParmManager implements IParmManager { /* * (non-Javadoc) - * + * * @see * com.raytheon.viz.gfe.core.IParmManager#updateModel(com.raytheon.uf.common * .dataplugin.gfe.db.objects.DatabaseID) @@ -1996,7 +1999,7 @@ public abstract class AbstractParmManager implements IParmManager { /* * (non-Javadoc) - * + * * @see com.raytheon.viz.gfe.core.IParmManager#deleteTemporaryParms() */ @Override @@ -2069,7 +2072,7 @@ public abstract class AbstractParmManager implements IParmManager { /** * Returns the Virtual Parm index into vcModules for the given ParmID. - * + * * @param pid * ParmID to search for. * @return The index of the ParmID if it is in vcModules. Else, -1. diff --git a/cave/com.raytheon.viz.gfe/src/com/raytheon/viz/gfe/core/parm/Parm.java b/cave/com.raytheon.viz.gfe/src/com/raytheon/viz/gfe/core/parm/Parm.java index 686dba3947..69d2335a85 100644 --- a/cave/com.raytheon.viz.gfe/src/com/raytheon/viz/gfe/core/parm/Parm.java +++ b/cave/com.raytheon.viz.gfe/src/com/raytheon/viz/gfe/core/parm/Parm.java @@ -181,6 +181,7 @@ import com.vividsolutions.jts.geom.Coordinate; * 02/13/13 #1597 randerso Removed debug logging to improve performance * Mar 13, 2013 1792 bsteffen Improve performance of gfe parm average * ant time weighted average. + * Apr 02, 2013 #1774 randerso Fixed a possible deadlock issue. * * * @author chammack @@ -3028,9 +3029,9 @@ public abstract class Parm implements Comparable { } } finally { // always release locks in reverse order of acquiring to prevent - // deadlock - otherParm.grids.releaseWriteLock(); + // deadlock (note that the grids were swapped inside this try block) this.grids.releaseWriteLock(); + otherParm.grids.releaseWriteLock(); } // the swap is now complete, send out the parm id changed notifications diff --git a/cave/com.raytheon.viz.gfe/src/com/raytheon/viz/gfe/dialogs/formatterlauncher/StoreTransmitDlg.java b/cave/com.raytheon.viz.gfe/src/com/raytheon/viz/gfe/dialogs/formatterlauncher/StoreTransmitDlg.java index 98174d46bc..e4f92df686 100755 --- a/cave/com.raytheon.viz.gfe/src/com/raytheon/viz/gfe/dialogs/formatterlauncher/StoreTransmitDlg.java +++ b/cave/com.raytheon.viz.gfe/src/com/raytheon/viz/gfe/dialogs/formatterlauncher/StoreTransmitDlg.java @@ -65,6 +65,7 @@ import com.raytheon.viz.ui.dialogs.CaveSWTDialog; * 28May2010 2187 cjeanbap Added StdTextProductFactory * functionality. * 09 NOV 2012 1298 rferrel Changes for non-blocking dialog. + * 02apr2013 15564 mgamazaychikov Ensured awipsWanPil to be 10 characters space-padded long * * * @author lvenable @@ -387,7 +388,8 @@ public class StoreTransmitDlg extends CaveSWTDialog implements } else { req = new OUPRequest(); OfficialUserProduct oup = new OfficialUserProduct(); - String awipsWanPil = productIdTF.getText(); + // make sure the awipsWanPil is exactly 10 characters space-padded long + String awipsWanPil = String.format("%-10s", productIdTF.getText().trim()); oup.setAwipsWanPil(awipsWanPil); oup.setProductText(productText); diff --git a/cave/com.raytheon.viz.gfe/src/com/raytheon/viz/gfe/gridmanager/GridCanvas.java b/cave/com.raytheon.viz.gfe/src/com/raytheon/viz/gfe/gridmanager/GridCanvas.java index ceebd780f3..fd7b86ddb1 100644 --- a/cave/com.raytheon.viz.gfe/src/com/raytheon/viz/gfe/gridmanager/GridCanvas.java +++ b/cave/com.raytheon.viz.gfe/src/com/raytheon/viz/gfe/gridmanager/GridCanvas.java @@ -111,6 +111,8 @@ import com.raytheon.viz.ui.cmenu.AbstractRightClickAction; * 11/30/2012 #1328 mschenke Made GFE use descriptor for time matching * and time storage and manipulation * 01/22/2013 #1518 randerso Removed use of Map with Parms as keys + * 03/28/2013 #1838 randerso Fixed selected time range when Select Grids When + * Stepping is enabled. Cleaned up deprecated warnings. * * * @@ -563,8 +565,7 @@ public class GridCanvas extends Canvas implements IMessageClient { List popUpActions = new ArrayList(0); if (gmEditActions.length > 0) { // Only show tools this parm supports - String[] parmTools = DataManager - .getCurrentInstance() + String[] parmTools = dataMgr .getSmartToolInterface().listTools(parm); List parmToolList = Arrays .asList(parmTools); @@ -815,8 +816,7 @@ public class GridCanvas extends Canvas implements IMessageClient { grid = null; } grid.changeValidTime(lastDestinationTR, false); - grid.updateHistoryToModified(DataManager.getCurrentInstance() - .getWsId()); + grid.updateHistoryToModified(dataMgr.getWsId()); newGrids.add(grid); @@ -924,8 +924,6 @@ public class GridCanvas extends Canvas implements IMessageClient { Date clickTime = gridManager.getUtil().pixelToDate(e.x); GridID clickGridID = new GridID(parm, clickTime); - gridManager.setSelectedTime(clickTime); - // make it active, make it inactive depending upon okToEdit try { if (clickGridID.grid() != null && clickGridID.grid().isOkToEdit()) { @@ -967,6 +965,8 @@ public class GridCanvas extends Canvas implements IMessageClient { statusHandler.handle(Priority.PROBLEM, "Error activating parm " + parm.getParmID().compositeNameUI(), e1); } + + gridManager.setSelectedTime(clickTime); } private void resize() { diff --git a/cave/com.raytheon.viz.gfe/src/com/raytheon/viz/gfe/procedures/ProcedureJob.java b/cave/com.raytheon.viz.gfe/src/com/raytheon/viz/gfe/procedures/ProcedureJob.java index bd6ab0f0f4..21c8f4523e 100644 --- a/cave/com.raytheon.viz.gfe/src/com/raytheon/viz/gfe/procedures/ProcedureJob.java +++ b/cave/com.raytheon.viz.gfe/src/com/raytheon/viz/gfe/procedures/ProcedureJob.java @@ -58,6 +58,8 @@ import com.raytheon.viz.gfe.jobs.AsyncProgressJob; * Oct 8, 2009 njensen Initial creation * Jan 8, 2013 1486 dgilling Support changes to BaseGfePyController. * Jan 18, 2013 1509 njensen Garbage collect after running procedure + * Apr 03, 2013 1855 njensen Never dispose interpreters until shutdown and + * reuse interpreter if called from procedure * * * @@ -69,12 +71,7 @@ public class ProcedureJob extends AbstractQueueJob { /** * Maximum number of jobs to keep for a given Data Manager. */ - private final static int maxJobs = 3; - - /** - * Amount of time to keep inactive jobs servers around. - */ - private final static long expireTime = 5L * 60L * 1000L; + private final static int maxJobs = 4; /** * Index of job with the queue. Will break code if not zero. @@ -121,14 +118,12 @@ public class ProcedureJob extends AbstractQueueJob { */ @Override protected IStatus run(IProgressMonitor monitor) { - long starTime = System.currentTimeMillis(); - boolean expireJob = instanceMap.get(dataMgr).get(QUEUE_JOB_INDEX) != this; try { python = ProcedureFactory.buildController(dataMgr); } catch (JepException e) { ProcedureJob.removeJob(dataMgr, this); return new Status(IStatus.ERROR, StatusConstants.PLUGIN_ID, - "Error initializing guidance python", e); + "Error initializing procedure python", e); } try { @@ -151,15 +146,10 @@ public class ProcedureJob extends AbstractQueueJob { if (request != null) { request.requestComplete(null); } - } else if (expireJob - && ((instanceMap.get(dataMgr).size() > maxJobs) || (System - .currentTimeMillis() - starTime) > expireTime)) { - ProcedureJob.removeJob(dataMgr, this); - break; } } catch (Throwable t) { statusHandler.handle(Priority.PROBLEM, - "Error running tool ", t); + "Error running procedure ", t); if (request != null) { request.requestComplete(t); } @@ -229,6 +219,7 @@ public class ProcedureJob extends AbstractQueueJob { instanceMap = new HashMap>(); } + Thread currentThread = Thread.currentThread(); List jobList = instanceMap.get(dataMgr); if (jobList == null) { jobList = new ArrayList(); @@ -241,17 +232,26 @@ public class ProcedureJob extends AbstractQueueJob { job.schedule(); } boolean jobAvailable = false; + ProcedureJob alreadyOnThread = null; for (ProcedureJob job : jobList) { - if (job.request == null) { + Thread jobThread = job.getThread(); + if (currentThread == jobThread) { + // this occurs when a running procedure uses + // SmartScript.callProcedure() + // for efficiency we want to just stay on this thread + alreadyOnThread = job; + jobAvailable = true; + break; + } else if (job.request == null) { jobAvailable = true; break; } } - // All jobs for data manager are busy add another. - // To mimic AWIPS I allow any number of jobs. - // The check in the run will reduce the number to maxJobs. - if (jobAvailable == false) { + // All jobs for data manager are busy, add another if we haven't + // reached the limit. + if (alreadyOnThread == null && !jobAvailable + && jobList.size() < maxJobs) { ProcedureJob job = new ProcedureJob(dataMgr); job.setSystem(true); jobList.add(job); @@ -261,7 +261,18 @@ public class ProcedureJob extends AbstractQueueJob { jobAvailable = true; } - jobList.get(QUEUE_JOB_INDEX).enqueue(request); + if (alreadyOnThread != null) { + try { + alreadyOnThread.processRequest(request); + request.requestComplete(null); + } catch (Throwable t) { + statusHandler.handle(Priority.PROBLEM, + "Error running procedure ", t); + request.requestComplete(t); + } + } else { + jobList.get(QUEUE_JOB_INDEX).enqueue(request); + } return jobAvailable; } diff --git a/cave/com.raytheon.viz.gfe/src/com/raytheon/viz/gfe/smarttool/Tool.java b/cave/com.raytheon.viz.gfe/src/com/raytheon/viz/gfe/smarttool/Tool.java index b6854ff0be..bf2c07e451 100644 --- a/cave/com.raytheon.viz.gfe/src/com/raytheon/viz/gfe/smarttool/Tool.java +++ b/cave/com.raytheon.viz.gfe/src/com/raytheon/viz/gfe/smarttool/Tool.java @@ -1,19 +1,19 @@ /** * This software was developed and / or modified by Raytheon Company, * pursuant to Contract DG133W-05-CQ-1067 with the US Government. - * + * * U.S. EXPORT CONTROLLED TECHNICAL DATA * This software product contains export-restricted data whose * export/transfer/disclosure is restricted by U.S. law. Dissemination * to non-U.S. persons whether in the United States or abroad requires * an export license or other authorization. - * + * * Contractor Name: Raytheon Company * Contractor Address: 6825 Pine Street, Suite 340 * Mail Stop B8 * Omaha, NE 68106 * 402.291.0100 - * + * * See the AWIPS II Master Rights File ("Master Rights File.pdf") for * further licensing information. **/ @@ -62,7 +62,7 @@ import com.raytheon.viz.gfe.smarttool.script.SmartToolController; /** * Ported from Tool.py - * + * *
  * SOFTWARE HISTORY
  * Date         Ticket#    Engineer    Description
@@ -71,9 +71,10 @@ import com.raytheon.viz.gfe.smarttool.script.SmartToolController;
  * Jan 08, 2013  1486      dgilling    Support changes to BaseGfePyController.
  * 02/14/2013              mnash       Change QueryScript to use new Python concurrency
  * 02/20/2013        #1597 randerso    Added logging to support GFE Performance metrics
- * 
+ * 04/10/2013    16028     ryu         Check for null seTime in execute()
+ *
  * 
- * + * * @author njensen * @version 1.0 */ @@ -109,7 +110,7 @@ public class Tool { /** * Constructor - * + * * @param aParmMgr * the parm manager * @param aToolName @@ -142,7 +143,7 @@ public class Tool { /** * Returns the objects that should be passed to the smart tool in python - * + * * @param args * the names of the arguments * @param gridTimeRange @@ -225,7 +226,7 @@ public class Tool { /** * Returns the attribute for a particular parm's name - * + * * @param arg * the name of the parm * @param attrStr @@ -260,7 +261,7 @@ public class Tool { /** * Returns the grid data for the specified parameters - * + * * @param arg * the name of the parm * @param mode @@ -309,7 +310,7 @@ public class Tool { /** * Returns the grid history corresponding to the parm name and time range - * + * * @param arg * the name of the parm * @param gridTimeRange @@ -349,7 +350,7 @@ public class Tool { /** * Returns the grid info corresponding to the parm name and time range - * + * * @param arg * the name of the parm * @param gridTimeRange @@ -383,7 +384,7 @@ public class Tool { /** * Executes a smart tool - * + * * @param toolName * the name of the tool * @param inputParm @@ -490,7 +491,8 @@ public class Tool { final Date timeInfluence; Date seTime = DataManagerUIFactory.getCurrentInstance() .getSpatialDisplayManager().getSpatialEditorTime(); - if (grids.length == 1 && grid.getGridTime().contains(seTime)) { + if (seTime != null && + grids.length == 1 && grid.getGridTime().contains(seTime)) { timeInfluence = seTime; } else { timeInfluence = grid.getGridTime().getStart(); @@ -579,7 +581,7 @@ public class Tool { /** * Executes the numeric smart tool - * + * * @param parmToEdit * the parm to edit * @param first @@ -651,7 +653,7 @@ public class Tool { /** * Cleans up a smart tool execution or failure and displays any missing data * message - * + * * @param parmToEdit * the parm to edit * @param save diff --git a/cave/com.raytheon.viz.gfe/src/com/raytheon/viz/gfe/smarttool/script/SmartToolJob.java b/cave/com.raytheon.viz.gfe/src/com/raytheon/viz/gfe/smarttool/script/SmartToolJob.java index c154f9439b..2165c3dfa4 100644 --- a/cave/com.raytheon.viz.gfe/src/com/raytheon/viz/gfe/smarttool/script/SmartToolJob.java +++ b/cave/com.raytheon.viz.gfe/src/com/raytheon/viz/gfe/smarttool/script/SmartToolJob.java @@ -54,6 +54,7 @@ import com.raytheon.viz.gfe.smarttool.Tool; * ------------ ---------- ----------- -------------------------- * Jan 19, 2010 njensen Initial creation * Jan 18, 2013 1509 njensen Garbage collect after running tool + * Apr 03, 2013 1855 njensen Never dispose interpreters until shutdown * * * @@ -68,11 +69,6 @@ public class SmartToolJob extends AbstractQueueJob { */ private final static int maxJobs = 3; - /** - * Amount of time to keep inactive jobs servers around. - */ - private final static long expireTime = 5L * 60L * 1000L; - /** * Index of job with the queue. Will break code if not zero. */ @@ -114,14 +110,12 @@ public class SmartToolJob extends AbstractQueueJob { @Override protected IStatus run(IProgressMonitor monitor) { SmartToolController python = null; - long starTime = System.currentTimeMillis(); - boolean expireJob = instanceMap.get(dataMgr).get(QUEUE_JOB_INDEX) != this; try { python = SmartToolFactory.buildController(dataMgr); } catch (JepException e) { SmartToolJob.removeJob(dataMgr, this); return new Status(IStatus.ERROR, StatusConstants.PLUGIN_ID, - "Error initializing guidance python", e); + "Error initializing smart tool python", e); } try { @@ -139,7 +133,6 @@ public class SmartToolJob extends AbstractQueueJob { synchronized (this) { if (request != null) { - starTime = System.currentTimeMillis(); python.processFileUpdates(); EditAction ea = request.getPreview() .getEditAction(); @@ -170,11 +163,6 @@ public class SmartToolJob extends AbstractQueueJob { req = request; request = null; } - } else if (expireJob - && ((instanceMap.get(dataMgr).size() > maxJobs) || (System - .currentTimeMillis() - starTime) > expireTime)) { - SmartToolJob.removeJob(dataMgr, this); - break; } } } catch (InterruptedException e) { @@ -201,10 +189,8 @@ public class SmartToolJob extends AbstractQueueJob { req = null; } } - System.err.println("SmartToolJob exit loop: " - + monitor.isCanceled()); } finally { - System.err.println("shutdown instance of SmartToolJob"); + System.err.println("Shutdown instance of SmartToolJob"); if (python != null) { python.dispose(); python = null; @@ -277,10 +263,9 @@ public class SmartToolJob extends AbstractQueueJob { } } - // All jobs for data manager are busy add another. - // To mimic AWIPS I allow any number of jobs. - // The check in the run will reduce the number to maxJobs. - if (jobAvailable == false) { + // All jobs for data manager are busy, add another if we haven't reached + // the limit + if (!jobAvailable && jobList.size() < maxJobs) { SmartToolJob job = new SmartToolJob(dataMgr); job.setSystem(true); jobList.add(job); diff --git a/cave/com.raytheon.viz.radar/src/com/raytheon/viz/radar/rsc/AbstractRadarResource.java b/cave/com.raytheon.viz.radar/src/com/raytheon/viz/radar/rsc/AbstractRadarResource.java index 990b6cc7df..231b98d211 100644 --- a/cave/com.raytheon.viz.radar/src/com/raytheon/viz/radar/rsc/AbstractRadarResource.java +++ b/cave/com.raytheon.viz.radar/src/com/raytheon/viz/radar/rsc/AbstractRadarResource.java @@ -78,6 +78,7 @@ import com.vividsolutions.jts.geom.Coordinate; * ------------ ---------- ----------- -------------------------- * Aug 03, 2010 mnash Initial creation * MAR 05, 2013 15313 kshresth Added sampling for DMD + * Apr 11, 2013 DR 16030 D. Friedman Fix NPE. * * * @@ -414,7 +415,7 @@ public class AbstractRadarResource extends displayedData.append("@" + dataMap.get("Azimuth")); } - if (!dataMap.get("Mnemonic").equalsIgnoreCase("DMD")) + if (!"DMD".equalsIgnoreCase(dataMap.get("Mnemonic"))) { if (labels.contains(InspectLabels.ICAO)) { displayedData.append(' ').append(dataMap.get("ICAO")); diff --git a/cave/com.raytheon.viz.ui/src/com/raytheon/viz/ui/perspectives/AbstractVizPerspectiveManager.java b/cave/com.raytheon.viz.ui/src/com/raytheon/viz/ui/perspectives/AbstractVizPerspectiveManager.java index 0158db65a1..b16f4a7e22 100644 --- a/cave/com.raytheon.viz.ui/src/com/raytheon/viz/ui/perspectives/AbstractVizPerspectiveManager.java +++ b/cave/com.raytheon.viz.ui/src/com/raytheon/viz/ui/perspectives/AbstractVizPerspectiveManager.java @@ -171,6 +171,16 @@ public abstract class AbstractVizPerspectiveManager implements try { mgr.activateDefaultTool(((AbstractEditor) part) .getDefaultTool()); + if (mgr.getToolManager().getSelectedModalTools() + .isEmpty()) { + // Hack due to tool activation not sending whether + // it should be activated or deactivated and is just + // toggling instead. TODO: Make AbstractModalTool + // required command parameter for activate or + // deactivate + mgr.activateDefaultTool(((AbstractEditor) part) + .getDefaultTool()); + } } catch (VizException e) { statusHandler.handle(Priority.SIGNIFICANT, "Error activating tool set", e); diff --git a/cave/com.raytheon.viz.ui/src/com/raytheon/viz/ui/widgets/DateTimeEntry.java b/cave/com.raytheon.viz.ui/src/com/raytheon/viz/ui/widgets/DateTimeEntry.java index e0cd1ddc0b..d68b7622d3 100644 --- a/cave/com.raytheon.viz.ui/src/com/raytheon/viz/ui/widgets/DateTimeEntry.java +++ b/cave/com.raytheon.viz.ui/src/com/raytheon/viz/ui/widgets/DateTimeEntry.java @@ -27,6 +27,8 @@ import java.util.TimeZone; import org.eclipse.core.runtime.ListenerList; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.swt.SWT; +import org.eclipse.swt.events.DisposeEvent; +import org.eclipse.swt.events.DisposeListener; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.graphics.Image; @@ -50,7 +52,8 @@ import com.raytheon.viz.ui.dialogs.AwipsCalendar; * * Date Ticket# Engineer Description * ------------ ---------- ----------- -------------------------- - * Dec 6, 2012 randerso Initial creation + * Dec 6, 2012 randerso Initial creation + * Apr 9, 2013 #1860 randerso Fix image disposed issued on Windows * * * @@ -110,9 +113,15 @@ public class DateTimeEntry extends Composite { Button button = new Button(this, SWT.PUSH); ImageDescriptor imageDesc = UiPlugin .getImageDescriptor("icons/calendar.gif"); - Image image = imageDesc.createImage(); + final Image image = imageDesc.createImage(); button.setImage(image); - image.dispose(); + button.addDisposeListener(new DisposeListener() { + + @Override + public void widgetDisposed(DisposeEvent e) { + image.dispose(); + } + }); button.addSelectionListener(new SelectionAdapter() { @Override diff --git a/cave/com.raytheon.viz.warngen/src/com/raytheon/viz/warngen/gui/WarngenLayer.java b/cave/com.raytheon.viz.warngen/src/com/raytheon/viz/warngen/gui/WarngenLayer.java index ac7a649d5f..efe7626d00 100644 --- a/cave/com.raytheon.viz.warngen/src/com/raytheon/viz/warngen/gui/WarngenLayer.java +++ b/cave/com.raytheon.viz.warngen/src/com/raytheon/viz/warngen/gui/WarngenLayer.java @@ -164,6 +164,10 @@ import com.vividsolutions.jts.io.WKTReader; * 03/28/2013 DR 15973 Qinglu Lin Added adjustVertex() and applied it invalid polygon. * 03/28/2013 DR 15974 D. Friedman Preserve the set of selected counties when recreating the polygon from the * hatched area and remember marked counties outside the polygon on followup. + * 04/03/2013 1858 jsanchez Handled creating follow up warnings when created before 0z but issued after 0z. + * 03/13/2013 DR 15942 Qinglu Lin Added code to prevent small area from being toggled on that + * does not meet inclusionPercent/inclusionArea criteria. + * 04/10/2013 DR 16044 D. Friedman Fix NPE in getAllFipsInArea. * * * @author mschenke @@ -293,7 +297,7 @@ public class WarngenLayer extends AbstractStormTrackResource { GeometryFactory gf = new GeometryFactory(); LinearRing lr = gf.createLinearRing(coords); hatchedArea = gf.createPolygon(lr, null); - if (! hatchedArea.isValid()) + if (!hatchedArea.isValid()) hatchedArea = adjustVertex(hatchedArea); hatchedWarningArea = createWarnedArea( latLonToLocal(hatchedArea), @@ -1217,7 +1221,8 @@ public class WarngenLayer extends AbstractStormTrackResource { for (String id : ids) { if (fips.endsWith(id)) { if (idsOutsidePolygon != null) { - idsOutsidePolygon.remove(fips.substring(0, 2) + '-' + id); + idsOutsidePolygon.remove(fips.substring(0, 2) + + '-' + id); } newList.add(geom); break; @@ -1320,7 +1325,8 @@ public class WarngenLayer extends AbstractStormTrackResource { } } - public void updateWarnedAreas(boolean snapHatchedAreaToPolygon) throws VizException { + public void updateWarnedAreas(boolean snapHatchedAreaToPolygon) + throws VizException { updateWarnedAreas(snapHatchedAreaToPolygon, false); } @@ -1331,8 +1337,8 @@ public class WarngenLayer extends AbstractStormTrackResource { * eliminated. * @throws VizException */ - public void updateWarnedAreas(boolean snapHatchedAreaToPolygon, boolean preservedSelection) - throws VizException { + public void updateWarnedAreas(boolean snapHatchedAreaToPolygon, + boolean preservedSelection) throws VizException { if (getPolygon() == null) { return; } @@ -1341,9 +1347,11 @@ public class WarngenLayer extends AbstractStormTrackResource { Geometry warningArea = state.getWarningArea(); Geometry warningPolygon = state.getWarningPolygon(); - Geometry newWarningArea = createWarnedArea(latLonToLocal((snapHatchedAreaToPolygon || warningArea == null) ? warningPolygon - : warningArea), - preservedSelection && warningArea != null ? latLonToLocal(warningArea) : null); + Geometry newWarningArea = createWarnedArea( + latLonToLocal((snapHatchedAreaToPolygon || warningArea == null) ? warningPolygon + : warningArea), preservedSelection + && warningArea != null ? latLonToLocal(warningArea) + : null); updateWarnedAreaState(newWarningArea, snapHatchedAreaToPolygon); System.out.println("determining hatchedArea took " @@ -1360,7 +1368,8 @@ public class WarngenLayer extends AbstractStormTrackResource { * inclusion filter * @return */ - private Geometry createWarnedArea(Geometry hatchedArea, Geometry preservedSelection) { + private Geometry createWarnedArea(Geometry hatchedArea, + Geometry preservedSelection) { Geometry oldWarningPolygon = latLonToLocal(state.getOldWarningPolygon()); Geometry oldWarningArea = latLonToLocal(state.getOldWarningArea()); Geometry newHatchedArea = null; @@ -1381,33 +1390,39 @@ public class WarngenLayer extends AbstractStormTrackResource { // Get intersection between county and hatched boundary intersection = GeometryUtil.intersection(hatchedArea, prepGeom); if (oldWarningArea != null) { - intersection = GeometryUtil.intersection(intersection, oldWarningArea); + intersection = GeometryUtil.intersection(intersection, + oldWarningArea); } if (intersection.isEmpty()) { if (selectedFips == null || !selectedFips.contains(getFips(f))) { continue; - } else if (! selectedFips.isEmpty()) { + } else if (!selectedFips.isEmpty()) { /* * Add whatever part of the area was previously hatched * despite being outside the new polygon. */ if (selectedGeoms == null) { selectedGeoms = new ArrayList(); - GeometryUtil.buildGeometryList(selectedGeoms, preservedSelection); + GeometryUtil.buildGeometryList(selectedGeoms, + preservedSelection); } intersection = null; - String prefix = GeometryUtil.getPrefix(f.geometry.getUserData()); + String prefix = GeometryUtil.getPrefix(f.geometry + .getUserData()); for (Geometry g : selectedGeoms) { if (g.getUserData() != null) { - if (prefix.equals(GeometryUtil.getPrefix(g.getUserData()))) { - intersection = intersection == null ? g : - GeometryUtil.union(intersection, g); + if (prefix.equals(GeometryUtil.getPrefix(g + .getUserData()))) { + intersection = intersection == null ? g + : GeometryUtil.union(intersection, + g); } } } if (intersection == null) { - // This part of the area was not previously selected. + // This part of the area was not previously + // selected. continue; } } @@ -1424,8 +1439,7 @@ public class WarngenLayer extends AbstractStormTrackResource { else include = filterArea(f, intersection, true) && (oldWarningPolygon == null - || prepGeom.intersects(oldWarningPolygon) - || isOldAreaOutsidePolygon(f)); + || prepGeom.intersects(oldWarningPolygon) || isOldAreaOutsidePolygon(f)); if (include) { if (newHatchedArea == null) { newHatchedArea = intersection; @@ -1508,7 +1522,7 @@ public class WarngenLayer extends AbstractStormTrackResource { String[] gids = GeometryUtil.getGID(newArea); boolean flag = false; for (String gid : gids) { - if (! selectedGids.contains(gid)) { + if (!selectedGids.contains(gid)) { flag = true; break; } @@ -1584,9 +1598,11 @@ public class WarngenLayer extends AbstractStormTrackResource { warningAreaChanged(); } } - - /** Determine if the given area of the reference area passes the - * inclusion filter. Subroutine of {@link #filterArea}. + + /** + * Determine if the given area of the reference area passes the inclusion + * filter. Subroutine of {@link #filterArea}. + * * @param areaToConsider * @param wholeArea * @param areaInMetersSq @@ -1605,23 +1621,28 @@ public class WarngenLayer extends AbstractStormTrackResource { boolean areaOk = areaInKmSqOfIntersection > getConfiguration() .getHatchedAreaSource().getInclusionArea(); return getConfiguration().getHatchedAreaSource().getInclusionAndOr() - .equalsIgnoreCase("AND") ? - percentOk && areaOk : percentOk || areaOk; + .equalsIgnoreCase("AND") ? percentOk && areaOk : percentOk + || areaOk; } - /** Determine if a feature should be included based on how much of it - * is hatched and the configured inclusion criteria. + /** + * Determine if a feature should be included based on how much of it is + * hatched and the configured inclusion criteria. + * * @param feature - * @param featureAreaToConsider the portion of the feature that is hatched - * @param localCoordinates if true, use local CRS; otherwise, use lat/lon - * @param anyAmountOfArea if true, ignore the configured criteria and - * include the feature if event a small amount is hatched. + * @param featureAreaToConsider + * the portion of the feature that is hatched + * @param localCoordinates + * if true, use local CRS; otherwise, use lat/lon + * @param anyAmountOfArea + * if true, ignore the configured criteria and include the + * feature if event a small amount is hatched. * @return true if the feature should be included */ - private boolean filterArea(GeospatialData feature, Geometry featureAreaToConsider, boolean localCRS) { - Geometry geom = localCRS ? - (Geometry) feature.attributes.get(GeospatialDataList.LOCAL_GEOM) : - feature.geometry; + private boolean filterArea(GeospatialData feature, + Geometry featureAreaToConsider, boolean localCRS) { + Geometry geom = localCRS ? (Geometry) feature.attributes + .get(GeospatialDataList.LOCAL_GEOM) : feature.geometry; double areaOfGeom = (Double) feature.attributes.get(AREA); if (filterCheck(featureAreaToConsider, geom, areaOfGeom)) @@ -1642,18 +1663,19 @@ public class WarngenLayer extends AbstractStormTrackResource { List geoms = new ArrayList(); GeometryUtil.buildGeometryList(geoms, oldWarningArea); Geometry oldSelectedArea = null; - String prefix = GeometryUtil.getPrefix(feature.geometry.getUserData()); + String prefix = GeometryUtil.getPrefix(feature.geometry + .getUserData()); for (Geometry g : geoms) { if (g.getUserData() != null) { if (prefix.equals(GeometryUtil.getPrefix(g.getUserData()))) { - oldSelectedArea = oldSelectedArea == null ? g : - GeometryUtil.union(oldSelectedArea, g); + oldSelectedArea = oldSelectedArea == null ? g + : GeometryUtil.union(oldSelectedArea, g); } } } if (oldSelectedArea != null) { - double ratioOfOldArea = featureAreaToConsider.getArea() / - oldSelectedArea.getArea(); + double ratioOfOldArea = featureAreaToConsider.getArea() + / oldSelectedArea.getArea(); /* * Ideally, we would only allow the exact same area, but due to * possible loss of precision in all of the calculations, we @@ -2104,6 +2126,10 @@ public class WarngenLayer extends AbstractStormTrackResource { int day = warnRecord.getIssueTime().get(Calendar.DAY_OF_MONTH); int hour = Integer.parseInt(m.group(1)); int minute = Integer.parseInt(m.group(2)); + // Handles when a warning is created before 0Z but issued after 0Z + if (hour > warnRecord.getIssueTime().get(Calendar.HOUR_OF_DAY)) { + day -= 1; + } frameTime = Calendar.getInstance(TimeZone.getTimeZone("GMT")); frameTime.set(Calendar.DAY_OF_MONTH, day); frameTime.set(Calendar.HOUR_OF_DAY, hour); @@ -2512,6 +2538,8 @@ public class WarngenLayer extends AbstractStormTrackResource { geom = geom.getFactory() .createGeometryCollection( parts.toArray(new Geometry[0])); + if (!filterArea(f, geom, false)) + continue; } state.setWarningArea(GeometryUtil.union( state.getWarningArea(), geom)); @@ -2573,7 +2601,7 @@ public class WarngenLayer extends AbstractStormTrackResource { Set fipsIds = new HashSet(); for (int n = 0; n < warningArea.getNumGeometries(); ++n) { Geometry area = warningArea.getGeometryN(n); - fipsIds.add(getFips(((CountyUserData) area.getUserData()).entry)); + fipsIds.add(getFips(area)); } return fipsIds; } @@ -2584,7 +2612,8 @@ public class WarngenLayer extends AbstractStormTrackResource { return removeCounties(warningArea, set); } - private Geometry removeCounties(Geometry warningArea, Set fipsToRemove) { + private Geometry removeCounties(Geometry warningArea, + Set fipsToRemove) { if (fipsToRemove == null || fipsToRemove.isEmpty()) return warningArea; List toKeep = new ArrayList( @@ -2818,12 +2847,13 @@ public class WarngenLayer extends AbstractStormTrackResource { index[3] = index[2] + 1; if (index[3] >= length) index[3] = index[3] - length + 1; - ls1 = new LineSegment(coord[index[0]],coord[index[1]]); - ls2 = new LineSegment(coord[index[2]],coord[index[3]]); + ls1 = new LineSegment(coord[index[0]], coord[index[1]]); + ls2 = new LineSegment(coord[index[2]], coord[index[3]]); intersectCoord = ls1.intersection(ls2); if (intersectCoord != null) { - for (int j = 0; j < index.length-2; j++) { - d[j] = calculateDistance(intersectCoord,coord[index[j]]); + for (int j = 0; j < index.length - 2; j++) { + d[j] = calculateDistance(intersectCoord, + coord[index[j]]); } if (d[0] < d[1]) { index[4] = index[0]; @@ -2843,7 +2873,8 @@ public class WarngenLayer extends AbstractStormTrackResource { d[5] = d[3]; indexOfTheOtherEnd[1] = index[2]; } - // index of the vertex on a line segment (line segment A), which will be moved along line segment A. + // index of the vertex on a line segment (line segment A), + // which will be moved along line segment A. int replaceIndex; // index of the vertex at the other end of line segment A. int theOtherIndex; @@ -2851,32 +2882,40 @@ public class WarngenLayer extends AbstractStormTrackResource { replaceIndex = index[4]; theOtherIndex = indexOfTheOtherEnd[0]; } else { - replaceIndex= index[5]; + replaceIndex = index[5]; theOtherIndex = indexOfTheOtherEnd[1]; } - // move the bad vertex, which is on line segment A and has the shortest distance to intersectCoord, - // along line segment A to the other side of line segment B which intersects with line segment A. + // move the bad vertex, which is on line segment A and has + // the shortest distance to intersectCoord, + // along line segment A to the other side of line segment B + // which intersects with line segment A. double delta; double min = 0.00001; if (Math.abs(intersectCoord.x - coord[replaceIndex].x) < min) { // move the bad vertex along a vertical line segment. delta = intersectCoord.y - coord[theOtherIndex].y; - coord[replaceIndex].y += 0.01 * (delta / Math.abs(delta)); - } else if (Math.abs(intersectCoord.y - coord[replaceIndex].y) < min) { + coord[replaceIndex].y += 0.01 * (delta / Math + .abs(delta)); + } else if (Math.abs(intersectCoord.y + - coord[replaceIndex].y) < min) { // move the bad vertex along a horizontal line segment. delta = intersectCoord.x - coord[theOtherIndex].x; - coord[replaceIndex].x += 0.01 * (delta / Math.abs(delta)); + coord[replaceIndex].x += 0.01 * (delta / Math + .abs(delta)); } else { - // move the bad vertex along a line segment which is neither vertical nor horizontal. - double slope = computeSlope(coord, replaceIndex, theOtherIndex); + // move the bad vertex along a line segment which is + // neither vertical nor horizontal. + double slope = computeSlope(coord, replaceIndex, + theOtherIndex); delta = coord[theOtherIndex].y - intersectCoord.y; - coord[replaceIndex].y = intersectCoord.y + 0.005 * (delta / Math.abs(delta)); - coord[replaceIndex].x = (coord[replaceIndex].y - coord[theOtherIndex].y) / slope - + coord[theOtherIndex].x; + coord[replaceIndex].y = intersectCoord.y + 0.005 + * (delta / Math.abs(delta)); + coord[replaceIndex].x = (coord[replaceIndex].y - coord[theOtherIndex].y) + / slope + coord[theOtherIndex].x; } PolygonUtil.round(coord, 2); if (replaceIndex == 0) - coord[length-1] = new Coordinate(coord[replaceIndex]); + coord[length - 1] = new Coordinate(coord[replaceIndex]); else if (replaceIndex == length - 1) coord[0] = new Coordinate(coord[replaceIndex]); lr = gf.createLinearRing(coord); @@ -2896,10 +2935,10 @@ public class WarngenLayer extends AbstractStormTrackResource { public double computeSlope(Coordinate[] coords, int i, int j) { double min = 1.0E-08; - double dx = coords[i].x-coords[j].x; + double dx = coords[i].x - coords[j].x; double slope = 0.0; - if (Math.abs(dx)>min) { - slope = (coords[i].y-coords[j].y)/dx; + if (Math.abs(dx) > min) { + slope = (coords[i].y - coords[j].y) / dx; } return slope; } diff --git a/deltaScripts/13.3.1/reftimeIndexUpdate.sh b/deltaScripts/13.3.1/reftimeIndexUpdate.sh new file mode 100644 index 0000000000..2fec04898b --- /dev/null +++ b/deltaScripts/13.3.1/reftimeIndexUpdate.sh @@ -0,0 +1,81 @@ +#!/bin/bash +# DR #1846 - this DR will remove all reftimeindex indices from the metadata database. + +PSQL="/awips2/psql/bin/psql" + +#_timeindexname="timeindex" +RETRIEVE_INDEX_SQL="SELECT indexname FROM pg_indexes WHERE indexname LIKE '%reftimeindex%' ORDER BY indexname;" +_index_list_txt=indexlist.txt +#_reftime_indx_length=12 + +echo "INFO: update started" + +# retrieve the reftime indices +${PSQL} -U awips -d metadata -c "${RETRIEVE_INDEX_SQL}" -t -o ${_index_list_txt} +if [ $? -ne 0 ]; then + echo "ERROR: Failed to retrieve the list of data uri indices." + echo "FATAL: The update has failed." + exit 1 +fi + +for index in `cat ${_index_list_txt}`; +do + # determine which table the index is in. + SQL="SELECT tablename FROM pg_indexes WHERE indexname = '${index}';" + table=`${PSQL} -U awips -d metadata -c "${SQL}" -t` + if [ $? -ne 0 ]; then + echo "ERROR: Failed to determine which table ${index} belongs to." + echo "FATAL: The update has failed." + exit 1 + fi + + #length=${#index} + #let LENGTH_DIFF=${length}-${_reftime_indx_length} + #prefix=${index:0:$LENGTH_DIFF} + + # remove the index + ${PSQL} -U awips -d metadata -c "DROP INDEX ${index};" + if [ $? -ne 0 ]; then + echo "ERROR: Failed to drop index - ${index}." + echo "FATAL: The update has failed." + exit 1 + fi + + # create the new index + SQL="CREATE INDEX ${index} ON ${table} USING btree(reftime, forecasttime);" + ${PSQL} -U awips -d metadata -c "${SQL}" + if [ $? -ne 0 ]; then + echo "ERROR: Failed to create index ${table}${_timeindexname} on table ${table}." + echo "FATAL: The update has failed." + exit 1 + fi +done + +rm -f ${_index_list_txt} + +RETRIEVE_INDEX_SQL="SELECT indexname FROM pg_indexes WHERE indexname LIKE '%fcsttimeindex%' ORDER BY indexname;" + +# retrieve the fcsttime indices +${PSQL} -U awips -d metadata -c "${RETRIEVE_INDEX_SQL}" -t -o ${_index_list_txt} +if [ $? -ne 0 ]; then + echo "ERROR: Failed to retrieve the list of data uri indices." + echo "FATAL: The update has failed." + exit 1 +fi + +for index in `cat ${_index_list_txt}`; +do + # remove the index + ${PSQL} -U awips -d metadata -c "DROP INDEX ${index};" + if [ $? -ne 0 ]; then + echo "ERROR: Failed to drop index - ${index}." + echo "FATAL: The update has failed." + exit 1 + fi +done + +rm -f ${_index_list_txt} + +echo "INFO: the update has completed successfully!" + +exit 0 diff --git a/deltaScripts/13.3.1/removeDataURIIndex.sh b/deltaScripts/13.3.1/removeDataURIIndex.sh new file mode 100644 index 0000000000..276565c7c2 --- /dev/null +++ b/deltaScripts/13.3.1/removeDataURIIndex.sh @@ -0,0 +1,33 @@ +#!/bin/bash +# DR #1846 - this update script will remove all datauri_idx indices from the metadata database + +PSQL="/awips2/psql/bin/psql" +RETRIEVE_INDEX_SQL="SELECT indexname FROM pg_indexes WHERE indexname LIKE '%datauri_idx%' ORDER BY indexname;" +_index_list_txt=indexlist.txt + +echo "INFO: update started" + +# retrieve the indices +${PSQL} -U awips -d metadata -c "${RETRIEVE_INDEX_SQL}" -t -o ${_index_list_txt} +if [ $? -ne 0 ]; then + echo "ERROR: Failed to retrieve the list of data uri indices." + echo "FATAL: The update has failed." + exit 1 +fi + +for index in `cat ${_index_list_txt}`; +do + # remove the index + ${PSQL} -U awips -d metadata -c "DROP INDEX ${index};" + if [ $? -ne 0 ]; then + echo "ERROR: Failed to drop index - ${index}." + echo "FATAL: The update has failed." + exit 1 + fi +done + +rm -f ${_index_list_txt} + +echo "INFO: the update has completed successfully!" + +exit 0 diff --git a/edexOsgi/build.edex/esb/bin/start.sh b/edexOsgi/build.edex/esb/bin/start.sh index 8bfd54ed3c..2c176ed8ba 100644 --- a/edexOsgi/build.edex/esb/bin/start.sh +++ b/edexOsgi/build.edex/esb/bin/start.sh @@ -94,7 +94,7 @@ export AMQP_SPEC=$awips_home/python/share/amqp/amqp.0-10.xml #read and interpret the command line arguments #------------------------------------------------------------------------- CONSOLE_FLAG=on -CONSOLE_LOGLEVEL=INFO +CONSOLE_LOGLEVEL=DEBUG DEBUG_FLAG=off PROFILE_FLAG=off HIGH_MEM_FLAG=off diff --git a/edexOsgi/build.edex/esb/conf/wrapper.conf b/edexOsgi/build.edex/esb/conf/wrapper.conf index d2166cee62..a86819ea2d 100644 --- a/edexOsgi/build.edex/esb/conf/wrapper.conf +++ b/edexOsgi/build.edex/esb/conf/wrapper.conf @@ -184,7 +184,7 @@ wrapper.trigger.action=RESTART wrapper.console.format=M # Log Level for console output. (See docs for log levels) -wrapper.console.loglevel=DEBUG +wrapper.console.loglevel=${CONSOLE_LOGLEVEL} # Log file to use for wrapper output logging. wrapper.logfile=${EDEX_HOME}/logs/edex-${EDEX_RUN_MODE}-YYYYMMDD.log diff --git a/edexOsgi/build.edex/yajsw-stable-11.04.tar b/edexOsgi/build.edex/yajsw-stable-11.04.tar index c3feabf6b6..b6d1b00fbf 100644 Binary files a/edexOsgi/build.edex/yajsw-stable-11.04.tar and b/edexOsgi/build.edex/yajsw-stable-11.04.tar differ diff --git a/edexOsgi/com.raytheon.edex.plugin.bufrmos/src/com/raytheon/edex/plugin/bufrmos/common/BufrMosAvnData.java b/edexOsgi/com.raytheon.edex.plugin.bufrmos/src/com/raytheon/edex/plugin/bufrmos/common/BufrMosAvnData.java index 8afc8240a1..0b65eeebb0 100644 --- a/edexOsgi/com.raytheon.edex.plugin.bufrmos/src/com/raytheon/edex/plugin/bufrmos/common/BufrMosAvnData.java +++ b/edexOsgi/com.raytheon.edex.plugin.bufrmos/src/com/raytheon/edex/plugin/bufrmos/common/BufrMosAvnData.java @@ -25,6 +25,7 @@ import javax.persistence.UniqueConstraint; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlRootElement; +import org.hibernate.annotations.Index; import com.raytheon.uf.common.serialization.annotations.DynamicSerialize; @@ -38,6 +39,7 @@ import com.raytheon.uf.common.serialization.annotations.DynamicSerialize; * Date Ticket# Engineer Description * ------------ ---------- ----------- -------------------------- * May 25, 2011 rjpeter Initial creation + * Apr 4, 2013 1846 bkowal Added an index on refTime and forecastTime * * * @@ -46,6 +48,16 @@ import com.raytheon.uf.common.serialization.annotations.DynamicSerialize; */ @Entity @Table(name = "bufrmosAvn", uniqueConstraints = { @UniqueConstraint(columnNames = { "dataURI" }) }) +/* + * Both refTime and forecastTime are included in the refTimeIndex since + * forecastTime is unlikely to be used. + */ +@org.hibernate.annotations.Table( + appliesTo = "bufrmosAvn", + indexes = { + @Index(name = "bufrmosAvn_refTimeIndex", columnNames = { "refTime", "forecastTime" } ) + } +) @XmlRootElement @XmlAccessorType(XmlAccessType.NONE) @DynamicSerialize diff --git a/edexOsgi/com.raytheon.edex.plugin.bufrmos/src/com/raytheon/edex/plugin/bufrmos/common/BufrMosEtaData.java b/edexOsgi/com.raytheon.edex.plugin.bufrmos/src/com/raytheon/edex/plugin/bufrmos/common/BufrMosEtaData.java index dc7cb9b3e1..3b9480de16 100644 --- a/edexOsgi/com.raytheon.edex.plugin.bufrmos/src/com/raytheon/edex/plugin/bufrmos/common/BufrMosEtaData.java +++ b/edexOsgi/com.raytheon.edex.plugin.bufrmos/src/com/raytheon/edex/plugin/bufrmos/common/BufrMosEtaData.java @@ -25,6 +25,7 @@ import javax.persistence.UniqueConstraint; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlRootElement; +import org.hibernate.annotations.Index; import com.raytheon.uf.common.serialization.annotations.DynamicSerialize; @@ -38,6 +39,7 @@ import com.raytheon.uf.common.serialization.annotations.DynamicSerialize; * Date Ticket# Engineer Description * ------------ ---------- ----------- -------------------------- * May 25, 2011 rjpeter Initial creation + * Apr 4, 2013 1846 bkowal Added an index on refTime and forecastTime * * * @@ -46,6 +48,16 @@ import com.raytheon.uf.common.serialization.annotations.DynamicSerialize; */ @Entity @Table(name = "bufrmosEta", uniqueConstraints = { @UniqueConstraint(columnNames = { "dataURI" }) }) +/* + * Both refTime and forecastTime are included in the refTimeIndex since + * forecastTime is unlikely to be used. + */ +@org.hibernate.annotations.Table( + appliesTo = "bufrmosEta", + indexes = { + @Index(name = "bufrmosEta_refTimeIndex", columnNames = { "refTime", "forecastTime" } ) + } +) @XmlRootElement @XmlAccessorType(XmlAccessType.NONE) @DynamicSerialize diff --git a/edexOsgi/com.raytheon.edex.plugin.bufrmos/src/com/raytheon/edex/plugin/bufrmos/common/BufrMosGfsData.java b/edexOsgi/com.raytheon.edex.plugin.bufrmos/src/com/raytheon/edex/plugin/bufrmos/common/BufrMosGfsData.java index d9a3448c45..fa01fe21c6 100644 --- a/edexOsgi/com.raytheon.edex.plugin.bufrmos/src/com/raytheon/edex/plugin/bufrmos/common/BufrMosGfsData.java +++ b/edexOsgi/com.raytheon.edex.plugin.bufrmos/src/com/raytheon/edex/plugin/bufrmos/common/BufrMosGfsData.java @@ -25,6 +25,7 @@ import javax.persistence.UniqueConstraint; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlRootElement; +import org.hibernate.annotations.Index; import com.raytheon.uf.common.serialization.annotations.DynamicSerialize; @@ -38,6 +39,7 @@ import com.raytheon.uf.common.serialization.annotations.DynamicSerialize; * Date Ticket# Engineer Description * ------------ ---------- ----------- -------------------------- * May 25, 2011 rjpeter Initial creation + * Apr 4, 2013 1846 bkowal Added an index on refTime and forecastTime * * * @@ -46,6 +48,16 @@ import com.raytheon.uf.common.serialization.annotations.DynamicSerialize; */ @Entity @Table(name = "bufrmosGfs", uniqueConstraints = { @UniqueConstraint(columnNames = { "dataURI" }) }) +/* + * Both refTime and forecastTime are included in the refTimeIndex since + * forecastTime is unlikely to be used. + */ +@org.hibernate.annotations.Table( + appliesTo = "bufrmosGfs", + indexes = { + @Index(name = "bufrmosGfs_refTimeIndex", columnNames = { "refTime", "forecastTime" } ) + } +) @XmlRootElement @XmlAccessorType(XmlAccessType.NONE) @DynamicSerialize diff --git a/edexOsgi/com.raytheon.edex.plugin.bufrmos/src/com/raytheon/edex/plugin/bufrmos/common/BufrMosHpcData.java b/edexOsgi/com.raytheon.edex.plugin.bufrmos/src/com/raytheon/edex/plugin/bufrmos/common/BufrMosHpcData.java index 0a658bec42..7538c7340a 100644 --- a/edexOsgi/com.raytheon.edex.plugin.bufrmos/src/com/raytheon/edex/plugin/bufrmos/common/BufrMosHpcData.java +++ b/edexOsgi/com.raytheon.edex.plugin.bufrmos/src/com/raytheon/edex/plugin/bufrmos/common/BufrMosHpcData.java @@ -25,6 +25,7 @@ import javax.persistence.UniqueConstraint; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlRootElement; +import org.hibernate.annotations.Index; import com.raytheon.uf.common.serialization.annotations.DynamicSerialize; @@ -38,6 +39,7 @@ import com.raytheon.uf.common.serialization.annotations.DynamicSerialize; * Date Ticket# Engineer Description * ------------ ---------- ----------- -------------------------- * May 25, 2011 rjpeter Initial creation + * Apr 4, 2013 1846 bkowal Added an index on refTime and forecastTime * * * @@ -46,6 +48,16 @@ import com.raytheon.uf.common.serialization.annotations.DynamicSerialize; */ @Entity @Table(name = "bufrmosHpc", uniqueConstraints = { @UniqueConstraint(columnNames = { "dataURI" }) }) +/* + * Both refTime and forecastTime are included in the refTimeIndex since + * forecastTime is unlikely to be used. + */ +@org.hibernate.annotations.Table( + appliesTo = "bufrmosHpc", + indexes = { + @Index(name = "bufrmosHpc_refTimeIndex", columnNames = { "refTime", "forecastTime" } ) + } +) @XmlRootElement @XmlAccessorType(XmlAccessType.NONE) @DynamicSerialize diff --git a/edexOsgi/com.raytheon.edex.plugin.bufrmos/src/com/raytheon/edex/plugin/bufrmos/common/BufrMosLampData.java b/edexOsgi/com.raytheon.edex.plugin.bufrmos/src/com/raytheon/edex/plugin/bufrmos/common/BufrMosLampData.java index d31a3a3cf7..d2d77d9760 100644 --- a/edexOsgi/com.raytheon.edex.plugin.bufrmos/src/com/raytheon/edex/plugin/bufrmos/common/BufrMosLampData.java +++ b/edexOsgi/com.raytheon.edex.plugin.bufrmos/src/com/raytheon/edex/plugin/bufrmos/common/BufrMosLampData.java @@ -25,6 +25,7 @@ import javax.persistence.UniqueConstraint; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlRootElement; +import org.hibernate.annotations.Index; import com.raytheon.uf.common.serialization.annotations.DynamicSerialize; @@ -38,6 +39,7 @@ import com.raytheon.uf.common.serialization.annotations.DynamicSerialize; * Date Ticket# Engineer Description * ------------ ---------- ----------- -------------------------- * May 25, 2011 rjpeter Initial creation + * Apr 4, 2013 1846 bkowal Added an index on refTime and forecastTime * * * @@ -46,6 +48,16 @@ import com.raytheon.uf.common.serialization.annotations.DynamicSerialize; */ @Entity @Table(name = "bufrmosLamp", uniqueConstraints = { @UniqueConstraint(columnNames = { "dataURI" }) }) +/* + * Both refTime and forecastTime are included in the refTimeIndex since + * forecastTime is unlikely to be used. + */ +@org.hibernate.annotations.Table( + appliesTo = "bufrmosLamp", + indexes = { + @Index(name = "bufrmosLamp_refTimeIndex", columnNames = { "refTime", "forecastTime" } ) + } +) @XmlRootElement @XmlAccessorType(XmlAccessType.NONE) @DynamicSerialize diff --git a/edexOsgi/com.raytheon.edex.plugin.bufrmos/src/com/raytheon/edex/plugin/bufrmos/common/BufrMosMrfData.java b/edexOsgi/com.raytheon.edex.plugin.bufrmos/src/com/raytheon/edex/plugin/bufrmos/common/BufrMosMrfData.java index 04e03a059a..54a165757b 100644 --- a/edexOsgi/com.raytheon.edex.plugin.bufrmos/src/com/raytheon/edex/plugin/bufrmos/common/BufrMosMrfData.java +++ b/edexOsgi/com.raytheon.edex.plugin.bufrmos/src/com/raytheon/edex/plugin/bufrmos/common/BufrMosMrfData.java @@ -25,6 +25,7 @@ import javax.persistence.UniqueConstraint; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlRootElement; +import org.hibernate.annotations.Index; import com.raytheon.uf.common.serialization.annotations.DynamicSerialize; @@ -38,6 +39,7 @@ import com.raytheon.uf.common.serialization.annotations.DynamicSerialize; * Date Ticket# Engineer Description * ------------ ---------- ----------- -------------------------- * May 25, 2011 rjpeter Initial creation + * Apr 4, 2013 1846 bkowal Added an index on refTime and forecastTime * * * @@ -46,6 +48,16 @@ import com.raytheon.uf.common.serialization.annotations.DynamicSerialize; */ @Entity @Table(name = "bufrmosMrf", uniqueConstraints = { @UniqueConstraint(columnNames = { "dataURI" }) }) +/* + * Both refTime and forecastTime are included in the refTimeIndex since + * forecastTime is unlikely to be used. + */ +@org.hibernate.annotations.Table( + appliesTo = "bufrmosMrf", + indexes = { + @Index(name = "bufrmosMrf_refTimeIndex", columnNames = { "refTime", "forecastTime" } ) + } +) @XmlRootElement @XmlAccessorType(XmlAccessType.NONE) @DynamicSerialize diff --git a/edexOsgi/com.raytheon.edex.plugin.bufrmos/src/com/raytheon/edex/plugin/bufrmos/common/BufrMosNgmData.java b/edexOsgi/com.raytheon.edex.plugin.bufrmos/src/com/raytheon/edex/plugin/bufrmos/common/BufrMosNgmData.java index 42c9edb879..2d19379551 100644 --- a/edexOsgi/com.raytheon.edex.plugin.bufrmos/src/com/raytheon/edex/plugin/bufrmos/common/BufrMosNgmData.java +++ b/edexOsgi/com.raytheon.edex.plugin.bufrmos/src/com/raytheon/edex/plugin/bufrmos/common/BufrMosNgmData.java @@ -26,6 +26,8 @@ import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlRootElement; +import org.hibernate.annotations.Index; + import com.raytheon.uf.common.serialization.annotations.DynamicSerialize; /** @@ -38,6 +40,7 @@ import com.raytheon.uf.common.serialization.annotations.DynamicSerialize; * Date Ticket# Engineer Description * ------------ ---------- ----------- -------------------------- * May 25, 2011 rjpeter Initial creation + * Apr 4, 2013 1846 bkowal Added an index on refTime and forecastTime * * * @@ -46,6 +49,16 @@ import com.raytheon.uf.common.serialization.annotations.DynamicSerialize; */ @Entity @Table(name = "bufrmosNgm", uniqueConstraints = { @UniqueConstraint(columnNames = { "dataURI" }) }) +/* + * Both refTime and forecastTime are included in the refTimeIndex since + * forecastTime is unlikely to be used. + */ +@org.hibernate.annotations.Table( + appliesTo = "bufrmosNgm", + indexes = { + @Index(name = "bufrmosNgm_refTimeIndex", columnNames = { "refTime", "forecastTime" } ) + } +) @XmlRootElement @XmlAccessorType(XmlAccessType.NONE) @DynamicSerialize diff --git a/edexOsgi/com.raytheon.edex.plugin.ccfp/src/com/raytheon/edex/plugin/ccfp/CcfpRecord.java b/edexOsgi/com.raytheon.edex.plugin.ccfp/src/com/raytheon/edex/plugin/ccfp/CcfpRecord.java index fdb6ce34c8..412bb7e987 100644 --- a/edexOsgi/com.raytheon.edex.plugin.ccfp/src/com/raytheon/edex/plugin/ccfp/CcfpRecord.java +++ b/edexOsgi/com.raytheon.edex.plugin.ccfp/src/com/raytheon/edex/plugin/ccfp/CcfpRecord.java @@ -30,6 +30,7 @@ import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; +import org.hibernate.annotations.Index; import com.raytheon.uf.common.dataplugin.IDecoderGettable; import com.raytheon.uf.common.dataplugin.PluginDataObject; @@ -49,6 +50,7 @@ import com.raytheon.uf.common.serialization.annotations.DynamicSerializeElement; * 03/03/2007 908 bwoodle initial creation * 09/15/2009 3027 njensen Use dates for times * 09/21/2009 3072 bsteffen Removed times because they are stored in DataTime + * Apr 4, 2013 1846 bkowal Added an index on refTime and forecastTime * * * @@ -58,6 +60,16 @@ import com.raytheon.uf.common.serialization.annotations.DynamicSerializeElement; */ @Entity @Table(name = "ccfp", uniqueConstraints = { @UniqueConstraint(columnNames = { "dataURI" }) }) +/* + * Both refTime and forecastTime are included in the refTimeIndex since + * forecastTime is unlikely to be used. + */ +@org.hibernate.annotations.Table( + appliesTo = "ccfp", + indexes = { + @Index(name = "ccfp_refTimeIndex", columnNames = { "refTime", "forecastTime" } ) + } +) @XmlRootElement @XmlAccessorType(XmlAccessType.NONE) @DynamicSerialize diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/res/spring/gfe-spring.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/res/spring/gfe-spring.xml index 2af6e8d17a..df0cc4ff7c 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/res/spring/gfe-spring.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/res/spring/gfe-spring.xml @@ -72,10 +72,15 @@ + + + - + - + @@ -86,11 +91,6 @@ - - - diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/src/com/raytheon/edex/plugin/gfe/cache/d2dparms/D2DParmIdCache.java b/edexOsgi/com.raytheon.edex.plugin.gfe/src/com/raytheon/edex/plugin/gfe/cache/d2dparms/D2DParmIdCache.java index 29bcce99fd..57695e597e 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/src/com/raytheon/edex/plugin/gfe/cache/d2dparms/D2DParmIdCache.java +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/src/com/raytheon/edex/plugin/gfe/cache/d2dparms/D2DParmIdCache.java @@ -21,7 +21,6 @@ package com.raytheon.edex.plugin.gfe.cache.d2dparms; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; @@ -40,19 +39,16 @@ import com.raytheon.edex.plugin.gfe.server.database.D2DGridDatabase; import com.raytheon.edex.plugin.gfe.server.database.D2DSatDatabase; import com.raytheon.edex.plugin.gfe.server.database.D2DSatDatabaseManager; import com.raytheon.edex.plugin.gfe.server.database.GridDatabase; -import com.raytheon.edex.plugin.gfe.util.SendNotifications; +import com.raytheon.edex.plugin.gfe.server.notify.GfeIngestNotificationFilter; import com.raytheon.uf.common.dataplugin.PluginException; -import com.raytheon.uf.common.dataplugin.gfe.GridDataHistory; import com.raytheon.uf.common.dataplugin.gfe.db.objects.DatabaseID; import com.raytheon.uf.common.dataplugin.gfe.db.objects.ParmID; import com.raytheon.uf.common.dataplugin.gfe.exception.GfeException; import com.raytheon.uf.common.dataplugin.gfe.server.message.ServerResponse; import com.raytheon.uf.common.dataplugin.gfe.server.notify.GridUpdateNotification; -import com.raytheon.uf.common.message.WsId; import com.raytheon.uf.common.status.IUFStatusHandler; import com.raytheon.uf.common.status.UFStatus; import com.raytheon.uf.common.status.UFStatus.Priority; -import com.raytheon.uf.common.time.TimeRange; import com.raytheon.uf.edex.database.DataAccessLayerException; import com.raytheon.uf.edex.site.SiteAwareRegistry; @@ -70,7 +66,8 @@ import com.raytheon.uf.edex.site.SiteAwareRegistry; * D2DParmIdCache toGfeIngestNotificationFilter. * Added code to match wind components and send * GridUpdateNotifications. - * Mar 20, 2013 #1774 randerso Changde to use GFDD2DDao + * Mar 20, 2013 #1774 randerso Changed to use GFDD2DDao + * Apr 01, 2013 #1774 randerso Moved wind component checking to GfeIngestNotificaionFilter * * * @@ -87,20 +84,9 @@ public class D2DParmIdCache { private static final Pattern RangeFilter = Pattern .compile("(.*?)\\d{1,2}hr"); - private static final Map WIND_COMP_PARMS; - static { - WIND_COMP_PARMS = new HashMap(); - WIND_COMP_PARMS.put("uw", "vw"); - WIND_COMP_PARMS.put("vw", "uw"); - WIND_COMP_PARMS.put("ws", "wd"); - WIND_COMP_PARMS.put("wd", "ws"); - } - /** Map containing the ParmIDs */ private Map> parmIds; - private Map> windComps; - private static D2DParmIdCache instance; public static synchronized D2DParmIdCache getInstance() { @@ -115,7 +101,6 @@ public class D2DParmIdCache { */ public D2DParmIdCache() { parmIds = new HashMap>(); - windComps = new HashMap>(); } /** @@ -360,18 +345,9 @@ public class D2DParmIdCache { for (DatabaseID dbId : dbsToRemove) { GridParmManager.removeDbFromMap(dbId); } - // purge the windComps - List wcToRemove = new ArrayList(); - synchronized (windComps) { - for (ParmID id : windComps.keySet()) { - if (dbsToRemove.contains(id.getDbId())) { - wcToRemove.add(id); - } - } - for (ParmID id : wcToRemove) { - windComps.remove(id); - } - } + + // inform GfeIngestNotificationFilter of removed dbs + GfeIngestNotificationFilter.purgeDbs(dbsToRemove); statusHandler.handle(Priority.EVENTA, "Total time to build D2DParmIdCache for " + siteID @@ -404,60 +380,6 @@ public class D2DParmIdCache { public void processGridUpdateNotification(GridUpdateNotification gun) { ParmID parmId = gun.getParmId(); - - String otherCompName = WIND_COMP_PARMS.get(parmId.getParmName()); - if (otherCompName == null) { - // if it's not a wind component just add it to the cache - putParmID(parmId); - } else { - Set windTrs = null; - synchronized (windComps) { - // add this parms times to windComps map - Set trs = windComps.get(parmId); - if (trs == null) { - trs = new HashSet(); - windComps.put(parmId, trs); - } - trs.addAll(gun.getHistories().keySet()); - - // get the other components times - ParmID otherCompId = new ParmID(otherCompName, - parmId.getDbId(), parmId.getParmLevel()); - Set otherTrs = windComps.get(otherCompId); - - // if we have both components - if (otherTrs != null) { - // find times where we have both components - windTrs = new HashSet(trs); - windTrs.retainAll(otherTrs); - - // remove the matching times since we don't need them - // anymore - trs.removeAll(windTrs); - otherTrs.removeAll(windTrs); - } - } - - // if we found any matching times for both components - if (windTrs != null && !windTrs.isEmpty()) { - // add the wind parmId to the cache - ParmID windId = new ParmID("wind", parmId.getDbId(), - parmId.getParmLevel()); - putParmID(windId); - - // create GridUpdateNotifications for the wind parm - Map> history = new HashMap>(); - ArrayList guns = new ArrayList( - windTrs.size()); - for (TimeRange tr : windTrs) { - history.put(tr, Arrays.asList(new GridDataHistory( - GridDataHistory.OriginType.INITIALIZED, windId, tr, - null, (WsId) null))); - guns.add(new GridUpdateNotification(windId, tr, history, - null, windId.getDbId().getSiteId())); - } - SendNotifications.send(guns); - } - } + putParmID(parmId); } } diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/src/com/raytheon/edex/plugin/gfe/exception/GfeConfigurationException.java b/edexOsgi/com.raytheon.edex.plugin.gfe/src/com/raytheon/edex/plugin/gfe/exception/GfeConfigurationException.java index 09a04f28b3..58e2043772 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/src/com/raytheon/edex/plugin/gfe/exception/GfeConfigurationException.java +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/src/com/raytheon/edex/plugin/gfe/exception/GfeConfigurationException.java @@ -30,6 +30,8 @@ import com.raytheon.uf.common.dataplugin.gfe.exception.GfeException; * Date Ticket# Engineer Description * ------------ ---------- ----------- -------------------------- * 04/08/08 #875 bphillip Initial Creation + * 03/28/13 #1837 dgilling Implement missing constructors from + * super-class. * * * @@ -40,6 +42,10 @@ public class GfeConfigurationException extends GfeException { private static final long serialVersionUID = 1L; + public GfeConfigurationException() { + super(); + } + /** * @param aCause */ @@ -58,4 +64,7 @@ public class GfeConfigurationException extends GfeException { super(aCause, anException); } + public GfeConfigurationException(Throwable anException) { + super(anException); + } } diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/src/com/raytheon/edex/plugin/gfe/exception/MissingLocalMapsException.java b/edexOsgi/com.raytheon.edex.plugin.gfe/src/com/raytheon/edex/plugin/gfe/exception/MissingLocalMapsException.java new file mode 100644 index 0000000000..7863b59ed7 --- /dev/null +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/src/com/raytheon/edex/plugin/gfe/exception/MissingLocalMapsException.java @@ -0,0 +1,59 @@ +/** + * This software was developed and / or modified by Raytheon Company, + * pursuant to Contract DG133W-05-CQ-1067 with the US Government. + * + * U.S. EXPORT CONTROLLED TECHNICAL DATA + * This software product contains export-restricted data whose + * export/transfer/disclosure is restricted by U.S. law. Dissemination + * to non-U.S. persons whether in the United States or abroad requires + * an export license or other authorization. + * + * Contractor Name: Raytheon Company + * Contractor Address: 6825 Pine Street, Suite 340 + * Mail Stop B8 + * Omaha, NE 68106 + * 402.291.0100 + * + * See the AWIPS II Master Rights File ("Master Rights File.pdf") for + * further licensing information. + **/ +package com.raytheon.edex.plugin.gfe.exception; + +/** + * Exception thrown when a database table referenced in localMaps.py cannot be + * located in the maps database. + * + *
+ * 
+ * SOFTWARE HISTORY
+ * 
+ * Date         Ticket#    Engineer    Description
+ * ------------ ---------- ----------- --------------------------
+ * Mar 28, 2013            dgilling     Initial creation
+ * 
+ * 
+ * + * @author dgilling + * @version 1.0 + */ + +public class MissingLocalMapsException extends GfeConfigurationException { + + private static final long serialVersionUID = 1L; + + public MissingLocalMapsException() { + super(); + } + + public MissingLocalMapsException(String message) { + super(message); + } + + public MissingLocalMapsException(String message, Throwable cause) { + super(message, cause); + } + + public MissingLocalMapsException(Throwable cause) { + super(cause); + } +} diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/src/com/raytheon/edex/plugin/gfe/reference/DbShapeSource.java b/edexOsgi/com.raytheon.edex.plugin.gfe/src/com/raytheon/edex/plugin/gfe/reference/DbShapeSource.java index 807eb9225a..39ec233862 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/src/com/raytheon/edex/plugin/gfe/reference/DbShapeSource.java +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/src/com/raytheon/edex/plugin/gfe/reference/DbShapeSource.java @@ -46,10 +46,10 @@ import org.opengis.filter.Filter; import org.opengis.filter.FilterFactory2; import org.opengis.geometry.BoundingBox; +import com.raytheon.edex.plugin.gfe.exception.MissingLocalMapsException; import com.raytheon.uf.common.dataquery.db.QueryResult; import com.raytheon.uf.common.status.IUFStatusHandler; import com.raytheon.uf.common.status.UFStatus; -import com.raytheon.uf.common.status.UFStatus.Priority; import com.raytheon.uf.edex.core.EDEXUtil; import com.raytheon.uf.edex.database.tasks.SqlQueryTask; import com.vividsolutions.jts.geom.Geometry; @@ -68,6 +68,8 @@ import com.vividsolutions.jts.geom.Polygon; * Date Ticket# Engineer Description * ------------ ---------- ----------- -------------------------- * Sep 18, 2012 #1091 randerso Initial creation + * Mar 28, 2013 #1837 dgilling Change error handling in + * getLastUpdated(). * * * @@ -172,11 +174,16 @@ public class DbShapeSource { /** * @throws IOException + * @throws MissingLocalMapsException * */ - public void open() throws IOException { + public void open() throws IOException, MissingLocalMapsException { DataStore dataStore = getDataStore(); - schema = dataStore.getSchema(this.tableName); + try { + schema = dataStore.getSchema(this.tableName); + } catch (IOException e) { + throw new MissingLocalMapsException(e); + } shapeField = schema.getGeometryDescriptor().getLocalName(); featureCollection = null; @@ -247,7 +254,8 @@ public class DbShapeSource { return featureIterator.next(); } - public synchronized ShapeType getShapeType() throws IOException { + public synchronized ShapeType getShapeType() throws IOException, + MissingLocalMapsException { if (this.type == null) { boolean closeIt = false; if (schema == null) { @@ -418,6 +426,9 @@ public class DbShapeSource { } catch (IOException e) { statusHandler.error( "IOException reading " + dbShape.getTableName(), e); + } catch (MissingLocalMapsException e) { + statusHandler.error("Could not locate " + dbShape.getTableName() + + " in the maps database.", e); } finally { try { if (dbShape != null) { @@ -431,18 +442,17 @@ public class DbShapeSource { System.out.println("Took " + (System.currentTimeMillis() - t0) + " ms"); } - public Date getLastUpdated() { - Date retVal = new Date(); + public Date getLastUpdated() throws MissingLocalMapsException { String sqlQuery = "SELECT import_time FROM " + SCHEMA_NAME + ".map_version WHERE table_name = '" + this.tableName + "';"; try { SqlQueryTask task = new SqlQueryTask(sqlQuery, DB_NAME); QueryResult result = task.execute(); - retVal = (Date) result.getRowColumnValue(0, 0); + return (Date) result.getRowColumnValue(0, 0); } catch (Exception e) { - statusHandler.handle(Priority.PROBLEM, e.getLocalizedMessage(), e); + // statusHandler.handle(Priority.PROBLEM, e.getLocalizedMessage(), + // e); + throw new MissingLocalMapsException(e); } - - return retVal; } } diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/src/com/raytheon/edex/plugin/gfe/reference/MapManager.java b/edexOsgi/com.raytheon.edex.plugin.gfe/src/com/raytheon/edex/plugin/gfe/reference/MapManager.java index b0c3a1b81c..28d960360d 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/src/com/raytheon/edex/plugin/gfe/reference/MapManager.java +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/src/com/raytheon/edex/plugin/gfe/reference/MapManager.java @@ -47,6 +47,7 @@ import org.opengis.metadata.spatial.PixelOrientation; import org.opengis.referencing.operation.MathTransform; import com.raytheon.edex.plugin.gfe.config.IFPServerConfig; +import com.raytheon.edex.plugin.gfe.exception.MissingLocalMapsException; import com.raytheon.edex.plugin.gfe.reference.DbShapeSource.ShapeType; import com.raytheon.edex.plugin.gfe.textproducts.AreaDictionaryMaker; import com.raytheon.edex.plugin.gfe.textproducts.CombinationsFileMaker; @@ -73,6 +74,7 @@ import com.raytheon.uf.common.status.IUFStatusHandler; import com.raytheon.uf.common.status.UFStatus; import com.raytheon.uf.common.status.UFStatus.Priority; import com.raytheon.uf.common.util.FileUtil; +import com.raytheon.uf.edex.core.EDEXUtil; import com.vividsolutions.jts.geom.Coordinate; import com.vividsolutions.jts.geom.Geometry; import com.vividsolutions.jts.geom.GeometryFactory; @@ -96,6 +98,9 @@ import com.vividsolutions.jts.simplify.TopologyPreservingSimplifier; * Jun 25, 2008 #1210 randerso Modified to get directories from UtilityContext * Oct 13, 2008 #1607 njensen Added genCombinationsFiles() * Sep 18, 2012 #1091 randerso Changed to use Maps.py and localMaps.py + * Mar 28, 2013 #1837 dgilling Better error reporting if a map table + * from localMaps.py could not be found, + * warnings clean up. * * * @@ -243,28 +248,21 @@ public class MapManager { /** * @param maps */ - @SuppressWarnings("unused") - /** - * Searches the parent directory of a provided list of shape files to - * determine whether or not they contain a file that is newer than any files - * in a specified directory. - * - * @param maps - * An array of shape files. - * @param directory - * A directory containing the resultant edit areas from the shape - * files. - * @return True, if any file in the parent folder of any of the shape files - * is newer than anything in the specified directory. Else, false. - */ private boolean updateNeeded(List maps, final String directory) { // calc newest file inside maps.directory() long newestSource = Long.MIN_VALUE; + List failedMaps = new ArrayList(); for (DbShapeSource map : maps) { - newestSource = Math.max(newestSource, map.getLastUpdated() - .getTime()); + try { + newestSource = Math.max(newestSource, map.getLastUpdated() + .getTime()); + } catch (MissingLocalMapsException e) { + reportMissingLocalMap(map, "retrieving last update time", e); + failedMaps.add(map); + } } + maps.removeAll(failedMaps); // Determine time of last modification of Maps.py, serverConfig, // localConfig, localMaps, and siteConfig. @@ -388,6 +386,10 @@ public class MapManager { } makeReferenceData(m); + } catch (MissingLocalMapsException e) { + String error = reportMissingLocalMap(m, "retrieving map data", + e); + _mapErrors.add(error); } catch (Exception e) { String error = "********* EDIT AREA GENERATION ERROR - MakeReferenceData *********\n" + "Error in generating edit areas, map #" @@ -578,8 +580,8 @@ public class MapManager { // old one, write a warning to the log. ReferenceData other = null; try { - other = (ReferenceData) SerializationUtil - .jaxbUnmarshalFromXmlFile(path); + other = SerializationUtil.jaxbUnmarshalFromXmlFile( + ReferenceData.class, path); } catch (Exception e) { statusHandler.error("Error reading edit area file " + path.getAbsolutePath(), e); @@ -965,4 +967,23 @@ public class MapManager { return s; } + + private String reportMissingLocalMap(DbShapeSource missingMap, + String operation, MissingLocalMapsException e) { + String errorLog = "Error in " + operation + " for map named [" + + missingMap.getDisplayName() + "]: Could not find table [" + + missingMap.getTableName() + "] in maps database."; + statusHandler.error(errorLog, e); + + String errorUser = errorLog + + " Edit areas for this map will not be generated." + + " Check site [" + + _config.getSiteID().get(0) + + "] localMaps.py configuration and verify all necessary shape files have been imported."; + EDEXUtil.sendMessageAlertViz(Priority.ERROR, + "com.raytheon.edex.plugin.gfe", "GFE", "GFE", errorUser, + errorUser, null); + + return errorLog; + } } diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/src/com/raytheon/edex/plugin/gfe/server/database/D2DGridDatabase.java b/edexOsgi/com.raytheon.edex.plugin.gfe/src/com/raytheon/edex/plugin/gfe/server/database/D2DGridDatabase.java index 9650c06cd1..b840e1cc36 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/src/com/raytheon/edex/plugin/gfe/server/database/D2DGridDatabase.java +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/src/com/raytheon/edex/plugin/gfe/server/database/D2DGridDatabase.java @@ -101,6 +101,7 @@ import com.raytheon.uf.edex.database.DataAccessLayerException; * data instead of full grid. Added logging to support * GFE performance testing * 03/19/2013 #1774 randerso Fix accumulative grid time ranges + * Apr 01, 2013 #1774 randerso Moved wind component checking to GfeIngestNotificaionFilter * * * @@ -197,7 +198,7 @@ public class D2DGridDatabase extends VGridDatabase { private final IPerformanceStatusHandler perfLog = PerformanceStatus .getHandler("GFE:"); - private static class D2DParm { + public static class D2DParm { private ParmID parmId; private GridParmInfo gpi; @@ -1174,7 +1175,7 @@ public class D2DGridDatabase extends VGridDatabase { // no-op } - public ParmID getParmId(String d2dParmName, Level level) { + public D2DParm getD2DParm(String d2dParmName, Level level) { String gfeParmName = getGfeParmName(d2dParmName); String levelName = GridTranslator.getShortLevelName(level @@ -1182,24 +1183,26 @@ public class D2DGridDatabase extends VGridDatabase { .getLeveltwovalue()); D2DParm parm = d2dParms.get(compositeName(gfeParmName, levelName)); - if (parm != null) { - return parm.getParmId(); - } - - Matcher matcher = parmHrPattern.matcher(d2dParmName); - if (matcher.find()) { - String abbrev = matcher.group(1); - gfeParmName = getGfeParmName(abbrev); - parm = d2dParms.get(compositeName(gfeParmName, levelName)); - if (parm != null) { - return parm.getParmId(); + if (parm == null) { + // try to find one with duration (XXXnnhr) + Matcher matcher = parmHrPattern.matcher(d2dParmName); + if (matcher.find()) { + String abbrev = matcher.group(1); + gfeParmName = getGfeParmName(abbrev); + parm = d2dParms.get(compositeName(gfeParmName, levelName)); } } - return null; + if (parm == null) { + statusHandler.warn("No gridParameterInfo found for " + + compositeName(gfeParmName, levelName) + ":" + + dbId.getModelId() + ". Check parameterInfo file."); + } + + return parm; } - private String getGfeParmName(String d2dParmName) { + public String getGfeParmName(String d2dParmName) { String gfeParmName = null; try { gfeParmName = ParameterMapper.getInstance().lookupAlias( diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/src/com/raytheon/edex/plugin/gfe/server/notify/GfeIngestNotificationFilter.java b/edexOsgi/com.raytheon.edex.plugin.gfe/src/com/raytheon/edex/plugin/gfe/server/notify/GfeIngestNotificationFilter.java index c648026d6d..9e0caed7b8 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/src/com/raytheon/edex/plugin/gfe/server/notify/GfeIngestNotificationFilter.java +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/src/com/raytheon/edex/plugin/gfe/server/notify/GfeIngestNotificationFilter.java @@ -37,6 +37,7 @@ import com.raytheon.edex.plugin.gfe.exception.GfeConfigurationException; import com.raytheon.edex.plugin.gfe.server.D2DSatParm; import com.raytheon.edex.plugin.gfe.server.GridParmManager; import com.raytheon.edex.plugin.gfe.server.database.D2DGridDatabase; +import com.raytheon.edex.plugin.gfe.server.database.D2DGridDatabase.D2DParm; import com.raytheon.edex.plugin.gfe.server.database.D2DSatDatabase; import com.raytheon.edex.plugin.gfe.server.database.D2DSatDatabaseManager; import com.raytheon.edex.plugin.gfe.smartinit.SmartInitQueue; @@ -76,6 +77,7 @@ import com.raytheon.uf.edex.core.EDEXUtil; * Mar 25, 2013 1823 dgilling Trigger SAT smart init based only on record's * SectorId and PhysicalElement. * Mar 20, 2013 #1774 randerso Refactor to use grid durations from D2DGridDatabase + * Apr 01, 2013 #1774 randerso Moved wind component checking to GfeIngestNotificaionFilter * * * @@ -88,10 +90,17 @@ public class GfeIngestNotificationFilter { private static final transient IUFStatusHandler statusHandler = UFStatus .getHandler(GfeIngestNotificationFilter.class); + // private final IPerformanceStatusHandler perfLog = PerformanceStatus + // .getHandler("GFE:"); + + private static Map> windComps = new HashMap>(); + private SmartInitQueue smartInitQueue = null; public void filterDataURINotifications(DataURINotificationMessage message) throws Exception { + // ITimer timer = TimeUtil.getTimer(); + // timer.start(); Date arrivalTime = new Date(); List gridRecords = new ArrayList(500); List satRecords = new ArrayList(100); @@ -109,6 +118,10 @@ public class GfeIngestNotificationFilter { if (!satRecords.isEmpty()) { filterSatelliteRecords(satRecords, arrivalTime); } + // timer.stop(); + // perfLog.logDuration( + // "GfeIngestNotificationFilter: processing DataURINotificationMessage", + // timer.getElapsedTime()); } public void filterGridRecords(List gridRecords, Date arrivalTime) @@ -154,20 +167,62 @@ public class GfeIngestNotificationFilter { sendNotification(dbInv); } - String abbrev = grid.getParameter().getAbbreviation(); + String d2dParamName = grid.getParameter().getAbbreviation(); Level level = grid.getLevel(); + Integer fcstHour = grid.getDataTime().getFcstTime(); D2DGridDatabase db = (D2DGridDatabase) GridParmManager .getDb(dbId); - ParmID parmID = db.getParmId(abbrev, level); + String gfeParamName = db.getGfeParmName(d2dParamName); + D2DParm parm = db.getD2DParm(d2dParamName, level); + if (parm == null) { + continue; + } + ParmID parmID = parm.getParmId(); + + // check for wind + String otherComponent = null; + String[] components = parm.getComponents(); + if (components.length > 1) { + if (components[0].equals(gfeParamName)) { + otherComponent = components[1]; + } else { + otherComponent = components[0]; + } + } + + // if wind see if other component is available + if (otherComponent != null) { + ParmID otherPid = new ParmID(otherComponent, + parmID.getDbId(), parmID.getParmLevel()); + synchronized (windComps) { + // get the other components times + Set otherTimes = windComps.get(otherPid); + + // if we don't have the other component for this + // fcstHour + if (otherTimes == null + || !otherTimes.remove(fcstHour)) { + // need to wait for other component + ParmID compPid = new ParmID(gfeParamName, + parmID.getDbId(), parmID.getParmLevel()); + Set times = windComps.get(compPid); + if (times == null) { + times = new HashSet(); + windComps.put(compPid, times); + } + times.add(fcstHour); + continue; + } + } + } List trs = gridInv.get(parmID); if (trs == null) { trs = new ArrayList(); gridInv.put(parmID, trs); } - Integer fcstHour = grid.getDataTime().getFcstTime(); TimeRange tr = db.getTimeRange(parmID, fcstHour); if (tr != null) { trs.add(tr); @@ -325,4 +380,19 @@ public class GfeIngestNotificationFilter { public void setSmartInitQueue(SmartInitQueue smartInitQueue) { this.smartInitQueue = smartInitQueue; } + + public static void purgeDbs(List dbsToRemove) { + List wcToRemove = new ArrayList(); + synchronized (windComps) { + for (ParmID id : windComps.keySet()) { + if (dbsToRemove.contains(id.getDbId())) { + wcToRemove.add(id); + } + } + for (ParmID id : wcToRemove) { + windComps.remove(id); + } + } + } + } diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/AKwave10.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/AKwave10.xml index 9d0064aca2..ba20006160 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/AKwave10.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/AKwave10.xml @@ -115,7 +115,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/AKwave4.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/AKwave4.xml index 9d0064aca2..ba20006160 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/AKwave4.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/AKwave4.xml @@ -115,7 +115,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/AWCicgturb.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/AWCicgturb.xml index b84e8d2c1b..aaa2eabb95 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/AWCicgturb.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/AWCicgturb.xml @@ -63,7 +63,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/AkNamDNG5.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/AkNamDNG5.xml index ea300cda92..0824daeb63 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/AkNamDNG5.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/AkNamDNG5.xml @@ -64,7 +64,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/EPwave10.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/EPwave10.xml index 9d0064aca2..ba20006160 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/EPwave10.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/EPwave10.xml @@ -115,7 +115,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/GRLKwave.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/GRLKwave.xml index 46645cd57e..72d70f1725 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/GRLKwave.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/GRLKwave.xml @@ -217,7 +217,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/GlobalWave.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/GlobalWave.xml index 9d0064aca2..ba20006160 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/GlobalWave.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/GlobalWave.xml @@ -115,7 +115,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/HIrtmaNDFD.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/HIrtmaNDFD.xml index a3389cf549..0d704cb1cc 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/HIrtmaNDFD.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/HIrtmaNDFD.xml @@ -43,7 +43,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/HPCqpfNDFD.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/HPCqpfNDFD.xml index 721a8610ed..e015dacf62 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/HPCqpfNDFD.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/HPCqpfNDFD.xml @@ -77,7 +77,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/HiNamDNG5.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/HiNamDNG5.xml index ea300cda92..0824daeb63 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/HiNamDNG5.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/HiNamDNG5.xml @@ -64,7 +64,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/HiResW-arwAK.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/HiResW-arwAK.xml index 51576187db..350d111137 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/HiResW-arwAK.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/HiResW-arwAK.xml @@ -22,8 +22,8 @@ weasd water equivalent of accumulated snow depth - ml - mil + mm + millimeter waterEqvAccSnowDepth 0.0 1000.0 @@ -106,7 +106,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 @@ -258,7 +258,7 @@ cicep Categorical ice pellets - yes=1, no=0 + yn CategoricalIcePlt 0.0 @@ -333,7 +333,7 @@ cfrzr Categorical freezing rain - yes=1, no=0 + yn CategoricalFrzRain 0.0 @@ -378,7 +378,7 @@ csnow Categorical snow - yes=1, no=0 + yn CategoricalSnow 0.0 @@ -491,7 +491,7 @@ crain Categorical rain - yes=1, no=0 + yn CategoricalRain 0.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/HiResW-arwEast.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/HiResW-arwEast.xml index 51576187db..350d111137 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/HiResW-arwEast.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/HiResW-arwEast.xml @@ -22,8 +22,8 @@ weasd water equivalent of accumulated snow depth - ml - mil + mm + millimeter waterEqvAccSnowDepth 0.0 1000.0 @@ -106,7 +106,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 @@ -258,7 +258,7 @@ cicep Categorical ice pellets - yes=1, no=0 + yn CategoricalIcePlt 0.0 @@ -333,7 +333,7 @@ cfrzr Categorical freezing rain - yes=1, no=0 + yn CategoricalFrzRain 0.0 @@ -378,7 +378,7 @@ csnow Categorical snow - yes=1, no=0 + yn CategoricalSnow 0.0 @@ -491,7 +491,7 @@ crain Categorical rain - yes=1, no=0 + yn CategoricalRain 0.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/HiResW-arwHI.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/HiResW-arwHI.xml index 51576187db..350d111137 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/HiResW-arwHI.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/HiResW-arwHI.xml @@ -22,8 +22,8 @@ weasd water equivalent of accumulated snow depth - ml - mil + mm + millimeter waterEqvAccSnowDepth 0.0 1000.0 @@ -106,7 +106,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 @@ -258,7 +258,7 @@ cicep Categorical ice pellets - yes=1, no=0 + yn CategoricalIcePlt 0.0 @@ -333,7 +333,7 @@ cfrzr Categorical freezing rain - yes=1, no=0 + yn CategoricalFrzRain 0.0 @@ -378,7 +378,7 @@ csnow Categorical snow - yes=1, no=0 + yn CategoricalSnow 0.0 @@ -491,7 +491,7 @@ crain Categorical rain - yes=1, no=0 + yn CategoricalRain 0.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/HiResW-arwSJU.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/HiResW-arwSJU.xml index 51576187db..350d111137 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/HiResW-arwSJU.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/HiResW-arwSJU.xml @@ -22,8 +22,8 @@ weasd water equivalent of accumulated snow depth - ml - mil + mm + millimeter waterEqvAccSnowDepth 0.0 1000.0 @@ -106,7 +106,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 @@ -258,7 +258,7 @@ cicep Categorical ice pellets - yes=1, no=0 + yn CategoricalIcePlt 0.0 @@ -333,7 +333,7 @@ cfrzr Categorical freezing rain - yes=1, no=0 + yn CategoricalFrzRain 0.0 @@ -378,7 +378,7 @@ csnow Categorical snow - yes=1, no=0 + yn CategoricalSnow 0.0 @@ -491,7 +491,7 @@ crain Categorical rain - yes=1, no=0 + yn CategoricalRain 0.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/HiResW-arwWest.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/HiResW-arwWest.xml index 51576187db..350d111137 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/HiResW-arwWest.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/HiResW-arwWest.xml @@ -22,8 +22,8 @@ weasd water equivalent of accumulated snow depth - ml - mil + mm + millimeter waterEqvAccSnowDepth 0.0 1000.0 @@ -106,7 +106,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 @@ -258,7 +258,7 @@ cicep Categorical ice pellets - yes=1, no=0 + yn CategoricalIcePlt 0.0 @@ -333,7 +333,7 @@ cfrzr Categorical freezing rain - yes=1, no=0 + yn CategoricalFrzRain 0.0 @@ -378,7 +378,7 @@ csnow Categorical snow - yes=1, no=0 + yn CategoricalSnow 0.0 @@ -491,7 +491,7 @@ crain Categorical rain - yes=1, no=0 + yn CategoricalRain 0.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/HiResW-nmmAK.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/HiResW-nmmAK.xml index 51576187db..350d111137 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/HiResW-nmmAK.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/HiResW-nmmAK.xml @@ -22,8 +22,8 @@ weasd water equivalent of accumulated snow depth - ml - mil + mm + millimeter waterEqvAccSnowDepth 0.0 1000.0 @@ -106,7 +106,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 @@ -258,7 +258,7 @@ cicep Categorical ice pellets - yes=1, no=0 + yn CategoricalIcePlt 0.0 @@ -333,7 +333,7 @@ cfrzr Categorical freezing rain - yes=1, no=0 + yn CategoricalFrzRain 0.0 @@ -378,7 +378,7 @@ csnow Categorical snow - yes=1, no=0 + yn CategoricalSnow 0.0 @@ -491,7 +491,7 @@ crain Categorical rain - yes=1, no=0 + yn CategoricalRain 0.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/HiResW-nmmEast.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/HiResW-nmmEast.xml index 51576187db..350d111137 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/HiResW-nmmEast.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/HiResW-nmmEast.xml @@ -22,8 +22,8 @@ weasd water equivalent of accumulated snow depth - ml - mil + mm + millimeter waterEqvAccSnowDepth 0.0 1000.0 @@ -106,7 +106,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 @@ -258,7 +258,7 @@ cicep Categorical ice pellets - yes=1, no=0 + yn CategoricalIcePlt 0.0 @@ -333,7 +333,7 @@ cfrzr Categorical freezing rain - yes=1, no=0 + yn CategoricalFrzRain 0.0 @@ -378,7 +378,7 @@ csnow Categorical snow - yes=1, no=0 + yn CategoricalSnow 0.0 @@ -491,7 +491,7 @@ crain Categorical rain - yes=1, no=0 + yn CategoricalRain 0.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/HiResW-nmmHI.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/HiResW-nmmHI.xml index 51576187db..350d111137 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/HiResW-nmmHI.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/HiResW-nmmHI.xml @@ -22,8 +22,8 @@ weasd water equivalent of accumulated snow depth - ml - mil + mm + millimeter waterEqvAccSnowDepth 0.0 1000.0 @@ -106,7 +106,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 @@ -258,7 +258,7 @@ cicep Categorical ice pellets - yes=1, no=0 + yn CategoricalIcePlt 0.0 @@ -333,7 +333,7 @@ cfrzr Categorical freezing rain - yes=1, no=0 + yn CategoricalFrzRain 0.0 @@ -378,7 +378,7 @@ csnow Categorical snow - yes=1, no=0 + yn CategoricalSnow 0.0 @@ -491,7 +491,7 @@ crain Categorical rain - yes=1, no=0 + yn CategoricalRain 0.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/HiResW-nmmSJU.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/HiResW-nmmSJU.xml index 51576187db..350d111137 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/HiResW-nmmSJU.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/HiResW-nmmSJU.xml @@ -22,8 +22,8 @@ weasd water equivalent of accumulated snow depth - ml - mil + mm + millimeter waterEqvAccSnowDepth 0.0 1000.0 @@ -106,7 +106,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 @@ -258,7 +258,7 @@ cicep Categorical ice pellets - yes=1, no=0 + yn CategoricalIcePlt 0.0 @@ -333,7 +333,7 @@ cfrzr Categorical freezing rain - yes=1, no=0 + yn CategoricalFrzRain 0.0 @@ -378,7 +378,7 @@ csnow Categorical snow - yes=1, no=0 + yn CategoricalSnow 0.0 @@ -491,7 +491,7 @@ crain Categorical rain - yes=1, no=0 + yn CategoricalRain 0.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/HiResW-nmmWest.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/HiResW-nmmWest.xml index 51576187db..350d111137 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/HiResW-nmmWest.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/HiResW-nmmWest.xml @@ -22,8 +22,8 @@ weasd water equivalent of accumulated snow depth - ml - mil + mm + millimeter waterEqvAccSnowDepth 0.0 1000.0 @@ -106,7 +106,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 @@ -258,7 +258,7 @@ cicep Categorical ice pellets - yes=1, no=0 + yn CategoricalIcePlt 0.0 @@ -333,7 +333,7 @@ cfrzr Categorical freezing rain - yes=1, no=0 + yn CategoricalFrzRain 0.0 @@ -378,7 +378,7 @@ csnow Categorical snow - yes=1, no=0 + yn CategoricalSnow 0.0 @@ -491,7 +491,7 @@ crain Categorical rain - yes=1, no=0 + yn CategoricalRain 0.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/NamDNG5.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/NamDNG5.xml index ea300cda92..0824daeb63 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/NamDNG5.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/NamDNG5.xml @@ -64,7 +64,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/PRrtmaNDFD.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/PRrtmaNDFD.xml index a3389cf549..0d704cb1cc 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/PRrtmaNDFD.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/PRrtmaNDFD.xml @@ -43,7 +43,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/PrNamDNG5.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/PrNamDNG5.xml index ea300cda92..0824daeb63 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/PrNamDNG5.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/PrNamDNG5.xml @@ -64,7 +64,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/TPCSurgeProb.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/TPCSurgeProb.xml index 988546d938..701c191069 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/TPCSurgeProb.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/TPCSurgeProb.xml @@ -95,7 +95,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/TPCtcm175.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/TPCtcm175.xml index d6aea5b670..adf466bd40 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/TPCtcm175.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/TPCtcm175.xml @@ -54,7 +54,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/TPCtcm226.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/TPCtcm226.xml index d6aea5b670..adf466bd40 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/TPCtcm226.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/TPCtcm226.xml @@ -54,7 +54,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/TPCtcm250.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/TPCtcm250.xml index d6aea5b670..adf466bd40 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/TPCtcm250.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/TPCtcm250.xml @@ -54,7 +54,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/WCwave10.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/WCwave10.xml index 9d0064aca2..ba20006160 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/WCwave10.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/WCwave10.xml @@ -115,7 +115,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/WCwave4.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/WCwave4.xml index 9d0064aca2..ba20006160 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/WCwave4.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/WCwave4.xml @@ -115,7 +115,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/WNAwave10.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/WNAwave10.xml index 9d0064aca2..ba20006160 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/WNAwave10.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/WNAwave10.xml @@ -115,7 +115,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/WNAwave4.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/WNAwave4.xml index 9d0064aca2..ba20006160 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/WNAwave4.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/WNAwave4.xml @@ -115,7 +115,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/aKrtmaNDFD.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/aKrtmaNDFD.xml index a3389cf549..0d704cb1cc 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/aKrtmaNDFD.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/aKrtmaNDFD.xml @@ -43,7 +43,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/aiv211.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/aiv211.xml index 1eb43d22ae..953ee491c2 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/aiv211.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/aiv211.xml @@ -77,7 +77,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/akWave239.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/akWave239.xml index e06fcdd2fc..e7ce9cc910 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/akWave239.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/akWave239.xml @@ -87,7 +87,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/avn201.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/avn201.xml index e93e7d5c2c..cc206e1000 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/avn201.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/avn201.xml @@ -160,7 +160,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/avn202.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/avn202.xml index cf2f838760..09a39908d6 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/avn202.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/avn202.xml @@ -72,7 +72,7 @@ av absolute vorticity - 1/s + /s 1/second absVort -0.00999999977648 @@ -129,7 +129,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/avn203.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/avn203.xml index 574e46a4a6..7e135fe455 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/avn203.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/avn203.xml @@ -111,7 +111,7 @@ av absolute vorticity - 1/s + /s 1/second absVort -0.00999999977648 @@ -168,7 +168,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/avn211.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/avn211.xml index c122bbd6af..725b00d83e 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/avn211.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/avn211.xml @@ -156,7 +156,7 @@ av absolute vorticity - 1/s + /s 1/second absVort -0.00999999977648 @@ -244,7 +244,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/avn213.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/avn213.xml index f8c35328c6..543d6b6ed4 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/avn213.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/avn213.xml @@ -93,7 +93,7 @@ av absolute vorticity - 1/s + /s 1/second absVort -0.00999999977648 @@ -210,7 +210,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/avn225.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/avn225.xml index 3fd2299810..b2ac4ef228 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/avn225.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/avn225.xml @@ -109,7 +109,7 @@ av absolute vorticity - 1/s + /s 1/second absVort -0.00999999977648 @@ -226,7 +226,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/avnGBL.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/avnGBL.xml index 0e6be7fc0e..b14fe0e866 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/avnGBL.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/avnGBL.xml @@ -73,7 +73,7 @@ av absolute vorticity - 1/s + /s 1/second absVort -0.00999999977648 @@ -145,7 +145,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/avnNH.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/avnNH.xml index 0e6be7fc0e..b14fe0e866 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/avnNH.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/avnNH.xml @@ -73,7 +73,7 @@ av absolute vorticity - 1/s + /s 1/second absVort -0.00999999977648 @@ -145,7 +145,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/dgex185.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/dgex185.xml index 0c64f37fef..28f73e85bc 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/dgex185.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/dgex185.xml @@ -134,7 +134,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/dgex186.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/dgex186.xml index 0c64f37fef..28f73e85bc 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/dgex186.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/dgex186.xml @@ -134,7 +134,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/ecmfGBL.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/ecmfGBL.xml index 55e0f54694..08984bb520 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/ecmfGBL.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/ecmfGBL.xml @@ -56,7 +56,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/ecmfNH.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/ecmfNH.xml index 5e4946eaf3..eed8b6384e 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/ecmfNH.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/ecmfNH.xml @@ -56,7 +56,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/ecmwf.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/ecmwf.xml index 40e69daf6e..e6fa7c4c70 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/ecmwf.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/ecmwf.xml @@ -185,7 +185,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/enpWave253.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/enpWave253.xml index e06fcdd2fc..e7ce9cc910 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/enpWave253.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/enpWave253.xml @@ -87,7 +87,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/ensembleGBL.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/ensembleGBL.xml index e7fc6ca2b7..29a22991f6 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/ensembleGBL.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/ensembleGBL.xml @@ -407,7 +407,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/ensembleNH.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/ensembleNH.xml index e7fc6ca2b7..29a22991f6 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/ensembleNH.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/ensembleNH.xml @@ -407,7 +407,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/eta207.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/eta207.xml index 6d4cd31723..12c8564171 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/eta207.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/eta207.xml @@ -113,7 +113,7 @@ av absolute vorticity - 1/s + /s 1/second absVort -0.00999999977648 @@ -231,7 +231,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/eta211.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/eta211.xml index 264385fe91..731c2196b4 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/eta211.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/eta211.xml @@ -113,7 +113,7 @@ av absolute vorticity - 1/s + /s 1/second absVort -0.00999999977648 @@ -231,7 +231,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/eta212.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/eta212.xml index a052052f95..210ae0cfca 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/eta212.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/eta212.xml @@ -113,7 +113,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/eta218.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/eta218.xml index 82a702e6db..571ab07fed 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/eta218.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/eta218.xml @@ -34,8 +34,8 @@ weasd water equivalent of accumulated snow depth - ml - mil + mm + millimeter waterEqvAccSnowDepth 0.0 1000.0 @@ -138,7 +138,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 @@ -446,7 +446,7 @@ cicep Categorical ice pellets - yes=1, no=0 + yn CategoricalIcePlt 0.0 @@ -521,7 +521,7 @@ cfrzr Categorical freezing rain - yes=1, no=0 + yn CategoricalFrzRain 0.0 @@ -566,7 +566,7 @@ csnow Categorical snow - yes=1, no=0 + yn CategoricalSnow 0.0 @@ -685,7 +685,7 @@ crain Categorical rain - yes=1, no=0 + yn CategoricalRain 0.0 @@ -700,7 +700,7 @@ av absolute vorticity - 1/s + /s 1/second absVort -0.00999999977648 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/eta242.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/eta242.xml index 82a702e6db..571ab07fed 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/eta242.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/eta242.xml @@ -34,8 +34,8 @@ weasd water equivalent of accumulated snow depth - ml - mil + mm + millimeter waterEqvAccSnowDepth 0.0 1000.0 @@ -138,7 +138,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 @@ -446,7 +446,7 @@ cicep Categorical ice pellets - yes=1, no=0 + yn CategoricalIcePlt 0.0 @@ -521,7 +521,7 @@ cfrzr Categorical freezing rain - yes=1, no=0 + yn CategoricalFrzRain 0.0 @@ -566,7 +566,7 @@ csnow Categorical snow - yes=1, no=0 + yn CategoricalSnow 0.0 @@ -685,7 +685,7 @@ crain Categorical rain - yes=1, no=0 + yn CategoricalRain 0.0 @@ -700,7 +700,7 @@ av absolute vorticity - 1/s + /s 1/second absVort -0.00999999977648 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/fcst.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/fcst.xml index 50f091c689..c060409809 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/fcst.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/fcst.xml @@ -170,7 +170,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/gfe.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/gfe.xml index ba269e8c24..50cc95b6e9 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/gfe.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/gfe.xml @@ -81,7 +81,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/gfs160.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/gfs160.xml index 090a44042c..e3fb9bbcc4 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/gfs160.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/gfs160.xml @@ -46,7 +46,7 @@ crain Categorical rain - yes=1, no=0 + yn CategoricalRain 0.0 @@ -61,7 +61,7 @@ cfrzr Categorical freezing rain - yes=1, no=0 + yn CategoricalFrzRain 0.0 @@ -191,8 +191,8 @@ weasd water equivalent of accumulated snow depth - ml - mil + mm + millimeter waterEqvAccSnowDepth 0.0 1000.0 @@ -212,7 +212,7 @@ av absolute vorticity - 1/s + /s 1/second absVort -0.00999999977648 @@ -366,13 +366,13 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 csnow Categorical snow - yes=1, no=0 + yn CategoricalSnow 0.0 @@ -453,7 +453,7 @@ cicep Categorical ice pellets - yes=1, no=0 + yn CategoricalIcePlt 0.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/gfs161.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/gfs161.xml index 090a44042c..e3fb9bbcc4 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/gfs161.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/gfs161.xml @@ -46,7 +46,7 @@ crain Categorical rain - yes=1, no=0 + yn CategoricalRain 0.0 @@ -61,7 +61,7 @@ cfrzr Categorical freezing rain - yes=1, no=0 + yn CategoricalFrzRain 0.0 @@ -191,8 +191,8 @@ weasd water equivalent of accumulated snow depth - ml - mil + mm + millimeter waterEqvAccSnowDepth 0.0 1000.0 @@ -212,7 +212,7 @@ av absolute vorticity - 1/s + /s 1/second absVort -0.00999999977648 @@ -366,13 +366,13 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 csnow Categorical snow - yes=1, no=0 + yn CategoricalSnow 0.0 @@ -453,7 +453,7 @@ cicep Categorical ice pellets - yes=1, no=0 + yn CategoricalIcePlt 0.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/gfs201.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/gfs201.xml index d81c728ac1..a5c3648be5 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/gfs201.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/gfs201.xml @@ -117,7 +117,7 @@ av absolute vorticity - 1/s + /s 1/second absVort -0.00999999977648 @@ -204,7 +204,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/gfs212.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/gfs212.xml index 8a3ea78290..1234531a1a 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/gfs212.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/gfs212.xml @@ -46,7 +46,7 @@ crain Categorical rain - yes=1, no=0 + yn CategoricalRain 0.0 @@ -61,7 +61,7 @@ cfrzr Categorical freezing rain - yes=1, no=0 + yn CategoricalFrzRain 0.0 @@ -191,8 +191,8 @@ weasd water equivalent of accumulated snow depth - ml - mil + mm + millimeter waterEqvAccSnowDepth 0.0 1000.0 @@ -212,7 +212,7 @@ av absolute vorticity - 1/s + /s 1/second absVort -0.00999999977648 @@ -366,13 +366,13 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 csnow Categorical snow - yes=1, no=0 + yn CategoricalSnow 0.0 @@ -453,7 +453,7 @@ cicep Categorical ice pellets - yes=1, no=0 + yn CategoricalIcePlt 0.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/gfs213.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/gfs213.xml index 1b8b5d23c1..d6347c6724 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/gfs213.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/gfs213.xml @@ -117,7 +117,7 @@ av absolute vorticity - 1/s + /s 1/second absVort -0.00999999977648 @@ -240,7 +240,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/gfs254.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/gfs254.xml index 090a44042c..e3fb9bbcc4 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/gfs254.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/gfs254.xml @@ -46,7 +46,7 @@ crain Categorical rain - yes=1, no=0 + yn CategoricalRain 0.0 @@ -61,7 +61,7 @@ cfrzr Categorical freezing rain - yes=1, no=0 + yn CategoricalFrzRain 0.0 @@ -191,8 +191,8 @@ weasd water equivalent of accumulated snow depth - ml - mil + mm + millimeter waterEqvAccSnowDepth 0.0 1000.0 @@ -212,7 +212,7 @@ av absolute vorticity - 1/s + /s 1/second absVort -0.00999999977648 @@ -366,13 +366,13 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 csnow Categorical snow - yes=1, no=0 + yn CategoricalSnow 0.0 @@ -453,7 +453,7 @@ cicep Categorical ice pellets - yes=1, no=0 + yn CategoricalIcePlt 0.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/gfsGuide232.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/gfsGuide232.xml index c91df8530a..2e13c88558 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/gfsGuide232.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/gfsGuide232.xml @@ -66,7 +66,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/glerl.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/glerl.xml index 675bb66421..6e2a437115 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/glerl.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/glerl.xml @@ -111,7 +111,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/gww233.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/gww233.xml index e06fcdd2fc..e7ce9cc910 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/gww233.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/gww233.xml @@ -87,7 +87,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/hpcDelta215.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/hpcDelta215.xml index 0ea9af700d..62bf05746d 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/hpcDelta215.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/hpcDelta215.xml @@ -79,7 +79,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/hpcGuideNDFD.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/hpcGuideNDFD.xml index a2d5d454fb..52f61e8294 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/hpcGuideNDFD.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/hpcGuideNDFD.xml @@ -108,7 +108,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/lampNDFD.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/lampNDFD.xml index 225b1ab52c..ae6c060dad 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/lampNDFD.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/lampNDFD.xml @@ -28,7 +28,7 @@ ctstm Categorical Thunder Storm - yes=1, no=0 + yn CategoricalTSTM 0.0 @@ -70,7 +70,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/laps.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/laps.xml index 191184e7cc..193ccadd14 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/laps.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/laps.xml @@ -168,7 +168,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/maps.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/maps.xml index c6d6814bfa..d55231efbe 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/maps.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/maps.xml @@ -57,7 +57,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/maps40.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/maps40.xml index cfc713e22c..8691683e06 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/maps40.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/maps40.xml @@ -217,7 +217,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/mesoEta212.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/mesoEta212.xml index cacbbacc9a..5967ee8b78 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/mesoEta212.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/mesoEta212.xml @@ -199,7 +199,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/mesoEta215.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/mesoEta215.xml index e239ed80f7..a71eee32cd 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/mesoEta215.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/mesoEta215.xml @@ -94,7 +94,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 @@ -435,7 +435,7 @@ av absolute vorticity - 1/s + /s 1/second absVort -0.00999999977648 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/mesoEta216.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/mesoEta216.xml index f932d315f4..0182bf09d6 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/mesoEta216.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/mesoEta216.xml @@ -148,7 +148,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/mesoEta217.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/mesoEta217.xml index da824e78c7..920cd67960 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/mesoEta217.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/mesoEta217.xml @@ -34,7 +34,7 @@ crain Categorical rain - yes=1, no=0 + yn CategoricalRain 0.0 @@ -49,7 +49,7 @@ cfrzr Categorical freezing rain - yes=1, no=0 + yn CategoricalFrzRain 0.0 @@ -146,7 +146,7 @@ csnow Categorical snow - yes=1, no=0 + yn CategoricalSnow 0.0 @@ -191,7 +191,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 @@ -229,7 +229,7 @@ cicep Categorical ice pellets - yes=1, no=0 + yn CategoricalIcePlt 0.0 @@ -280,8 +280,8 @@ weasd water equivalent of accumulated snow depth - ml - mil + mm + millimeter waterEqvAccSnowDepth 0.0 1000.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/mm5.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/mm5.xml index 58336dc11e..20d9e52736 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/mm5.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/mm5.xml @@ -174,7 +174,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/mosGuideNDFD.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/mosGuideNDFD.xml index d6db06600b..3abaac7fde 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/mosGuideNDFD.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/mosGuideNDFD.xml @@ -239,7 +239,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/mosGuideNDFD_AK.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/mosGuideNDFD_AK.xml index 170e7f1a80..08d862120d 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/mosGuideNDFD_AK.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/mosGuideNDFD_AK.xml @@ -119,7 +119,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/mrf201.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/mrf201.xml index a177d384c9..be0fd5b98c 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/mrf201.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/mrf201.xml @@ -53,7 +53,7 @@ av absolute vorticity - 1/s + /s 1/second absVort -0.00999999977648 @@ -136,7 +136,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/mrf202.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/mrf202.xml index a177d384c9..be0fd5b98c 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/mrf202.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/mrf202.xml @@ -53,7 +53,7 @@ av absolute vorticity - 1/s + /s 1/second absVort -0.00999999977648 @@ -136,7 +136,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/mrf203.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/mrf203.xml index a177d384c9..be0fd5b98c 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/mrf203.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/mrf203.xml @@ -53,7 +53,7 @@ av absolute vorticity - 1/s + /s 1/second absVort -0.00999999977648 @@ -136,7 +136,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/mrf204.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/mrf204.xml index a177d384c9..be0fd5b98c 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/mrf204.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/mrf204.xml @@ -53,7 +53,7 @@ av absolute vorticity - 1/s + /s 1/second absVort -0.00999999977648 @@ -136,7 +136,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/mrf205.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/mrf205.xml index a177d384c9..be0fd5b98c 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/mrf205.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/mrf205.xml @@ -53,7 +53,7 @@ av absolute vorticity - 1/s + /s 1/second absVort -0.00999999977648 @@ -136,7 +136,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/mrf213.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/mrf213.xml index 84c0bcf124..027539792d 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/mrf213.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/mrf213.xml @@ -53,7 +53,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/msas.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/msas.xml index 930aed4c6f..ff6f107802 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/msas.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/msas.xml @@ -57,7 +57,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/ngm202.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/ngm202.xml index fdd06f61ba..5e7fd4ee21 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/ngm202.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/ngm202.xml @@ -74,7 +74,7 @@ av absolute vorticity - 1/s + /s 1/second absVort -0.00999999977648 @@ -156,7 +156,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/ngm207.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/ngm207.xml index f283cbf054..116284ad4b 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/ngm207.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/ngm207.xml @@ -97,7 +97,7 @@ av absolute vorticity - 1/s + /s 1/second absVort -0.00999999977648 @@ -194,7 +194,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/ngm211.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/ngm211.xml index f8beda744d..27c1c995e1 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/ngm211.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/ngm211.xml @@ -97,7 +97,7 @@ av absolute vorticity - 1/s + /s 1/second absVort -0.00999999977648 @@ -194,7 +194,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/ngm213.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/ngm213.xml index 00ff131a27..3daf7abbe6 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/ngm213.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/ngm213.xml @@ -57,7 +57,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/nic218.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/nic218.xml index 452c495c2f..89f9caaa6b 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/nic218.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/nic218.xml @@ -33,7 +33,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/nic242.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/nic242.xml index 452c495c2f..89f9caaa6b 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/nic242.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/nic242.xml @@ -33,7 +33,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/nogaps.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/nogaps.xml index dadcdc5afd..46075b507e 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/nogaps.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/nogaps.xml @@ -124,7 +124,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/opcWave180.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/opcWave180.xml index b2936322d8..79b3ba69a4 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/opcWave180.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/opcWave180.xml @@ -29,7 +29,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/opcWave181.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/opcWave181.xml index b2936322d8..79b3ba69a4 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/opcWave181.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/opcWave181.xml @@ -29,7 +29,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/opcWave182.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/opcWave182.xml index b2936322d8..79b3ba69a4 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/opcWave182.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/opcWave182.xml @@ -29,7 +29,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/roc_rams.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/roc_rams.xml index 4b2ff3ec82..131f20fdd9 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/roc_rams.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/roc_rams.xml @@ -184,7 +184,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/rsmMerc10km.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/rsmMerc10km.xml index f1f8560ee5..f524a45ff9 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/rsmMerc10km.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/rsmMerc10km.xml @@ -100,7 +100,7 @@ av absolute vorticity - 1/s + /s 1/second absVort -0.00999999977648 @@ -227,7 +227,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/rtgsst235.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/rtgsst235.xml index 34ee89d4d5..10004dacfb 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/rtgsst235.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/rtgsst235.xml @@ -33,7 +33,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/rtmaNDFD.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/rtmaNDFD.xml index 09089972b6..768a3085f2 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/rtmaNDFD.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/rtmaNDFD.xml @@ -118,7 +118,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/ruc130.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/ruc130.xml index 0dd80bdcdc..a80f85844e 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/ruc130.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/ruc130.xml @@ -107,7 +107,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 @@ -359,7 +359,7 @@ cicep Categorical ice pellets - yes=1, no=0 + yn CategoricalIcePlt 0.0 @@ -395,7 +395,7 @@ cfrzr Categorical freezing rain - yes=1, no=0 + yn CategoricalFrzRain 0.0 @@ -440,7 +440,7 @@ csnow Categorical snow - yes=1, no=0 + yn CategoricalSnow 0.0 @@ -587,7 +587,7 @@ crain Categorical rain - yes=1, no=0 + yn CategoricalRain 0.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/ruc211.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/ruc211.xml index 8884b33091..368f82db12 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/ruc211.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/ruc211.xml @@ -44,8 +44,8 @@ weasd water equivalent of accumulated snow depth - ml - mil + mm + millimeter waterEqvAccSnowDepth 0.0 1000.0 @@ -198,7 +198,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/ruc236.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/ruc236.xml index 66edd74011..536e49420c 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/ruc236.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/ruc236.xml @@ -79,7 +79,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 @@ -258,7 +258,7 @@ cicep Categorical ice pellets - yes=1, no=0 + yn CategoricalIcePlt 0.0 @@ -294,7 +294,7 @@ cfrzr Categorical freezing rain - yes=1, no=0 + yn CategoricalFrzRain 0.0 @@ -339,7 +339,7 @@ csnow Categorical snow - yes=1, no=0 + yn CategoricalSnow 0.0 @@ -465,7 +465,7 @@ crain Categorical rain - yes=1, no=0 + yn CategoricalRain 0.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/seaIce219.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/seaIce219.xml index 6f78489475..925ec01318 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/seaIce219.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/seaIce219.xml @@ -33,7 +33,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/sfm.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/sfm.xml index b80a64a312..2ee706e5dd 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/sfm.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/sfm.xml @@ -74,7 +74,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/sref212.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/sref212.xml index ad202909b7..75c207741f 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/sref212.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/sref212.xml @@ -636,7 +636,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/sref216.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/sref216.xml index f3685163f8..a2f0ff474f 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/sref216.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/sref216.xml @@ -104,7 +104,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/sref243.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/sref243.xml index 81180b07b9..99cfb6cf22 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/sref243.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/sref243.xml @@ -89,7 +89,7 @@ cfrzrmean Categorical freezing rain mean - yes=1, no=0 + yn CategoricalFrzRainmean 0.0 @@ -232,7 +232,7 @@ crainmean Categorical rain mean - yes=1, no=0 + yn CategoricalRainmean 0.0 @@ -247,7 +247,7 @@ cfrzrsprd Categorical freezing rain sprd - yes=1, no=0 + yn CategoricalFrzRainsprd 0.0 @@ -304,7 +304,7 @@ cicepmean Categorical ice pellets mean - yes=1, no=0 + yn CategoricalIcePltmean 0.0 @@ -334,7 +334,7 @@ csnowsprd Categorical snow sprd - yes=1, no=0 + yn CategoricalSnowsprd 0.0 @@ -401,7 +401,7 @@ cicepsprd Categorical ice pellets sprd - yes=1, no=0 + yn CategoricalIcePltsprd 0.0 @@ -429,7 +429,7 @@ crainsprd Categorical rain sprd - yes=1, no=0 + yn CategoricalRainsprd 0.0 @@ -558,7 +558,7 @@ csnowmean Categorical snow mean - yes=1, no=0 + yn CategoricalSnowmean 0.0 @@ -659,7 +659,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/tpcWind231.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/tpcWind231.xml index 85ce00d3be..31e69f0961 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/tpcWind231.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/tpcWind231.xml @@ -62,7 +62,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/turb212.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/turb212.xml index 38c0906865..13628d1895 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/turb212.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/turb212.xml @@ -73,7 +73,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/ukmetGBL.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/ukmetGBL.xml index 40e29055a1..7726819417 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/ukmetGBL.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/ukmetGBL.xml @@ -112,7 +112,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/ukmetNH.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/ukmetNH.xml index 40e29055a1..7726819417 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/ukmetNH.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/ukmetNH.xml @@ -112,7 +112,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/wnaWave238.xml b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/wnaWave238.xml index e06fcdd2fc..e7ce9cc910 100644 --- a/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/wnaWave238.xml +++ b/edexOsgi/com.raytheon.edex.plugin.gfe/utility/edex_static/base/grid/parameterInfo/wnaWave238.xml @@ -87,7 +87,7 @@ staticCoriolis Coriolis parameter - /second + /s -99999.0 diff --git a/edexOsgi/com.raytheon.edex.plugin.ldadmanual/src/com/raytheon/edex/plugin/ldadmanual/dao/ManualLdadRecord.java b/edexOsgi/com.raytheon.edex.plugin.ldadmanual/src/com/raytheon/edex/plugin/ldadmanual/dao/ManualLdadRecord.java index cb1a74381c..7bb5eb3c3d 100644 --- a/edexOsgi/com.raytheon.edex.plugin.ldadmanual/src/com/raytheon/edex/plugin/ldadmanual/dao/ManualLdadRecord.java +++ b/edexOsgi/com.raytheon.edex.plugin.ldadmanual/src/com/raytheon/edex/plugin/ldadmanual/dao/ManualLdadRecord.java @@ -41,6 +41,7 @@ import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; +import org.hibernate.annotations.Index; import com.raytheon.uf.common.dataplugin.IDecoderGettable; import com.raytheon.uf.common.dataplugin.PluginDataObject; @@ -61,6 +62,7 @@ import com.vividsolutions.jts.geom.Geometry; * ate Ticket# Engineer Description * ----------- ---------- ----------- -------------------------- * 9/30/09 vkorolev Initial creation + * Apr 4, 2013 1846 bkowal Added an index on refTime and forecastTime * * * @author vkorolev @@ -69,6 +71,16 @@ import com.vividsolutions.jts.geom.Geometry; @Entity @Table(name = "ldad_manual", uniqueConstraints = { @UniqueConstraint(columnNames = { "dataURI" }) }) +/* + * Both refTime and forecastTime are included in the refTimeIndex since + * forecastTime is unlikely to be used. + */ +@org.hibernate.annotations.Table( + appliesTo = "ldad_manual", + indexes = { + @Index(name = "ldad_manual_refTimeIndex", columnNames = { "refTime", "forecastTime" } ) + } +) @XmlRootElement @XmlAccessorType(XmlAccessType.NONE) @DynamicSerialize diff --git a/edexOsgi/com.raytheon.edex.plugin.ldadprofiler/src/com/raytheon/edex/plugin/ldadprofiler/common/ProfilerLdadObs.java b/edexOsgi/com.raytheon.edex.plugin.ldadprofiler/src/com/raytheon/edex/plugin/ldadprofiler/common/ProfilerLdadObs.java index f938c8dc0d..fe124acb64 100644 --- a/edexOsgi/com.raytheon.edex.plugin.ldadprofiler/src/com/raytheon/edex/plugin/ldadprofiler/common/ProfilerLdadObs.java +++ b/edexOsgi/com.raytheon.edex.plugin.ldadprofiler/src/com/raytheon/edex/plugin/ldadprofiler/common/ProfilerLdadObs.java @@ -40,6 +40,7 @@ import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; +import org.hibernate.annotations.Index; import com.raytheon.uf.common.dataplugin.IDecoderGettable; import com.raytheon.uf.common.dataplugin.annotations.DataURI; @@ -63,6 +64,7 @@ import com.raytheon.uf.common.serialization.annotations.DynamicSerializeElement; * ate Ticket# Engineer Description * ----------- ---------- ----------- -------------------------- * 10/07/09 vkorolev Initial creation + * Apr 4, 2013 1846 bkowal Added an index on refTime and forecastTime * * * @author vkorolev @@ -71,6 +73,16 @@ import com.raytheon.uf.common.serialization.annotations.DynamicSerializeElement; @Entity @Table(name = "ldadprofiler", uniqueConstraints = { @UniqueConstraint(columnNames = { "dataURI" }) }) +/* + * Both refTime and forecastTime are included in the refTimeIndex since + * forecastTime is unlikely to be used. + */ +@org.hibernate.annotations.Table( + appliesTo = "ldadprofiler", + indexes = { + @Index(name = "ldadprofiler_refTimeIndex", columnNames = { "refTime", "forecastTime" } ) + } +) @XmlRootElement @XmlAccessorType(XmlAccessType.NONE) @DynamicSerialize diff --git a/edexOsgi/com.raytheon.edex.plugin.modelsounding/src/com/raytheon/edex/plugin/modelsounding/common/SoundingSite.java b/edexOsgi/com.raytheon.edex.plugin.modelsounding/src/com/raytheon/edex/plugin/modelsounding/common/SoundingSite.java index 4fd119a545..344380b984 100644 --- a/edexOsgi/com.raytheon.edex.plugin.modelsounding/src/com/raytheon/edex/plugin/modelsounding/common/SoundingSite.java +++ b/edexOsgi/com.raytheon.edex.plugin.modelsounding/src/com/raytheon/edex/plugin/modelsounding/common/SoundingSite.java @@ -34,6 +34,7 @@ import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; +import org.hibernate.annotations.Index; import com.raytheon.uf.common.dataplugin.IDecoderGettable; import com.raytheon.uf.common.dataplugin.annotations.DataURI; @@ -57,6 +58,7 @@ import com.vividsolutions.jts.geom.Geometry; * Date Ticket# Engineer Description * ------------ ---------- ----------- -------------------------- * 20080303 1026 jkorman Initial implementation. + * Apr 4, 2013 1846 bkowal Added an index on refTime and forecastTime * * * @@ -65,6 +67,16 @@ import com.vividsolutions.jts.geom.Geometry; */ @Entity @Table(name = "modelsounding", uniqueConstraints = { @UniqueConstraint(columnNames = { "dataURI" }) }) +/* + * Both refTime and forecastTime are included in the refTimeIndex since + * forecastTime is unlikely to be used. + */ +@org.hibernate.annotations.Table( + appliesTo = "modelsounding", + indexes = { + @Index(name = "modelsounding_refTimeIndex", columnNames = { "refTime", "forecastTime" } ) + } +) @DynamicSerialize @XmlAccessorType(XmlAccessType.NONE) @XmlRootElement diff --git a/edexOsgi/com.raytheon.edex.plugin.obs/src/com/raytheon/edex/plugin/obs/mesowest/MesowestRecord.java b/edexOsgi/com.raytheon.edex.plugin.obs/src/com/raytheon/edex/plugin/obs/mesowest/MesowestRecord.java index 5419147fcb..ea1c21084b 100644 --- a/edexOsgi/com.raytheon.edex.plugin.obs/src/com/raytheon/edex/plugin/obs/mesowest/MesowestRecord.java +++ b/edexOsgi/com.raytheon.edex.plugin.obs/src/com/raytheon/edex/plugin/obs/mesowest/MesowestRecord.java @@ -43,7 +43,8 @@ import com.raytheon.uf.common.serialization.annotations.DynamicSerializeElement; * Date Ticket# Engineer Description * ------------ ---------- ----------- -------------------------- * 2/14/2007 139 Phillippe Initial creation - * 20071129 472 jkorman Added IDecoderGettable interface. + * 20071129 472 jkorman Added IDecoderGettable interface. + * * * * diff --git a/edexOsgi/com.raytheon.edex.plugin.recco/src/com/raytheon/edex/plugin/recco/common/RECCORecord.java b/edexOsgi/com.raytheon.edex.plugin.recco/src/com/raytheon/edex/plugin/recco/common/RECCORecord.java index 2af416fc34..661b4da5fa 100644 --- a/edexOsgi/com.raytheon.edex.plugin.recco/src/com/raytheon/edex/plugin/recco/common/RECCORecord.java +++ b/edexOsgi/com.raytheon.edex.plugin.recco/src/com/raytheon/edex/plugin/recco/common/RECCORecord.java @@ -40,6 +40,7 @@ import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; +import org.hibernate.annotations.Index; import com.raytheon.uf.common.dataplugin.IDecoderGettable; import com.raytheon.uf.common.dataplugin.PluginDataObject; @@ -61,6 +62,7 @@ import com.vividsolutions.jts.geom.Geometry; * ------------ ---------- ----------- -------------------------- * 20080103 384 jkorman Initial Coding. * 20080107 720 jkorman remove default assignments from attributes. + * Apr 4, 2013 1846 bkowal Added an index on refTime and forecastTime * * * @author jkorman @@ -68,6 +70,16 @@ import com.vividsolutions.jts.geom.Geometry; */ @Entity @Table(name = "recco", uniqueConstraints = { @UniqueConstraint(columnNames = { "dataURI" }) }) +/* + * Both refTime and forecastTime are included in the refTimeIndex since + * forecastTime is unlikely to be used. + */ +@org.hibernate.annotations.Table( + appliesTo = "recco", + indexes = { + @Index(name = "recco_refTimeIndex", columnNames = { "refTime", "forecastTime" } ) + } +) @XmlRootElement @XmlAccessorType(XmlAccessType.NONE) @DynamicSerialize diff --git a/edexOsgi/com.raytheon.edex.plugin.redbook/src/com/raytheon/edex/plugin/redbook/common/RedbookRecord.java b/edexOsgi/com.raytheon.edex.plugin.redbook/src/com/raytheon/edex/plugin/redbook/common/RedbookRecord.java index 6f343f7484..ced66f1cde 100644 --- a/edexOsgi/com.raytheon.edex.plugin.redbook/src/com/raytheon/edex/plugin/redbook/common/RedbookRecord.java +++ b/edexOsgi/com.raytheon.edex.plugin.redbook/src/com/raytheon/edex/plugin/redbook/common/RedbookRecord.java @@ -31,6 +31,7 @@ import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlRootElement; +import org.hibernate.annotations.Index; import com.raytheon.uf.common.dataplugin.IDecoderGettable; import com.raytheon.uf.common.dataplugin.PluginException; @@ -56,6 +57,7 @@ import com.raytheon.uf.common.time.DataTime; * ------------ ---------- ----------- -------------------------- * 20080512 1131 jkorman Initial implementation. * 20080529 1131 jkorman getPersistenceTime now returns system time. + * Apr 4, 2013 1846 bkowal Added an index on refTime and forecastTime * * * @author jkorman @@ -63,6 +65,16 @@ import com.raytheon.uf.common.time.DataTime; */ @Entity @Table(name = "redbook", uniqueConstraints = { @UniqueConstraint(columnNames = { "dataURI" }) }) +/* + * Both refTime and forecastTime are included in the refTimeIndex since + * forecastTime is unlikely to be used. + */ +@org.hibernate.annotations.Table( + appliesTo = "redbook", + indexes = { + @Index(name = "redbook_refTimeIndex", columnNames = { "refTime", "forecastTime" } ) + } +) @XmlRootElement @XmlAccessorType(XmlAccessType.NONE) @DynamicSerialize diff --git a/edexOsgi/com.raytheon.edex.plugin.taf/src/com/raytheon/edex/plugin/taf/common/TafRecord.java b/edexOsgi/com.raytheon.edex.plugin.taf/src/com/raytheon/edex/plugin/taf/common/TafRecord.java index adcdf0c9fb..f52b2e1b93 100644 --- a/edexOsgi/com.raytheon.edex.plugin.taf/src/com/raytheon/edex/plugin/taf/common/TafRecord.java +++ b/edexOsgi/com.raytheon.edex.plugin.taf/src/com/raytheon/edex/plugin/taf/common/TafRecord.java @@ -61,6 +61,7 @@ import com.raytheon.uf.common.serialization.annotations.DynamicSerializeElement; * 2/14/07 139 bphillip Initial Creation * 6/21/07 180 bphillip Updated to use new plugin pattern * 20071129 472 jkorman Added IDecoderGettable interface. + * Apr 4, 2013 1846 bkowal Added an index on refTime and forecastTime * * * @@ -69,6 +70,16 @@ import com.raytheon.uf.common.serialization.annotations.DynamicSerializeElement; */ @Entity @Table(name = "taf", uniqueConstraints = { @UniqueConstraint(columnNames = { "dataURI" }) }) +/* + * Both refTime and forecastTime are included in the refTimeIndex since + * forecastTime is unlikely to be used. + */ +@org.hibernate.annotations.Table( + appliesTo = "taf", + indexes = { + @Index(name = "taf_refTimeIndex", columnNames = { "refTime", "forecastTime" } ) + } +) @DynamicSerialize @XmlAccessorType(XmlAccessType.NONE) @XmlRootElement diff --git a/edexOsgi/com.raytheon.edex.plugin.text/src/com/raytheon/edex/plugin/text/dao/AfosToAwipsDao.java b/edexOsgi/com.raytheon.edex.plugin.text/src/com/raytheon/edex/plugin/text/dao/AfosToAwipsDao.java index 26b328862c..7c10c32caf 100644 --- a/edexOsgi/com.raytheon.edex.plugin.text/src/com/raytheon/edex/plugin/text/dao/AfosToAwipsDao.java +++ b/edexOsgi/com.raytheon.edex.plugin.text/src/com/raytheon/edex/plugin/text/dao/AfosToAwipsDao.java @@ -53,6 +53,7 @@ import com.raytheon.uf.edex.database.dao.DaoConfig; * ------------ ---------- ----------- -------------------------- * Aug 28, 2009 2924 rjpeter Initial creation * Nov 16, 2009 3336 njensen Added lookupAfosId(String, String, String) + * 02apr2013 15564 mgamazaychikov Ensured afosid to be 9 characters space-padded long * * * @@ -137,7 +138,9 @@ public class AfosToAwipsDao extends CoreDao { while (rs.next()) { AfosToAwips id = new AfosToAwips(); - id.setAfosid(rs.getString(1)); + // make sure that the afosid is 9 characters space-padded long + String afosid = String.format("%-9s", rs.getString(1).trim()); + id.setAfosid(afosid); id.setWmottaaii(ttaaii); id.setWmocccc(cccc); rval.add(id); diff --git a/edexOsgi/com.raytheon.uf.common.dataplugin.acars/META-INF/MANIFEST.MF b/edexOsgi/com.raytheon.uf.common.dataplugin.acars/META-INF/MANIFEST.MF index 28abad42c1..adfe4a641d 100644 --- a/edexOsgi/com.raytheon.uf.common.dataplugin.acars/META-INF/MANIFEST.MF +++ b/edexOsgi/com.raytheon.uf.common.dataplugin.acars/META-INF/MANIFEST.MF @@ -12,7 +12,8 @@ Require-Bundle: org.apache.commons.logging, javax.persistence, javax.measure, com.raytheon.uf.common.serialization, - com.raytheon.uf.common.dataplugin + com.raytheon.uf.common.dataplugin, + org.hibernate;bundle-version="1.0.0" Export-Package: com.raytheon.uf.common.dataplugin.acars Import-Package: com.raytheon.uf.common.geospatial, com.raytheon.uf.common.time diff --git a/edexOsgi/com.raytheon.uf.common.dataplugin.acars/src/com/raytheon/uf/common/dataplugin/acars/ACARSRecord.java b/edexOsgi/com.raytheon.uf.common.dataplugin.acars/src/com/raytheon/uf/common/dataplugin/acars/ACARSRecord.java index 101fae6ee8..05d9d61f0f 100644 --- a/edexOsgi/com.raytheon.uf.common.dataplugin.acars/src/com/raytheon/uf/common/dataplugin/acars/ACARSRecord.java +++ b/edexOsgi/com.raytheon.uf.common.dataplugin.acars/src/com/raytheon/uf/common/dataplugin/acars/ACARSRecord.java @@ -43,6 +43,8 @@ import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; +import org.hibernate.annotations.Index; + import com.raytheon.uf.common.dataplugin.IDecoderGettable; import com.raytheon.uf.common.dataplugin.PluginDataObject; import com.raytheon.uf.common.dataplugin.annotations.DataURI; @@ -66,6 +68,7 @@ import com.vividsolutions.jts.geom.Geometry; * Added a getMessageData method. * Apr 21, 2009 2245 jsanchez Returned temperature unit to kelvin. * May 21, 2009 2338 jsanchez Updated the getMessageData. + * Apr 4, 2013 1846 bkowal Added an index on refTime and forecastTime * * * @@ -74,6 +77,16 @@ import com.vividsolutions.jts.geom.Geometry; */ @Entity @Table(name = "acars", uniqueConstraints = { @UniqueConstraint(columnNames = { "dataURI" }) }) +/* + * Both refTime and forecastTime are included in the refTimeIndex since + * forecastTime is unlikely to be used. + */ +@org.hibernate.annotations.Table( + appliesTo = "acars", + indexes = { + @Index(name = "acars_refTimeIndex", columnNames = { "refTime", "forecastTime" } ) + } +) @XmlRootElement @XmlAccessorType(XmlAccessType.NONE) @DynamicSerialize diff --git a/edexOsgi/com.raytheon.uf.common.dataplugin.acarssounding/META-INF/MANIFEST.MF b/edexOsgi/com.raytheon.uf.common.dataplugin.acarssounding/META-INF/MANIFEST.MF index c4de3d8802..3c14cc690b 100644 --- a/edexOsgi/com.raytheon.uf.common.dataplugin.acarssounding/META-INF/MANIFEST.MF +++ b/edexOsgi/com.raytheon.uf.common.dataplugin.acarssounding/META-INF/MANIFEST.MF @@ -14,7 +14,8 @@ Require-Bundle: org.apache.commons.logging, javax.persistence, javax.measure, com.raytheon.uf.common.dataplugin, - com.raytheon.uf.common.serialization + com.raytheon.uf.common.serialization, + org.hibernate;bundle-version="1.0.0" Export-Package: com.raytheon.uf.common.dataplugin.acarssounding, com.raytheon.uf.common.dataplugin.acarssounding.tools Import-Package: com.raytheon.uf.common.dataplugin.acars, diff --git a/edexOsgi/com.raytheon.uf.common.dataplugin.acarssounding/src/com/raytheon/uf/common/dataplugin/acarssounding/ACARSSoundingRecord.java b/edexOsgi/com.raytheon.uf.common.dataplugin.acarssounding/src/com/raytheon/uf/common/dataplugin/acarssounding/ACARSSoundingRecord.java index ae8c22f61f..cd1d98c41f 100644 --- a/edexOsgi/com.raytheon.uf.common.dataplugin.acarssounding/src/com/raytheon/uf/common/dataplugin/acarssounding/ACARSSoundingRecord.java +++ b/edexOsgi/com.raytheon.uf.common.dataplugin.acarssounding/src/com/raytheon/uf/common/dataplugin/acarssounding/ACARSSoundingRecord.java @@ -41,6 +41,7 @@ import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; +import org.hibernate.annotations.Index; import com.raytheon.uf.common.dataplugin.IDecoderGettable; import com.raytheon.uf.common.dataplugin.PluginDataObject; @@ -60,6 +61,7 @@ import com.vividsolutions.jts.geom.Geometry; * Date Ticket# Engineer Description * ------------ ---------- ----------- -------------------------- * 20090403 1939 jkorman Initial creation + * Apr 4, 2013 1846 bkowal Added an index on refTime and forecastTime * * * @@ -68,6 +70,16 @@ import com.vividsolutions.jts.geom.Geometry; */ @Entity @Table(name = "acarssounding", uniqueConstraints = { @UniqueConstraint(columnNames = { "dataURI" }) }) +/* + * Both refTime and forecastTime are included in the refTimeIndex since + * forecastTime is unlikely to be used. + */ +@org.hibernate.annotations.Table( + appliesTo = "acarssounding", + indexes = { + @Index(name = "acarssounding_refTimeIndex", columnNames = { "refTime", "forecastTime" } ) + } +) @XmlRootElement @XmlAccessorType(XmlAccessType.NONE) @DynamicSerialize diff --git a/edexOsgi/com.raytheon.uf.common.dataplugin.airep/META-INF/MANIFEST.MF b/edexOsgi/com.raytheon.uf.common.dataplugin.airep/META-INF/MANIFEST.MF index e4344820c4..8a1ac7eae1 100644 --- a/edexOsgi/com.raytheon.uf.common.dataplugin.airep/META-INF/MANIFEST.MF +++ b/edexOsgi/com.raytheon.uf.common.dataplugin.airep/META-INF/MANIFEST.MF @@ -18,5 +18,6 @@ Import-Package: com.raytheon.uf.common.dataplugin, javax.measure.unit, javax.persistence Require-Bundle: com.raytheon.uf.common.dataplugin, - com.raytheon.uf.common.serialization;bundle-version="1.12.1174" + com.raytheon.uf.common.serialization;bundle-version="1.12.1174", + org.hibernate;bundle-version="1.0.0" Export-Package: com.raytheon.uf.common.dataplugin.airep diff --git a/edexOsgi/com.raytheon.uf.common.dataplugin.airep/src/com/raytheon/uf/common/dataplugin/airep/AirepRecord.java b/edexOsgi/com.raytheon.uf.common.dataplugin.airep/src/com/raytheon/uf/common/dataplugin/airep/AirepRecord.java index 3330cb6c82..f9e2b012e8 100644 --- a/edexOsgi/com.raytheon.uf.common.dataplugin.airep/src/com/raytheon/uf/common/dataplugin/airep/AirepRecord.java +++ b/edexOsgi/com.raytheon.uf.common.dataplugin.airep/src/com/raytheon/uf/common/dataplugin/airep/AirepRecord.java @@ -45,6 +45,7 @@ import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; +import org.hibernate.annotations.Index; import com.raytheon.uf.common.dataplugin.IDecoderGettable; import com.raytheon.uf.common.dataplugin.PluginDataObject; @@ -68,6 +69,7 @@ import com.vividsolutions.jts.geom.Geometry; * 20080107 720 jkorman remove default assignments from attributes. * 20120405 435 dgilling Prevent NullPointerExceptions in * buildMessageData(). + * Apr 4, 2013 1846 bkowal Added an index on refTime and forecastTime * ====================================== * AWIPS2 DR Work * 20120911 1011 jkorman Added ability to report turbulence from decoded @@ -79,6 +81,16 @@ import com.vividsolutions.jts.geom.Geometry; */ @Entity @Table(name = "airep", uniqueConstraints = { @UniqueConstraint(columnNames = { "dataURI" }) }) +/* + * Both refTime and forecastTime are included in the refTimeIndex since + * forecastTime is unlikely to be used. + */ +@org.hibernate.annotations.Table( + appliesTo = "airep", + indexes = { + @Index(name = "airep_refTimeIndex", columnNames = { "refTime", "forecastTime" } ) + } +) @XmlRootElement @XmlAccessorType(XmlAccessType.NONE) @DynamicSerialize diff --git a/edexOsgi/com.raytheon.uf.common.dataplugin.binlightning/src/com/raytheon/uf/common/dataplugin/binlightning/BinLightningRecord.java b/edexOsgi/com.raytheon.uf.common.dataplugin.binlightning/src/com/raytheon/uf/common/dataplugin/binlightning/BinLightningRecord.java index 83a6b9f80f..531618b21c 100644 --- a/edexOsgi/com.raytheon.uf.common.dataplugin.binlightning/src/com/raytheon/uf/common/dataplugin/binlightning/BinLightningRecord.java +++ b/edexOsgi/com.raytheon.uf.common.dataplugin.binlightning/src/com/raytheon/uf/common/dataplugin/binlightning/BinLightningRecord.java @@ -30,6 +30,7 @@ import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlRootElement; +import org.hibernate.annotations.Index; import com.raytheon.uf.common.dataplugin.IDecoderGettable; import com.raytheon.uf.common.dataplugin.annotations.DataURI; @@ -69,6 +70,7 @@ import com.raytheon.uf.edex.decodertools.time.TimeTools; * 20080708 1174 jkorman Added persistenceTime handling. * 20090206 1990 bphillip Removed populateDataStore method * 20130227 DCS 152 jgerth/elau Support for WWLLN and multiple sources + * Apr 4, 2013 1846 bkowal Added an index on refTime and forecastTime * * * @author jkorman @@ -76,6 +78,16 @@ import com.raytheon.uf.edex.decodertools.time.TimeTools; */ @Entity @Table(name = "binlightning", uniqueConstraints = { @UniqueConstraint(columnNames = { "dataURI" }) }) +/* + * Both refTime and forecastTime are included in the refTimeIndex since + * forecastTime is unlikely to be used. + */ +@org.hibernate.annotations.Table( + appliesTo = "binlightning", + indexes = { + @Index(name = "binlightning_refTimeIndex", columnNames = { "refTime", "forecastTime" } ) + } +) @XmlRootElement @DynamicSerialize @XmlAccessorType(XmlAccessType.NONE) diff --git a/edexOsgi/com.raytheon.uf.common.dataplugin.bufrascat/src/com/raytheon/uf/common/dataplugin/bufrascat/AScatObs.java b/edexOsgi/com.raytheon.uf.common.dataplugin.bufrascat/src/com/raytheon/uf/common/dataplugin/bufrascat/AScatObs.java index 5168630974..13df1f75d1 100644 --- a/edexOsgi/com.raytheon.uf.common.dataplugin.bufrascat/src/com/raytheon/uf/common/dataplugin/bufrascat/AScatObs.java +++ b/edexOsgi/com.raytheon.uf.common.dataplugin.bufrascat/src/com/raytheon/uf/common/dataplugin/bufrascat/AScatObs.java @@ -33,6 +33,7 @@ import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; +import org.hibernate.annotations.Index; import com.raytheon.uf.common.dataplugin.IDecoderGettable; import com.raytheon.uf.common.dataplugin.annotations.DataURI; @@ -56,6 +57,7 @@ import com.vividsolutions.jts.geom.Geometry; * Date Ticket# Engineer Description * ------------ ---------- ----------- -------------------------- * Jun 18, 2009 2624 jkorman Initial creation + * Apr 4, 2013 1846 bkowal Added an index on refTime and forecastTime * * * @@ -64,6 +66,16 @@ import com.vividsolutions.jts.geom.Geometry; */ @Entity @Table(name = "bufrascat", uniqueConstraints = { @UniqueConstraint(columnNames = { "dataURI" }) }) +/* + * Both refTime and forecastTime are included in the refTimeIndex since + * forecastTime is unlikely to be used. + */ +@org.hibernate.annotations.Table( + appliesTo = "bufrascat", + indexes = { + @Index(name = "bufrascat_refTimeIndex", columnNames = { "refTime", "forecastTime" } ) + } +) @XmlRootElement @XmlAccessorType(XmlAccessType.NONE) @DynamicSerialize diff --git a/edexOsgi/com.raytheon.uf.common.dataplugin.bufrhdw/src/com/raytheon/uf/common/dataplugin/bufrhdw/BufrHDWObs.java b/edexOsgi/com.raytheon.uf.common.dataplugin.bufrhdw/src/com/raytheon/uf/common/dataplugin/bufrhdw/BufrHDWObs.java index 767c970a92..b4b6b2e124 100644 --- a/edexOsgi/com.raytheon.uf.common.dataplugin.bufrhdw/src/com/raytheon/uf/common/dataplugin/bufrhdw/BufrHDWObs.java +++ b/edexOsgi/com.raytheon.uf.common.dataplugin.bufrhdw/src/com/raytheon/uf/common/dataplugin/bufrhdw/BufrHDWObs.java @@ -33,6 +33,7 @@ import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; +import org.hibernate.annotations.Index; import com.raytheon.uf.common.dataplugin.IDecoderGettable; import com.raytheon.uf.common.dataplugin.annotations.DataURI; @@ -55,6 +56,7 @@ import com.raytheon.uf.common.serialization.annotations.DynamicSerializeElement; * Date Ticket# Engineer Description * ------------ ---------- ----------- -------------------------- * Jun 18, 2009 jkorman Initial creation + * Apr 4, 2013 1846 bkowal Added an index on refTime and forecastTime * * * @@ -63,6 +65,16 @@ import com.raytheon.uf.common.serialization.annotations.DynamicSerializeElement; */ @Entity @Table(name = "bufrhdw", uniqueConstraints = { @UniqueConstraint(columnNames = { "dataURI" }) }) +/* + * Both refTime and forecastTime are included in the refTimeIndex since + * forecastTime is unlikely to be used. + */ +@org.hibernate.annotations.Table( + appliesTo = "bufrhdw", + indexes = { + @Index(name = "bufrhdw_refTimeIndex", columnNames = { "refTime", "forecastTime" } ) + } +) @XmlRootElement @XmlAccessorType(XmlAccessType.NONE) @DynamicSerialize diff --git a/edexOsgi/com.raytheon.uf.common.dataplugin.bufrmthdw/src/com/raytheon/uf/common/dataplugin/bufrmthdw/BufrMTHDWObs.java b/edexOsgi/com.raytheon.uf.common.dataplugin.bufrmthdw/src/com/raytheon/uf/common/dataplugin/bufrmthdw/BufrMTHDWObs.java index 722a7c7a01..117b6a86c7 100644 --- a/edexOsgi/com.raytheon.uf.common.dataplugin.bufrmthdw/src/com/raytheon/uf/common/dataplugin/bufrmthdw/BufrMTHDWObs.java +++ b/edexOsgi/com.raytheon.uf.common.dataplugin.bufrmthdw/src/com/raytheon/uf/common/dataplugin/bufrmthdw/BufrMTHDWObs.java @@ -33,6 +33,7 @@ import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; +import org.hibernate.annotations.Index; import com.raytheon.uf.common.dataplugin.IDecoderGettable; import com.raytheon.uf.common.dataplugin.annotations.DataURI; @@ -55,6 +56,7 @@ import com.raytheon.uf.common.serialization.annotations.DynamicSerializeElement; * Date Ticket# Engineer Description * ------------ ---------- ----------- -------------------------- * Jul 26, 2010 jkorman Initial creation + * Apr 4, 2013 1846 bkowal Added an index on refTime and forecastTime * * * @@ -63,6 +65,16 @@ import com.raytheon.uf.common.serialization.annotations.DynamicSerializeElement; */ @Entity @Table(name = "bufrmthdw", uniqueConstraints = { @UniqueConstraint(columnNames = { "dataURI" }) }) +/* + * Both refTime and forecastTime are included in the refTimeIndex since + * forecastTime is unlikely to be used. + */ +@org.hibernate.annotations.Table( + appliesTo = "bufrmthdw", + indexes = { + @Index(name = "bufrmthdw_refTimeIndex", columnNames = { "refTime", "forecastTime" } ) + } +) @XmlRootElement @XmlAccessorType(XmlAccessType.NONE) @DynamicSerialize diff --git a/edexOsgi/com.raytheon.uf.common.dataplugin.bufrncwf/src/com/raytheon/uf/common/dataplugin/ncwf/BUFRncwf.java b/edexOsgi/com.raytheon.uf.common.dataplugin.bufrncwf/src/com/raytheon/uf/common/dataplugin/ncwf/BUFRncwf.java index 4509b817a1..2a945898e5 100644 --- a/edexOsgi/com.raytheon.uf.common.dataplugin.bufrncwf/src/com/raytheon/uf/common/dataplugin/ncwf/BUFRncwf.java +++ b/edexOsgi/com.raytheon.uf.common.dataplugin.bufrncwf/src/com/raytheon/uf/common/dataplugin/ncwf/BUFRncwf.java @@ -31,6 +31,7 @@ import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; +import org.hibernate.annotations.Index; import com.raytheon.uf.common.dataplugin.IDecoderGettable; import com.raytheon.uf.common.dataplugin.annotations.DataURI; @@ -53,6 +54,7 @@ import com.raytheon.uf.common.serialization.annotations.DynamicSerializeElement; * Date Ticket# Engineer Description * ------------ ---------- ----------- -------------------------- * Aug 17, 2009 jkorman Initial creation + * Apr 4, 2013 1846 bkowal Added an index on refTime and forecastTime * * * @@ -61,6 +63,16 @@ import com.raytheon.uf.common.serialization.annotations.DynamicSerializeElement; */ @Entity @Table(name = "bufrncwf", uniqueConstraints = { @UniqueConstraint(columnNames = { "dataURI" }) }) +/* + * Both refTime and forecastTime are included in the refTimeIndex since + * forecastTime is unlikely to be used. + */ +@org.hibernate.annotations.Table( + appliesTo = "bufrncwf", + indexes = { + @Index(name = "bufrncwf_refTimeIndex", columnNames = { "refTime", "forecastTime" } ) + } +) @XmlRootElement @XmlAccessorType(XmlAccessType.NONE) @DynamicSerialize diff --git a/edexOsgi/com.raytheon.uf.common.dataplugin.bufrquikscat/src/com/raytheon/uf/common/dataplugin/bufrquikscat/QUIKScatObs.java b/edexOsgi/com.raytheon.uf.common.dataplugin.bufrquikscat/src/com/raytheon/uf/common/dataplugin/bufrquikscat/QUIKScatObs.java index dc2aad87b4..2cef2f4758 100644 --- a/edexOsgi/com.raytheon.uf.common.dataplugin.bufrquikscat/src/com/raytheon/uf/common/dataplugin/bufrquikscat/QUIKScatObs.java +++ b/edexOsgi/com.raytheon.uf.common.dataplugin.bufrquikscat/src/com/raytheon/uf/common/dataplugin/bufrquikscat/QUIKScatObs.java @@ -33,6 +33,7 @@ import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; +import org.hibernate.annotations.Index; import com.raytheon.uf.common.dataplugin.IDecoderGettable; import com.raytheon.uf.common.dataplugin.annotations.DataURI; @@ -56,6 +57,7 @@ import com.vividsolutions.jts.geom.Geometry; * Date Ticket# Engineer Description * ------------ ---------- ----------- -------------------------- * Jun 18, 2009 2520 jkorman Initial creation + * Apr 4, 2013 1846 bkowal Added an index on refTime and forecastTime * * * @@ -64,6 +66,16 @@ import com.vividsolutions.jts.geom.Geometry; */ @Entity @Table(name = "bufrquikscat", uniqueConstraints = { @UniqueConstraint(columnNames = { "dataURI" }) }) +/* + * Both refTime and forecastTime are included in the refTimeIndex since + * forecastTime is unlikely to be used. + */ +@org.hibernate.annotations.Table( + appliesTo = "bufrquikscat", + indexes = { + @Index(name = "bufrquikscat_refTimeIndex", columnNames = { "refTime", "forecastTime" } ) + } +) @XmlRootElement @XmlAccessorType(XmlAccessType.NONE) @DynamicSerialize diff --git a/edexOsgi/com.raytheon.uf.common.dataplugin.bufrsigwx/src/com/raytheon/uf/common/dataplugin/bufrsigwx/SigWxData.java b/edexOsgi/com.raytheon.uf.common.dataplugin.bufrsigwx/src/com/raytheon/uf/common/dataplugin/bufrsigwx/SigWxData.java index c91d810231..d5ff8389d6 100644 --- a/edexOsgi/com.raytheon.uf.common.dataplugin.bufrsigwx/src/com/raytheon/uf/common/dataplugin/bufrsigwx/SigWxData.java +++ b/edexOsgi/com.raytheon.uf.common.dataplugin.bufrsigwx/src/com/raytheon/uf/common/dataplugin/bufrsigwx/SigWxData.java @@ -32,6 +32,7 @@ import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; +import org.hibernate.annotations.Index; import com.raytheon.uf.common.dataplugin.IDecoderGettable; import com.raytheon.uf.common.dataplugin.annotations.DataURI; @@ -54,6 +55,7 @@ import com.raytheon.uf.common.serialization.annotations.DynamicSerializeElement; * Date Ticket# Engineer Description * ------------ ---------- ----------- -------------------------- * Jun 18, 2009 jkorman Initial creation + * Apr 4, 2013 1846 bkowal Added an index on refTime and forecastTime * * * @@ -62,6 +64,16 @@ import com.raytheon.uf.common.serialization.annotations.DynamicSerializeElement; */ @Entity @Table(name = "bufrsigwx", uniqueConstraints = { @UniqueConstraint(columnNames = { "dataURI" }) }) +/* + * Both refTime and forecastTime are included in the refTimeIndex since + * forecastTime is unlikely to be used. + */ +@org.hibernate.annotations.Table( + appliesTo = "bufrsigwx", + indexes = { + @Index(name = "bufrswigwx_refTimeIndex", columnNames = { "refTime", "forecastTime" } ) + } +) @DynamicSerialize @XmlRootElement @XmlAccessorType(XmlAccessType.NONE) diff --git a/edexOsgi/com.raytheon.uf.common.dataplugin.bufrssmi/src/com/raytheon/uf/common/dataplugin/bufrssmi/SSMIScanData.java b/edexOsgi/com.raytheon.uf.common.dataplugin.bufrssmi/src/com/raytheon/uf/common/dataplugin/bufrssmi/SSMIScanData.java index d50c7691ac..d076d5f660 100644 --- a/edexOsgi/com.raytheon.uf.common.dataplugin.bufrssmi/src/com/raytheon/uf/common/dataplugin/bufrssmi/SSMIScanData.java +++ b/edexOsgi/com.raytheon.uf.common.dataplugin.bufrssmi/src/com/raytheon/uf/common/dataplugin/bufrssmi/SSMIScanData.java @@ -33,6 +33,7 @@ import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; +import org.hibernate.annotations.Index; import com.raytheon.uf.common.dataplugin.IDecoderGettable; import com.raytheon.uf.common.dataplugin.annotations.DataURI; @@ -56,6 +57,7 @@ import com.vividsolutions.jts.geom.Geometry; * Date Ticket# Engineer Description * ------------ ---------- ----------- -------------------------- * Jun 18, 2009 jkorman Initial creation + * Apr 4, 2013 1846 bkowal Added an index on refTime and forecastTime * * * @@ -64,6 +66,16 @@ import com.vividsolutions.jts.geom.Geometry; */ @Entity @Table(name = "bufrssmi", uniqueConstraints = { @UniqueConstraint(columnNames = { "dataURI" }) }) +/* + * Both refTime and forecastTime are included in the refTimeIndex since + * forecastTime is unlikely to be used. + */ +@org.hibernate.annotations.Table( + appliesTo = "bufrssmi", + indexes = { + @Index(name = "bufrssmi_refTimeIndex", columnNames = { "refTime", "forecastTime" } ) + } +) @DynamicSerialize @XmlRootElement @XmlAccessorType(XmlAccessType.NONE) diff --git a/edexOsgi/com.raytheon.uf.common.dataplugin.bufrua/src/com/raytheon/uf/common/dataplugin/bufrua/UAObs.java b/edexOsgi/com.raytheon.uf.common.dataplugin.bufrua/src/com/raytheon/uf/common/dataplugin/bufrua/UAObs.java index eb626997cf..a97fdd54d9 100644 --- a/edexOsgi/com.raytheon.uf.common.dataplugin.bufrua/src/com/raytheon/uf/common/dataplugin/bufrua/UAObs.java +++ b/edexOsgi/com.raytheon.uf.common.dataplugin.bufrua/src/com/raytheon/uf/common/dataplugin/bufrua/UAObs.java @@ -47,6 +47,7 @@ import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; +import org.hibernate.annotations.Index; import com.raytheon.uf.common.dataplugin.IDecoderGettable; import com.raytheon.uf.common.dataplugin.annotations.DataURI; @@ -80,6 +81,7 @@ import com.vividsolutions.jts.geom.Geometry; * 20080108 382 jkorman Added IVerticalSoundingCreator impl. * 20080114 763 jkorman Added "below" ground level exclusion to * getValue. + * Apr 4, 2013 1846 bkowal Added an index on refTime and forecastTime * * * @author jkorman @@ -87,6 +89,16 @@ import com.vividsolutions.jts.geom.Geometry; */ @Entity @Table(name = "bufrua", uniqueConstraints = { @UniqueConstraint(columnNames = { "dataURI" }) }) +/* + * Both refTime and forecastTime are included in the refTimeIndex since + * forecastTime is unlikely to be used. + */ +@org.hibernate.annotations.Table( + appliesTo = "bufrua", + indexes = { + @Index(name = "bufrua_refTimeIndex", columnNames = { "refTime", "forecastTime" } ) + } +) @XmlRootElement @XmlAccessorType(XmlAccessType.NONE) @DynamicSerialize diff --git a/edexOsgi/com.raytheon.uf.common.dataplugin.cwa/src/com/raytheon/uf/common/dataplugin/cwa/CWARecord.java b/edexOsgi/com.raytheon.uf.common.dataplugin.cwa/src/com/raytheon/uf/common/dataplugin/cwa/CWARecord.java index 9455ba3a7e..6749b91063 100644 --- a/edexOsgi/com.raytheon.uf.common.dataplugin.cwa/src/com/raytheon/uf/common/dataplugin/cwa/CWARecord.java +++ b/edexOsgi/com.raytheon.uf.common.dataplugin.cwa/src/com/raytheon/uf/common/dataplugin/cwa/CWARecord.java @@ -30,6 +30,7 @@ import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; +import org.hibernate.annotations.Index; import com.raytheon.uf.common.dataplugin.IDecoderGettable; import com.raytheon.uf.common.dataplugin.annotations.DataURI; @@ -50,6 +51,7 @@ import com.vividsolutions.jts.geom.Coordinate; * Date Ticket# Engineer Description * ------------ ---------- ----------- -------------------------- * Feb 1, 2010 jsanchez Initial creation + * Apr 4, 2013 1846 bkowal Added an index on refTime and forecastTime * * * @@ -58,6 +60,16 @@ import com.vividsolutions.jts.geom.Coordinate; */ @Entity @Table(name = "cwa", uniqueConstraints = { @UniqueConstraint(columnNames = { "dataURI" }) }) +/* + * Both refTime and forecastTime are included in the refTimeIndex since + * forecastTime is unlikely to be used. + */ +@org.hibernate.annotations.Table( + appliesTo = "cwa", + indexes = { + @Index(name = "cwa_refTimeIndex", columnNames = { "refTime", "forecastTime" } ) + } +) @XmlRootElement @XmlAccessorType(XmlAccessType.NONE) @DynamicSerialize diff --git a/edexOsgi/com.raytheon.uf.common.dataplugin.cwat/src/com/raytheon/uf/common/dataplugin/cwat/CWATRecord.java b/edexOsgi/com.raytheon.uf.common.dataplugin.cwat/src/com/raytheon/uf/common/dataplugin/cwat/CWATRecord.java index 02e4baab2c..9cc4b9f58b 100644 --- a/edexOsgi/com.raytheon.uf.common.dataplugin.cwat/src/com/raytheon/uf/common/dataplugin/cwat/CWATRecord.java +++ b/edexOsgi/com.raytheon.uf.common.dataplugin.cwat/src/com/raytheon/uf/common/dataplugin/cwat/CWATRecord.java @@ -38,6 +38,7 @@ import org.geotools.coverage.grid.GeneralGridEnvelope; import org.geotools.coverage.grid.GridGeometry2D; import org.geotools.geometry.GeneralEnvelope; import org.opengis.referencing.crs.ProjectedCRS; +import org.hibernate.annotations.Index; import com.raytheon.uf.common.dataplugin.IDecoderGettable; import com.raytheon.uf.common.dataplugin.annotations.DataURI; @@ -69,6 +70,7 @@ import com.raytheon.uf.common.serialization.annotations.DynamicSerializeElement; * Date Ticket# Engineer Description * ------------ ---------- ----------- -------------------------- * 06/03/09 2037 D. Hladky Initial release + * Apr 4, 2013 1846 bkowal Added an index on refTime and forecastTime * * * @@ -77,6 +79,16 @@ import com.raytheon.uf.common.serialization.annotations.DynamicSerializeElement; */ @Entity @Table(name = "cwat", uniqueConstraints = { @UniqueConstraint(columnNames = { "dataURI" }) }) +/* + * Both refTime and forecastTime are included in the refTimeIndex since + * forecastTime is unlikely to be used. + */ +@org.hibernate.annotations.Table( + appliesTo = "cwat", + indexes = { + @Index(name = "cwat_refTimeIndex", columnNames = { "refTime", "forecastTime" } ) + } +) @XmlRootElement @XmlAccessorType(XmlAccessType.NONE) @DynamicSerialize diff --git a/edexOsgi/com.raytheon.uf.common.dataplugin.ffmp/src/com/raytheon/uf/common/dataplugin/ffmp/FFMPRecord.java b/edexOsgi/com.raytheon.uf.common.dataplugin.ffmp/src/com/raytheon/uf/common/dataplugin/ffmp/FFMPRecord.java index 31a1d2f49c..594e217127 100644 --- a/edexOsgi/com.raytheon.uf.common.dataplugin.ffmp/src/com/raytheon/uf/common/dataplugin/ffmp/FFMPRecord.java +++ b/edexOsgi/com.raytheon.uf.common.dataplugin.ffmp/src/com/raytheon/uf/common/dataplugin/ffmp/FFMPRecord.java @@ -37,6 +37,7 @@ import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; +import org.hibernate.annotations.Index; import com.raytheon.uf.common.dataplugin.IDecoderGettable; import com.raytheon.uf.common.dataplugin.annotations.DataURI; @@ -69,6 +70,7 @@ import com.raytheon.uf.common.status.UFStatus.Priority; * 06/03/09 2521 D. Hladky Initial release * 01/27/13 1478 D. Hladky OUN memory help * Feb 28, 2013 1729 dhladky Supressed un-necessary debug loggers + * Apr 4, 2013 1846 bkowal Added an index on refTime and forecastTime * * * @@ -77,6 +79,17 @@ import com.raytheon.uf.common.status.UFStatus.Priority; */ @Entity @Table(name = "ffmp", uniqueConstraints = { @UniqueConstraint(columnNames = { "dataURI" }) }) +/* + * Both refTime and forecastTime are included in the refTimeIndex since + * forecastTime is unlikely to be used. + */ +@org.hibernate.annotations.Table( + appliesTo = "ffmp", + indexes = { + @Index(name = "ffmp_refTimeIndex", columnNames = { "refTime", "forecastTime" } ) + } +) + @XmlRootElement @XmlAccessorType(XmlAccessType.NONE) @DynamicSerialize diff --git a/edexOsgi/com.raytheon.uf.common.dataplugin.fog/src/com/raytheon/uf/common/dataplugin/fog/FogRecord.java b/edexOsgi/com.raytheon.uf.common.dataplugin.fog/src/com/raytheon/uf/common/dataplugin/fog/FogRecord.java index 7b361e5aae..412f2af6fa 100644 --- a/edexOsgi/com.raytheon.uf.common.dataplugin.fog/src/com/raytheon/uf/common/dataplugin/fog/FogRecord.java +++ b/edexOsgi/com.raytheon.uf.common.dataplugin.fog/src/com/raytheon/uf/common/dataplugin/fog/FogRecord.java @@ -35,6 +35,7 @@ import org.geotools.coverage.grid.GeneralGridEnvelope; import org.geotools.coverage.grid.GridGeometry2D; import org.geotools.geometry.GeneralEnvelope; import org.opengis.referencing.crs.ProjectedCRS; +import org.hibernate.annotations.Index; import com.raytheon.uf.common.dataplugin.IDecoderGettable; import com.raytheon.uf.common.dataplugin.annotations.DataURI; @@ -58,6 +59,7 @@ import com.raytheon.uf.common.serialization.annotations.DynamicSerializeElement; * Date Ticket# Engineer Description * ------------ ---------- ----------- -------------------------- * 12/12/09 D. Hladky Initial release + * Apr 4, 2013 1846 bkowal Added an index on refTime and forecastTime * * * @@ -66,6 +68,16 @@ import com.raytheon.uf.common.serialization.annotations.DynamicSerializeElement; */ @Entity @Table(name = "fog", uniqueConstraints = { @UniqueConstraint(columnNames = { "dataURI" }) }) +/* + * Both refTime and forecastTime are included in the refTimeIndex since + * forecastTime is unlikely to be used. + */ +@org.hibernate.annotations.Table( + appliesTo = "fog", + indexes = { + @Index(name = "fog_refTimeIndex", columnNames = { "refTime", "forecastTime" } ) + } +) @XmlRootElement @XmlAccessorType(XmlAccessType.NONE) @DynamicSerialize diff --git a/edexOsgi/com.raytheon.uf.common.dataplugin.fssobs/META-INF/MANIFEST.MF b/edexOsgi/com.raytheon.uf.common.dataplugin.fssobs/META-INF/MANIFEST.MF index 2cd081cd5c..2b14e1822b 100644 --- a/edexOsgi/com.raytheon.uf.common.dataplugin.fssobs/META-INF/MANIFEST.MF +++ b/edexOsgi/com.raytheon.uf.common.dataplugin.fssobs/META-INF/MANIFEST.MF @@ -11,7 +11,8 @@ Require-Bundle: com.raytheon.uf.common.dataplugin, com.raytheon.uf.common.time, com.raytheon.uf.common.serialization, com.raytheon.uf.common.status, - com.raytheon.uf.common.datastorage + com.raytheon.uf.common.datastorage, + org.hibernate;bundle-version="1.0.0" Import-Package: com.raytheon.uf.common.geospatial, com.raytheon.uf.common.monitor.data, com.raytheon.uf.common.pointdata, diff --git a/edexOsgi/com.raytheon.uf.common.dataplugin.fssobs/src/com/raytheon/uf/common/dataplugin/fssobs/FSSObsRecord.java b/edexOsgi/com.raytheon.uf.common.dataplugin.fssobs/src/com/raytheon/uf/common/dataplugin/fssobs/FSSObsRecord.java index 0525208a17..2a3d297364 100644 --- a/edexOsgi/com.raytheon.uf.common.dataplugin.fssobs/src/com/raytheon/uf/common/dataplugin/fssobs/FSSObsRecord.java +++ b/edexOsgi/com.raytheon.uf.common.dataplugin.fssobs/src/com/raytheon/uf/common/dataplugin/fssobs/FSSObsRecord.java @@ -23,6 +23,7 @@ import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; +import org.hibernate.annotations.Index; import com.raytheon.uf.common.dataplugin.IDecoderGettable; import com.raytheon.uf.common.dataplugin.annotations.DataURI; @@ -38,6 +39,16 @@ import com.raytheon.uf.common.serialization.annotations.DynamicSerializeElement; @Entity @Table(name = "fssobs", uniqueConstraints = { @UniqueConstraint(columnNames = { "dataURI" }) }) +/* + * Both refTime and forecastTime are included in the refTimeIndex since + * forecastTime is unlikely to be used. + */ +@org.hibernate.annotations.Table( + appliesTo = "fssobs", + indexes = { + @Index(name = "fssobs_refTimeIndex", columnNames = { "refTime", "forecastTime" } ) + } +) @XmlRootElement @XmlAccessorType(XmlAccessType.NONE) @DynamicSerialize diff --git a/edexOsgi/com.raytheon.uf.common.dataplugin.gfe/src/com/raytheon/uf/common/dataplugin/gfe/db/objects/GFERecord.java b/edexOsgi/com.raytheon.uf.common.dataplugin.gfe/src/com/raytheon/uf/common/dataplugin/gfe/db/objects/GFERecord.java index 61c13dad69..5c88a483f7 100644 --- a/edexOsgi/com.raytheon.uf.common.dataplugin.gfe/src/com/raytheon/uf/common/dataplugin/gfe/db/objects/GFERecord.java +++ b/edexOsgi/com.raytheon.uf.common.dataplugin.gfe/src/com/raytheon/uf/common/dataplugin/gfe/db/objects/GFERecord.java @@ -72,7 +72,8 @@ import com.raytheon.uf.common.time.TimeRange; * 20070914 379 jkorman Added populateDataStore() and * getPersistenceTime() from new IPersistable * 20071129 472 jkorman Added IDecoderGettable interface. - * 06/17/08 #940 bphillip Implemented GFE Locking + * 06/17/08 #940 bphillip Implemented GFE Locking + * Apr 4, 2013 1846 bkowal Added an index on refTime and forecastTime * * * @author randerso @@ -83,6 +84,16 @@ import com.raytheon.uf.common.time.TimeRange; */ @Entity @Table(name = "gfe", uniqueConstraints = { @UniqueConstraint(columnNames = { "dataURI" }) }) +/* + * Both refTime and forecastTime are included in the refTimeIndex since + * forecastTime is unlikely to be used. + */ +@org.hibernate.annotations.Table( + appliesTo = "gfe", + indexes = { + @Index(name = "gfe_refTimeIndex", columnNames = { "refTime", "forecastTime" } ) + } +) @Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL) @XmlRootElement @XmlAccessorType(XmlAccessType.NONE) diff --git a/edexOsgi/com.raytheon.uf.common.dataplugin.gfe/src/com/raytheon/uf/common/dataplugin/gfe/db/objects/GridParmInfo.java b/edexOsgi/com.raytheon.uf.common.dataplugin.gfe/src/com/raytheon/uf/common/dataplugin/gfe/db/objects/GridParmInfo.java index e445e4b373..7d9410afbf 100644 --- a/edexOsgi/com.raytheon.uf.common.dataplugin.gfe/src/com/raytheon/uf/common/dataplugin/gfe/db/objects/GridParmInfo.java +++ b/edexOsgi/com.raytheon.uf.common.dataplugin.gfe/src/com/raytheon/uf/common/dataplugin/gfe/db/objects/GridParmInfo.java @@ -51,6 +51,7 @@ import com.raytheon.uf.common.status.UFStatus.Priority; * 02/27/2008 879 rbell Added constructors and equals(Object) * 03/20/2013 #1774 randerso Removed unnecessary XML annotations, * added isValid method to match A1 + * 04/02/2013 #1774 randerso Improved error message in validCheck * * * @@ -275,6 +276,11 @@ public class GridParmInfo implements Cloneable, ISerializableObject { sb.append(". Must be betwwen -2 and 5\n"); } + if (sb.length() > 0) { + sb.append("For parmID: "); + sb.append(parmID); + } + this.errorMessage = sb.toString(); if (errorMessage.isEmpty()) { return true; diff --git a/edexOsgi/com.raytheon.uf.common.dataplugin.gfe/src/com/raytheon/uf/common/dataplugin/gfe/exception/GfeException.java b/edexOsgi/com.raytheon.uf.common.dataplugin.gfe/src/com/raytheon/uf/common/dataplugin/gfe/exception/GfeException.java index 2b66403a78..d5652ac523 100644 --- a/edexOsgi/com.raytheon.uf.common.dataplugin.gfe/src/com/raytheon/uf/common/dataplugin/gfe/exception/GfeException.java +++ b/edexOsgi/com.raytheon.uf.common.dataplugin.gfe/src/com/raytheon/uf/common/dataplugin/gfe/exception/GfeException.java @@ -20,7 +20,6 @@ package com.raytheon.uf.common.dataplugin.gfe.exception; - /** * Base GFE Exception class * @@ -29,6 +28,8 @@ package com.raytheon.uf.common.dataplugin.gfe.exception; * Date Ticket# Engineer Description * ------------ ---------- ----------- -------------------------- * 04/08/08 #875 bphillip Initial Creation + * 03/28/13 #1837 dgilling Implement missing constructors from + * super-class. * * * @@ -40,7 +41,22 @@ public class GfeException extends Exception { private static final long serialVersionUID = 1L; /** - * @param aCause + * Constructs a new runtime exception with null as its detail + * message. The cause is not initialized, and may subsequently be + * initialized by a call to {@link #initCause}. + */ + public GfeException() { + super(); + } + + /** + * Constructs a new exception with the specified detail message. The cause + * is not initialized, and may subsequently be initialized by a call to + * {@link #initCause}. + * + * @param message + * the detail message. The detail message is saved for later + * retrieval by the {@link #getMessage()} method. */ public GfeException(String aCause) { super(aCause); @@ -51,9 +67,32 @@ public class GfeException extends Exception { * exception chaining to preserve state. * * @param aCause + * the detail message (which is saved for later retrieval by the + * {@link #getMessage()} method). * @param anException + * the cause (which is saved for later retrieval by the + * {@link #getCause()} method). (A null value is + * permitted, and indicates that the cause is nonexistent or + * unknown.) */ public GfeException(String aCause, Throwable anException) { super(aCause, anException); } + + /** + * Constructs a new exception with the specified cause and a detail message + * of (cause==null ? null : cause.toString()) (which typically + * contains the class and detail message of cause). This + * constructor is useful for exceptions that are little more than wrappers + * for other throwables. + * + * @param cause + * the cause (which is saved for later retrieval by the + * {@link #getCause()} method). (A null value is + * permitted, and indicates that the cause is nonexistent or + * unknown.) + */ + public GfeException(Throwable anException) { + super(anException); + } } diff --git a/edexOsgi/com.raytheon.uf.common.dataplugin.goessounding/META-INF/MANIFEST.MF b/edexOsgi/com.raytheon.uf.common.dataplugin.goessounding/META-INF/MANIFEST.MF index 0a82d4394c..0d99a7ae3f 100644 --- a/edexOsgi/com.raytheon.uf.common.dataplugin.goessounding/META-INF/MANIFEST.MF +++ b/edexOsgi/com.raytheon.uf.common.dataplugin.goessounding/META-INF/MANIFEST.MF @@ -19,7 +19,7 @@ Import-Package: com.raytheon.uf.common.dataplugin, com.vividsolutions.jts.geom, javax.measure.quantity, javax.measure.unit, - javax.persistence, - org.hibernate.annotations + javax.persistence Export-Package: com.raytheon.uf.common.dataplugin.goessounding -Require-Bundle: com.raytheon.uf.common.serialization;bundle-version="1.12.1174" +Require-Bundle: com.raytheon.uf.common.serialization;bundle-version="1.12.1174", + org.hibernate;bundle-version="1.0.0" diff --git a/edexOsgi/com.raytheon.uf.common.dataplugin.goessounding/src/com/raytheon/uf/common/dataplugin/goessounding/GOESSounding.java b/edexOsgi/com.raytheon.uf.common.dataplugin.goessounding/src/com/raytheon/uf/common/dataplugin/goessounding/GOESSounding.java index db0a09e34b..a58f50db7e 100644 --- a/edexOsgi/com.raytheon.uf.common.dataplugin.goessounding/src/com/raytheon/uf/common/dataplugin/goessounding/GOESSounding.java +++ b/edexOsgi/com.raytheon.uf.common.dataplugin.goessounding/src/com/raytheon/uf/common/dataplugin/goessounding/GOESSounding.java @@ -41,6 +41,7 @@ import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; import org.hibernate.annotations.Type; +import org.hibernate.annotations.Index; import com.raytheon.uf.common.dataplugin.IDecoderGettable; import com.raytheon.uf.common.dataplugin.annotations.DataURI; @@ -65,6 +66,7 @@ import com.vividsolutions.jts.geom.Geometry; * Date Ticket# Engineer Description * ------------ ---------- ----------- -------------------------- * 20080414 1077 jkorman Initial implementation. + * Apr 4, 2013 1846 bkowal Added an index on refTime and forecastTime * * * @@ -73,6 +75,16 @@ import com.vividsolutions.jts.geom.Geometry; */ @Entity @Table(name = "goessounding", uniqueConstraints = { @UniqueConstraint(columnNames = { "dataURI" }) }) +/* + * Both refTime and forecastTime are included in the refTimeIndex since + * forecastTime is unlikely to be used. + */ +@org.hibernate.annotations.Table( + appliesTo = "goessounding", + indexes = { + @Index(name = "goessounding_refTimeIndex", columnNames = { "refTime", "forecastTime" } ) + } +) @DynamicSerialize @XmlRootElement @XmlAccessorType(XmlAccessType.NONE) diff --git a/edexOsgi/com.raytheon.uf.common.dataplugin.grib/src/com/raytheon/uf/common/dataplugin/grib/GribRecord.java b/edexOsgi/com.raytheon.uf.common.dataplugin.grib/src/com/raytheon/uf/common/dataplugin/grib/GribRecord.java index 2ca402a1ca..60919c1c8b 100644 --- a/edexOsgi/com.raytheon.uf.common.dataplugin.grib/src/com/raytheon/uf/common/dataplugin/grib/GribRecord.java +++ b/edexOsgi/com.raytheon.uf.common.dataplugin.grib/src/com/raytheon/uf/common/dataplugin/grib/GribRecord.java @@ -38,6 +38,7 @@ import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; +import org.hibernate.annotations.Index; import com.raytheon.uf.common.dataplugin.IDecoderGettable; import com.raytheon.uf.common.dataplugin.IPrecomputedRange; @@ -60,6 +61,7 @@ import com.raytheon.uf.common.serialization.annotations.DynamicSerializeElement; * Date Ticket# Engineer Description * ------------ ---------- ----------- -------------------------- * 4/7/09 1994 bphillip Initial Creation + * Apr 4, 2013 1846 bkowal Added an index on refTime and forecastTime * * * @@ -68,6 +70,16 @@ import com.raytheon.uf.common.serialization.annotations.DynamicSerializeElement; */ @Entity @Table(name = "grib", uniqueConstraints = { @UniqueConstraint(columnNames = { "dataURI" }) }) +/* + * Both refTime and forecastTime are included in the refTimeIndex since + * forecastTime is unlikely to be used. + */ +@org.hibernate.annotations.Table( + appliesTo = "grib", + indexes = { + @Index(name = "grib_refTimeIndex", columnNames = { "refTime", "forecastTime" } ) + } +) @XmlRootElement @XmlAccessorType(XmlAccessType.NONE) @DynamicSerialize diff --git a/edexOsgi/com.raytheon.uf.common.dataplugin.grid/src/com/raytheon/uf/common/dataplugin/grid/GridRecord.java b/edexOsgi/com.raytheon.uf.common.dataplugin.grid/src/com/raytheon/uf/common/dataplugin/grid/GridRecord.java index df57ea4455..22e4d7ea05 100644 --- a/edexOsgi/com.raytheon.uf.common.dataplugin.grid/src/com/raytheon/uf/common/dataplugin/grid/GridRecord.java +++ b/edexOsgi/com.raytheon.uf.common.dataplugin.grid/src/com/raytheon/uf/common/dataplugin/grid/GridRecord.java @@ -28,6 +28,7 @@ import javax.persistence.PrimaryKeyJoinColumn; import javax.persistence.Table; import javax.persistence.Transient; import javax.persistence.UniqueConstraint; +import org.hibernate.annotations.Index; import com.raytheon.uf.common.dataplugin.IDecoderGettable; import com.raytheon.uf.common.dataplugin.annotations.DataURI; @@ -55,6 +56,7 @@ import com.raytheon.uf.common.serialization.annotations.DynamicSerializeElement; * Date Ticket# Engineer Description * ------------ ---------- ----------- -------------------------- * May 21, 2012 bsteffen Initial creation + * Apr 4, 2013 1846 bkowal Added an index on refTime and forecastTime * * * @@ -63,6 +65,16 @@ import com.raytheon.uf.common.serialization.annotations.DynamicSerializeElement; */ @Entity @Table(name = "grid", uniqueConstraints = { @UniqueConstraint(columnNames = { "dataURI" }) }) +/* + * Both refTime and forecastTime are included in the refTimeIndex since + * forecastTime is unlikely to be used. + */ +@org.hibernate.annotations.Table( + appliesTo = "grid", + indexes = { + @Index(name = "grid_refTimeIndex", columnNames = { "refTime", "forecastTime" } ) + } +) @DynamicSerialize public class GridRecord extends PersistablePluginDataObject implements ISpatialEnabled { diff --git a/edexOsgi/com.raytheon.uf.common.dataplugin.ldadhydro/META-INF/MANIFEST.MF b/edexOsgi/com.raytheon.uf.common.dataplugin.ldadhydro/META-INF/MANIFEST.MF index 17bfbd62c2..6dac136c48 100644 --- a/edexOsgi/com.raytheon.uf.common.dataplugin.ldadhydro/META-INF/MANIFEST.MF +++ b/edexOsgi/com.raytheon.uf.common.dataplugin.ldadhydro/META-INF/MANIFEST.MF @@ -19,3 +19,4 @@ Import-Package: com.raytheon.uf.common.dataplugin, javax.measure.unit, javax.persistence Export-Package: com.raytheon.uf.common.dataplugin.ldadhydro +Require-Bundle: org.hibernate;bundle-version="1.0.0" diff --git a/edexOsgi/com.raytheon.uf.common.dataplugin.ldadhydro/src/com/raytheon/uf/common/dataplugin/ldadhydro/HydroLdadRecord.java b/edexOsgi/com.raytheon.uf.common.dataplugin.ldadhydro/src/com/raytheon/uf/common/dataplugin/ldadhydro/HydroLdadRecord.java index 6850172a02..c971fc1f45 100644 --- a/edexOsgi/com.raytheon.uf.common.dataplugin.ldadhydro/src/com/raytheon/uf/common/dataplugin/ldadhydro/HydroLdadRecord.java +++ b/edexOsgi/com.raytheon.uf.common.dataplugin.ldadhydro/src/com/raytheon/uf/common/dataplugin/ldadhydro/HydroLdadRecord.java @@ -42,6 +42,7 @@ import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; +import org.hibernate.annotations.Index; import com.raytheon.uf.common.dataplugin.IDecoderGettable; import com.raytheon.uf.common.dataplugin.annotations.DataURI; @@ -65,6 +66,7 @@ import com.vividsolutions.jts.geom.Geometry; * ate Ticket# Engineer Description * ----------- ---------- ----------- -------------------------- * 9/30/09 vkorolev Initial creation + * Apr 4, 2013 1846 bkowal Added an index on refTime and forecastTime * * * @author vkorolev @@ -73,6 +75,16 @@ import com.vividsolutions.jts.geom.Geometry; @Entity @Table(name = "ldadhydro", uniqueConstraints = { @UniqueConstraint(columnNames = { "dataURI" }) }) +/* + * Both refTime and forecastTime are included in the refTimeIndex since + * forecastTime is unlikely to be used. + */ +@org.hibernate.annotations.Table( + appliesTo = "ldadhydro", + indexes = { + @Index(name = "ldadhydro_refTimeIndex", columnNames = { "refTime", "forecastTime" } ) + } +) @XmlRootElement @XmlAccessorType(XmlAccessType.NONE) @DynamicSerialize diff --git a/edexOsgi/com.raytheon.uf.common.dataplugin.ldadmesonet/src/com/raytheon/uf/common/dataplugin/ldadmesonet/MesonetLdadRecord.java b/edexOsgi/com.raytheon.uf.common.dataplugin.ldadmesonet/src/com/raytheon/uf/common/dataplugin/ldadmesonet/MesonetLdadRecord.java index 82cce2c049..c219a5c943 100644 --- a/edexOsgi/com.raytheon.uf.common.dataplugin.ldadmesonet/src/com/raytheon/uf/common/dataplugin/ldadmesonet/MesonetLdadRecord.java +++ b/edexOsgi/com.raytheon.uf.common.dataplugin.ldadmesonet/src/com/raytheon/uf/common/dataplugin/ldadmesonet/MesonetLdadRecord.java @@ -43,6 +43,7 @@ import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; +import org.hibernate.annotations.Index; import com.raytheon.uf.common.dataplugin.IDecoderGettable; import com.raytheon.uf.common.dataplugin.annotations.DataURI; @@ -66,6 +67,7 @@ import com.vividsolutions.jts.geom.Geometry; * ate Ticket# Engineer Description * ----------- ---------- ----------- -------------------------- * 9/4/09 vkorolev Initial creation + * Apr 4, 2013 1846 bkowal Added an index on refTime and forecastTime * * * @author vkorolev @@ -74,6 +76,16 @@ import com.vividsolutions.jts.geom.Geometry; @Entity @Table(name = "ldadmesonet", uniqueConstraints = { @UniqueConstraint(columnNames = { "dataURI" }) }) +/* + * Both refTime and forecastTime are included in the refTimeIndex since + * forecastTime is unlikely to be used. + */ +@org.hibernate.annotations.Table( + appliesTo = "ldadmesonet", + indexes = { + @Index(name = "ldadmesonet_refTimeIndex", columnNames = { "refTime", "forecastTime" } ) + } +) @XmlRootElement @XmlAccessorType(XmlAccessType.NONE) @DynamicSerialize diff --git a/edexOsgi/com.raytheon.uf.common.dataplugin.lsr/src/com/raytheon/uf/common/dataplugin/lsr/LocalStormReport.java b/edexOsgi/com.raytheon.uf.common.dataplugin.lsr/src/com/raytheon/uf/common/dataplugin/lsr/LocalStormReport.java index 7720c361c6..0ba573f822 100644 --- a/edexOsgi/com.raytheon.uf.common.dataplugin.lsr/src/com/raytheon/uf/common/dataplugin/lsr/LocalStormReport.java +++ b/edexOsgi/com.raytheon.uf.common.dataplugin.lsr/src/com/raytheon/uf/common/dataplugin/lsr/LocalStormReport.java @@ -41,6 +41,7 @@ import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; +import org.hibernate.annotations.Index; import com.raytheon.uf.common.dataplugin.IDecoderGettable; import com.raytheon.uf.common.dataplugin.annotations.DataURI; @@ -63,6 +64,7 @@ import com.vividsolutions.jts.geom.Geometry; * Date Ticket# Engineer Description * ------------ ---------- ----------- -------------------------- * Oct 1, 2009 jkorman Initial creation + * Apr 4, 2013 1846 bkowal Added an index on refTime and forecastTime * * * @@ -71,6 +73,16 @@ import com.vividsolutions.jts.geom.Geometry; */ @Entity @Table(name = "lsr", uniqueConstraints = { @UniqueConstraint(columnNames = { "dataURI" }) }) +/* + * Both refTime and forecastTime are included in the refTimeIndex since + * forecastTime is unlikely to be used. + */ +@org.hibernate.annotations.Table( + appliesTo = "lsr", + indexes = { + @Index(name = "lsr_refTimeIndex", columnNames = { "refTime", "forecastTime" } ) + } +) @XmlRootElement @XmlAccessorType(XmlAccessType.NONE) @DynamicSerialize diff --git a/edexOsgi/com.raytheon.uf.common.dataplugin.npp.viirs/src/com/raytheon/uf/common/dataplugin/npp/viirs/VIIRSDataRecord.java b/edexOsgi/com.raytheon.uf.common.dataplugin.npp.viirs/src/com/raytheon/uf/common/dataplugin/npp/viirs/VIIRSDataRecord.java index 7899ac18e9..efeafe9f36 100644 --- a/edexOsgi/com.raytheon.uf.common.dataplugin.npp.viirs/src/com/raytheon/uf/common/dataplugin/npp/viirs/VIIRSDataRecord.java +++ b/edexOsgi/com.raytheon.uf.common.dataplugin.npp.viirs/src/com/raytheon/uf/common/dataplugin/npp/viirs/VIIRSDataRecord.java @@ -29,6 +29,7 @@ import javax.persistence.ManyToOne; import javax.persistence.PrimaryKeyJoinColumn; import javax.persistence.Table; import javax.persistence.UniqueConstraint; +import org.hibernate.annotations.Index; import com.raytheon.uf.common.dataplugin.IDecoderGettable; import com.raytheon.uf.common.dataplugin.annotations.DataURI; @@ -48,6 +49,7 @@ import com.raytheon.uf.common.serialization.annotations.DynamicSerializeElement; * Date Ticket# Engineer Description * ------------ ---------- ----------- -------------------------- * Nov 30, 2011 mschenke Initial creation + * Apr 4, 2013 1846 bkowal Added an index on refTime and forecastTime * * * @@ -56,6 +58,16 @@ import com.raytheon.uf.common.serialization.annotations.DynamicSerializeElement; */ @Entity @Table(name = "viirs", uniqueConstraints = { @UniqueConstraint(columnNames = { "dataURI" }) }) +/* + * Both refTime and forecastTime are included in the refTimeIndex since + * forecastTime is unlikely to be used. + */ +@org.hibernate.annotations.Table( + appliesTo = "viirs", + indexes = { + @Index(name = "viirs_refTimeIndex", columnNames = { "refTime", "forecastTime" } ) + } +) @DynamicSerialize public class VIIRSDataRecord extends PersistablePluginDataObject implements ISpatialEnabled { diff --git a/edexOsgi/com.raytheon.uf.common.dataplugin.npp.viirs/src/com/raytheon/uf/common/dataplugin/npp/viirs/VIIRSSpatialCoverage.java b/edexOsgi/com.raytheon.uf.common.dataplugin.npp.viirs/src/com/raytheon/uf/common/dataplugin/npp/viirs/VIIRSSpatialCoverage.java index d8f0f032b4..05de80247d 100644 --- a/edexOsgi/com.raytheon.uf.common.dataplugin.npp.viirs/src/com/raytheon/uf/common/dataplugin/npp/viirs/VIIRSSpatialCoverage.java +++ b/edexOsgi/com.raytheon.uf.common.dataplugin.npp.viirs/src/com/raytheon/uf/common/dataplugin/npp/viirs/VIIRSSpatialCoverage.java @@ -33,6 +33,7 @@ import org.geotools.coverage.grid.GridGeometry2D; import org.geotools.geometry.GeneralEnvelope; import org.opengis.referencing.FactoryException; import org.opengis.referencing.crs.CoordinateReferenceSystem; +import org.hibernate.annotations.Index; import com.raytheon.uf.common.dataplugin.npp.viirs.projection.VIIRSMapProjectionFactory; import com.raytheon.uf.common.dataplugin.persist.PersistableDataObject; @@ -53,6 +54,7 @@ import com.vividsolutions.jts.geom.Geometry; * ------------ ---------- ----------- -------------------------- * Dec 1, 2011 mschenke Initial creation * Feb 21, 2012 #30 mschenke Removed unused envelopePercentage field + * Apr 4, 2013 1846 bkowal Added an index on refTime and forecastTime * * * @@ -61,6 +63,16 @@ import com.vividsolutions.jts.geom.Geometry; */ @Entity @Table(name = "viirs_spatial") +/* + * Both refTime and forecastTime are included in the refTimeIndex since + * forecastTime is unlikely to be used. + */ +@org.hibernate.annotations.Table( + appliesTo = "viirs_spatial", + indexes = { + @Index(name = "viirs_spatial_refTimeIndex", columnNames = { "refTime", "forecastTime" } ) + } +) @DynamicSerialize public class VIIRSSpatialCoverage extends PersistableDataObject implements ISpatialObject { diff --git a/edexOsgi/com.raytheon.uf.common.dataplugin.obs/META-INF/MANIFEST.MF b/edexOsgi/com.raytheon.uf.common.dataplugin.obs/META-INF/MANIFEST.MF index 6d848ff15d..575a6bf386 100644 --- a/edexOsgi/com.raytheon.uf.common.dataplugin.obs/META-INF/MANIFEST.MF +++ b/edexOsgi/com.raytheon.uf.common.dataplugin.obs/META-INF/MANIFEST.MF @@ -21,4 +21,5 @@ Import-Package: com.raytheon.uf.common.dataplugin, javax.measure.quantity, javax.measure.unit, javax.persistence -Require-Bundle: com.raytheon.uf.common.serialization;bundle-version="1.12.1174" +Require-Bundle: com.raytheon.uf.common.serialization;bundle-version="1.12.1174", + org.hibernate;bundle-version="1.0.0" diff --git a/edexOsgi/com.raytheon.uf.common.dataplugin.obs/src/com/raytheon/uf/common/dataplugin/obs/metar/MetarRecord.java b/edexOsgi/com.raytheon.uf.common.dataplugin.obs/src/com/raytheon/uf/common/dataplugin/obs/metar/MetarRecord.java index a4c8630e2e..a603ddef88 100644 --- a/edexOsgi/com.raytheon.uf.common.dataplugin.obs/src/com/raytheon/uf/common/dataplugin/obs/metar/MetarRecord.java +++ b/edexOsgi/com.raytheon.uf.common.dataplugin.obs/src/com/raytheon/uf/common/dataplugin/obs/metar/MetarRecord.java @@ -49,6 +49,7 @@ import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; +import org.hibernate.annotations.Index; import com.raytheon.uf.common.dataplugin.IDecoderGettable; import com.raytheon.uf.common.dataplugin.annotations.DataURI; @@ -83,6 +84,7 @@ import com.raytheon.uf.common.time.util.TimeUtil; * Added @DynamicSerializeElement to location. * 20090528 2225 jsanchez Implemented tempFromTenths and dewPointFromTenths. * 20090629 2538 jsanchez Made the sort public. + * Apr 4, 2013 1846 bkowal Added an index on refTime and forecastTime * * * @author bphillip @@ -90,6 +92,16 @@ import com.raytheon.uf.common.time.util.TimeUtil; */ @Entity @Table(name = "obs", uniqueConstraints = { @UniqueConstraint(columnNames = { "dataURI" }) }) +/* + * Both refTime and forecastTime are included in the refTimeIndex since + * forecastTime is unlikely to be used. + */ +@org.hibernate.annotations.Table( + appliesTo = "obs", + indexes = { + @Index(name = "obs_refTimeIndex", columnNames = { "refTime", "forecastTime" } ) + } +) @XmlRootElement @XmlAccessorType(XmlAccessType.NONE) @DynamicSerialize diff --git a/edexOsgi/com.raytheon.uf.common.dataplugin.pirep/META-INF/MANIFEST.MF b/edexOsgi/com.raytheon.uf.common.dataplugin.pirep/META-INF/MANIFEST.MF index fd2d6ea9de..e52f616128 100644 --- a/edexOsgi/com.raytheon.uf.common.dataplugin.pirep/META-INF/MANIFEST.MF +++ b/edexOsgi/com.raytheon.uf.common.dataplugin.pirep/META-INF/MANIFEST.MF @@ -19,5 +19,6 @@ Import-Package: com.raytheon.uf.common.dataplugin, javax.measure.unit, javax.persistence Require-Bundle: com.raytheon.uf.common.serialization;bundle-version="1.12.1174", - com.raytheon.uf.common.dataplugin + com.raytheon.uf.common.dataplugin, + org.hibernate;bundle-version="1.0.0" Export-Package: com.raytheon.uf.common.dataplugin.pirep diff --git a/edexOsgi/com.raytheon.uf.common.dataplugin.pirep/src/com/raytheon/uf/common/dataplugin/pirep/PirepRecord.java b/edexOsgi/com.raytheon.uf.common.dataplugin.pirep/src/com/raytheon/uf/common/dataplugin/pirep/PirepRecord.java index 6a0d595114..39c055ee89 100644 --- a/edexOsgi/com.raytheon.uf.common.dataplugin.pirep/src/com/raytheon/uf/common/dataplugin/pirep/PirepRecord.java +++ b/edexOsgi/com.raytheon.uf.common.dataplugin.pirep/src/com/raytheon/uf/common/dataplugin/pirep/PirepRecord.java @@ -47,6 +47,7 @@ import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; +import org.hibernate.annotations.Index; import com.raytheon.uf.common.dataplugin.IDecoderGettable; import com.raytheon.uf.common.dataplugin.PluginDataObject; @@ -70,6 +71,7 @@ import com.vividsolutions.jts.geom.Geometry; * 20090408 952 jsanchez Updated getValue and getStrings methods. * Added getMessageData method. * 20090521 2338 jsanchez Changed the unit of the alititude. + * Apr 4, 2013 1846 bkowal Added an index on refTime and forecastTime * ====================================== * AWIPS2 DR Work * 08/09/2012 1011 jkorman Added separate max icing level as well @@ -83,6 +85,16 @@ import com.vividsolutions.jts.geom.Geometry; */ @Entity @Table(name = "pirep", uniqueConstraints = { @UniqueConstraint(columnNames = { "dataURI" }) }) +/* + * Both refTime and forecastTime are included in the refTimeIndex since + * forecastTime is unlikely to be used. + */ +@org.hibernate.annotations.Table( + appliesTo = "pirep", + indexes = { + @Index(name = "pirep_refTimeIndex", columnNames = { "refTime", "forecastTime" } ) + } +) @XmlRootElement @XmlAccessorType(XmlAccessType.NONE) @DynamicSerialize diff --git a/edexOsgi/com.raytheon.uf.common.dataplugin.poessounding/META-INF/MANIFEST.MF b/edexOsgi/com.raytheon.uf.common.dataplugin.poessounding/META-INF/MANIFEST.MF index a3bd37383c..5a2340f928 100644 --- a/edexOsgi/com.raytheon.uf.common.dataplugin.poessounding/META-INF/MANIFEST.MF +++ b/edexOsgi/com.raytheon.uf.common.dataplugin.poessounding/META-INF/MANIFEST.MF @@ -20,4 +20,5 @@ Import-Package: com.raytheon.uf.common.dataplugin, javax.measure.unit, javax.persistence Export-Package: com.raytheon.uf.common.dataplugin.poessounding -Require-Bundle: com.raytheon.uf.common.serialization;bundle-version="1.12.1174" +Require-Bundle: com.raytheon.uf.common.serialization;bundle-version="1.12.1174", + org.hibernate;bundle-version="1.0.0" diff --git a/edexOsgi/com.raytheon.uf.common.dataplugin.poessounding/src/com/raytheon/uf/common/dataplugin/poessounding/POESSounding.java b/edexOsgi/com.raytheon.uf.common.dataplugin.poessounding/src/com/raytheon/uf/common/dataplugin/poessounding/POESSounding.java index 8cfc2f9c11..75a09c86bc 100644 --- a/edexOsgi/com.raytheon.uf.common.dataplugin.poessounding/src/com/raytheon/uf/common/dataplugin/poessounding/POESSounding.java +++ b/edexOsgi/com.raytheon.uf.common.dataplugin.poessounding/src/com/raytheon/uf/common/dataplugin/poessounding/POESSounding.java @@ -37,6 +37,7 @@ import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; +import org.hibernate.annotations.Index; import com.raytheon.uf.common.dataplugin.IDecoderGettable; import com.raytheon.uf.common.dataplugin.annotations.DataURI; @@ -59,6 +60,7 @@ import com.vividsolutions.jts.geom.Geometry; * Date Ticket# Engineer Description * ------------ ---------- ----------- -------------------------- * 20080303 1026 jkorman Initial implementation. + * Apr 4, 2013 1846 bkowal Added an index on refTime and forecastTime * * * @@ -67,6 +69,16 @@ import com.vividsolutions.jts.geom.Geometry; */ @Entity @Table(name = "poessounding", uniqueConstraints = { @UniqueConstraint(columnNames = { "dataURI" }) }) +/* + * Both refTime and forecastTime are included in the refTimeIndex since + * forecastTime is unlikely to be used. + */ +@org.hibernate.annotations.Table( + appliesTo = "poessounding", + indexes = { + @Index(name = "poessounding_refTimeIndex", columnNames = { "refTime", "forecastTime" } ) + } +) @DynamicSerialize @XmlAccessorType(XmlAccessType.NONE) @XmlRootElement diff --git a/edexOsgi/com.raytheon.uf.common.dataplugin.preciprate/src/com/raytheon/uf/common/dataplugin/preciprate/PrecipRateRecord.java b/edexOsgi/com.raytheon.uf.common.dataplugin.preciprate/src/com/raytheon/uf/common/dataplugin/preciprate/PrecipRateRecord.java index 57f9eb6d24..47a51c678b 100644 --- a/edexOsgi/com.raytheon.uf.common.dataplugin.preciprate/src/com/raytheon/uf/common/dataplugin/preciprate/PrecipRateRecord.java +++ b/edexOsgi/com.raytheon.uf.common.dataplugin.preciprate/src/com/raytheon/uf/common/dataplugin/preciprate/PrecipRateRecord.java @@ -39,6 +39,7 @@ import org.geotools.coverage.grid.GeneralGridEnvelope; import org.geotools.coverage.grid.GridGeometry2D; import org.geotools.geometry.GeneralEnvelope; import org.opengis.referencing.crs.ProjectedCRS; +import org.hibernate.annotations.Index; import com.raytheon.uf.common.dataplugin.IDecoderGettable; import com.raytheon.uf.common.dataplugin.annotations.DataURI; @@ -71,6 +72,7 @@ import com.raytheon.uf.common.status.UFStatus.Priority; * Date Ticket# Engineer Description * ------------ ---------- ----------- -------------------------- * 01/25/10 3796 D. Hladky Initial release + * Apr 4, 2013 1846 bkowal Added an index on refTime and forecastTime * * * @@ -79,6 +81,16 @@ import com.raytheon.uf.common.status.UFStatus.Priority; */ @Entity @Table(name = "preciprate", uniqueConstraints = { @UniqueConstraint(columnNames = { "dataURI" }) }) +/* + * Both refTime and forecastTime are included in the refTimeIndex since + * forecastTime is unlikely to be used. + */ +@org.hibernate.annotations.Table( + appliesTo = "preciprate", + indexes = { + @Index(name = "preciprate_refTimeIndex", columnNames = { "refTime", "forecastTime" } ) + } +) @XmlRootElement @XmlAccessorType(XmlAccessType.NONE) @DynamicSerialize diff --git a/edexOsgi/com.raytheon.uf.common.dataplugin.profiler/src/com/raytheon/uf/common/dataplugin/profiler/ProfilerObs.java b/edexOsgi/com.raytheon.uf.common.dataplugin.profiler/src/com/raytheon/uf/common/dataplugin/profiler/ProfilerObs.java index 7c8c7d9f84..54e5ac1e46 100644 --- a/edexOsgi/com.raytheon.uf.common.dataplugin.profiler/src/com/raytheon/uf/common/dataplugin/profiler/ProfilerObs.java +++ b/edexOsgi/com.raytheon.uf.common.dataplugin.profiler/src/com/raytheon/uf/common/dataplugin/profiler/ProfilerObs.java @@ -43,6 +43,7 @@ import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; +import org.hibernate.annotations.Index; import com.raytheon.uf.common.dataplugin.IDecoderGettable; import com.raytheon.uf.common.dataplugin.annotations.DataURI; @@ -69,6 +70,7 @@ import com.vividsolutions.jts.geom.Geometry; * 20090413 2251 jsanchez Implemented IDecoderGettable methods * and plotted Profiler plots. * 20090610 2489 jsanchez Updated the windSpeeed & windDirection. + * Apr 4, 2013 1846 bkowal Added an index on refTime and forecastTime * * * @@ -77,6 +79,16 @@ import com.vividsolutions.jts.geom.Geometry; */ @Entity @Table(name = "profiler", uniqueConstraints = { @UniqueConstraint(columnNames = { "dataURI" }) }) +/* + * Both refTime and forecastTime are included in the refTimeIndex since + * forecastTime is unlikely to be used. + */ +@org.hibernate.annotations.Table( + appliesTo = "profiler", + indexes = { + @Index(name = "profiler_refTimeIndex", columnNames = { "refTime", "forecastTime" } ) + } +) @XmlRootElement @XmlAccessorType(XmlAccessType.NONE) @DynamicSerialize diff --git a/edexOsgi/com.raytheon.uf.common.dataplugin.qc/src/com/raytheon/uf/common/dataplugin/qc/QCRecord.java b/edexOsgi/com.raytheon.uf.common.dataplugin.qc/src/com/raytheon/uf/common/dataplugin/qc/QCRecord.java index 365fc770c7..a80009e65b 100644 --- a/edexOsgi/com.raytheon.uf.common.dataplugin.qc/src/com/raytheon/uf/common/dataplugin/qc/QCRecord.java +++ b/edexOsgi/com.raytheon.uf.common.dataplugin.qc/src/com/raytheon/uf/common/dataplugin/qc/QCRecord.java @@ -33,6 +33,7 @@ import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; +import org.hibernate.annotations.Index; import com.raytheon.uf.common.dataplugin.IDecoderGettable; import com.raytheon.uf.common.dataplugin.PluginDataObject; @@ -52,6 +53,7 @@ import com.raytheon.uf.common.serialization.annotations.DynamicSerializeElement; * Date Ticket# Engineer Description * ------------ ---------- ----------- -------------------------- * 12/07/2009 3408 bphillip Initial creation + * Apr 4, 2013 1846 bkowal Added an index on refTime and forecastTime * * * @@ -60,6 +62,16 @@ import com.raytheon.uf.common.serialization.annotations.DynamicSerializeElement; */ @Entity @Table(name = "qc", uniqueConstraints = { @UniqueConstraint(columnNames = { "dataURI" }) }) +/* + * Both refTime and forecastTime are included in the refTimeIndex since + * forecastTime is unlikely to be used. + */ +@org.hibernate.annotations.Table( + appliesTo = "qc", + indexes = { + @Index(name = "qc_refTimeIndex", columnNames = { "refTime", "forecastTime" } ) + } +) @XmlRootElement @XmlAccessorType(XmlAccessType.NONE) @DynamicSerialize diff --git a/edexOsgi/com.raytheon.uf.common.dataplugin.qpf/src/com/raytheon/uf/common/dataplugin/qpf/QPFRecord.java b/edexOsgi/com.raytheon.uf.common.dataplugin.qpf/src/com/raytheon/uf/common/dataplugin/qpf/QPFRecord.java index 90bc5f5bf8..d91653f672 100644 --- a/edexOsgi/com.raytheon.uf.common.dataplugin.qpf/src/com/raytheon/uf/common/dataplugin/qpf/QPFRecord.java +++ b/edexOsgi/com.raytheon.uf.common.dataplugin.qpf/src/com/raytheon/uf/common/dataplugin/qpf/QPFRecord.java @@ -35,6 +35,7 @@ import org.geotools.coverage.grid.GeneralGridEnvelope; import org.geotools.coverage.grid.GridGeometry2D; import org.geotools.geometry.GeneralEnvelope; import org.opengis.referencing.crs.ProjectedCRS; +import org.hibernate.annotations.Index; import com.raytheon.uf.common.dataplugin.IDecoderGettable; import com.raytheon.uf.common.dataplugin.annotations.DataURI; @@ -62,6 +63,7 @@ import com.raytheon.uf.common.serialization.annotations.DynamicSerializeElement; * 2/24/09 2027 D. Hladky Initial release * 4/27/12 #562 dgilling Rename getters/setters to * match Java conventions. + * Apr 4, 2013 1846 bkowal Added an index on refTime and forecastTime * * * @@ -70,6 +72,16 @@ import com.raytheon.uf.common.serialization.annotations.DynamicSerializeElement; */ @Entity @Table(name = "qpf", uniqueConstraints = { @UniqueConstraint(columnNames = { "dataURI" }) }) +/* + * Both refTime and forecastTime are included in the refTimeIndex since + * forecastTime is unlikely to be used. + */ +@org.hibernate.annotations.Table( + appliesTo = "qpf", + indexes = { + @Index(name = "qpf_refTimeIndex", columnNames = { "refTime", "forecastTime" } ) + } +) @XmlRootElement @XmlAccessorType(XmlAccessType.NONE) @DynamicSerialize diff --git a/edexOsgi/com.raytheon.uf.common.dataplugin.radar/src/com/raytheon/uf/common/dataplugin/radar/RadarRecord.java b/edexOsgi/com.raytheon.uf.common.dataplugin.radar/src/com/raytheon/uf/common/dataplugin/radar/RadarRecord.java index 3523d8dba5..bcc37bfda0 100644 --- a/edexOsgi/com.raytheon.uf.common.dataplugin.radar/src/com/raytheon/uf/common/dataplugin/radar/RadarRecord.java +++ b/edexOsgi/com.raytheon.uf.common.dataplugin.radar/src/com/raytheon/uf/common/dataplugin/radar/RadarRecord.java @@ -44,6 +44,7 @@ import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; +import org.hibernate.annotations.Index; import org.geotools.referencing.GeodeticCalculator; import org.opengis.referencing.crs.ProjectedCRS; @@ -121,6 +122,7 @@ import com.vividsolutions.jts.geom.Coordinate; * 03/04/2013 DCS51 zwang Handle MIGFA product * Mar 18, 2013 1804 bsteffen Remove AlphanumericValues from radar * HDF5. + * Apr 4, 2013 1846 bkowal Added an index on refTime and forecastTime * * * @author bphillip @@ -128,6 +130,16 @@ import com.vividsolutions.jts.geom.Coordinate; */ @Entity @Table(name = "radar", uniqueConstraints = { @UniqueConstraint(columnNames = { "dataURI" }) }) +/* + * Both refTime and forecastTime are included in the refTimeIndex since + * forecastTime is unlikely to be used. + */ +@org.hibernate.annotations.Table( + appliesTo = "radar", + indexes = { + @Index(name = "radar_refTimeIndex", columnNames = { "refTime", "forecastTime" } ) + } +) @XmlRootElement @XmlAccessorType(XmlAccessType.NONE) @DynamicSerialize diff --git a/edexOsgi/com.raytheon.uf.common.dataplugin.satellite/src/com/raytheon/uf/common/dataplugin/satellite/SatelliteRecord.java b/edexOsgi/com.raytheon.uf.common.dataplugin.satellite/src/com/raytheon/uf/common/dataplugin/satellite/SatelliteRecord.java index 7f1716cfad..97d2e591a5 100644 --- a/edexOsgi/com.raytheon.uf.common.dataplugin.satellite/src/com/raytheon/uf/common/dataplugin/satellite/SatelliteRecord.java +++ b/edexOsgi/com.raytheon.uf.common.dataplugin.satellite/src/com/raytheon/uf/common/dataplugin/satellite/SatelliteRecord.java @@ -31,6 +31,7 @@ import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; +import org.hibernate.annotations.Index; import com.raytheon.uf.common.dataplugin.IDecoderGettable; import com.raytheon.uf.common.dataplugin.annotations.DataURI; @@ -56,6 +57,7 @@ import com.raytheon.uf.common.serialization.annotations.DynamicSerializeElement; * getPersistenceTime() from new IPersistable * 20071129 472 jkorman Added IDecoderGettable interface. * 20081106 1515 jkorman Changed units length from 16 to 26 + * Apr 4, 2013 1846 bkowal Added an index on refTime and forecastTime * - AWIPS2 Baseline Repository -------- * 07/30/2012 798 jkorman Support for common satellite data. * 03/25/2013 1823 dgilling Replace underscores with spaces in URI @@ -67,6 +69,16 @@ import com.raytheon.uf.common.serialization.annotations.DynamicSerializeElement; */ @Entity @Table(name = "satellite", uniqueConstraints = { @UniqueConstraint(columnNames = { "dataURI" }) }) +/* + * Both refTime and forecastTime are included in the refTimeIndex since + * forecastTime is unlikely to be used. + */ +@org.hibernate.annotations.Table( + appliesTo = "satellite", + indexes = { + @Index(name = "satellite_refTimeIndex", columnNames = { "refTime", "forecastTime" } ) + } +) @XmlRootElement @XmlAccessorType(XmlAccessType.NONE) @DynamicSerialize diff --git a/edexOsgi/com.raytheon.uf.common.dataplugin.scan/src/com/raytheon/uf/common/dataplugin/scan/ScanRecord.java b/edexOsgi/com.raytheon.uf.common.dataplugin.scan/src/com/raytheon/uf/common/dataplugin/scan/ScanRecord.java index 26f39fef49..a9e0d18611 100644 --- a/edexOsgi/com.raytheon.uf.common.dataplugin.scan/src/com/raytheon/uf/common/dataplugin/scan/ScanRecord.java +++ b/edexOsgi/com.raytheon.uf.common.dataplugin.scan/src/com/raytheon/uf/common/dataplugin/scan/ScanRecord.java @@ -34,6 +34,7 @@ import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; import org.geotools.coverage.grid.GridGeometry2D; +import org.hibernate.annotations.Index; import com.raytheon.uf.common.dataplugin.IDecoderGettable; import com.raytheon.uf.common.dataplugin.annotations.DataURI; @@ -66,6 +67,7 @@ import com.raytheon.uf.common.status.UFStatus.Priority; * 03/17/10 2521 D. Hladky Initial release * 02/01/13 1649 D. Hladky better logging, * Feb 28, 2013 1731 bsteffen Optimize construction of scan resource. + * Apr 4, 2013 1846 bkowal Added an index on refTime and forecastTime * * * @@ -75,6 +77,16 @@ import com.raytheon.uf.common.status.UFStatus.Priority; @Entity @Table(name = "scan", uniqueConstraints = { @UniqueConstraint(columnNames = { "dataURI" }) }) +/* + * Both refTime and forecastTime are included in the refTimeIndex since + * forecastTime is unlikely to be used. + */ +@org.hibernate.annotations.Table( + appliesTo = "scan", + indexes = { + @Index(name = "scan_refTimeIndex", columnNames = { "refTime", "forecastTime" } ) + } +) @XmlRootElement @XmlAccessorType(XmlAccessType.NONE) @DynamicSerialize diff --git a/edexOsgi/com.raytheon.uf.common.dataplugin.sfcobs/src/com/raytheon/uf/common/dataplugin/sfcobs/ObsCommon.java b/edexOsgi/com.raytheon.uf.common.dataplugin.sfcobs/src/com/raytheon/uf/common/dataplugin/sfcobs/ObsCommon.java index 1ea2c38703..0e198c21b3 100644 --- a/edexOsgi/com.raytheon.uf.common.dataplugin.sfcobs/src/com/raytheon/uf/common/dataplugin/sfcobs/ObsCommon.java +++ b/edexOsgi/com.raytheon.uf.common.dataplugin.sfcobs/src/com/raytheon/uf/common/dataplugin/sfcobs/ObsCommon.java @@ -68,6 +68,7 @@ import com.vividsolutions.jts.geom.Geometry; * Date Ticket# Engineer Description * ------------ ---------- ----------- -------------------------- * Oct 1, 2009 jkorman Initial creation + * Apr 4, 2013 1846 bkowal Added an index on refTime and forecastTime * * * @@ -76,6 +77,16 @@ import com.vividsolutions.jts.geom.Geometry; */ @Entity @Table(name = "sfcobs", uniqueConstraints = { @UniqueConstraint(columnNames = { "dataURI" }) }) +/* + * Both refTime and forecastTime are included in the refTimeIndex since + * forecastTime is unlikely to be used. + */ +@org.hibernate.annotations.Table( + appliesTo = "sfcobs", + indexes = { + @Index(name = "sfcobs_refTimeIndex", columnNames = { "refTime", "forecastTime" } ) + } +) @XmlRootElement @XmlAccessorType(XmlAccessType.NONE) @DynamicSerialize diff --git a/edexOsgi/com.raytheon.uf.common.dataplugin.svrwx/src/com/raytheon/uf/common/dataplugin/svrwx/SvrWxRecord.java b/edexOsgi/com.raytheon.uf.common.dataplugin.svrwx/src/com/raytheon/uf/common/dataplugin/svrwx/SvrWxRecord.java index 9da53c3b26..d39d17fc06 100644 --- a/edexOsgi/com.raytheon.uf.common.dataplugin.svrwx/src/com/raytheon/uf/common/dataplugin/svrwx/SvrWxRecord.java +++ b/edexOsgi/com.raytheon.uf.common.dataplugin.svrwx/src/com/raytheon/uf/common/dataplugin/svrwx/SvrWxRecord.java @@ -31,6 +31,7 @@ import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; +import org.hibernate.annotations.Index; import com.raytheon.uf.common.dataplugin.IDecoderGettable; import com.raytheon.uf.common.dataplugin.annotations.DataURI; @@ -52,6 +53,7 @@ import com.vividsolutions.jts.geom.Geometry; * Date Ticket# Engineer Description * ------------ ---------- ----------- -------------------------- * Jan 4, 2010 jsanchez Initial creation + * Apr 4, 2013 1846 bkowal Added an index on refTime and forecastTime * * * @@ -60,6 +62,16 @@ import com.vividsolutions.jts.geom.Geometry; */ @Entity @Table(name = "svrwx", uniqueConstraints = { @UniqueConstraint(columnNames = { "dataURI" }) }) +/* + * Both refTime and forecastTime are included in the refTimeIndex since + * forecastTime is unlikely to be used. + */ +@org.hibernate.annotations.Table( + appliesTo = "svrwx", + indexes = { + @Index(name = "svrwx_refTimeIndex", columnNames = { "refTime", "forecastTime" } ) + } +) @XmlRootElement @XmlAccessorType(XmlAccessType.NONE) @DynamicSerialize diff --git a/edexOsgi/com.raytheon.uf.common.dataplugin.tcg/src/com/raytheon/uf/common/dataplugin/tcg/TropicalCycloneGuidance.java b/edexOsgi/com.raytheon.uf.common.dataplugin.tcg/src/com/raytheon/uf/common/dataplugin/tcg/TropicalCycloneGuidance.java index 14d1fe5f99..b076b847f7 100644 --- a/edexOsgi/com.raytheon.uf.common.dataplugin.tcg/src/com/raytheon/uf/common/dataplugin/tcg/TropicalCycloneGuidance.java +++ b/edexOsgi/com.raytheon.uf.common.dataplugin.tcg/src/com/raytheon/uf/common/dataplugin/tcg/TropicalCycloneGuidance.java @@ -31,6 +31,7 @@ import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; +import org.hibernate.annotations.Index; import com.raytheon.uf.common.dataplugin.IDecoderGettable; import com.raytheon.uf.common.dataplugin.annotations.DataURI; @@ -52,6 +53,7 @@ import com.vividsolutions.jts.geom.Geometry; * Date Ticket# Engineer Description * ------------ ---------- ----------- -------------------------- * Oct 28, 2009 jsanchez Initial creation + * Apr 4, 2013 1846 bkowal Added an index on refTime and forecastTime * * * @@ -60,6 +62,16 @@ import com.vividsolutions.jts.geom.Geometry; */ @Entity @Table(name = "tcg", uniqueConstraints = { @UniqueConstraint(columnNames = { "dataURI" }) }) +/* + * Both refTime and forecastTime are included in the refTimeIndex since + * forecastTime is unlikely to be used. + */ +@org.hibernate.annotations.Table( + appliesTo = "tcg", + indexes = { + @Index(name = "tcg_refTimeIndex", columnNames = { "refTime", "forecastTime" } ) + } +) @XmlRootElement @XmlAccessorType(XmlAccessType.NONE) @DynamicSerialize diff --git a/edexOsgi/com.raytheon.uf.common.dataplugin.tcs/src/com/raytheon/uf/common/dataplugin/tcs/TropicalCycloneSummary.java b/edexOsgi/com.raytheon.uf.common.dataplugin.tcs/src/com/raytheon/uf/common/dataplugin/tcs/TropicalCycloneSummary.java index 62a3207967..bd5004d068 100644 --- a/edexOsgi/com.raytheon.uf.common.dataplugin.tcs/src/com/raytheon/uf/common/dataplugin/tcs/TropicalCycloneSummary.java +++ b/edexOsgi/com.raytheon.uf.common.dataplugin.tcs/src/com/raytheon/uf/common/dataplugin/tcs/TropicalCycloneSummary.java @@ -32,6 +32,7 @@ import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; +import org.hibernate.annotations.Index; import com.raytheon.uf.common.dataplugin.IDecoderGettable; import com.raytheon.uf.common.dataplugin.annotations.DataURI; @@ -53,6 +54,7 @@ import com.vividsolutions.jts.geom.Geometry; * Date Ticket# Engineer Description * ------------ ---------- ----------- -------------------------- * Nov 12, 2009 jsanchez Initial creation + * Apr 4, 2013 1846 bkowal Added an index on refTime and forecastTime * * * @@ -61,6 +63,16 @@ import com.vividsolutions.jts.geom.Geometry; */ @Entity @Table(name = "tcs", uniqueConstraints = { @UniqueConstraint(columnNames = { "dataURI" }) }) +/* + * Both refTime and forecastTime are included in the refTimeIndex since + * forecastTime is unlikely to be used. + */ +@org.hibernate.annotations.Table( + appliesTo = "tcs", + indexes = { + @Index(name = "tcs_refTimeIndex", columnNames = { "refTime", "forecastTime" } ) + } +) @XmlRootElement @XmlAccessorType(XmlAccessType.NONE) @DynamicSerialize diff --git a/edexOsgi/com.raytheon.uf.common.dataplugin.vaa/src/com/raytheon/uf/common/dataplugin/vaa/VAARecord.java b/edexOsgi/com.raytheon.uf.common.dataplugin.vaa/src/com/raytheon/uf/common/dataplugin/vaa/VAARecord.java index d30a12d03d..0827c8242d 100644 --- a/edexOsgi/com.raytheon.uf.common.dataplugin.vaa/src/com/raytheon/uf/common/dataplugin/vaa/VAARecord.java +++ b/edexOsgi/com.raytheon.uf.common.dataplugin.vaa/src/com/raytheon/uf/common/dataplugin/vaa/VAARecord.java @@ -35,6 +35,7 @@ import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; +import org.hibernate.annotations.Index; import com.raytheon.uf.common.dataplugin.IDecoderGettable; import com.raytheon.uf.common.dataplugin.PluginDataObject; @@ -55,6 +56,7 @@ import com.vividsolutions.jts.geom.Geometry; * Date Ticket# Engineer Description * ------------ ---------- ----------- -------------------------- * Nov 4, 2009 jkorman Initial creation + * Apr 4, 2013 1846 bkowal Added an index on refTime and forecastTime * * * @@ -65,6 +67,16 @@ import com.vividsolutions.jts.geom.Geometry; @Entity @Table(name = "vaa", uniqueConstraints = { @UniqueConstraint(columnNames = { "dataURI" }) }) +/* + * Both refTime and forecastTime are included in the refTimeIndex since + * forecastTime is unlikely to be used. + */ +@org.hibernate.annotations.Table( + appliesTo = "vaa", + indexes = { + @Index(name = "vaa_refTimeIndex", columnNames = { "refTime", "forecastTime" } ) + } +) @XmlRootElement @XmlAccessorType(XmlAccessType.NONE) @DynamicSerialize diff --git a/edexOsgi/com.raytheon.uf.common.dataplugin.vil/src/com/raytheon/uf/common/dataplugin/vil/VILRecord.java b/edexOsgi/com.raytheon.uf.common.dataplugin.vil/src/com/raytheon/uf/common/dataplugin/vil/VILRecord.java index 5a2b9afc39..3952ae87dc 100644 --- a/edexOsgi/com.raytheon.uf.common.dataplugin.vil/src/com/raytheon/uf/common/dataplugin/vil/VILRecord.java +++ b/edexOsgi/com.raytheon.uf.common.dataplugin.vil/src/com/raytheon/uf/common/dataplugin/vil/VILRecord.java @@ -35,6 +35,7 @@ import org.geotools.coverage.grid.GeneralGridEnvelope; import org.geotools.coverage.grid.GridGeometry2D; import org.geotools.geometry.GeneralEnvelope; import org.opengis.referencing.crs.ProjectedCRS; +import org.hibernate.annotations.Index; import com.raytheon.uf.common.dataplugin.IDecoderGettable; import com.raytheon.uf.common.dataplugin.annotations.DataURI; @@ -59,6 +60,7 @@ import com.raytheon.uf.common.serialization.annotations.DynamicSerializeElement; * Date Ticket# Engineer Description * ------------ ---------- ----------- -------------------------- * 1/14/09 2027 D. Hladky Initial release + * Apr 4, 2013 1846 bkowal Added an index on refTime and forecastTime * * * @@ -67,6 +69,16 @@ import com.raytheon.uf.common.serialization.annotations.DynamicSerializeElement; */ @Entity @Table(name = "vil", uniqueConstraints = { @UniqueConstraint(columnNames = { "dataURI" }) }) +/* + * Both refTime and forecastTime are included in the refTimeIndex since + * forecastTime is unlikely to be used. + */ +@org.hibernate.annotations.Table( + appliesTo = "vil", + indexes = { + @Index(name = "vil_refTimeIndex", columnNames = { "refTime", "forecastTime" } ) + } +) @XmlRootElement @XmlAccessorType(XmlAccessType.NONE) @DynamicSerialize diff --git a/edexOsgi/com.raytheon.uf.common.dataplugin.warning/src/com/raytheon/uf/common/dataplugin/warning/PracticeWarningRecord.java b/edexOsgi/com.raytheon.uf.common.dataplugin.warning/src/com/raytheon/uf/common/dataplugin/warning/PracticeWarningRecord.java index a48bf162c1..c55e0b5371 100644 --- a/edexOsgi/com.raytheon.uf.common.dataplugin.warning/src/com/raytheon/uf/common/dataplugin/warning/PracticeWarningRecord.java +++ b/edexOsgi/com.raytheon.uf.common.dataplugin.warning/src/com/raytheon/uf/common/dataplugin/warning/PracticeWarningRecord.java @@ -28,6 +28,7 @@ import javax.persistence.UniqueConstraint; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlRootElement; +import org.hibernate.annotations.Index; import com.raytheon.uf.common.serialization.annotations.DynamicSerialize; @@ -40,6 +41,7 @@ import com.raytheon.uf.common.serialization.annotations.DynamicSerialize; * Date Ticket# Engineer Description * ------------ ---------- ----------- -------------------------- * 10/04/2011 10049 bgonzale initial creation + * Apr 4, 2013 1846 bkowal Added an index on refTime and forecastTime * * * @@ -48,6 +50,16 @@ import com.raytheon.uf.common.serialization.annotations.DynamicSerialize; */ @Entity @Table(name = "practicewarning", uniqueConstraints = { @UniqueConstraint(columnNames = { "dataURI" }) }) +/* + * Both refTime and forecastTime are included in the refTimeIndex since + * forecastTime is unlikely to be used. + */ +@org.hibernate.annotations.Table( + appliesTo = "practicewarning", + indexes = { + @Index(name = "practicewarning_refTimeIndex", columnNames = { "refTime", "forecastTime" } ) + } +) @XmlRootElement @XmlAccessorType(XmlAccessType.NONE) @DynamicSerialize diff --git a/edexOsgi/com.raytheon.uf.common.dataplugin.warning/src/com/raytheon/uf/common/dataplugin/warning/WarningRecord.java b/edexOsgi/com.raytheon.uf.common.dataplugin.warning/src/com/raytheon/uf/common/dataplugin/warning/WarningRecord.java index 7c42661586..876d994a76 100644 --- a/edexOsgi/com.raytheon.uf.common.dataplugin.warning/src/com/raytheon/uf/common/dataplugin/warning/WarningRecord.java +++ b/edexOsgi/com.raytheon.uf.common.dataplugin.warning/src/com/raytheon/uf/common/dataplugin/warning/WarningRecord.java @@ -30,6 +30,7 @@ import javax.persistence.UniqueConstraint; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlRootElement; +import org.hibernate.annotations.Index; import com.raytheon.uf.common.serialization.annotations.DynamicSerialize; @@ -42,6 +43,7 @@ import com.raytheon.uf.common.serialization.annotations.DynamicSerialize; * Date Ticket# Engineer Description * ------------ ---------- ----------- -------------------------- * 03/12/2007 1003 bwoodle initial creation + * Apr 4, 2013 1846 bkowal Added an index on refTime and forecastTime * * * @@ -51,6 +53,17 @@ import com.raytheon.uf.common.serialization.annotations.DynamicSerialize; @Entity @XmlAccessorType(XmlAccessType.NONE) @Table(name = "warning", uniqueConstraints = { @UniqueConstraint(columnNames = { "dataURI" }) }) +/* + * Both refTime and forecastTime are included in the refTimeIndex since + * forecastTime is unlikely to be used. + */ +@org.hibernate.annotations.Table( + appliesTo = "warning", + indexes = { + @Index(name = "warning_refTimeIndex", columnNames = { "refTime", "forecastTime" } ) + } +) + @XmlRootElement @DynamicSerialize public class WarningRecord extends AbstractWarningRecord { diff --git a/edexOsgi/com.raytheon.uf.common.dataplugin/src/com/raytheon/uf/common/dataplugin/PluginDataObject.java b/edexOsgi/com.raytheon.uf.common.dataplugin/src/com/raytheon/uf/common/dataplugin/PluginDataObject.java index 89c059618c..f2028b31c5 100644 --- a/edexOsgi/com.raytheon.uf.common.dataplugin/src/com/raytheon/uf/common/dataplugin/PluginDataObject.java +++ b/edexOsgi/com.raytheon.uf.common.dataplugin/src/com/raytheon/uf/common/dataplugin/PluginDataObject.java @@ -95,7 +95,6 @@ public abstract class PluginDataObject extends PersistableDataObject implements @Column @XmlAttribute @DynamicSerializeElement - @Index(name = "dataURI_idx") protected String dataURI; /** The name of the plugin this object is associated with */ diff --git a/edexOsgi/com.raytheon.uf.common.time/src/com/raytheon/uf/common/time/DataTime.java b/edexOsgi/com.raytheon.uf.common.time/src/com/raytheon/uf/common/time/DataTime.java index ef7313212b..5d6de233ae 100644 --- a/edexOsgi/com.raytheon.uf.common.time/src/com/raytheon/uf/common/time/DataTime.java +++ b/edexOsgi/com.raytheon.uf.common.time/src/com/raytheon/uf/common/time/DataTime.java @@ -40,7 +40,6 @@ import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlElement; import org.apache.commons.lang.builder.HashCodeBuilder; -import org.hibernate.annotations.Index; import org.hibernate.annotations.Type; import com.raytheon.uf.common.serialization.ISerializableObject; @@ -112,7 +111,6 @@ import com.raytheon.uf.common.time.util.TimeUtil; @DynamicSerialize public class DataTime implements Comparable, Serializable, ISerializableObject, Cloneable { - /** * */ @@ -130,17 +128,15 @@ public class DataTime implements Comparable, Serializable, /** The minor sort key */ @Transient protected SortKey minorKey = SortKey.FORECAST_TIME; - + /** The reference time */ @Column(name = "refTime") - @Index(name = "refTimeIndex") @DynamicSerializeElement @XmlAttribute protected Date refTime; /** The forecast time (in seconds from reference time) */ @Column(name = "forecastTime") - @Index(name = "fcstTimeIndex") @DynamicSerializeElement @XmlAttribute protected int fcstTime = 0; diff --git a/javaUtilities/yajsw/.classpath b/javaUtilities/yajsw/.classpath new file mode 100644 index 0000000000..3120509882 --- /dev/null +++ b/javaUtilities/yajsw/.classpath @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/javaUtilities/yajsw/.project b/javaUtilities/yajsw/.project new file mode 100644 index 0000000000..08a04a8bff --- /dev/null +++ b/javaUtilities/yajsw/.project @@ -0,0 +1,17 @@ + + + yajsw + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/javaUtilities/yajsw/build.xml b/javaUtilities/yajsw/build.xml new file mode 100644 index 0000000000..9a152b931e --- /dev/null +++ b/javaUtilities/yajsw/build.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/javaUtilities/yajsw/build/MANIFEST.MF b/javaUtilities/yajsw/build/MANIFEST.MF new file mode 100644 index 0000000000..74befbde6c --- /dev/null +++ b/javaUtilities/yajsw/build/MANIFEST.MF @@ -0,0 +1,50 @@ +Manifest-Version: 1.0 +Class-Path-Wrapper-Core: + ./wrapperApp.jar + ./lib/core/yajsw/ahessian.jar + ./lib/core/netty/netty-3.5.1.Final.jar + ./lib/core/jna/jna-3.4.1.jar + ./lib/core/jna/platform-3.4.1.jar + ./lib/core/commons/commons-configuration-1.8.jar + ./lib/core/commons/commons-vfs2-2.0.jar + ./lib/core/groovy/groovy-all-1.8.6.jar + ./lib/core/commons/commons-collections-3.2.jar + ./lib/core/commons/commons-io-1.3.1.jar + ./lib/core/commons/commons-lang-2.4.jar + ./lib/core/commons/commons-logging-1.1.jar + ./lib/core/commons/commons-cli-2-SNAPSHOT.jar + ./lib/core/regex/jrexx-1.1.1.jar + +Class-Path-Wrapper-Extended: + ./lib/extended/commons/commons-httpclient-3.0.1.jar + ./lib/extended/commons/commons-codec-1.3.jar + ./lib/extended/yajsw/hessian4.jar + ./lib/extended/hessian/hessian-4.0.2.jar + ./lib/extended/hessian/servlet-api.jar + ./lib/extended/glazedlists/glazedlists-1.8.0_java15.jar + ./lib/extended/glazedlists/commons-beanutils-1.8.2.jar + ./lib/extended/quartz/quartz-1.8.0.jar + ./lib/extended/quartz/jta-spec1_0_1.jar + ./lib/extended/velocity/velocity-1.6.3.jar + ./lib/extended/jgoodies/forms-1.2.0.jar + ./lib/extended/vfs-webdav/jackrabbit-webdav-1.5.6.jar + ./lib/extended/vfs-webdav/xercesImpl.jar + ./lib/extended/vfs-webdav/slf4j-jdk14-1.5.0.jar + ./lib/extended/vfs-webdav/slf4j-api-1.5.0.jar + ./lib/extended/srvmgr-client/commons-beanutils-1.8.2.jar + ./lib/extended/srvmgr-client/glazedlists-1.8.0_java15.jar + ./lib/extended/abeille/formsrt.jar + +Class-Path-App: + ./wrapper.jar + ./lib/core/netty/netty-3.5.1.Final.jar + ./lib/core/commons/commons-configuration-1.8.jar + ./lib/core/commons/commons-vfs2-2.0.jar + ./lib/core/groovy/groovy-all-1.8.6.jar + ./lib/core/commons/commons-collections-3.2.jar + ./lib/core/commons/commons-io-1.3.1.jar + ./lib/core/commons/commons-lang-2.4.jar + ./lib/core/commons/commons-logging-1.1.jar + +Main-Class: org.rzo.yajsw.boot.WrapperExeBooter + diff --git a/javaUtilities/yajsw/build/ReadMe.txt b/javaUtilities/yajsw/build/ReadMe.txt new file mode 100644 index 0000000000..49c9b4d8af --- /dev/null +++ b/javaUtilities/yajsw/build/ReadMe.txt @@ -0,0 +1,15 @@ + +The manifest in this folder should be added to wrapper.jar and wrapperApp.jar +It assumes that the relative folder structure of the jar files is not changed. +If you would like to change the folder structure you should adapt the manifest accordingly. + +A build script is included in the subfolder gradle. + +Building SNAPSHOT jars is not included in this build. Please use the following subversion +revisions to build them: + +commons-vfs-2.0-SNAPSHOT.jar 784083 +commons-cli-2-SNAPSHOT.jar 647073 +commons-configuration-1.7-SNAPSHOT.jar 784085 + +NOTE: the above projects have not had new releases for years, but have been commiting new features which are used here. \ No newline at end of file diff --git a/javaUtilities/yajsw/build/abeille/ConsoleForm.xml b/javaUtilities/yajsw/build/abeille/ConsoleForm.xml new file mode 100644 index 0000000000..b455d2c438 --- /dev/null +++ b/javaUtilities/yajsw/build/abeille/ConsoleForm.xml @@ -0,0 +1,4527 @@ + + + + + + 2 + 0 + 0 + + + + + + + + 1 + 1 + 1 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.FormComponent + + Z:\dev\forms\abeille-2.1.0_M2\examples\forms\ConsoleForm2.xml + ConsoleForm2.xml + CENTER:2DLU:NONE,CENTER:DEFAULT:NONE,CENTER:2DLU:NONE,FILL:238PX:NONE,CENTER:2DLU:NONE,CENTER:DEFAULT:NONE,CENTER:DEFAULT:NONE,FILL:14DLU:NONE,CENTER:DEFAULT:NONE,CENTER:2DLU:NONE,CENTER:DEFAULT:NONE,CENTER:2DLU:NONE,CENTER:DEFAULT:NONE,CENTER:2DLU:NONE,CENTER:DEFAULT:NONE,CENTER:4DLU:NONE,CENTER:DEFAULT:NONE,CENTER:DEFAULT:NONE,CENTER:4DLU:NONE,CENTER:4DLU:NONE,CENTER:DEFAULT:NONE,CENTER:2DLU:NONE + FILL:4DLU:NONE,FILL:4DLU:NONE,FILL:4DLU:NONE,LEFT:100PX:NONE,FILL:4DLU:NONE,RIGHT:100PX:NONE,FILL:4DLU:NONE,LEFT:100PX:NONE,FILL:4DLU:NONE,LEFT:100PX:NONE,FILL:4DLU:NONE,LEFT:100PX:NONE,FILL:4DLU:NONE,FILL:4DLU:NONE,FILL:4DLU:NONE,RIGHT:100PX:NONE,FILL:4DLU:NONE,FILL:100PX:NONE,FILL:4DLU:NONE,FILL:100PX:NONE,FILL:4DLU:NONE + + + + + + + + + 4 + 2 + 1 + 1 + fill + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + com.jeta.forms.components.label.JETALabel + + + com.jeta.forms.components.label.JETALabel + + + Output + 14 + true + + + Tahoma + 1 + 11 + + + + 96 + + + fill + + + 204,204,204 + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 4 + 4 + 17 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + javax.swing.JTextArea + + + javax.swing.JTextArea + + + true + 232 + 848 + output + + + scollBars + 20 + 31 + + + + border + + + + + + + + border + + + + + + + + + + + + + + border + + + + + + + + border + + + + + + + + + true + + + + + + + + + + + + + + 4 + 6 + 1 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + com.jeta.forms.components.label.JETALabel + + + com.jeta.forms.components.label.JETALabel + + + Input (CR terminated) + 14 + + + Tahoma + 1 + 11 + + + + 96 + + + fill + + + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 4 + 9 + 1 + 1 + fill + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + com.jeta.forms.components.label.JETALabel + + + com.jeta.forms.components.label.JETALabel + + + 2 + State + 14 + true + + + Tahoma + 1 + 11 + + + + 96 + + + fill + + + 204,204,204 + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 6 + 6 + 15 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + javax.swing.JTextField + + + javax.swing.JTextField + + + 20 + 744 + input + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 4 + 18 + 1 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + javax.swing.JButton + + + javax.swing.JButton + + + 12 + 32 + START_BUTTON + Start + Start + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 6 + 18 + 1 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + javax.swing.JButton + + + javax.swing.JButton + + + 12 + 32 + STOP_BUTTON + Stop + Stop + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 8 + 18 + 1 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + javax.swing.JButton + + + javax.swing.JButton + + + 12 + 32 + RESTART_BUTTON + Restart + Restart + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 20 + 18 + 1 + 1 + right + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + javax.swing.JButton + + + javax.swing.JButton + + + 12 + 32 + EXIT_WRAPPER_BUTTON + Exit + Stop Wrapper + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 10 + 18 + 1 + 1 + left + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + javax.swing.JButton + + + javax.swing.JButton + + + 12 + 32 + THREAD_DUMP_BUTTON + Thread Dump + Thread Dump + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 10 + 11 + 1 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + com.jeta.forms.components.label.JETALabel + + + com.jeta.forms.components.label.JETALabel + + + - + 14 + 12 + appStopTime + + + fill + + + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 8 + 9 + 1 + 1 + fill + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + com.jeta.forms.components.label.JETALabel + + + com.jeta.forms.components.label.JETALabel + + + Started + 14 + true + + + Tahoma + 1 + 11 + + + + 96 + + + fill + + + 204,204,204 + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 4 + 11 + 1 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + com.jeta.forms.components.label.JETALabel + + + com.jeta.forms.components.label.JETALabel + + + IDLE + 14 + 22 + state + + + fill + + + 255,255,255 + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 10 + 9 + 1 + 1 + fill + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + com.jeta.forms.components.label.JETALabel + + + com.jeta.forms.components.label.JETALabel + + + Stopped + 14 + true + + + Tahoma + 1 + 11 + + + + 96 + + + fill + + + 204,204,204 + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 18 + 11 + 1 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + com.jeta.forms.components.label.JETALabel + + + com.jeta.forms.components.label.JETALabel + + + - + 14 + 96 + wStartTime + + + fill + + + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 20 + 11 + 1 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + com.jeta.forms.components.label.JETALabel + + + com.jeta.forms.components.label.JETALabel + + + - + 14 + 96 + trigger + + + fill + + + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 16 + 9 + 1 + 1 + fill + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + com.jeta.forms.components.label.JETALabel + + + com.jeta.forms.components.label.JETALabel + + + PID + 14 + true + + + Tahoma + 1 + 11 + + + + 96 + + + fill + + + 204,204,204 + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 18 + 9 + 1 + 1 + fill + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + com.jeta.forms.components.label.JETALabel + + + com.jeta.forms.components.label.JETALabel + + + Started + 14 + true + + + Tahoma + 1 + 11 + + + + 96 + + + fill + + + 204,204,204 + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 20 + 9 + 1 + 1 + fill + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + com.jeta.forms.components.label.JETALabel + + + com.jeta.forms.components.label.JETALabel + + + Trigger + 14 + true + + + Tahoma + 1 + 11 + + + + 96 + + + fill + + + 204,204,204 + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 18 + 18 + 1 + 1 + right + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + javax.swing.JButton + + + javax.swing.JButton + + + 12 + 32 + STOP_TIMER_BUTTON + Stop Timer / Condition + Stop Timer / Condition + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 6 + 11 + 1 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + com.jeta.forms.components.label.JETALabel + + + com.jeta.forms.components.label.JETALabel + + + - + 14 + 12 + appPid + + + fill + + + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 8 + 11 + 1 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + com.jeta.forms.components.label.JETALabel + + + com.jeta.forms.components.label.JETALabel + + + - + 14 + 12 + appStartTime + + + fill + + + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 16 + 11 + 1 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + com.jeta.forms.components.label.JETALabel + + + com.jeta.forms.components.label.JETALabel + + + - + 14 + 12 + wPid + + + fill + + + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 3 + 8 + 10 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + com.jeta.forms.components.border.TitledBorderLabel + + + com.jeta.forms.components.border.TitledBorderLabel + + + Application + 17 + + 526 + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 2 + 8 + 1 + 12 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + com.jeta.forms.components.border.TitledBorderSide + + + com.jeta.forms.components.border.TitledBorderSide + + + 142 + + 2 + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 13 + 8 + 1 + 12 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + com.jeta.forms.components.border.TitledBorderSide + + + com.jeta.forms.components.border.TitledBorderSide + + + 1 + 142 + + 2 + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 3 + 19 + 10 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + com.jeta.forms.components.border.TitledBorderBottom + + + com.jeta.forms.components.border.TitledBorderBottom + + + 2 + + 526 + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 15 + 8 + 6 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + com.jeta.forms.components.border.TitledBorderLabel + + + com.jeta.forms.components.border.TitledBorderLabel + + + Wrapper + 17 + + 314 + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 14 + 8 + 1 + 12 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + com.jeta.forms.components.border.TitledBorderSide + + + com.jeta.forms.components.border.TitledBorderSide + + + 142 + + 2 + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 21 + 8 + 1 + 12 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + com.jeta.forms.components.border.TitledBorderSide + + + com.jeta.forms.components.border.TitledBorderSide + + + 1 + 142 + + 2 + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 15 + 19 + 6 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + com.jeta.forms.components.border.TitledBorderBottom + + + com.jeta.forms.components.border.TitledBorderBottom + + + 2 + + 314 + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 20 + 21 + 1 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + javax.swing.JButton + + + javax.swing.JButton + + + Close Console + 22 + + 96 + Close Console + Close Console The Console Window + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 16 + 15 + 1 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + com.jeta.forms.components.label.JETALabel + + + com.jeta.forms.components.label.JETALabel + + + - + 14 + 12 + timer + + + fill + + + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 18 + 15 + 1 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + com.jeta.forms.components.label.JETALabel + + + com.jeta.forms.components.label.JETALabel + + + - + 14 + 96 + condition + + + fill + + + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 20 + 15 + 1 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + com.jeta.forms.components.label.JETALabel + + + com.jeta.forms.components.label.JETALabel + + + - + 14 + 96 + wrapperType + + + fill + + + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 6 + 9 + 1 + 1 + fill + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + com.jeta.forms.components.label.JETALabel + + + com.jeta.forms.components.label.JETALabel + + + 2 + PID + 14 + true + + + Tahoma + 1 + 11 + + + + 96 + + + fill + + + 204,204,204 + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 16 + 13 + 1 + 1 + fill + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + com.jeta.forms.components.label.JETALabel + + + com.jeta.forms.components.label.JETALabel + + + 2 + Timer + 14 + true + + + Tahoma + 1 + 11 + + + + 96 + + + fill + + + 204,204,204 + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 18 + 13 + 1 + 1 + fill + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + com.jeta.forms.components.label.JETALabel + + + com.jeta.forms.components.label.JETALabel + + + 2 + Condition + 14 + true + + + Tahoma + 1 + 11 + + + + 96 + + + fill + + + 204,204,204 + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 20 + 13 + 1 + 1 + fill + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + com.jeta.forms.components.label.JETALabel + + + com.jeta.forms.components.label.JETALabel + + + 2 + Type + 14 + true + + + Tahoma + 1 + 11 + + + + 96 + + + fill + + + 204,204,204 + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 4 + 13 + 1 + 1 + fill + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + com.jeta.forms.components.label.JETALabel + + + com.jeta.forms.components.label.JETALabel + + + 2 + CPU + 14 + true + + + Tahoma + 1 + 11 + + + + 96 + + + fill + + + 204,204,204 + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 4 + 15 + 1 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + com.jeta.forms.components.label.JETALabel + + + com.jeta.forms.components.label.JETALabel + + + - + 14 + 12 + cpu + + + fill + + + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 6 + 13 + 1 + 1 + fill + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + com.jeta.forms.components.label.JETALabel + + + com.jeta.forms.components.label.JETALabel + + + 2 + Memory + 14 + true + + + Tahoma + 1 + 11 + + + + 96 + + + fill + + + 204,204,204 + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 6 + 15 + 1 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + com.jeta.forms.components.label.JETALabel + + + com.jeta.forms.components.label.JETALabel + + + - + 14 + 12 + memory + + + fill + + + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 8 + 13 + 1 + 1 + fill + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + com.jeta.forms.components.label.JETALabel + + + com.jeta.forms.components.label.JETALabel + + + 2 + Handles + 14 + true + + + Tahoma + 1 + 11 + + + + 96 + + + fill + + + 204,204,204 + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 8 + 15 + 1 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + com.jeta.forms.components.label.JETALabel + + + com.jeta.forms.components.label.JETALabel + + + - + 14 + 12 + handles + + + fill + + + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 10 + 13 + 1 + 1 + fill + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + com.jeta.forms.components.label.JETALabel + + + com.jeta.forms.components.label.JETALabel + + + 2 + Threads + 14 + true + + + Tahoma + 1 + 11 + + + + 96 + + + fill + + + 204,204,204 + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 10 + 15 + 1 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + com.jeta.forms.components.label.JETALabel + + + com.jeta.forms.components.label.JETALabel + + + - + 14 + 12 + threads + + + fill + + + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 12 + 15 + 1 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + com.jeta.forms.components.label.JETALabel + + + com.jeta.forms.components.label.JETALabel + + + - + 14 + 12 + count + + + fill + + + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 12 + 13 + 1 + 1 + fill + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + com.jeta.forms.components.label.JETALabel + + + com.jeta.forms.components.label.JETALabel + + + Restarts + 14 + true + + + Tahoma + 1 + 11 + + + + 96 + + + fill + + + 204,204,204 + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 12 + 9 + 1 + 1 + fill + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + com.jeta.forms.components.label.JETALabel + + + com.jeta.forms.components.label.JETALabel + + + Exit Code + 14 + true + + + Tahoma + 1 + 11 + + + + 96 + + + fill + + + 204,204,204 + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 12 + 11 + 1 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + com.jeta.forms.components.label.JETALabel + + + com.jeta.forms.components.label.JETALabel + + + - + 14 + 12 + exitCode + + + fill + + + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 16 + 18 + 1 + 1 + right + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + javax.swing.JButton + + + javax.swing.JButton + + + 12 + 32 + THREAD_DUMP_WRAPPER_BUTTON + Exit + Thread Dump Wrapper + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 18 + 21 + 1 + 1 + right + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + javax.swing.JButton + + + javax.swing.JButton + + + 12 + 32 + EXIT_TRAY_ICON_BUTTON + Exit + Exit Tray Icon + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 6 + 2 + 1 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + javax.swing.JButton + + + javax.swing.JButton + + + false + 12 + 32 + START_OUTPUT_BUTTON + Start Console Output + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 8 + 2 + 1 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + javax.swing.JButton + + + javax.swing.JButton + + + 12 + 32 + PAUSE_OUTPUT_BUTTON + Pause Console Output + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 16 + 2 + 1 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + com.jeta.forms.components.label.JETALabel + + + com.jeta.forms.components.label.JETALabel + + + Filter + 14 + + + Tahoma + 1 + 11 + + + + 29 + + + fill + + + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 18 + 2 + 3 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + javax.swing.JTextField + + + javax.swing.JTextField + + + 20 + 202 + _OUTPUT_FILTER + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 10 + 2 + 1 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + javax.swing.JButton + + + javax.swing.JButton + + + 12 + 32 + CLEAR_OUTPUT_BUTTON + Clear Output + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 12 + 18 + 1 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + javax.swing.JButton + + + javax.swing.JButton + + + 12 + 32 + GC_BUTTON + Thread Dump + Thread Dump + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 10 + 17 + 1 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + javax.swing.JButton + + + javax.swing.JButton + + + 12 + 32 + DUMP_HEAP_BUTTON + Thread Dump + Thread Dump + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + com.jeta.forms.gui.form.GridView + + + + + + fill + + + + + scollBars + 21 + 31 + + + + border + + + + + + + + border + + + + + + + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + fill + + + + + dyncolor + constant + 204,204,204 + + + + + + + + + fill + + + + + dyncolor + constant + 204,204,204 + + + + + + + + + fill + + + + + dyncolor + constant + 204,204,204 + + + + + + + + + fill + + + + + dyncolor + constant + 204,204,204 + + + + + + + + + fill + + + + + dyncolor + constant + 204,204,204 + + + + + + + + + fill + + + + + dyncolor + constant + 204,204,204 + + + + + + + + + fill + + + + + dyncolor + constant + 204,204,204 + + + + + + + + + fill + + + + + dyncolor + constant + 204,204,204 + + + + + + + + + fill + + + + + dyncolor + constant + 204,204,204 + + + + + + + + + fill + + + + + dyncolor + constant + 204,204,204 + + + + + + + + + fill + + + + + dyncolor + constant + 204,204,204 + + + + + + + + + fill + + + + + dyncolor + constant + 204,204,204 + + + + + + + + + fill + + + + + dyncolor + constant + 204,204,204 + + + + + + + + + fill + + + + + dyncolor + constant + 204,204,204 + + + + + + + + + fill + + + + + dyncolor + constant + 204,204,204 + + + + + + + + + fill + + + + + dyncolor + constant + 204,204,204 + + + + + + + + + fill + + + + + dyncolor + constant + 204,204,204 + + + + + + + + + + + + + + + + + + + + + + fill + + + + + dyncolor + constant + 204,204,204 + + + + + + + + + fill + + + + + dyncolor + constant + 204,204,204 + + + + + + + + + fill + + + + + dyncolor + constant + 204,204,204 + + + + + + + + + + + + + + + + + + + fill + + + + + fill + + + + + fill + + + + + fill + + + + + fill + + + + + + + + + + + + fill + + + + + fill + + + + + fill + + + + + fill + + + + + + + + + + + + fill + + + + + fill + + + + + fill + + + + + fill + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + fill + + + + + dyncolor + constant + 204,204,204 + + + + + + + + + fill + + + + + dyncolor + constant + 204,204,204 + + + + + + + + + fill + + + + + dyncolor + constant + 204,204,204 + + + + + + + + + fill + + + + + dyncolor + constant + 204,204,204 + + + + + + + + + fill + + + + + dyncolor + constant + 204,204,204 + + + + + + + + + fill + + + + + dyncolor + constant + 204,204,204 + + + + + + + + + fill + + + + + dyncolor + constant + 204,204,204 + + + + + + + + + fill + + + + + dyncolor + constant + 204,204,204 + + + + + + + + + fill + + + + + dyncolor + constant + 204,204,204 + + + + + + + + + fill + + + + + dyncolor + constant + 204,204,204 + + + + + + + + + fill + + + + + dyncolor + constant + 204,204,204 + + + + + + + + + fill + + + + + dyncolor + constant + 204,204,204 + + + + + + + + + fill + + + + + dyncolor + constant + 204,204,204 + + + + + + + + + fill + + + + + dyncolor + constant + 204,204,204 + + + + + + + + + fill + + + + + dyncolor + constant + 204,204,204 + + + + + + + + + fill + + + + + dyncolor + constant + 204,204,204 + + + + + + + + + fill + + + + + dyncolor + constant + 204,204,204 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/javaUtilities/yajsw/build/abeille/ReadMe.txt b/javaUtilities/yajsw/build/abeille/ReadMe.txt new file mode 100644 index 0000000000..fe72c9a1fa --- /dev/null +++ b/javaUtilities/yajsw/build/abeille/ReadMe.txt @@ -0,0 +1,4 @@ +These files have been created with abeille gui designer +https://abeille.dev.java.net/ + +They are used to generate the according gui classes. \ No newline at end of file diff --git a/javaUtilities/yajsw/build/abeille/WSForm.xml b/javaUtilities/yajsw/build/abeille/WSForm.xml new file mode 100644 index 0000000000..e83d1c7ff1 --- /dev/null +++ b/javaUtilities/yajsw/build/abeille/WSForm.xml @@ -0,0 +1,1642 @@ + + + + + + 2 + 0 + 0 + + + + + + + + 1 + 1 + 1 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.FormComponent + + Z:\dev\forms\abeille-2.1.0_M2\examples\forms\yajsw_ws.xml + CENTER:DEFAULT:NONE,CENTER:DEFAULT:NONE,CENTER:DEFAULT:NONE,CENTER:DEFAULT:NONE,CENTER:2DLU:NONE,CENTER:DEFAULT:NONE,CENTER:2DLU:NONE,CENTER:DEFAULT:NONE,CENTER:DEFAULT:NONE,CENTER:2DLU:NONE,CENTER:DEFAULT:NONE,CENTER:2DLU:NONE,CENTER:DEFAULT:NONE,FILL:174PX:NONE,CENTER:4DLU:NONE,CENTER:DEFAULT:NONE + FILL:DEFAULT:NONE,FILL:8DLU:NONE,FILL:84PX:NONE,FILL:DEFAULT:NONE,FILL:97PX:NONE,FILL:DEFAULT:NONE,FILL:109PX:NONE,FILL:DEFAULT:NONE,FILL:95PX:NONE,FILL:25PX:NONE,FILL:8DLU:NONE,FILL:8DLU:NONE,FILL:DEFAULT:NONE + + + + + + + + + 3 + 14 + 9 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + javax.swing.JTextArea + + + javax.swing.JTextArea + + + true + 168 + 464 + LOG_AREA + + + scollBars + 20 + 30 + + + + border + + + + + + + + border + + + + + + + + + + + + + + border + + + + + + + + border + + + + + + + + + true + + + + + + + + + + + + + + 3 + 13 + 9 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + com.jeta.forms.components.separator.TitledSeparator + + + com.jeta.forms.components.separator.TitledSeparator + + + Log + 16 + + 466 + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 3 + 15 + 9 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + com.jeta.forms.components.border.TitledBorderBottom + + + com.jeta.forms.components.border.TitledBorderBottom + + + 2 + + 466 + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 3 + 8 + 1 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + com.jeta.forms.components.label.JETALabel + + + com.jeta.forms.components.label.JETALabel + + + Action + 14 + + 80 + + + fill + + + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 5 + 8 + 1 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + javax.swing.JRadioButton + + + javax.swing.JRadioButton + + + Install Service + 16 + + + buttonGroup + 1 + + + 93 + INSTALL_OPTION + Install Service + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 5 + 9 + 1 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + javax.swing.JRadioButton + + + javax.swing.JRadioButton + + + Run Console + 16 + + + buttonGroup + 1 + + + 93 + CONSOLE_OPTION + Run Console + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 3 + 4 + 1 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + com.jeta.forms.components.label.JETALabel + + + com.jeta.forms.components.label.JETALabel + + + Application + 14 + + 80 + + + fill + + + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 3 + 6 + 1 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + com.jeta.forms.components.label.JETALabel + + + com.jeta.forms.components.label.JETALabel + + + Install Folder + 14 + + 80 + + + fill + + + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 5 + 4 + 6 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + com.jeta.forms.components.label.JETALabel + + + com.jeta.forms.components.label.JETALabel + + + ??? + 14 + 354 + APPLICATION + + + fill + + + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 2 + 13 + 1 + 3 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + com.jeta.forms.components.border.TitledBorderSide + + + com.jeta.forms.components.border.TitledBorderSide + + + 196 + + 8 + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 7 + 8 + 1 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + javax.swing.JCheckBox + + + javax.swing.JCheckBox + + + Start Service + 16 + 105 + START_OPTION + & Start + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 5 + 6 + 6 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + javax.swing.JTextField + + + javax.swing.JTextField + + + 20 + 354 + INSTALL_FOLDER + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 9 + 8 + 1 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + javax.swing.JCheckBox + + + javax.swing.JCheckBox + + + Tray Icon + 16 + 91 + TRAY_ICON_OPTION + & Tray Icon + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 2 + 11 + 11 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.FormComponent + + embedded.28161976 + CENTER:DEFAULT:NONE + FILL:DEFAULT:NONE,FILL:DEFAULT:NONE,FILL:235PX:NONE,FILL:86PX:NONE,FILL:DEFAULT:NONE,FILL:DEFAULT:NONE + + + + + + + + + 2 + 1 + 1 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + javax.swing.JButton + + + javax.swing.JButton + + + Continue + 22 + 72 + GO_BUTTON + Start + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 5 + 1 + 1 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + javax.swing.JButton + + + javax.swing.JButton + + + Close + 22 + 56 + CANCEL_BUTTON + Cancel + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 3 + 1 + 1 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + com.jeta.forms.components.label.JETALabel + + + com.jeta.forms.components.label.JETALabel + + + 0 + Starting + 15 + + + Tahoma + 1 + 12 + + + 231 + STATE + + + fill + + + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 4 + 1 + 1 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + com.jeta.forms.components.label.JETALabel + + + com.jeta.forms.components.label.JETALabel + + + 12 + 82 + SPEED + + + fill + + + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + com.jeta.forms.gui.form.GridView + + + + + + fill + + + + + dyncolor + constant + 204,204,204 + + + + + + + + + scollBars + 21 + 31 + + + + border + + + + + + + + border + + + + + + + + + + + + + + border + + + + + + + + + + + + + + + + + + + fill + + + + + dyncolor + constant + 153,153,153 + + + + + + + + + fill + + + + + dyncolor + constant + 153,153,153 + + + + + + + + + fill + + + + + dyncolor + constant + 153,153,153 + + + + + + + + + fill + + + + + dyncolor + constant + 153,153,153 + + + + + + + + + fill + + + + + dyncolor + constant + 153,153,153 + + + + + + + + + fill + + + + + dyncolor + constant + 153,153,153 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 12 + 6 + 1 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + javax.swing.JButton + + + javax.swing.JButton + + + ... + 22 + 8 + SELECT_FOLDER_BUTTON + ... + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 12 + 4 + 1 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + javax.swing.JButton + + + javax.swing.JButton + + + ... + 22 + 8 + SHOW_CONF_BUTTON + ... + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 2 + 2 + 11 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + com.jeta.forms.components.label.JETALabel + + + com.jeta.forms.components.label.JETALabel + + + 0 + YAJSW - Java Web Start Booter + 20 + + + Tahoma + 1 + 16 + + + + 490 + + + fill + + + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 12 + 13 + 1 + 3 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + com.jeta.forms.components.border.TitledBorderSide + + + com.jeta.forms.components.border.TitledBorderSide + + + 1 + 196 + + 8 + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + com.jeta.forms.gui.form.GridView + + + + + + fill + + + + + scollBars + 21 + 31 + + + + border + + + + + + + + border + + + + + + + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + fill + + + + + dyncolor + constant + 153,153,153 + + + + + + + + + fill + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/javaUtilities/yajsw/build/abeille/srvmgr_install_dialog.xml b/javaUtilities/yajsw/build/abeille/srvmgr_install_dialog.xml new file mode 100644 index 0000000000..672ac361df --- /dev/null +++ b/javaUtilities/yajsw/build/abeille/srvmgr_install_dialog.xml @@ -0,0 +1,771 @@ + + + + + + 2 + 0 + 0 + + + + + + + + 1 + 1 + 1 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.FormComponent + + Z:\dev\forms\abeille-2.1.0_M2\examples\forms\srvmgr_install_dialog.xml + forms\srvmgr_install_dialog.xml + CENTER:DEFAULT:NONE,CENTER:DEFAULT:NONE,CENTER:DEFAULT:NONE,CENTER:DEFAULT:NONE,CENTER:111PX:NONE,CENTER:4DLU:NONE,CENTER:DEFAULT:NONE,CENTER:12DLU:NONE,CENTER:DEFAULT:NONE,CENTER:DEFAULT:NONE,CENTER:DEFAULT:NONE + FILL:DEFAULT:NONE,FILL:DEFAULT:NONE,FILL:4DLU:NONE,FILL:143PX:NONE,FILL:249PX:NONE,FILL:DEFAULT:NONE + + + + + + + + + 2 + 7 + 1 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + com.jeta.forms.components.label.JETALabel + + + com.jeta.forms.components.label.JETALabel + + + YAJSW Configuration + 14 + + + Tahoma + 1 + 11 + + + + 118 + + + fill + + + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 2 + 9 + 4 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.FormComponent + + embedded.13305839 + CENTER:DEFAULT:NONE + FILL:308PX:NONE,FILL:89PX:NONE,FILL:22PX:NONE,FILL:87PX:NONE + + + + + + + + + 4 + 1 + 1 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + javax.swing.JButton + + + javax.swing.JButton + + + CLOSE + 22 + 83 + CANCEL_BUTTON + Cancel + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 2 + 1 + 1 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + javax.swing.JButton + + + javax.swing.JButton + + + INSTALL + 22 + 85 + OK_BUTTON + OK + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + com.jeta.forms.gui.form.GridView + + + + + + fill + + + + + scollBars + 21 + 31 + + + + border + + + + + + + + border + + + + + + + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 2 + 2 + 4 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + com.jeta.forms.components.label.JETALabel + + + com.jeta.forms.components.label.JETALabel + + + 0 + Install/Reinstall YAJSW Service + 15 + + + Tahoma + 1 + 12 + + + + 516 + + + fill + + + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 4 + 7 + 2 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + javax.swing.JComboBox + + + javax.swing.JComboBox + + + 20 + false + + + items + + + 3 + 388 + CONFIGURATION + + + + border + + + + + + + + border + + + + + + + + + true + + + + + + + + + + + + + + 2 + 10 + 4 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + com.jeta.forms.components.label.JETALabel + + + com.jeta.forms.components.label.JETALabel + + + 12 + 516 + MESSAGE + + + fill + + + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 4 + 4 + 1 + 2 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + javax.swing.JList + + + javax.swing.JList + + + true + 123 + + + items + + + 137 + HOSTS_LIST + + + scollBars + 20 + 30 + + + + border + + + + + + + + border + + + + + + + + + + + + + + border + + + + + + + + border + + + + + + + + + true + + + + + + + + + + + + + + 2 + 4 + 1 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + com.jeta.forms.components.label.JETALabel + + + com.jeta.forms.components.label.JETALabel + + + Hosts + 14 + + + Tahoma + 1 + 11 + + + + 118 + + + fill + + + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + com.jeta.forms.gui.form.GridView + + + + + + fill + + + + + scollBars + 21 + 31 + + + + border + + + + + + + + border + + + + + + + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/javaUtilities/yajsw/build/abeille/srvmgr_newHostDialog.xml b/javaUtilities/yajsw/build/abeille/srvmgr_newHostDialog.xml new file mode 100644 index 0000000000..b8dceda212 --- /dev/null +++ b/javaUtilities/yajsw/build/abeille/srvmgr_newHostDialog.xml @@ -0,0 +1,725 @@ + + + + + + 2 + 0 + 0 + + + + + + + + 1 + 1 + 1 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.FormComponent + + Z:\dev\forms\abeille-2.1.0_M2\examples\forms\newHostDialog.xml + CENTER:DEFAULT:NONE,CENTER:DEFAULT:NONE,CENTER:DEFAULT:NONE,CENTER:DEFAULT:NONE,CENTER:4DLU:NONE,CENTER:DEFAULT:NONE,CENTER:12DLU:NONE,CENTER:DEFAULT:NONE,CENTER:DEFAULT:NONE,CENTER:DEFAULT:NONE + FILL:DEFAULT:NONE,FILL:DEFAULT:NONE,FILL:4DLU:NONE,FILL:143PX:NONE,FILL:120PX:NONE,FILL:DEFAULT:NONE + + + + + + + + + 2 + 6 + 1 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + com.jeta.forms.components.label.JETALabel + + + com.jeta.forms.components.label.JETALabel + + + Port + 14 + + + Tahoma + 1 + 11 + + + 26 + + + + fill + + + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 2 + 2 + 4 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + com.jeta.forms.components.label.JETALabel + + + com.jeta.forms.components.label.JETALabel + + + 0 + Add Host + 15 + + + Tahoma + 1 + 12 + + + 295 + + + + fill + + + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 2 + 4 + 1 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + com.jeta.forms.components.label.JETALabel + + + com.jeta.forms.components.label.JETALabel + + + Host + 14 + + + Tahoma + 1 + 11 + + + 26 + + + + fill + + + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 4 + 4 + 2 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + javax.swing.JTextField + + + javax.swing.JTextField + + + 20 + 259 + HOST + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 2 + 9 + 4 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + com.jeta.forms.components.label.JETALabel + + + com.jeta.forms.components.label.JETALabel + + + 12 + 295 + MESSAGE + + + fill + + + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 4 + 6 + 2 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + javax.swing.JTextField + + + javax.swing.JTextField + + + 20 + 259 + PORT + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 2 + 8 + 4 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.FormComponent + + embedded.7021349 + CENTER:DEFAULT:NONE + FILL:93PX:NONE,FILL:89PX:NONE,FILL:22PX:NONE,FILL:87PX:NONE + + + + + + + + + 4 + 1 + 1 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + javax.swing.JButton + + + javax.swing.JButton + + + CANCEL + 22 + 83 + CANCEL_BUTTON + Cancel + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 2 + 1 + 1 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + javax.swing.JButton + + + javax.swing.JButton + + + ADD HOST + 22 + 85 + OK_BUTTON + OK + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + com.jeta.forms.gui.form.GridView + + + + + + fill + + + + + scollBars + 21 + 31 + + + + border + + + + + + + + border + + + + + + + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + com.jeta.forms.gui.form.GridView + + + + + + fill + + + + + scollBars + 21 + 31 + + + + border + + + + + + + + border + + + + + + + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/javaUtilities/yajsw/build/abeille/srvmgr_reload_console_dialog.xml b/javaUtilities/yajsw/build/abeille/srvmgr_reload_console_dialog.xml new file mode 100644 index 0000000000..ae47a34fb9 --- /dev/null +++ b/javaUtilities/yajsw/build/abeille/srvmgr_reload_console_dialog.xml @@ -0,0 +1,734 @@ + + + + + + 2 + 0 + 0 + + + + + + + + 1 + 1 + 1 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.FormComponent + + Z:\dev\forms\abeille-2.1.0_M2\examples\forms\srvmgr_reload_console.xml + CENTER:DEFAULT:NONE,CENTER:DEFAULT:NONE,CENTER:DEFAULT:NONE,CENTER:DEFAULT:NONE,CENTER:4DLU:NONE,CENTER:DEFAULT:NONE,CENTER:12DLU:NONE,CENTER:DEFAULT:NONE,CENTER:DEFAULT:NONE,CENTER:DEFAULT:NONE + FILL:DEFAULT:NONE,FILL:DEFAULT:NONE,FILL:4DLU:NONE,FILL:143PX:NONE,FILL:249PX:NONE,FILL:DEFAULT:NONE + + + + + + + + + 2 + 6 + 1 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + com.jeta.forms.components.label.JETALabel + + + com.jeta.forms.components.label.JETALabel + + + YAJSW Configuration + 14 + + + Tahoma + 1 + 11 + + + + 118 + + + fill + + + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 2 + 8 + 4 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.FormComponent + + embedded.31326333 + CENTER:DEFAULT:NONE + FILL:308PX:NONE,FILL:89PX:NONE,FILL:22PX:NONE,FILL:87PX:NONE + + + + + + + + + 4 + 1 + 1 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + javax.swing.JButton + + + javax.swing.JButton + + + CLOSE + 22 + 83 + CANCEL_BUTTON + Cancel + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 2 + 1 + 1 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + javax.swing.JButton + + + javax.swing.JButton + + + INSTALL + 22 + 85 + OK_BUTTON + OK + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + com.jeta.forms.gui.form.GridView + + + + + + fill + + + + + scollBars + 21 + 31 + + + + border + + + + + + + + border + + + + + + + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 2 + 2 + 4 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + com.jeta.forms.components.label.JETALabel + + + com.jeta.forms.components.label.JETALabel + + + 0 + Reload YAJSW Console Application + 15 + + + Tahoma + 1 + 12 + + + + 516 + + + fill + + + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 4 + 6 + 2 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + javax.swing.JComboBox + + + javax.swing.JComboBox + + + 20 + false + + + items + + + 3 + 388 + CONFIGURATION + + + + border + + + + + + + + border + + + + + + + + + true + + + + + + + + + + + + + + 2 + 4 + 1 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + com.jeta.forms.components.label.JETALabel + + + com.jeta.forms.components.label.JETALabel + + + Console + 14 + + + Tahoma + 1 + 11 + + + + 118 + + + fill + + + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 4 + 4 + 1 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + javax.swing.JTextField + + + javax.swing.JTextField + + + false + 20 + 139 + SERVICE + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 2 + 9 + 4 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + com.jeta.forms.components.label.JETALabel + + + com.jeta.forms.components.label.JETALabel + + + 12 + 516 + MESSAGE + + + fill + + + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + com.jeta.forms.gui.form.GridView + + + + + + fill + + + + + scollBars + 21 + 31 + + + + border + + + + + + + + border + + + + + + + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/javaUtilities/yajsw/build/abeille/srvmgr_uninstall_dialog.xml b/javaUtilities/yajsw/build/abeille/srvmgr_uninstall_dialog.xml new file mode 100644 index 0000000000..4681e3f88f --- /dev/null +++ b/javaUtilities/yajsw/build/abeille/srvmgr_uninstall_dialog.xml @@ -0,0 +1,508 @@ + + + + + + 2 + 0 + 0 + + + + + + + + 1 + 1 + 1 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.FormComponent + + 6.Do, Dez 3, 2009 - 10:23:25 + CENTER:DEFAULT:NONE,CENTER:DEFAULT:NONE,CENTER:DEFAULT:NONE,CENTER:DEFAULT:NONE,CENTER:DEFAULT:NONE,CENTER:DEFAULT:NONE,CENTER:DEFAULT:NONE,CENTER:DEFAULT:NONE,CENTER:DEFAULT:NONE + FILL:DEFAULT:NONE,FILL:DEFAULT:NONE,FILL:8DLU:NONE + + + + + + + + + 2 + 4 + 1 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + com.jeta.forms.components.label.JETALabel + + + com.jeta.forms.components.label.JETALabel + + + The following services will be removed. Note: only YAJSW can be removed + 14 + + + Tahoma + 1 + 11 + + + 415 + + + + fill + + + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 2 + 6 + 1 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + com.jeta.forms.components.label.JETALabel + + + com.jeta.forms.components.label.JETALabel + + + Host/service name, Host/Service name + 14 + 415 + SERVICES + + + fill + + + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 2 + 8 + 1 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.FormComponent + + embedded.8.Do, Dez 3, 2009 - 10:26:04 + CENTER:DEFAULT:NONE + FILL:280PX:NONE,FILL:DEFAULT:NONE,FILL:DEFAULT:NONE,FILL:DEFAULT:NONE + + + + + + + + + 4 + 1 + 1 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + javax.swing.JButton + + + javax.swing.JButton + + + Cancel + 22 + 64 + CANCEL_BUTTON + Cancel + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 2 + 1 + 1 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + javax.swing.JButton + + + javax.swing.JButton + + + OK + 22 + 46 + OK_BUTTON + OK + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + com.jeta.forms.gui.form.GridView + + + + + + fill + + + + + scollBars + 21 + 31 + + + + + + border + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 2 + 2 + 1 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + com.jeta.forms.components.label.JETALabel + + + com.jeta.forms.components.label.JETALabel + + + 0 + Uninstall Services + 15 + + + Tahoma + 1 + 12 + + + 415 + + + + fill + + + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + com.jeta.forms.gui.form.GridView + + + + + + fill + + + + + scollBars + 21 + 31 + + + + + + border + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/javaUtilities/yajsw/build/abeille/srvmgr_window.xml b/javaUtilities/yajsw/build/abeille/srvmgr_window.xml new file mode 100644 index 0000000000..c0acb57bf9 --- /dev/null +++ b/javaUtilities/yajsw/build/abeille/srvmgr_window.xml @@ -0,0 +1,1354 @@ + + + + + + 2 + 0 + 0 + + + + + + + + 1 + 1 + 1 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.FormComponent + + Z:\dev\forms\abeille-2.1.0_M2\examples\forms\srvmgr.xml + CENTER:DEFAULT:NONE,CENTER:DEFAULT:NONE,CENTER:2DLU:NONE,FILL:PREF:NONE,CENTER:DEFAULT:NONE,CENTER:DEFAULT:NONE,CENTER:2DLU:NONE,CENTER:DEFAULT:NONE,CENTER:DEFAULT:NONE,CENTER:DEFAULT:NONE,CENTER:DEFAULT:NONE,CENTER:60PX:NONE,CENTER:DEFAULT:NONE,CENTER:DEFAULT:NONE,CENTER:DEFAULT:NONE,CENTER:66PX:NONE,CENTER:DEFAULT:NONE,CENTER:DEFAULT:NONE,CENTER:2DLU:NONE,CENTER:95PX:NONE,CENTER:DEFAULT:NONE,CENTER:DEFAULT:NONE,CENTER:DEFAULT:NONE,CENTER:138PX:NONE,CENTER:DEFAULT:NONE + FILL:DEFAULT:NONE,FILL:DEFAULT:NONE,FILL:DEFAULT:NONE,FILL:DEFAULT:NONE,FILL:DEFAULT:NONE,FILL:214PX:NONE,FILL:4DLU:NONE,FILL:56PX:NONE,FILL:4DLU:NONE,FILL:PREF:NONE,FILL:DEFAULT:NONE + + + + + + + + + 8 + 13 + 1 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + javax.swing.JButton + + + javax.swing.JButton + + + >> + 22 + 52 + ADD_HOSTS_BUTTON + 0 + + + Add Services from Host + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 8 + 15 + 1 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + javax.swing.JButton + + + javax.swing.JButton + + + << + 22 + 52 + REMOVE_HOSTS_BUTTON + - + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 8 + 21 + 1 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + javax.swing.JButton + + + javax.swing.JButton + + + << + 22 + 52 + ADD_HIDDEN_BUTTON + + + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 8 + 23 + 1 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + javax.swing.JButton + + + javax.swing.JButton + + + >> + 22 + 52 + REMOVE_HIDDEN_BUTTON + - + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 2 + 18 + 5 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + com.jeta.forms.components.label.JETALabel + + + com.jeta.forms.components.label.JETALabel + + + 0 + Hidden Services + 14 + + + Tahoma + 1 + 11 + + + + 274 + + + fill + + + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 2 + 20 + 5 + 5 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + javax.swing.JTable + + + javax.swing.JTable + + + 32 + 272 + HIDDEN_TABLE + + + scollBars + 20 + 30 + + + + border + + + + + + + + border + + + + + + + + + + + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 10 + 4 + 1 + 3 + fill + fill + 0,0,0,0 + + + com.jeta.forms.gui.form.FormComponent + + embedded.17546562 + CENTER:DEFAULT:NONE + FILL:DEFAULT:NONE,FILL:DEFAULT:NONE,FILL:DEFAULT:NONE,FILL:39PX:NONE,FILL:DEFAULT:NONE,FILL:DEFAULT:NONE,FILL:DEFAULT:NONE,FILL:68PX:NONE,FILL:DEFAULT:NONE,FILL:10PX:NONE + + + + + + + + + 1 + 1 + 1 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + javax.swing.JButton + + + javax.swing.JButton + + + Start + 17 + 56 + START_BUTTON + Start + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 3 + 1 + 1 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + javax.swing.JButton + + + javax.swing.JButton + + + Stop + 17 + 54 + STOP_BUTTON + Stop + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 5 + 1 + 1 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + javax.swing.JButton + + + javax.swing.JButton + + + Install + 17 + 61 + INSTALL_BUTTON + Install + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 7 + 1 + 1 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + javax.swing.JButton + + + javax.swing.JButton + + + Uninstall + 17 + 72 + UNINSTALL_BUTTON + Uninstall + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 9 + 1 + 1 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + javax.swing.JButton + + + javax.swing.JButton + + + ReloadConsoleApp + 17 + 122 + RELOAD_CONSOLE + ReloadConsoleApp + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + com.jeta.forms.gui.form.GridView + + + + + + fill + + + + + scollBars + 21 + 31 + + + + border + + + + + + + + border + + + + + + + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 10 + 2 + 1 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + com.jeta.forms.components.label.JETALabel + + + com.jeta.forms.components.label.JETALabel + + + 0 + Services + 14 + + + Tahoma + 1 + 11 + + + + 535 + + + fill + + + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 2 + 4 + 5 + 13 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + javax.swing.JTable + + + javax.swing.JTable + + + 32 + 272 + HOSTS_TABLE + + + scollBars + 20 + 30 + + + + border + + + + + + + + border + + + + + + + + + + + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 2 + 2 + 5 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + com.jeta.forms.components.label.JETALabel + + + com.jeta.forms.components.label.JETALabel + + + 0 + Hosts + 14 + + + Tahoma + 1 + 11 + + + + 274 + + + fill + + + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 8 + 11 + 1 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + javax.swing.JButton + + + javax.swing.JButton + + + - + 22 + 52 + DELETE_HOST_BUTTON + - + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 8 + 9 + 1 + 1 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + javax.swing.JButton + + + javax.swing.JButton + + + + + 22 + 52 + NEW_HOST_BUTTON + + + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + 10 + 8 + 1 + 17 + default + default + 0,0,0,0 + + + com.jeta.forms.gui.form.StandardComponent + + com.jeta.forms.gui.beans.JETABean + javax.swing.JTable + + + javax.swing.JTable + + + 32 + 533 + SERVICES_TABLE + + + scollBars + 20 + 30 + + + + border + + + + + + + + border + + + + + + + + + + + + + + border + + + + + + + + border + + + + + + + + + + + + + + + + + + + + com.jeta.forms.gui.form.GridView + + + + + + fill + + + + + scollBars + 21 + 31 + + + + border + + + + + + + + border + + + + + + + + + + + + + + border + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/javaUtilities/yajsw/build/gradle/.gradle/0.9.2/taskArtifacts/cache.bin b/javaUtilities/yajsw/build/gradle/.gradle/0.9.2/taskArtifacts/cache.bin new file mode 100644 index 0000000000..24b5d09730 Binary files /dev/null and b/javaUtilities/yajsw/build/gradle/.gradle/0.9.2/taskArtifacts/cache.bin differ diff --git a/javaUtilities/yajsw/build/gradle/.gradle/0.9.2/taskArtifacts/cache.properties b/javaUtilities/yajsw/build/gradle/.gradle/0.9.2/taskArtifacts/cache.properties new file mode 100644 index 0000000000..4a6916e1f8 --- /dev/null +++ b/javaUtilities/yajsw/build/gradle/.gradle/0.9.2/taskArtifacts/cache.properties @@ -0,0 +1 @@ +#Tue Feb 19 12:06:40 CST 2013 diff --git a/javaUtilities/yajsw/build/gradle/ahessian/build.gradle b/javaUtilities/yajsw/build/gradle/ahessian/build.gradle new file mode 100644 index 0000000000..0d144ace40 --- /dev/null +++ b/javaUtilities/yajsw/build/gradle/ahessian/build.gradle @@ -0,0 +1,18 @@ +projectHome = '../../..' + +sourceSets.main.java.srcDirs = ["$projectHome/src/ahessian"] + +// this project uses the ahessian sub-project +dependencies { + compile project(':hessian4') + compile group: 'netty', name: 'netty', version: "$netty_version" +} + +println '---------------------------' +println 'dependency jars:' +println '---------------------------' +configurations.compile.each { File file -> println file.absolutePath } +println '---------------------------' + +jar.baseName = 'ahessian' + diff --git a/javaUtilities/yajsw/build/gradle/build.gradle b/javaUtilities/yajsw/build/gradle/build.gradle new file mode 100644 index 0000000000..7b7011e2bd --- /dev/null +++ b/javaUtilities/yajsw/build/gradle/build.gradle @@ -0,0 +1,69 @@ +task createWrapper(type: Wrapper) { + gradleVersion = '0.9.2' +} + +projectHome = '../..' +defaultTasks 'clean', 'build' + +// common settings for all sub-projects +subprojects { + +apply plugin: 'java' +defaultTasks 'clean', 'build' + +coreLibsDir = "$projectHome/lib/core" +extendedLibsDir = "$projectHome/lib/extended" + +// dependency versions + +commons_cli_version = '2-SNAPSHOT' +commons_collections_version = '3.2' +commons_configuration_version = '1.8' +commons_io_version = '1.3.1' +commons_lang_version = '2.4' +commons_logging_version = '1.1' +commons_vfs2_version = '2.0' +groovy_all_version = '1.8.6' +jna_version = '3.4.1' +platform_version = '3.4.1' +netty_version = '3.5.1.Final' +jrexx_version = '1.1.1' +quartz_version = '1.8.0' +formsrt_version = '' +forms_version = '1.2.0' +velocity_version = '1.6.3' +glazedlists_version = '1.8.0_java15' +commons_beanutils_version = '1.8.2' + + + + +// for simplicity and to avoid dependency issues which arise due to the usage of SNAPSHOT libs +// we use libs from local disk instead of public repository +repositories { + flatDir name: 'localRepository', + dirs: [ + "$coreLibsDir/commons", + "$coreLibsDir/groovy", + "$coreLibsDir/jna", + "$coreLibsDir/netty", + "$coreLibsDir/regex", + "$coreLibsDir/yajsw", + "$extendedLibsDir/quartz", + "$extendedLibsDir/abeille", + "$extendedLibsDir/jgoodies", + "$extendedLibsDir/glazedlists", + "$extendedLibsDir/velocity" + ] + } + + + + +sourceCompatibility = 1.5 +version = '' + +} + + + diff --git a/javaUtilities/yajsw/build/gradle/gradle/wrapper/gradle-wrapper.jar b/javaUtilities/yajsw/build/gradle/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000..9d7bbe005f Binary files /dev/null and b/javaUtilities/yajsw/build/gradle/gradle/wrapper/gradle-wrapper.jar differ diff --git a/javaUtilities/yajsw/build/gradle/gradle/wrapper/gradle-wrapper.properties b/javaUtilities/yajsw/build/gradle/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000000..75dfa96aae --- /dev/null +++ b/javaUtilities/yajsw/build/gradle/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Thu Feb 10 17:06:13 CET 2011 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=gradle-0.9.2-bin.zip diff --git a/javaUtilities/yajsw/build/gradle/gradlew.bat b/javaUtilities/yajsw/build/gradle/gradlew.bat new file mode 100644 index 0000000000..4b82abb6d7 --- /dev/null +++ b/javaUtilities/yajsw/build/gradle/gradlew.bat @@ -0,0 +1,84 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem ## +@rem Gradle startup script for Windows ## +@rem ## +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set JAVA_OPTS=%JAVA_OPTS% -Dhttp.proxyHost=srvproxy.init-ka.lan -Dhttp.proxyPort=8080 + +@rem Uncomment those lines to set JVM options. GRADLE_OPTS and JAVA_OPTS can be used together. +@rem set GRADLE_OPTS=%GRADLE_OPTS% -Xmx512m +@rem set JAVA_OPTS=%JAVA_OPTS% -Xmx512m + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=.\ + +@rem Find java.exe +set JAVA_EXE=java.exe +if not defined JAVA_HOME goto init + +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. +echo. +goto end + +:init +@rem Get command-line arguments, handling Windowz variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%eval[2+2]" == "4" goto 4NT_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +set CMD_LINE_ARGS=%$ + +:execute +@rem Setup the command line + +set STARTER_MAIN_CLASS=org.gradle.wrapper.GradleWrapperMain +set CLASSPATH=%DIRNAME%\gradle\wrapper\gradle-wrapper.jar +set WRAPPER_PROPERTIES=%DIRNAME%\gradle\wrapper\gradle-wrapper.properties + +set GRADLE_OPTS=%JAVA_OPTS% %GRADLE_OPTS% -Dorg.gradle.wrapper.properties="%WRAPPER_PROPERTIES%" + +@rem Execute Gradle +"%JAVA_EXE%" %GRADLE_OPTS% -classpath "%CLASSPATH%" %STARTER_MAIN_CLASS% %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +if not "%OS%"=="Windows_NT" echo 1 > nul | choice /n /c:1 + +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit "%ERRORLEVEL%" +exit /b "%ERRORLEVEL%" + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega \ No newline at end of file diff --git a/javaUtilities/yajsw/build/gradle/gradlew.sh b/javaUtilities/yajsw/build/gradle/gradlew.sh new file mode 100644 index 0000000000..e806a754c3 --- /dev/null +++ b/javaUtilities/yajsw/build/gradle/gradlew.sh @@ -0,0 +1,169 @@ +#!/bin/bash + +############################################################################## +## ## +## Gradle wrapper script for UN*X ## +## ## +############################################################################## + +# Uncomment those lines to set JVM options. GRADLE_OPTS and JAVA_OPTS can be used together. +# GRADLE_OPTS="$GRADLE_OPTS -Xmx512m" +# JAVA_OPTS="$JAVA_OPTS -Xmx512m" +JAVA_OPTS="$JAVA_OPTS -Dhttp.pxoyPort=80 -Dhttp.proxyHost=http://proxy.ext.ray.com" + +GRADLE_APP_NAME=Gradle + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; +esac + +# Attempt to set JAVA_HOME if it's not already set. +if [ -z "$JAVA_HOME" ] ; then + if $darwin ; then + [ -z "$JAVA_HOME" -a -d "/Library/Java/Home" ] && export JAVA_HOME="/Library/Java/Home" + [ -z "$JAVA_HOME" -a -d "/System/Library/Frameworks/JavaVM.framework/Home" ] && export JAVA_HOME="/System/Library/Frameworks/JavaVM.framework/Home" + else + javaExecutable="`which javac`" + [ -z "$javaExecutable" -o "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ] && die "JAVA_HOME not set and cannot find javac to deduce location, please set JAVA_HOME." + # readlink(1) is not available as standard on Solaris 10. + readLink=`which readlink` + [ `expr "$readLink" : '\([^ ]*\)'` = "no" ] && die "JAVA_HOME not set and readlink not available, please set JAVA_HOME." + javaExecutable="`readlink -f \"$javaExecutable\"`" + javaHome="`dirname \"$javaExecutable\"`" + javaHome=`expr "$javaHome" : '\(.*\)/bin'` + export JAVA_HOME="$javaHome" + fi +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched. +if $cygwin ; then + [ -n "$JAVACMD" ] && JAVACMD=`cygpath --unix "$JAVACMD"` + [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` +fi + +STARTER_MAIN_CLASS=org.gradle.wrapper.GradleWrapperMain +CLASSPATH=`dirname "$0"`/gradle/wrapper/gradle-wrapper.jar +WRAPPER_PROPERTIES=`dirname "$0"`/gradle/wrapper/gradle-wrapper.properties +# Determine the Java command to use to start the JVM. +if [ -z "$JAVACMD" ] ; then + if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + else + JAVACMD="java" + fi +fi +if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi +if [ -z "$JAVA_HOME" ] ; then + warn "JAVA_HOME environment variable is not set" +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query businessSystem maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add GRADLE_APP_NAME to the JAVA_OPTS as -Xdock:name +if $darwin; then + JAVA_OPTS="$JAVA_OPTS -Xdock:name=$GRADLE_APP_NAME" +# we may also want to set -Xdock:image +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + JAVA_HOME=`cygpath --path --mixed "$JAVA_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +GRADLE_APP_BASE_NAME=`basename "$0"` + +exec "$JAVACMD" $JAVA_OPTS $GRADLE_OPTS \ + -classpath "$CLASSPATH" \ + -Dorg.gradle.appname="$GRADLE_APP_BASE_NAME" \ + -Dorg.gradle.wrapper.properties="$WRAPPER_PROPERTIES" \ + $STARTER_MAIN_CLASS \ + "$@" diff --git a/javaUtilities/yajsw/build/gradle/hessian4/build.gradle b/javaUtilities/yajsw/build/gradle/hessian4/build.gradle new file mode 100644 index 0000000000..fafba67e42 --- /dev/null +++ b/javaUtilities/yajsw/build/gradle/hessian4/build.gradle @@ -0,0 +1,16 @@ +projectHome = '../../..' + +sourceSets.main.java.srcDirs = ["$projectHome/src/hessian"] + +dependencies { + compile group: 'netty', name: 'netty', version: "$netty_version" +} + +println '---------------------------' +println 'dependency jars:' +println '---------------------------' +configurations.compile.each { File file -> println file.absolutePath } +println '---------------------------' + +jar.baseName = 'hessian4' + diff --git a/javaUtilities/yajsw/build/gradle/readMe.txt b/javaUtilities/yajsw/build/gradle/readMe.txt new file mode 100644 index 0000000000..d2f853f8c1 --- /dev/null +++ b/javaUtilities/yajsw/build/gradle/readMe.txt @@ -0,0 +1,25 @@ +YAJSW uses gradle as build tool: + +http://www.gradle.org/ + +The build is a multi project script. Gradle requires per project a build folder. +The following build folders are used: + +wrapper -> wrapper.jar main yajsw jar +wrapper-app -> wrapperApp.jar wraps the application +ahessian -> ahessian.jar netty/hessian based asynch communication between wrapper and system tray icon. hessian packages renamed to avoid conflict with existing hessian libs +srvmgr -> srvmgr.jar experimental - monitoring of multiple servers + +To execute a build: +- /build/gradle +- Navigte with a console to /build/gradle +- If you are behind a http proxy edit gradlew.bat/gardlew.sh and set the proxy in the java options +- execute gradlew + +This will download gradle and execute the build script. + +The produced jar files are found in the folders: + +/build/gradle//build/libs + +Eclipse project files can be generated by adding the according gradle tasks to the gradle build scripts. \ No newline at end of file diff --git a/javaUtilities/yajsw/build/gradle/settings.gradle b/javaUtilities/yajsw/build/gradle/settings.gradle new file mode 100644 index 0000000000..45ebd48b43 --- /dev/null +++ b/javaUtilities/yajsw/build/gradle/settings.gradle @@ -0,0 +1,2 @@ +// defines a list of subprojects +include "wrapper", "wrapper-app", "ahessian", "hessian4", "srvmgr" diff --git a/javaUtilities/yajsw/build/gradle/srvmgr/build.gradle b/javaUtilities/yajsw/build/gradle/srvmgr/build.gradle new file mode 100644 index 0000000000..8a8f352b25 --- /dev/null +++ b/javaUtilities/yajsw/build/gradle/srvmgr/build.gradle @@ -0,0 +1,32 @@ +projectHome = '../../..' + +sourceSets.main.java.srcDirs = ["$projectHome/src/srvmgr/java"] + +dependencies { + compile project(':wrapper') + compile project(':ahessian') +} + +dependencies { + compile group: 'netty', name: 'netty', version: "$netty_version" + compile group: 'glazedlists', name: 'glazedlists', version: "$glazedlists_version" + compile group: 'commons-beanutils', name: 'commons-beanutils', version: "$commons_beanutils_version" + compile group: 'commons-configuration', name: 'commons-configuration', version: "$commons_configuration_version" + compile group: 'groovy-all', name: 'groovy-all', version: "$groovy_all_version" + compile group: 'jna', name: 'jna', version: "$jna_version" + compile group: 'platform', name: 'platform', version: "$platform_version" + compile group: 'abeille', name: 'formsrt', version: "$formsrt_version" + compile group: 'forms', name: 'forms', version: "$forms_version" + +} + +//println '---------------------------' +//println 'dependency jars:' +//println '---------------------------' +//configurations.compile.each { File file -> println file.absolutePath } +//println '---------------------------' + +// name of jar +jar.baseName = 'srvmgr' + +// use default manifest \ No newline at end of file diff --git a/javaUtilities/yajsw/build/gradle/wrapper-app/build.gradle b/javaUtilities/yajsw/build/gradle/wrapper-app/build.gradle new file mode 100644 index 0000000000..29040a5c5b --- /dev/null +++ b/javaUtilities/yajsw/build/gradle/wrapper-app/build.gradle @@ -0,0 +1,31 @@ +projectHome = '../../..' +sourceSets.main.java.srcDirs = ["$projectHome/src/app"] + +dependencies { + compile project(':wrapper') +} + +dependencies { + compile group: 'commons-collections', name: 'commons-collections', version: "$commons_collections_version" + compile group: 'commons-configuration', name: 'commons-configuration', version: "$commons_configuration_version" + compile group: 'commons-io', name: 'commons-io', version: "$commons_io_version" + compile group: 'commons-lang', name: 'commons-lang', version: "$commons_lang_version" + compile group: 'commons-logging', name: 'commons-logging', version: "$commons_logging_version" + compile group: 'netty', name: 'netty', version: "$netty_version" +} + +//println '---------------------------' +//println 'dependency jars:' +//println '---------------------------' +//configurations.compile.each { File file -> println file.absolutePath } +//println '---------------------------' + +// name of jar file +jar.baseName = 'wrapperApp' + +// TODO generate manifest +jar { + manifest { + from '../../MANIFEST.MF' + } +} diff --git a/javaUtilities/yajsw/build/gradle/wrapper/build.gradle b/javaUtilities/yajsw/build/gradle/wrapper/build.gradle new file mode 100644 index 0000000000..ba679a9f73 --- /dev/null +++ b/javaUtilities/yajsw/build/gradle/wrapper/build.gradle @@ -0,0 +1,54 @@ +projectHome = '../../..' + +// set the sources for this build +sourceSets.main.java.srcDirs = ["$projectHome/src/main/java", "$projectHome/src/app/java"] +sourceSets.main.resources.srcDirs = ["$projectHome/src/main/java"] +sourceSets.main.resources.includes = ['resources/*'] + +// this project uses the ahessian sub-project +dependencies { + compile project(':ahessian') +} + +dependencies { + compile group: 'commons-cli', name: 'commons-cli', version: "$commons_cli_version" + compile group: 'commons-collections', name: 'commons-collections', version: "$commons_collections_version" + compile group: 'commons-configuration', name: 'commons-configuration', version: "$commons_configuration_version" + compile group: 'commons-io', name: 'commons-io', version: "$commons_io_version" + compile group: 'commons-lang', name: 'commons-lang', version: "$commons_lang_version" + compile group: 'commons-logging', name: 'commons-logging', version: "$commons_logging_version" + compile group: 'commons-vfs2', name: 'commons-vfs2', version: "$commons_vfs2_version" + compile group: 'groovy-all', name: 'groovy-all', version: "$groovy_all_version" + compile group: 'jna', name: 'jna', version: "$jna_version" + compile group: 'platform', name: 'platform', version: "$platform_version" + compile group: 'netty', name: 'netty', version: "$netty_version" + compile group: 'jrexx', name: 'jrexx', version: "$jrexx_version" + compile group: 'quartz', name: 'quartz', version: "$quartz_version" + compile group: 'abeille', name: 'formsrt', version: "$formsrt_version" + compile group: 'forms', name: 'forms', version: "$forms_version" + compile group: 'velocity', name: 'velocity', version: "$velocity_version" +} + +//println '---------------------------' +//println 'dependency jars:' +//println '---------------------------' +//configurations.compile.each { File file -> println file.absolutePath } +//println '---------------------------' + +// name of jar file +jar.baseName = 'wrapper' + +// TODO generate manifest instead of editing +jar { + manifest { + from '../../MANIFEST.MF' + } + exclude 'org/rzo/yajsw/app/AbstractWrapperJVMMain*' + exclude 'org/rzo/yajsw/app/WrapperGroovyMain*' + exclude 'org/rzo/yajsw/app/WrapperJVMMain*' + exclude 'org/rzo/yajsw/app/WrapperMainServiceUnix*' + exclude 'org/rzo/yajsw/app/WrapperMainServiceWin*' + exclude 'org/rzo/yajsw/app/WrapperManager.class' + exclude 'org/rzo/yajsw/app/WrapperManagerClassLoader*' + exclude 'org/rzo/yajsw/app/WrapperManagerProxy*' +} diff --git a/javaUtilities/yajsw/build/ws/ReadMe.txt b/javaUtilities/yajsw/build/ws/ReadMe.txt new file mode 100644 index 0000000000..b65aed80f4 --- /dev/null +++ b/javaUtilities/yajsw/build/ws/ReadMe.txt @@ -0,0 +1,4 @@ +These are files used to sign wrapperWS.jar +It is taken from + +http://weblogs.java.net/blog/kirillcool/archive/2005/05/signing_jars_fo.html \ No newline at end of file diff --git a/javaUtilities/yajsw/build/ws/genKeyStore.bat b/javaUtilities/yajsw/build/ws/genKeyStore.bat new file mode 100644 index 0000000000..cac2e65765 --- /dev/null +++ b/javaUtilities/yajsw/build/ws/genKeyStore.bat @@ -0,0 +1 @@ +keytool -genkey -keystore jaxb.keys -alias https://jaxb-workshop.dev.java.net/ -validity 1491 diff --git a/javaUtilities/yajsw/build/ws/jaxb.keys b/javaUtilities/yajsw/build/ws/jaxb.keys new file mode 100644 index 0000000000..ac114c0dd6 Binary files /dev/null and b/javaUtilities/yajsw/build/ws/jaxb.keys differ diff --git a/javaUtilities/yajsw/build/ws/sign.bat b/javaUtilities/yajsw/build/ws/sign.bat new file mode 100644 index 0000000000..6979272991 --- /dev/null +++ b/javaUtilities/yajsw/build/ws/sign.bat @@ -0,0 +1 @@ +jarsigner -keystore jaxb.keys -storepass testyajsw ../wrapperWS.jar https://jaxb-workshop.dev.java.net/ \ No newline at end of file diff --git a/javaUtilities/yajsw/lib/core/ReadMe.txt b/javaUtilities/yajsw/lib/core/ReadMe.txt new file mode 100644 index 0000000000..3878b72511 --- /dev/null +++ b/javaUtilities/yajsw/lib/core/ReadMe.txt @@ -0,0 +1,23 @@ +This is the minimal set of libraries required by YAJSW + +It includes the following core functionalities: + +All platforms + * runConsole + * filter triggers + * regex filter triggers + * logging + * restart on exit code + * groovy scripting + * network start + +Windows + * installService, uninstallService, startService, stopService + +It does not include the following functions: + + * installDaemon, uninstallDaemon, startDaemon, stopDaemon + * system tray icon + * timers + * services manager + * network start per webdav or http or ftp \ No newline at end of file diff --git a/javaUtilities/yajsw/lib/core/commons/commons-cli-2-SNAPSHOT.jar b/javaUtilities/yajsw/lib/core/commons/commons-cli-2-SNAPSHOT.jar new file mode 100644 index 0000000000..7819797827 Binary files /dev/null and b/javaUtilities/yajsw/lib/core/commons/commons-cli-2-SNAPSHOT.jar differ diff --git a/javaUtilities/yajsw/lib/core/commons/commons-collections-3.2.jar b/javaUtilities/yajsw/lib/core/commons/commons-collections-3.2.jar new file mode 100644 index 0000000000..75580be255 Binary files /dev/null and b/javaUtilities/yajsw/lib/core/commons/commons-collections-3.2.jar differ diff --git a/javaUtilities/yajsw/lib/core/commons/commons-configuration-1.8.jar b/javaUtilities/yajsw/lib/core/commons/commons-configuration-1.8.jar new file mode 100644 index 0000000000..ae9ae9969b Binary files /dev/null and b/javaUtilities/yajsw/lib/core/commons/commons-configuration-1.8.jar differ diff --git a/javaUtilities/yajsw/lib/core/commons/commons-io-1.3.1.jar b/javaUtilities/yajsw/lib/core/commons/commons-io-1.3.1.jar new file mode 100644 index 0000000000..7affdefcd2 Binary files /dev/null and b/javaUtilities/yajsw/lib/core/commons/commons-io-1.3.1.jar differ diff --git a/javaUtilities/yajsw/lib/core/commons/commons-lang-2.4.jar b/javaUtilities/yajsw/lib/core/commons/commons-lang-2.4.jar new file mode 100644 index 0000000000..532939ecab Binary files /dev/null and b/javaUtilities/yajsw/lib/core/commons/commons-lang-2.4.jar differ diff --git a/javaUtilities/yajsw/lib/core/commons/commons-logging-1.1.jar b/javaUtilities/yajsw/lib/core/commons/commons-logging-1.1.jar new file mode 100644 index 0000000000..2ff9bbd90d Binary files /dev/null and b/javaUtilities/yajsw/lib/core/commons/commons-logging-1.1.jar differ diff --git a/javaUtilities/yajsw/lib/core/commons/commons-vfs2-2.0.jar b/javaUtilities/yajsw/lib/core/commons/commons-vfs2-2.0.jar new file mode 100644 index 0000000000..5e1b0b4d2f Binary files /dev/null and b/javaUtilities/yajsw/lib/core/commons/commons-vfs2-2.0.jar differ diff --git a/javaUtilities/yajsw/lib/core/groovy/groovy-all-1.8.6.jar b/javaUtilities/yajsw/lib/core/groovy/groovy-all-1.8.6.jar new file mode 100644 index 0000000000..03c02efae3 Binary files /dev/null and b/javaUtilities/yajsw/lib/core/groovy/groovy-all-1.8.6.jar differ diff --git a/javaUtilities/yajsw/lib/core/jna/jna-3.4.1.jar b/javaUtilities/yajsw/lib/core/jna/jna-3.4.1.jar new file mode 100644 index 0000000000..8e6d0c32a3 Binary files /dev/null and b/javaUtilities/yajsw/lib/core/jna/jna-3.4.1.jar differ diff --git a/javaUtilities/yajsw/lib/core/jna/platform-3.4.1.jar b/javaUtilities/yajsw/lib/core/jna/platform-3.4.1.jar new file mode 100644 index 0000000000..8357d2e214 Binary files /dev/null and b/javaUtilities/yajsw/lib/core/jna/platform-3.4.1.jar differ diff --git a/javaUtilities/yajsw/lib/core/netty/netty-3.5.1.Final.jar b/javaUtilities/yajsw/lib/core/netty/netty-3.5.1.Final.jar new file mode 100644 index 0000000000..2c6f4b05b7 Binary files /dev/null and b/javaUtilities/yajsw/lib/core/netty/netty-3.5.1.Final.jar differ diff --git a/javaUtilities/yajsw/lib/core/regex/jrexx-1.1.1.jar b/javaUtilities/yajsw/lib/core/regex/jrexx-1.1.1.jar new file mode 100644 index 0000000000..5968c9a0a1 Binary files /dev/null and b/javaUtilities/yajsw/lib/core/regex/jrexx-1.1.1.jar differ diff --git a/javaUtilities/yajsw/lib/core/yajsw/ahessian.jar b/javaUtilities/yajsw/lib/core/yajsw/ahessian.jar new file mode 100644 index 0000000000..6a86db57b0 Binary files /dev/null and b/javaUtilities/yajsw/lib/core/yajsw/ahessian.jar differ diff --git a/javaUtilities/yajsw/lib/extended/ReadMe.txt b/javaUtilities/yajsw/lib/extended/ReadMe.txt new file mode 100644 index 0000000000..078c77b8e5 --- /dev/null +++ b/javaUtilities/yajsw/lib/extended/ReadMe.txt @@ -0,0 +1,25 @@ +The following libraries provide extended functionalites to YAJSW: + +Timers require: + * quartz + +Posix Installation of daemons requires: + * velocity + +System tray requires: + * abeille + * jgoodies + * yajsw + +ServiceManager requires: + * abeille + * jgoodies + * glazed lists + * hessian + +Network loading in general + * commons + +Network loading per webdav + * vfs-webdav + diff --git a/javaUtilities/yajsw/lib/extended/abeille/formsrt.jar b/javaUtilities/yajsw/lib/extended/abeille/formsrt.jar new file mode 100644 index 0000000000..cd94310604 Binary files /dev/null and b/javaUtilities/yajsw/lib/extended/abeille/formsrt.jar differ diff --git a/javaUtilities/yajsw/lib/extended/commons/commons-codec-1.3.jar b/javaUtilities/yajsw/lib/extended/commons/commons-codec-1.3.jar new file mode 100644 index 0000000000..957b6752af Binary files /dev/null and b/javaUtilities/yajsw/lib/extended/commons/commons-codec-1.3.jar differ diff --git a/javaUtilities/yajsw/lib/extended/commons/commons-httpclient-3.0.1.jar b/javaUtilities/yajsw/lib/extended/commons/commons-httpclient-3.0.1.jar new file mode 100644 index 0000000000..cfc777c71d Binary files /dev/null and b/javaUtilities/yajsw/lib/extended/commons/commons-httpclient-3.0.1.jar differ diff --git a/javaUtilities/yajsw/lib/extended/commons/commons-net-1.4.1.jar b/javaUtilities/yajsw/lib/extended/commons/commons-net-1.4.1.jar new file mode 100644 index 0000000000..9666a92c80 Binary files /dev/null and b/javaUtilities/yajsw/lib/extended/commons/commons-net-1.4.1.jar differ diff --git a/javaUtilities/yajsw/lib/extended/glazedlists/commons-beanutils-1.8.2.jar b/javaUtilities/yajsw/lib/extended/glazedlists/commons-beanutils-1.8.2.jar new file mode 100644 index 0000000000..6baf5ecbab Binary files /dev/null and b/javaUtilities/yajsw/lib/extended/glazedlists/commons-beanutils-1.8.2.jar differ diff --git a/javaUtilities/yajsw/lib/extended/glazedlists/glazedlists-1.8.0_java15.jar b/javaUtilities/yajsw/lib/extended/glazedlists/glazedlists-1.8.0_java15.jar new file mode 100644 index 0000000000..9da3532145 Binary files /dev/null and b/javaUtilities/yajsw/lib/extended/glazedlists/glazedlists-1.8.0_java15.jar differ diff --git a/javaUtilities/yajsw/lib/extended/jgoodies/forms-1.2.0.jar b/javaUtilities/yajsw/lib/extended/jgoodies/forms-1.2.0.jar new file mode 100644 index 0000000000..83f1f60818 Binary files /dev/null and b/javaUtilities/yajsw/lib/extended/jgoodies/forms-1.2.0.jar differ diff --git a/javaUtilities/yajsw/lib/extended/quartz/quartz-1.8.0.jar b/javaUtilities/yajsw/lib/extended/quartz/quartz-1.8.0.jar new file mode 100644 index 0000000000..ed7d614139 Binary files /dev/null and b/javaUtilities/yajsw/lib/extended/quartz/quartz-1.8.0.jar differ diff --git a/javaUtilities/yajsw/lib/extended/velocity/velocity-1.6.3.jar b/javaUtilities/yajsw/lib/extended/velocity/velocity-1.6.3.jar new file mode 100644 index 0000000000..c6a2bd7e3b Binary files /dev/null and b/javaUtilities/yajsw/lib/extended/velocity/velocity-1.6.3.jar differ diff --git a/javaUtilities/yajsw/lib/extended/vfs-webdav/jackrabbit-webdav-1.5.6.jar b/javaUtilities/yajsw/lib/extended/vfs-webdav/jackrabbit-webdav-1.5.6.jar new file mode 100644 index 0000000000..c76530de7d Binary files /dev/null and b/javaUtilities/yajsw/lib/extended/vfs-webdav/jackrabbit-webdav-1.5.6.jar differ diff --git a/javaUtilities/yajsw/lib/extended/vfs-webdav/slf4j-api-1.5.0.jar b/javaUtilities/yajsw/lib/extended/vfs-webdav/slf4j-api-1.5.0.jar new file mode 100644 index 0000000000..f4f06f0412 Binary files /dev/null and b/javaUtilities/yajsw/lib/extended/vfs-webdav/slf4j-api-1.5.0.jar differ diff --git a/javaUtilities/yajsw/lib/extended/vfs-webdav/slf4j-jdk14-1.5.0.jar b/javaUtilities/yajsw/lib/extended/vfs-webdav/slf4j-jdk14-1.5.0.jar new file mode 100644 index 0000000000..4f96eb8c31 Binary files /dev/null and b/javaUtilities/yajsw/lib/extended/vfs-webdav/slf4j-jdk14-1.5.0.jar differ diff --git a/javaUtilities/yajsw/lib/extended/vfs-webdav/xercesImpl.jar b/javaUtilities/yajsw/lib/extended/vfs-webdav/xercesImpl.jar new file mode 100644 index 0000000000..8f762e1a85 Binary files /dev/null and b/javaUtilities/yajsw/lib/extended/vfs-webdav/xercesImpl.jar differ diff --git a/javaUtilities/yajsw/lib/extended/yajsw/hessian4.jar b/javaUtilities/yajsw/lib/extended/yajsw/hessian4.jar new file mode 100644 index 0000000000..d7ae876aed Binary files /dev/null and b/javaUtilities/yajsw/lib/extended/yajsw/hessian4.jar differ diff --git a/javaUtilities/yajsw/lib/extended/yajsw/srvmgr.jar b/javaUtilities/yajsw/lib/extended/yajsw/srvmgr.jar new file mode 100644 index 0000000000..f3f7d0ff67 Binary files /dev/null and b/javaUtilities/yajsw/lib/extended/yajsw/srvmgr.jar differ diff --git a/javaUtilities/yajsw/lib/groovy/ReadMe.txt b/javaUtilities/yajsw/lib/groovy/ReadMe.txt new file mode 100644 index 0000000000..63120644c2 --- /dev/null +++ b/javaUtilities/yajsw/lib/groovy/ReadMe.txt @@ -0,0 +1,11 @@ +The following libraries are loaded when a groovy script is executed. +To add libraries required by groovy scripts create a folder here and add the jar files to the folder. + +sendMail.gv requires: + * mail + +sendSnmpTrap.gv requires: + * snmp + +timeCondition.gv requires: + * joda \ No newline at end of file diff --git a/javaUtilities/yajsw/lib/groovy/joda/joda-time-1.6.jar b/javaUtilities/yajsw/lib/groovy/joda/joda-time-1.6.jar new file mode 100644 index 0000000000..68068a4bcc Binary files /dev/null and b/javaUtilities/yajsw/lib/groovy/joda/joda-time-1.6.jar differ diff --git a/javaUtilities/yajsw/lib/groovy/mail/activation.jar b/javaUtilities/yajsw/lib/groovy/mail/activation.jar new file mode 100644 index 0000000000..29a59a9ee1 Binary files /dev/null and b/javaUtilities/yajsw/lib/groovy/mail/activation.jar differ diff --git a/javaUtilities/yajsw/lib/groovy/mail/mail.jar b/javaUtilities/yajsw/lib/groovy/mail/mail.jar new file mode 100644 index 0000000000..59543774f2 Binary files /dev/null and b/javaUtilities/yajsw/lib/groovy/mail/mail.jar differ diff --git a/javaUtilities/yajsw/lib/groovy/snmp/SNMP4J.jar b/javaUtilities/yajsw/lib/groovy/snmp/SNMP4J.jar new file mode 100644 index 0000000000..298299b1fd Binary files /dev/null and b/javaUtilities/yajsw/lib/groovy/snmp/SNMP4J.jar differ diff --git a/javaUtilities/yajsw/modifiedSource.txt b/javaUtilities/yajsw/modifiedSource.txt new file mode 100644 index 0000000000..20e194de63 --- /dev/null +++ b/javaUtilities/yajsw/modifiedSource.txt @@ -0,0 +1,7 @@ +yajsw/src/main/java/org/rzo/yajsw/WrapperExe.java +yajsw/src/main/java/org/rzo/yajsw/app/WrapperManagerImpl.java +yajsw/src/main/java/org/rzo/yajsw/os/posix/PosixProcess.java +yajsw/src/main/java/org/rzo/yajsw/os/posix/bsd/AppStarter.java +yajsw/src/main/java/org/rzo/yajsw/os/posix/bsd/BSDProcess.java +yajsw/src/main/java/org/rzo/yajsw/wrapper/AbstractWrappedProcess.java +yajsw/src/main/java/org/rzo/yajsw/wrapper/WrappedJavaProcess.java diff --git a/javaUtilities/yajsw/src/ahessian/org/jboss/netty/handler/ipfilter/CIDR.java b/javaUtilities/yajsw/src/ahessian/org/jboss/netty/handler/ipfilter/CIDR.java new file mode 100644 index 0000000000..a2968feea6 --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/jboss/netty/handler/ipfilter/CIDR.java @@ -0,0 +1,264 @@ +/* + * Copyright 2009 Red Hat, Inc. + * + * Red Hat licenses this file to you under the Apache License, version 2.0 + * (the "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package org.jboss.netty.handler.ipfilter; + +import java.net.Inet4Address; +import java.net.Inet6Address; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.StringTokenizer; + +/** + * @author frederic bregier + */ +public abstract class CIDR implements Comparable +{ + /** + * The base address of the CIDR notation + */ + protected InetAddress baseAddress; + + /** + * The mask used in the CIDR notation + */ + protected int cidrMask; + + /** + * Create CIDR using the CIDR Notation + * @param baseAddress + * @param cidrMask + * @return the generated CIDR + * @throws UnknownHostException + */ + public static CIDR newCIDR(InetAddress baseAddress, int cidrMask) throws UnknownHostException + { + if (cidrMask < 0) + { + throw new UnknownHostException("Invalid mask length used: " + cidrMask); + } + if (baseAddress instanceof Inet4Address) + { + if (cidrMask > 32) + { + throw new UnknownHostException("Invalid mask length used: " + cidrMask); + } + return new CIDR4((Inet4Address) baseAddress, cidrMask); + } + // IPv6. + if (cidrMask > 128) + { + throw new UnknownHostException("Invalid mask length used: " + cidrMask); + } + return new CIDR6((Inet6Address) baseAddress, cidrMask); + } + + /** + * Create CIDR using the normal Notation + * @param baseAddress + * @param scidrMask + * @return the generated CIDR + * @throws UnknownHostException + */ + public static CIDR newCIDR(InetAddress baseAddress, String scidrMask) throws UnknownHostException + { + int cidrMask = getNetMask(scidrMask); + if (cidrMask < 0) + { + throw new UnknownHostException("Invalid mask length used: " + cidrMask); + } + if (baseAddress instanceof Inet4Address) + { + if (cidrMask > 32) + { + throw new UnknownHostException("Invalid mask length used: " + cidrMask); + } + return new CIDR4((Inet4Address) baseAddress, cidrMask); + } + cidrMask += 96; + // IPv6. + if (cidrMask > 128) + { + throw new UnknownHostException("Invalid mask length used: " + cidrMask); + } + return new CIDR6((Inet6Address) baseAddress, cidrMask); + } + + /** + * Create CIDR using the CIDR or normal Notation
+ * i.e.: + * CIDR subnet = newCIDR ("10.10.10.0/24"); or + * CIDR subnet = newCIDR ("1fff:0:0a88:85a3:0:0:ac1f:8001/24"); or + * CIDR subnet = newCIDR ("10.10.10.0/255.255.255.0"); + * @param cidr + * @return the generated CIDR + * @throws UnknownHostException + */ + public static CIDR newCIDR(String cidr) throws UnknownHostException + { + int p = cidr.indexOf("/"); + if (p < 0) + { + throw new UnknownHostException("Invalid CIDR notation used: " + cidr); + } + String addrString = cidr.substring(0, p); + String maskString = cidr.substring(p + 1); + InetAddress addr = addressStringToInet(addrString); + int mask = 0; + if (maskString.indexOf(".") < 0) + { + mask = parseInt(maskString, -1); + } + else + { + mask = getNetMask(maskString); + if (addr instanceof Inet6Address) + { + mask += 96; + } + } + if (mask < 0) + { + throw new UnknownHostException("Invalid mask length used: " + maskString); + } + return newCIDR(addr, mask); + } + + /** @return the baseAddress of the CIDR block. */ + public InetAddress getBaseAddress() + { + return baseAddress; + } + + /** @return the Mask length. */ + public int getMask() + { + return cidrMask; + } + + /** @return the textual CIDR notation. */ + @Override + public String toString() + { + return baseAddress.getHostAddress() + "/" + cidrMask; + } + + /** @return the end address of this block. */ + public abstract InetAddress getEndAddress(); + + /** + * Compares the given InetAddress against the CIDR and returns true if + * the ip is in the subnet-ip-range and false if not. + * @param inetAddress + * @return returns true if the given IP address is inside the currently + * set network. + */ + public abstract boolean contains(InetAddress inetAddress); + + /** Convert an IPv4 or IPv6 textual representation into an + * InetAddress. + * @param addr + * @return the created InetAddress + * @throws UnknownHostException + */ + private static InetAddress addressStringToInet(String addr) throws UnknownHostException + { + return InetAddress.getByName(addr); + } + + /** + * Get the Subnet's Netmask in Decimal format.
+ * i.e.: getNetMask("255.255.255.0") returns the integer CIDR mask + * @param netMask a network mask + * @return the integer CIDR mask + * */ + private static int getNetMask(String netMask) + { + StringTokenizer nm = new StringTokenizer(netMask, "."); + int i = 0; + int[] netmask = new int[4]; + while (nm.hasMoreTokens()) + { + netmask[i] = Integer.parseInt(nm.nextToken()); + i++; + } + int mask1 = 0; + for (i = 0; i < 4; i++) + { + mask1 += Integer.bitCount(netmask[i]); + } + return mask1; + } + + /** @param intstr a string containing an integer. + * @param def the default if the string does not contain a valid + * integer. + * @return the inetAddress from the integer + */ + private static int parseInt(String intstr, int def) + { + Integer res; + if (intstr == null) + { + return def; + } + try + { + res = Integer.decode(intstr); + } + catch (Exception e) + { + res = new Integer(def); + } + return res.intValue(); + } + + /** + * Compute a byte representation of IpV4 from a IpV6 + * @param address + * @return the byte representation + * @throws IllegalArgumentException if the IpV6 cannot be mapped to IpV4 + */ + public static byte[] getIpV4FromIpV6(Inet6Address address) throws IllegalArgumentException + { + byte[] baddr = address.getAddress(); + for (int i = 0; i < 9; i++) + { + if (baddr[i] != 0) + { + throw new IllegalArgumentException("This IPv6 address cannot be used in IPv4 context"); + } + } + if (baddr[10] != 0 && baddr[10] != 0xFF || baddr[11] != 0 && baddr[11] != 0xFF) + { + throw new IllegalArgumentException("This IPv6 address cannot be used in IPv4 context"); + } + return new byte[] + {baddr[12], baddr[13], baddr[14], baddr[15]}; + } + + /** + * Compute a byte representation of IpV6 from a IpV4 + * @param address + * @return the byte representation + * @throws IllegalArgumentException if the IpV6 cannot be mapped to IpV4 + */ + public static byte[] getIpV6FromIpV4(Inet4Address address) throws IllegalArgumentException + { + byte[] baddr = address.getAddress(); + return new byte[] + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, baddr[0], baddr[1], baddr[2], baddr[3]}; + } +} diff --git a/javaUtilities/yajsw/src/ahessian/org/jboss/netty/handler/ipfilter/CIDR4.java b/javaUtilities/yajsw/src/ahessian/org/jboss/netty/handler/ipfilter/CIDR4.java new file mode 100644 index 0000000000..a02fb09920 --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/jboss/netty/handler/ipfilter/CIDR4.java @@ -0,0 +1,196 @@ +/* + * Copyright 2009 Red Hat, Inc. + * + * Red Hat licenses this file to you under the Apache License, version 2.0 + * (the "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package org.jboss.netty.handler.ipfilter; + +import java.net.Inet4Address; +import java.net.Inet6Address; +import java.net.InetAddress; +import java.net.UnknownHostException; + +/** + * @author frederic bregier + */ +public class CIDR4 extends CIDR +{ + /** + * The integer for the base address + */ + private int addressInt; + + /** + * The integer for the end address + */ + private final int addressEndInt; + + /** + * @param newaddr + * @param mask + */ + protected CIDR4(Inet4Address newaddr, int mask) + { + cidrMask = mask; + addressInt = ipv4AddressToInt(newaddr); + int newmask = ipv4PrefixLengthToMask(mask); + addressInt &= newmask; + try + { + baseAddress = intToIPv4Address(addressInt); + } + catch (UnknownHostException e) + { + // this should never happen + } + addressEndInt = addressInt + ipv4PrefixLengthToLength(cidrMask) - 1; + } + + @Override + public InetAddress getEndAddress() + { + try + { + return intToIPv4Address(addressEndInt); + } + catch (UnknownHostException e) + { + // this should never happen + return null; + } + } + + public int compareTo(CIDR arg) + { + if (arg instanceof CIDR6) + { + byte[] address = getIpV4FromIpV6((Inet6Address) arg.baseAddress); + int net = ipv4AddressToInt(address); + if (net == addressInt && arg.cidrMask == cidrMask) + { + return 0; + } + if (net < addressInt) + { + return 1; + } + else if (net > addressInt) + { + return -1; + } + else if (arg.cidrMask < cidrMask) + { + return -1; + } + return 1; + } + CIDR4 o = (CIDR4) arg; + if (o.addressInt == addressInt && o.cidrMask == cidrMask) + { + return 0; + } + if (o.addressInt < addressInt) + { + return 1; + } + else if (o.addressInt > addressInt) + { + return -1; + } + else if (o.cidrMask < cidrMask) + { + // greater Mask means less IpAddresses so -1 + return -1; + } + return 1; + } + + /* (non-Javadoc) + * @see org.jboss.netty.handler.ipfilter.CIDR#contains(java.net.InetAddress) + */ + @Override + public boolean contains(InetAddress inetAddress) + { + int search = ipv4AddressToInt(inetAddress); + return search >= addressInt && search <= addressEndInt; + } + + /** Given an IPv4 baseAddress length, return the block length. I.e., a + * baseAddress length of 24 will return 256. */ + private static int ipv4PrefixLengthToLength(int prefix_length) + { + return 1 << 32 - prefix_length; + } + + /** Given a baseAddress length, return a netmask. I.e, a baseAddress length + * of 24 will return 0xFFFFFF00. */ + private static int ipv4PrefixLengthToMask(int prefix_length) + { + return ~((1 << 32 - prefix_length) - 1); + } + + /** Convert an integer into an (IPv4) InetAddress. + * @param addr + * @return the created InetAddress + * @throws UnknownHostException + * @throws UnknownHostException + */ + private static InetAddress intToIPv4Address(int addr) throws UnknownHostException + { + byte[] a = new byte[4]; + a[0] = (byte) (addr >> 24 & 0xFF); + a[1] = (byte) (addr >> 16 & 0xFF); + a[2] = (byte) (addr >> 8 & 0xFF); + a[3] = (byte) (addr & 0xFF); + return InetAddress.getByAddress(a); + } + + /** Given an IPv4 address, convert it into an integer. + * @param addr + * @return the integer representation of the InetAddress + * + * @throws IllegalArgumentException if the address is really an + * IPv6 address. + */ + private static int ipv4AddressToInt(InetAddress addr) + { + byte[] address = null; + if (addr instanceof Inet6Address) + { + address = getIpV4FromIpV6((Inet6Address) addr); + } + else + { + address = addr.getAddress(); + } + return ipv4AddressToInt(address); + } + + /** Given an IPv4 address as array of bytes, convert it into an integer. + * @param address + * @return the integer representation of the InetAddress + * + * @throws IllegalArgumentException if the address is really an + * IPv6 address. + */ + private static int ipv4AddressToInt(byte[] address) + { + int net = 0; + for (byte addres : address) + { + net <<= 8; + net |= addres & 0xFF; + } + return net; + } +} diff --git a/javaUtilities/yajsw/src/ahessian/org/jboss/netty/handler/ipfilter/CIDR6.java b/javaUtilities/yajsw/src/ahessian/org/jboss/netty/handler/ipfilter/CIDR6.java new file mode 100644 index 0000000000..566469ee4d --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/jboss/netty/handler/ipfilter/CIDR6.java @@ -0,0 +1,200 @@ +/* + * Copyright 2009 Red Hat, Inc. + * + * Red Hat licenses this file to you under the Apache License, version 2.0 + * (the "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package org.jboss.netty.handler.ipfilter; + +import java.math.BigInteger; +import java.net.Inet4Address; +import java.net.Inet6Address; +import java.net.InetAddress; +import java.net.UnknownHostException; + +import org.jboss.netty.logging.InternalLogger; +import org.jboss.netty.logging.InternalLoggerFactory; + +/** + * @author frederic bregier + */ +public class CIDR6 extends CIDR +{ + + private static final InternalLogger logger = InternalLoggerFactory.getInstance(CIDR6.class); + + /** + * The big integer for the base address + */ + private BigInteger addressBigInt; + + /** + * The big integer for the end address + */ + private final BigInteger addressEndBigInt; + + /** + * @param newaddress + * @param newmask + */ + protected CIDR6(Inet6Address newaddress, int newmask) + { + cidrMask = newmask; + addressBigInt = ipv6AddressToBigInteger(newaddress); + BigInteger mask = ipv6CidrMaskToMask(newmask); + try + { + addressBigInt = addressBigInt.and(mask); + baseAddress = bigIntToIPv6Address(addressBigInt); + } + catch (UnknownHostException e) + { + // this should never happen. + } + addressEndBigInt = addressBigInt.add(ipv6CidrMaskToBaseAddress(cidrMask)).subtract(BigInteger.ONE); + } + + @Override + public InetAddress getEndAddress() + { + try + { + return bigIntToIPv6Address(addressEndBigInt); + } + catch (UnknownHostException e) + { + logger.error("invalid ip address calculated as an end address"); + return null; + } + } + + public int compareTo(CIDR arg) + { + if (arg instanceof CIDR4) + { + BigInteger net = ipv6AddressToBigInteger(arg.baseAddress); + int res = net.compareTo(addressBigInt); + if (res == 0) + { + if (arg.cidrMask == cidrMask) + { + return 0; + } + else if (arg.cidrMask < cidrMask) + { + return -1; + } + return 1; + } + return res; + } + CIDR6 o = (CIDR6) arg; + if (o.addressBigInt.equals(addressBigInt) && o.cidrMask == cidrMask) + { + return 0; + } + int res = o.addressBigInt.compareTo(addressBigInt); + if (res == 0) + { + if (o.cidrMask < cidrMask) + { + // greater Mask means less IpAddresses so -1 + return -1; + } + return 1; + } + return res; + } + + /* (non-Javadoc) + * @see org.jboss.netty.handler.ipfilter.CIDR#contains(java.net.InetAddress) + */ + @Override + public boolean contains(InetAddress inetAddress) + { + BigInteger search = ipv6AddressToBigInteger(inetAddress); + return search.compareTo(addressBigInt) >= 0 && search.compareTo(addressEndBigInt) <= 0; + } + + /** Given an IPv6 baseAddress length, return the block length. I.e., a + * baseAddress length of 96 will return 2**32. */ + private static BigInteger ipv6CidrMaskToBaseAddress(int cidrMask) + { + return BigInteger.ONE.shiftLeft(128 - cidrMask); + } + + private static BigInteger ipv6CidrMaskToMask(int cidrMask) + { + return BigInteger.ONE.shiftLeft(128 - cidrMask).subtract(BigInteger.ONE).not(); + } + + /** Given an IPv6 address, convert it into a BigInteger. + * @param addr + * @return the integer representation of the InetAddress + * + * @throws IllegalArgumentException if the address is not an IPv6 + * address. + */ + private static BigInteger ipv6AddressToBigInteger(InetAddress addr) + { + byte[] ipv6; + if (addr instanceof Inet4Address) + { + ipv6 = getIpV6FromIpV4((Inet4Address) addr); + } + else + { + ipv6 = addr.getAddress(); + } + if (ipv6[0] == -1) + { + return new BigInteger(1, ipv6); + } + return new BigInteger(ipv6); + } + + /** Convert a big integer into an IPv6 address. + * @param addr + * @return the inetAddress from the integer + * + * @throws UnknownHostException if the big integer is too large, + * and thus an invalid IPv6 address. + */ + private static InetAddress bigIntToIPv6Address(BigInteger addr) throws UnknownHostException + { + byte[] a = new byte[16]; + byte[] b = addr.toByteArray(); + if (b.length > 16 && !(b.length == 17 && b[0] == 0)) + { + throw new UnknownHostException("invalid IPv6 address (too big)"); + } + if (b.length == 16) + { + return InetAddress.getByAddress(b); + } + // handle the case where the IPv6 address starts with "FF". + if (b.length == 17) + { + System.arraycopy(b, 1, a, 0, 16); + } + else + { + // copy the address into a 16 byte array, zero-filled. + int p = 16 - b.length; + for (int i = 0; i < b.length; i++) + { + a[p + i] = b[i]; + } + } + return InetAddress.getByAddress(a); + } +} diff --git a/javaUtilities/yajsw/src/ahessian/org/jboss/netty/handler/ipfilter/IpFilterListener.java b/javaUtilities/yajsw/src/ahessian/org/jboss/netty/handler/ipfilter/IpFilterListener.java new file mode 100644 index 0000000000..225e32db22 --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/jboss/netty/handler/ipfilter/IpFilterListener.java @@ -0,0 +1,74 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2010, Red Hat Middleware LLC, and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jboss.netty.handler.ipfilter; + +import java.net.InetSocketAddress; + +import org.jboss.netty.channel.ChannelEvent; +import org.jboss.netty.channel.ChannelFuture; +import org.jboss.netty.channel.ChannelHandlerContext; + +/** + * The listener interface for receiving ipFilter events. + * + * @see IpFilteringHandler + * + * @author Ron + */ +public interface IpFilterListener +{ + + /** + * Called when the channel has the CONNECTED status and the channel was allowed by a previous call to accept(). + * This method enables your implementation to send a message back to the client before closing + * or whatever you need. This method returns a ChannelFuture on which the implementation + * can wait uninterruptibly before continuing.
+ * For instance, If a message is sent back, the corresponding ChannelFuture has to be returned. + * @param ctx + * @param e + * @param inetSocketAddress the remote {@link InetSocketAddress} from client + * @return the associated ChannelFuture to be waited for before closing the channel. Null is allowed. + */ + public ChannelFuture allowed(ChannelHandlerContext ctx, ChannelEvent e, InetSocketAddress inetSocketAddress); + + /** + * Called when the channel has the CONNECTED status and the channel was refused by a previous call to accept(). + * This method enables your implementation to send a message back to the client before closing + * or whatever you need. This method returns a ChannelFuture on which the implementation + * will wait uninterruptibly before closing the channel.
+ * For instance, If a message is sent back, the corresponding ChannelFuture has to be returned. + * @param ctx + * @param e + * @param inetSocketAddress the remote {@link InetSocketAddress} from client + * @return the associated ChannelFuture to be waited for before closing the channel. Null is allowed. + */ + public ChannelFuture refused(ChannelHandlerContext ctx, ChannelEvent e, InetSocketAddress inetSocketAddress); + + /** + * Called in handleUpstream, if this channel was previously blocked, + * to check if whatever the event, it should be passed to the next entry in the pipeline.
+ * If one wants to not block events, just overridden this method by returning always true.

+ * Note that OPENED and BOUND events are still passed to the next entry in the pipeline since + * those events come out before the CONNECTED event and so the possibility to filter the connection. + * @param ctx + * @param e + * @return True if the event should continue, False if the event should not continue + * since this channel was blocked by this filter + */ + public boolean continues(ChannelHandlerContext ctx, ChannelEvent e); + +} diff --git a/javaUtilities/yajsw/src/ahessian/org/jboss/netty/handler/ipfilter/IpFilterRule.java b/javaUtilities/yajsw/src/ahessian/org/jboss/netty/handler/ipfilter/IpFilterRule.java new file mode 100644 index 0000000000..cae6b16270 --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/jboss/netty/handler/ipfilter/IpFilterRule.java @@ -0,0 +1,37 @@ +/* + * Copyright 2009 Red Hat, Inc. + * + * Red Hat licenses this file to you under the Apache License, version 2.0 + * (the "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package org.jboss.netty.handler.ipfilter; + +/** + * This Interface defines an Ip Filter Rule. + * + * @author frederic bregier + * + */ +public interface IpFilterRule extends IpSet +{ + /** + * + * @return True if this Rule is an ALLOW rule + */ + public boolean isAllowRule(); + + /** + * + * @return True if this Rule is a DENY rule + */ + public boolean isDenyRule(); +} diff --git a/javaUtilities/yajsw/src/ahessian/org/jboss/netty/handler/ipfilter/IpFilterRuleHandler.java b/javaUtilities/yajsw/src/ahessian/org/jboss/netty/handler/ipfilter/IpFilterRuleHandler.java new file mode 100644 index 0000000000..6f4c90856f --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/jboss/netty/handler/ipfilter/IpFilterRuleHandler.java @@ -0,0 +1,327 @@ +/* + * Copyright 2009 Red Hat, Inc. + * + * Red Hat licenses this file to you under the Apache License, version 2.0 + * (the "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package org.jboss.netty.handler.ipfilter; + +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; + +import org.jboss.netty.channel.ChannelEvent; +import org.jboss.netty.channel.ChannelHandlerContext; +import org.jboss.netty.channel.ChannelPipelineCoverage; + +/** + * Implementation of Filter of IP based on ALLOW and DENY rules.
+ *

+ * This implementation could be changed by implementing a new {@link IpFilterRule} than default + * {@link IpV4SubnetFilterRule} (IPV4 support only), {@link IpSubnetFilterRule} (IPV4 and IPV6 support) or {@link IpPatternFilterRule} (IP and host name string pattern support) .
+ *
+ * The check is done by going from step to step in the underlying array of IpFilterRule.
+ * Each {@link IpFilterRule} answers to the method accept if the {@link InetAddress} is accepted or not, + * according to its implementation. If an InetAddress arrives at the end of the list, as in Firewall + * usual rules, the InetAddress is therefore accepted by default.
+ *
    + *
  • If it was constructed with True as first argument, + * the IpFilterRule is an ALLOW rule (every InetAddress that fits in the rule will be accepted).
  • + *
  • If it was constructed with False as first argument, + * the IpFilterRule is a DENY rule (every InetAddress that fits in the rule will be refused).
  • + *

+ *
+ * An empty list means allow all (no limitation).

+ * For efficiency reason, you should not add/remove too frequently IpFilterRules to/from this handler. + * You should prefer to replace an entry (set method) with an ALLOW/DENY ALL IpFilterRule + * if possible.


+ * This handler should be created only once and reused on every pipeline since it handles + * a global status of what is allowed or blocked.

+ * + * Note that {@link IpSubnetFilterRule} which supports IPV4 and IPV6 should be used with as much as + * possible no mixed IP protocol. Both IPV4 and IPV6 are supported but a mix (IpFilter in IPV6 notation + * and the address from the channel in IPV4, or the reverse) can lead to wrong result. + * @author frederic bregier + * + */ +@ChannelPipelineCoverage("all") +public class IpFilterRuleHandler extends IpFilteringHandlerImpl +{ + /** + * List of {@link IpFilterRule} + */ + private final CopyOnWriteArrayList ipFilterRuleList = new CopyOnWriteArrayList(); + + /** + * Constructor from a new list of IpFilterRule + * @param newList + */ + public IpFilterRuleHandler(List newList) + { + if (newList != null) + { + ipFilterRuleList.addAll(newList); + } + } + + /** + * Empty constructor (no IpFilterRule in the List at construction). In such a situation, + * empty list implies allow all. + */ + public IpFilterRuleHandler() + { + } + + // Below are methods directly inspired from CopyOnWriteArrayList methods + /** + * Add an ipFilterRule in the list at the end + * @param ipFilterRule + */ + public void add(IpFilterRule ipFilterRule) + { + if (ipFilterRule == null) + { + throw new NullPointerException("IpFilterRule can not be null"); + } + ipFilterRuleList.add(ipFilterRule); + } + + /** + * Add an ipFilterRule in the list at the specified position (shifting to the right other elements) + * @param index + * @param ipFilterRule + */ + public void add(int index, IpFilterRule ipFilterRule) + { + if (ipFilterRule == null) + { + throw new NullPointerException("IpFilterRule can not be null"); + } + ipFilterRuleList.add(index, ipFilterRule); + } + + /** + * Appends all of the elements in the specified collection to the end of this list, + * in the order that they are returned by the specified collection's iterator. + * @param c + */ + public void addAll(Collection c) + { + if (c == null) + { + throw new NullPointerException("Collection can not be null"); + } + ipFilterRuleList.addAll(c); + } + + /** + * Inserts all of the elements in the specified collection into this list, starting at the specified position. + * @param index + * @param c + */ + public void addAll(int index, Collection c) + { + if (c == null) + { + throw new NullPointerException("Collection can not be null"); + } + ipFilterRuleList.addAll(index, c); + } + + /** + * Append the element if not present. + * @param c + * @return the number of elements added + */ + public int addAllAbsent(Collection c) + { + if (c == null) + { + throw new NullPointerException("Collection can not be null"); + } + return ipFilterRuleList.addAllAbsent(c); + } + + /** + * Append the element if not present. + * @param ipFilterRule + * @return true if the element was added + */ + public boolean addIfAbsent(IpFilterRule ipFilterRule) + { + if (ipFilterRule == null) + { + throw new NullPointerException("IpFilterRule can not be null"); + } + return ipFilterRuleList.addIfAbsent(ipFilterRule); + } + + /** + * Clear the list + */ + public void clear() + { + ipFilterRuleList.clear(); + } + + /** + * Returns true if this list contains the specified element + * @param ipFilterRule + * @return true if this list contains the specified element + */ + public boolean contains(IpFilterRule ipFilterRule) + { + if (ipFilterRule == null) + { + throw new NullPointerException("IpFilterRule can not be null"); + } + return ipFilterRuleList.contains(ipFilterRule); + } + + /** + * Returns true if this list contains all of the elements of the specified collection + * @param c + * @return true if this list contains all of the elements of the specified collection + */ + public boolean containsAll(Collection c) + { + if (c == null) + { + throw new NullPointerException("Collection can not be null"); + } + return ipFilterRuleList.containsAll(c); + } + + /** + * Returns the element at the specified position in this list + * @param index + * @return the element at the specified position in this list + */ + public IpFilterRule get(int index) + { + return ipFilterRuleList.get(index); + } + + /** + * Returns true if this list contains no elements + * @return true if this list contains no elements + */ + public boolean isEmpty() + { + return ipFilterRuleList.isEmpty(); + } + + /** + * Remove the ipFilterRule from the list + * @param ipFilterRule + */ + public void remove(IpFilterRule ipFilterRule) + { + if (ipFilterRule == null) + { + throw new NullPointerException("IpFilterRule can not be null"); + } + ipFilterRuleList.remove(ipFilterRule); + } + + /** + * Removes the element at the specified position in this list + * @param index + * @return the element previously at the specified position + */ + public IpFilterRule remove(int index) + { + return ipFilterRuleList.remove(index); + } + + /** + * Removes from this list all of its elements that are contained in the specified collection + * @param c + */ + public void removeAll(Collection c) + { + if (c == null) + { + throw new NullPointerException("Collection can not be null"); + } + ipFilterRuleList.removeAll(c); + } + + /** + * Retains only the elements in this list that are contained in the specified collection + * @param c + */ + public void retainAll(Collection c) + { + if (c == null) + { + throw new NullPointerException("Collection can not be null"); + } + ipFilterRuleList.retainAll(c); + } + + /** + * Replaces the element at the specified position in this list with the specified element + * @param index + * @param ipFilterRule + * @return the element previously at the specified position + */ + public IpFilterRule set(int index, IpFilterRule ipFilterRule) + { + if (ipFilterRule == null) + { + throw new NullPointerException("IpFilterRule can not be null"); + } + return ipFilterRuleList.set(index, ipFilterRule); + } + + /** + * Returns the number of elements in this list. + * @return the number of elements in this list. + */ + public int size() + { + return ipFilterRuleList.size(); + } + + /* (non-Javadoc) + * @see org.jboss.netty.handler.ipfilter.IpFilteringHandler#accept(org.jboss.netty.channel.ChannelHandlerContext, org.jboss.netty.channel.ChannelEvent, java.net.InetSocketAddress) + */ + @Override + protected boolean accept(ChannelHandlerContext ctx, ChannelEvent e, InetSocketAddress inetSocketAddress) + throws Exception + { + if (ipFilterRuleList.isEmpty()) + { + // No limitation neither in deny or allow, so accept + return true; + } + InetAddress inetAddress = inetSocketAddress.getAddress(); + Iterator iterator = ipFilterRuleList.iterator(); + IpFilterRule ipFilterRule = null; + while (iterator.hasNext()) + { + ipFilterRule = iterator.next(); + if (ipFilterRule.contains(inetAddress)) + { + // Match founds, is it a ALLOW or DENY rule + return ipFilterRule.isAllowRule(); + } + } + // No limitation founds and no allow either, but as it is like Firewall rules, it is therefore accepted + return true; + } + +} diff --git a/javaUtilities/yajsw/src/ahessian/org/jboss/netty/handler/ipfilter/IpFilterRuleList.java b/javaUtilities/yajsw/src/ahessian/org/jboss/netty/handler/ipfilter/IpFilterRuleList.java new file mode 100644 index 0000000000..d6ee0081f0 --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/jboss/netty/handler/ipfilter/IpFilterRuleList.java @@ -0,0 +1,100 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2010, Red Hat Middleware LLC, and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jboss.netty.handler.ipfilter; + +import java.net.UnknownHostException; +import java.util.ArrayList; + +import org.jboss.netty.logging.InternalLogger; +import org.jboss.netty.logging.InternalLoggerFactory; + +/** + * The Class IpFilterRuleList is a helper class to generate a List of Rules from a string. + * In case of parse errors no exceptions are thrown. The error is logged. + *
+ * Rule List Syntax: + *
+ *
+ * RuleList ::= Rule[,Rule]*
+ * Rule ::= AllowRule | BlockRule
+ * AllowRule ::= +Filter
+ * BlockRule ::= -Filter
+ * Filter ::= PatternFilter | CIDRFilter
+ * PatternFilter ::= @see PatternRule
+ * CIDRFilter ::= c:CIDRFilter
+ * CIDRFilter ::= @see CIDR.newCIDR(String)
+ * 
+ *
+ * Example: allow only localhost: + *
+ * new IPFilterRuleHandler().addAll(new IpFilterRuleList("+n:localhost, -n:*")); + *
+ * + * @author Ron + */ +public class IpFilterRuleList extends ArrayList +{ + private static final long serialVersionUID = -6164162941749588780L; + + private static final InternalLogger logger = InternalLoggerFactory.getInstance(IpFilterRuleList.class); + + /** + * Instantiates a new ip filter rule list. + * + * @param rules the rules + */ + public IpFilterRuleList(String rules) + { + super(); + parseRules(rules); + } + + private void parseRules(String rules) + { + String[] ruless = rules.split(","); + for (String rule : ruless) + { + parseRule(rule.trim()); + } + } + + private void parseRule(String rule) + { + if (rule == null || rule.length() == 0) + return; + if (!(rule.startsWith("+") || rule.startsWith("-"))) + { + logger.error("syntax error in ip filter rule:" + rule); + return; + } + + boolean allow = rule.startsWith("+"); + if (rule.charAt(1) == 'n' || rule.charAt(1) == 'i') + this.add(new PatternRule(allow, rule.substring(1))); + else if (rule.charAt(1) == 'c') + try + { + this.add(new IpSubnetFilterRule(allow, rule.substring(3))); + } + catch (UnknownHostException e) + { + logger.error("error parsing ip filter " + rule, e); + } + else + logger.error("syntax error in ip filter rule:" + rule); + } +} diff --git a/javaUtilities/yajsw/src/ahessian/org/jboss/netty/handler/ipfilter/IpFilteringHandler.java b/javaUtilities/yajsw/src/ahessian/org/jboss/netty/handler/ipfilter/IpFilteringHandler.java new file mode 100644 index 0000000000..d56dca643e --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/jboss/netty/handler/ipfilter/IpFilteringHandler.java @@ -0,0 +1,43 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2010, Red Hat Middleware LLC, and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jboss.netty.handler.ipfilter; + +import org.jboss.netty.channel.ChannelUpstreamHandler; + +/** + * The Interface IpFilteringHandler. + * A Filter of IP. + *
+ * Users can add an {@link IpFilterListener} to add specific actions in case a connection is allowed or refused. + * + * @author Ron + */ +public interface IpFilteringHandler extends ChannelUpstreamHandler +{ + + /** + * Sets the filter listener. + * + * @param listener the new ip filter listener + */ + public void setIpFilterListener(IpFilterListener listener); + + /** + * Remove the filter listener. + */ + public void removeIpFilterListener(); +} diff --git a/javaUtilities/yajsw/src/ahessian/org/jboss/netty/handler/ipfilter/IpFilteringHandlerImpl.java b/javaUtilities/yajsw/src/ahessian/org/jboss/netty/handler/ipfilter/IpFilteringHandlerImpl.java new file mode 100644 index 0000000000..892ab2f7ed --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/jboss/netty/handler/ipfilter/IpFilteringHandlerImpl.java @@ -0,0 +1,203 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2010, Red Hat Middleware LLC, and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jboss.netty.handler.ipfilter; + +import java.net.InetSocketAddress; + +import org.jboss.netty.channel.ChannelEvent; +import org.jboss.netty.channel.ChannelFuture; +import org.jboss.netty.channel.ChannelFutureListener; +import org.jboss.netty.channel.ChannelHandlerContext; +import org.jboss.netty.channel.ChannelStateEvent; +import org.jboss.netty.channel.Channels; + +// TODO: Auto-generated Javadoc +/** + * General class that handle Ip Filtering. + * + * @author frederic bregier + */ +public abstract class IpFilteringHandlerImpl implements IpFilteringHandler +{ + + private IpFilterListener listener = null; + + /** + * Called when the channel is connected. It returns True if the corresponding connection + * is to be allowed. Else it returns False. + * @param ctx + * @param e + * @param inetSocketAddress the remote {@link InetSocketAddress} from client + * @return True if the corresponding connection is allowed, else False. + * @throws Exception + */ + protected abstract boolean accept(ChannelHandlerContext ctx, ChannelEvent e, InetSocketAddress inetSocketAddress) + throws Exception; + + /** + * Called when the channel has the CONNECTED status and the channel was refused by a previous call to accept(). + * This method enables your implementation to send a message back to the client before closing + * or whatever you need. This method returns a ChannelFuture on which the implementation + * will wait uninterruptibly before closing the channel.
+ * For instance, If a message is sent back, the corresponding ChannelFuture has to be returned. + * @param ctx + * @param e + * @param inetSocketAddress the remote {@link InetSocketAddress} from client + * @return the associated ChannelFuture to be waited for before closing the channel. Null is allowed. + * @throws Exception + */ + protected ChannelFuture handleRefusedChannel(ChannelHandlerContext ctx, ChannelEvent e, + InetSocketAddress inetSocketAddress) throws Exception + { + if (listener == null) + return null; + ChannelFuture result = listener.refused(ctx, e, inetSocketAddress); + return result; + } + + protected ChannelFuture handleAllowedChannel(ChannelHandlerContext ctx, ChannelEvent e, + InetSocketAddress inetSocketAddress) throws Exception + { + if (listener == null) + return null; + ChannelFuture result = listener.allowed(ctx, e, inetSocketAddress); + return result; + } + + /** + * Internal method to test if the current channel is blocked. Should not be overridden. + * @param ctx + * @return True if the current channel is blocked, else False + */ + protected boolean isBlocked(ChannelHandlerContext ctx) + { + return ctx.getAttachment() != null; + } + + /** + * Called in handleUpstream, if this channel was previously blocked, + * to check if whatever the event, it should be passed to the next entry in the pipeline.
+ * If one wants to not block events, just overridden this method by returning always true.

+ * Note that OPENED and BOUND events are still passed to the next entry in the pipeline since + * those events come out before the CONNECTED event and so the possibility to filter the connection. + * @param ctx + * @param e + * @return True if the event should continue, False if the event should not continue + * since this channel was blocked by this filter + * @throws Exception + */ + protected boolean continues(ChannelHandlerContext ctx, ChannelEvent e) throws Exception + { + if (listener != null) + return listener.continues(ctx, e); + else + return false; + } + + /* (non-Javadoc) + * @see org.jboss.netty.channel.ChannelUpstreamHandler#handleUpstream(org.jboss.netty.channel.ChannelHandlerContext, org.jboss.netty.channel.ChannelEvent) + */ + public void handleUpstream(ChannelHandlerContext ctx, ChannelEvent e) throws Exception + { + if (e instanceof ChannelStateEvent) + { + ChannelStateEvent evt = (ChannelStateEvent) e; + switch (evt.getState()) + { + case OPEN : + case BOUND : + // Special case: OPEND and BOUND events are before CONNECTED, + // but CLOSED and UNBOUND events are after DISCONNECTED: should those events be blocked too? + if (isBlocked(ctx) && !continues(ctx, evt)) + { + // don't pass to next level since channel was blocked early + return; + } + else + { + ctx.sendUpstream(e); + return; + } + case CONNECTED : + if (evt.getValue() != null) + { + // CONNECTED + InetSocketAddress inetSocketAddress = (InetSocketAddress) e.getChannel().getRemoteAddress(); + if (!accept(ctx, e, inetSocketAddress)) + { + ctx.setAttachment(Boolean.TRUE); + ChannelFuture future = handleRefusedChannel(ctx, e, inetSocketAddress); + if (future != null) + { + future.addListener(ChannelFutureListener.CLOSE); + } + else + { + Channels.close(e.getChannel()); + } + if (isBlocked(ctx) && !continues(ctx, evt)) + { + // don't pass to next level since channel was blocked early + return; + } + } + else + { + handleAllowedChannel(ctx, e, inetSocketAddress); + } + // This channel is not blocked + ctx.setAttachment(null); + } + else + { + // DISCONNECTED + if (isBlocked(ctx) && !continues(ctx, evt)) + { + // don't pass to next level since channel was blocked early + return; + } + } + break; + } + } + if (isBlocked(ctx) && !continues(ctx, e)) + { + // don't pass to next level since channel was blocked early + return; + } + // Whatever it is, if not blocked, goes to the next level + ctx.sendUpstream(e); + } + + /* (non-Javadoc) + * @see org.jboss.netty.handler.ipfilter.IpFilteringHandler#setIpFilterListener(org.jboss.netty.handler.ipfilter.IpFilterListener) + */ + public void setIpFilterListener(IpFilterListener listener) + { + this.listener = listener; + } + + /* (non-Javadoc) + * @see org.jboss.netty.handler.ipfilter.IpFilteringHandler#removeIpFilterListener() + */ + public void removeIpFilterListener() + { + this.listener = null; + + } + +} diff --git a/javaUtilities/yajsw/src/ahessian/org/jboss/netty/handler/ipfilter/IpSet.java b/javaUtilities/yajsw/src/ahessian/org/jboss/netty/handler/ipfilter/IpSet.java new file mode 100644 index 0000000000..a349b3f4f1 --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/jboss/netty/handler/ipfilter/IpSet.java @@ -0,0 +1,36 @@ +/* + * Copyright 2009 Red Hat, Inc. + * + * Red Hat licenses this file to you under the Apache License, version 2.0 + * (the "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package org.jboss.netty.handler.ipfilter; + +import java.net.InetAddress; + +/** + * This Interface defines an IpSet object. + * + * @author frederic bregier + * + */ +public interface IpSet +{ + /** + * Compares the given InetAddress against the IpSet and returns true if + * the InetAddress is contained in this Rule and false if not. + * @param inetAddress1 + * @return returns true if the given IP address is contained in the current + * IpSet. + */ + public boolean contains(InetAddress inetAddress1); +} diff --git a/javaUtilities/yajsw/src/ahessian/org/jboss/netty/handler/ipfilter/IpSubnet.java b/javaUtilities/yajsw/src/ahessian/org/jboss/netty/handler/ipfilter/IpSubnet.java new file mode 100644 index 0000000000..d5dfd5f26d --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/jboss/netty/handler/ipfilter/IpSubnet.java @@ -0,0 +1,198 @@ +/* + * Copyright 2009 Red Hat, Inc. + * + * Red Hat licenses this file to you under the Apache License, version 2.0 + * (the "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package org.jboss.netty.handler.ipfilter; + +import java.net.InetAddress; +import java.net.UnknownHostException; + +/** + * This class allows to check if an IP V4 or V6 Address is contained in a subnet.
+ * + * Supported IP V4 Formats for the Subnets are: 1.1.1.1/255.255.255.255 or 1.1.1.1/32 (CIDR-Notation) + * and (InetAddress,Mask) where Mask is a integer for CIDR-notation or a String for Standard Mask notation.
+ *

Example1:
+ * IpV4Subnet ips = new IpV4Subnet("192.168.1.0/24");
+ * System.out.println("Result: "+ ips.contains("192.168.1.123"));
+ * System.out.println("Result: "+ ips.contains(inetAddress2));
+ *
Example1 bis:
+ * IpV4Subnet ips = new IpV4Subnet(inetAddress, 24);
+ * where inetAddress is 192.168.1.0 and inetAddress2 is 192.168.1.123
+ *

Example2:
+ * IpV4Subnet ips = new IpV4Subnet("192.168.1.0/255.255.255.0");
+ * System.out.println("Result: "+ ips.contains("192.168.1.123"));
+ * System.out.println("Result: "+ ips.contains(inetAddress2));
+ *
Example2 bis:
+ * IpV4Subnet ips = new IpV4Subnet(inetAddress, "255.255.255.0");
+ * where inetAddress is 192.168.1.0 and inetAddress2 is 192.168.1.123
+ *
+ * Supported IP V6 Formats for the Subnets are: a:b:c:d:e:f:g:h/NN (CIDR-Notation) + * or any IPV6 notations (like a:b:c:d::/NN, a:b:c:d:e:f:w.x.y.z/NN) + * and (InetAddress,Mask) where Mask is a integer for CIDR-notation + * and (InetAddress,subnet).
+ *

Example1:
+ * IpSubnet ips = new IpSubnet("1fff:0:0a88:85a3:0:0:0:0/24");
+ * IpSubnet ips = new IpSubnet("1fff:0:0a88:85a3::/24");
+ * System.out.println("Result: "+ ips.contains("1fff:0:0a88:85a3:0:0:ac1f:8001"));
+ * System.out.println("Result: "+ ips.contains(inetAddress2));
+ *
Example1 bis:
+ * IpSubnet ips = new IpSubnet(inetAddress, 24);
+ * where inetAddress2 is 1fff:0:0a88:85a3:0:0:ac1f:8001
+ * + * @author frederic bregier + * + */ +public class IpSubnet implements IpSet, Comparable +{ + /** + * Internal representation + */ + private CIDR cidr = null; + + /** + * Create IpSubnet for ALL (used for ALLOW or DENY ALL) + */ + public IpSubnet() + { + // ALLOW or DENY ALL + cidr = null; + } + + /** + * Create IpSubnet using the CIDR or normal Notation
+ * i.e.:
+ * IpSubnet subnet = new IpSubnet("10.10.10.0/24"); or
+ * IpSubnet subnet = new IpSubnet("10.10.10.0/255.255.255.0"); or
+ * IpSubnet subnet = new IpSubnet("1fff:0:0a88:85a3:0:0:0:0/24"); + * @param netAddress a network address as string. + * @throws UnknownHostException + * */ + public IpSubnet(String netAddress) throws UnknownHostException + { + cidr = CIDR.newCIDR(netAddress); + } + + /** + * Create IpSubnet using the CIDR Notation + * @param inetAddress + * @param cidrNetMask + * @throws UnknownHostException + */ + public IpSubnet(InetAddress inetAddress, int cidrNetMask) throws UnknownHostException + { + cidr = CIDR.newCIDR(inetAddress, cidrNetMask); + } + + /** + * Create IpSubnet using the normal Notation + * @param inetAddress + * @param netMask + * @throws UnknownHostException + */ + public IpSubnet(InetAddress inetAddress, String netMask) throws UnknownHostException + { + cidr = CIDR.newCIDR(inetAddress, netMask); + } + + /** + * Compares the given IP-Address against the Subnet and returns true if + * the ip is in the subnet-ip-range and false if not. + * @param ipAddr an ipaddress + * @return returns true if the given IP address is inside the currently + * set network. + * @throws UnknownHostException + * */ + public boolean contains(String ipAddr) throws UnknownHostException + { + InetAddress inetAddress1 = InetAddress.getByName(ipAddr); + return this.contains(inetAddress1); + } + + /** + * Compares the given InetAddress against the Subnet and returns true if + * the ip is in the subnet-ip-range and false if not. + * @param inetAddress + * @return returns true if the given IP address is inside the currently + * set network. + * */ + public boolean contains(InetAddress inetAddress) + { + if (cidr == null) + { + // ANY + return true; + } + return cidr.contains(inetAddress); + } + + @Override + public String toString() + { + return cidr.toString(); + } + + @Override + public boolean equals(Object o) + { + if (!(o instanceof IpSubnet)) + { + return false; + } + IpSubnet ipSubnet = (IpSubnet) o; + return ipSubnet.cidr.equals(cidr); + } + + /** + * Compare two IpSubnet + */ + public int compareTo(IpSubnet o) + { + return cidr.toString().compareTo(o.cidr.toString()); + } + + /** + * Simple test functions + * @param args + * where args[0] is the netmask (standard or CIDR notation) and optional args[1] is + * the inetAddress to test with this IpSubnet + */ + public static void main(String[] args) + { + if (args.length != 0) + { + IpSubnet ipSubnet = null; + try + { + ipSubnet = new IpSubnet(args[0]); + } + catch (UnknownHostException e) + { + return; + } + System.out.println("IpSubnet: " + ipSubnet.toString() + " from " + ipSubnet.cidr.getBaseAddress() + " to " + + ipSubnet.cidr.getEndAddress() + " mask " + ipSubnet.cidr.getMask()); + if (args.length > 1) + { + try + { + System.out.println("Is IN: " + args[1] + " " + ipSubnet.contains(args[1])); + } + catch (UnknownHostException e) + { + } + } + } + } +} diff --git a/javaUtilities/yajsw/src/ahessian/org/jboss/netty/handler/ipfilter/IpSubnetFilterRule.java b/javaUtilities/yajsw/src/ahessian/org/jboss/netty/handler/ipfilter/IpSubnetFilterRule.java new file mode 100644 index 0000000000..3cb95e41d3 --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/jboss/netty/handler/ipfilter/IpSubnetFilterRule.java @@ -0,0 +1,91 @@ +/* + * Copyright 2009 Red Hat, Inc. + * + * Red Hat licenses this file to you under the Apache License, version 2.0 + * (the "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package org.jboss.netty.handler.ipfilter; + +import java.net.InetAddress; +import java.net.UnknownHostException; + +/** + * Ip V4 and Ip V6 filter rule.
+ *
+ * Note that mix of IPV4 and IPV6 is allowed but it is not recommended. So it is preferable to not + * mix IPV4 addresses with IPV6 rules, even if it should work. + * @author frederic bregier + * + */ +public class IpSubnetFilterRule extends IpSubnet implements IpFilterRule +{ + /** + * Is this IpV4Subnet an ALLOW or DENY rule + */ + private boolean isAllowRule = true; + + /** + * Constructor for a ALLOW or DENY ALL + * @param allow True for ALLOW, False for DENY + */ + public IpSubnetFilterRule(boolean allow) + { + super(); + isAllowRule = allow; + } + + /** + * @param allow True for ALLOW, False for DENY + * @param inetAddress + * @param cidrNetMask + * @throws UnknownHostException + */ + public IpSubnetFilterRule(boolean allow, InetAddress inetAddress, int cidrNetMask) throws UnknownHostException + { + super(inetAddress, cidrNetMask); + isAllowRule = allow; + } + + /** + * @param allow True for ALLOW, False for DENY + * @param inetAddress + * @param netMask + * @throws UnknownHostException + */ + public IpSubnetFilterRule(boolean allow, InetAddress inetAddress, String netMask) throws UnknownHostException + { + super(inetAddress, netMask); + isAllowRule = allow; + } + + /** + * @param allow True for ALLOW, False for DENY + * @param netAddress + * @throws UnknownHostException + */ + public IpSubnetFilterRule(boolean allow, String netAddress) throws UnknownHostException + { + super(netAddress); + isAllowRule = allow; + } + + public boolean isAllowRule() + { + return isAllowRule; + } + + public boolean isDenyRule() + { + return !isAllowRule; + } + +} diff --git a/javaUtilities/yajsw/src/ahessian/org/jboss/netty/handler/ipfilter/IpV4Subnet.java b/javaUtilities/yajsw/src/ahessian/org/jboss/netty/handler/ipfilter/IpV4Subnet.java new file mode 100644 index 0000000000..627a162632 --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/jboss/netty/handler/ipfilter/IpV4Subnet.java @@ -0,0 +1,332 @@ +/* + * Copyright 2009 Red Hat, Inc. + * + * Red Hat licenses this file to you under the Apache License, version 2.0 + * (the "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package org.jboss.netty.handler.ipfilter; + +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.StringTokenizer; +import java.util.Vector; + +/** + * This class allows to check if an IP-V4-Address is contained in a subnet.
+ * Supported Formats for the Subnets are: 1.1.1.1/255.255.255.255 or 1.1.1.1/32 (CIDR-Notation) + * and (InetAddress,Mask) where Mask is a integer for CIDR-notation or a String for Standard Mask notation.
+ *

Example1:
+ * IpV4Subnet ips = new IpV4Subnet("192.168.1.0/24");
+ * System.out.println("Result: "+ ips.contains("192.168.1.123"));
+ * System.out.println("Result: "+ ips.contains(inetAddress2));
+ *
Example1 bis:
+ * IpV4Subnet ips = new IpV4Subnet(inetAddress, 24);
+ * where inetAddress is 192.168.1.0 and inetAddress2 is 192.168.1.123
+ *

Example2:
+ * IpV4Subnet ips = new IpV4Subnet("192.168.1.0/255.255.255.0");
+ * System.out.println("Result: "+ ips.contains("192.168.1.123"));
+ * System.out.println("Result: "+ ips.contains(inetAddress2));
+ *
Example2 bis:
+ * IpV4Subnet ips = new IpV4Subnet(inetAddress, "255.255.255.0");
+ * where inetAddress is 192.168.1.0 and inetAddress2 is 192.168.1.123
+ + * @author frederic bregier + * + */ +public class IpV4Subnet implements IpSet, Comparable +{ + private static final int SUBNET_MASK = 0x80000000; + + private static final int BYTE_ADDRESS_MASK = 0xFF; + + private InetAddress inetAddress; + + private int subnet; + + private int mask; + + private int cidrMask; + + /** + * Create IpV4Subnet for ALL (used for ALLOW or DENY ALL) + */ + public IpV4Subnet() + { + // ALLOW or DENY ALL + mask = -1; + // other will be ignored + inetAddress = null; + subnet = 0; + cidrMask = 0; + } + + /** + * Create IpV4Subnet using the CIDR or normal Notation
+ * i.e.: + * IpV4Subnet subnet = new IpV4Subnet("10.10.10.0/24"); or + * IpV4Subnet subnet = new IpV4Subnet("10.10.10.0/255.255.255.0"); + * @param netAddress a network address as string. + * @throws UnknownHostException + * */ + public IpV4Subnet(String netAddress) throws UnknownHostException + { + setNetAddress(netAddress); + } + + /** + * Create IpV4Subnet using the CIDR Notation + * @param inetAddress + * @param cidrNetMask + */ + public IpV4Subnet(InetAddress inetAddress, int cidrNetMask) + { + setNetAddress(inetAddress, cidrNetMask); + } + + /** + * Create IpV4Subnet using the normal Notation + * @param inetAddress + * @param netMask + */ + public IpV4Subnet(InetAddress inetAddress, String netMask) + { + setNetAddress(inetAddress, netMask); + } + + /** + * Sets the Network Address in either CIDR or Decimal Notation.
+ * i.e.: setNetAddress("1.1.1.1/24"); or
+ * setNetAddress("1.1.1.1/255.255.255.0");
+ * @param netAddress a network address as string. + * @throws UnknownHostException + * */ + private void setNetAddress(String netAddress) throws UnknownHostException + { + Vector vec = new Vector(); + StringTokenizer st = new StringTokenizer(netAddress, "/"); + while (st.hasMoreTokens()) + { + vec.add(st.nextElement()); + } + + if (vec.get(1).toString().length() < 3) + { + setNetId(vec.get(0).toString()); + setCidrNetMask(Integer.parseInt(vec.get(1).toString())); + } + else + { + setNetId(vec.get(0).toString()); + setNetMask(vec.get(1).toString()); + } + } + + /** + * Sets the Network Address in CIDR Notation. + * @param inetAddress + * @param cidrNetMask + * */ + private void setNetAddress(InetAddress inetAddress, int cidrNetMask) + { + setNetId(inetAddress); + setCidrNetMask(cidrNetMask); + } + + /** + * Sets the Network Address in Decimal Notation. + * @param inetAddress + * @param netMask + * */ + private void setNetAddress(InetAddress inetAddress, String netMask) + { + setNetId(inetAddress); + setNetMask(netMask); + } + + /** + * Sets the BaseAdress of the Subnet.
+ * i.e.: setNetId("192.168.1.0"); + * @param netId a network ID + * @throws UnknownHostException + * */ + private void setNetId(String netId) throws UnknownHostException + { + InetAddress inetAddress1 = InetAddress.getByName(netId); + this.setNetId(inetAddress1); + } + + /** + * Compute integer representation of InetAddress + * @param inetAddress1 + * @return the integer representation + */ + private int toInt(InetAddress inetAddress1) + { + byte[] address = inetAddress1.getAddress(); + int net = 0; + for (byte addres : address) + { + net <<= 8; + net |= addres & BYTE_ADDRESS_MASK; + } + return net; + } + + /** + * Sets the BaseAdress of the Subnet. + * @param inetAddress + * */ + private void setNetId(InetAddress inetAddress) + { + this.inetAddress = inetAddress; + subnet = toInt(inetAddress); + } + + /** + * Sets the Subnet's Netmask in Decimal format.
+ * i.e.: setNetMask("255.255.255.0"); + * @param netMask a network mask + * */ + private void setNetMask(String netMask) + { + StringTokenizer nm = new StringTokenizer(netMask, "."); + int i = 0; + int[] netmask = new int[4]; + while (nm.hasMoreTokens()) + { + netmask[i] = Integer.parseInt(nm.nextToken()); + i++; + } + int mask1 = 0; + for (i = 0; i < 4; i++) + { + mask1 += Integer.bitCount(netmask[i]); + } + setCidrNetMask(mask1); + } + + /** + * Sets the CIDR Netmask
+ * i.e.: setCidrNetMask(24); + * @param cidrNetMask a netmask in CIDR notation + * */ + private void setCidrNetMask(int cidrNetMask) + { + cidrMask = cidrNetMask; + mask = SUBNET_MASK >> cidrMask - 1; + } + + /** + * Compares the given IP-Address against the Subnet and returns true if + * the ip is in the subnet-ip-range and false if not. + * @param ipAddr an ipaddress + * @return returns true if the given IP address is inside the currently + * set network. + * @throws UnknownHostException + * */ + public boolean contains(String ipAddr) throws UnknownHostException + { + InetAddress inetAddress1 = InetAddress.getByName(ipAddr); + return this.contains(inetAddress1); + } + + /** + * Compares the given InetAddress against the Subnet and returns true if + * the ip is in the subnet-ip-range and false if not. + * @param inetAddress1 + * @return returns true if the given IP address is inside the currently + * set network. + * */ + public boolean contains(InetAddress inetAddress1) + { + if (mask == -1) + { + // ANY + return true; + } + return (toInt(inetAddress1) & mask) == subnet; + } + + @Override + public String toString() + { + return inetAddress.getHostAddress() + "/" + cidrMask; + } + + @Override + public boolean equals(Object o) + { + if (!(o instanceof IpV4Subnet)) + { + return false; + } + IpV4Subnet ipV4Subnet = (IpV4Subnet) o; + return ipV4Subnet.subnet == subnet && ipV4Subnet.cidrMask == cidrMask; + } + + /** + * Compare two IpV4Subnet + */ + public int compareTo(IpV4Subnet o) + { + if (o.subnet == subnet && o.cidrMask == cidrMask) + { + return 0; + } + if (o.subnet < subnet) + { + return 1; + } + else if (o.subnet > subnet) + { + return -1; + } + else if (o.cidrMask < cidrMask) + { + // greater Mask means less IpAddresses so -1 + return -1; + } + return 1; + } + + /** + * Simple test functions + * @param args + * where args[0] is the netmask (standard or CIDR notation) and optional args[1] is + * the inetAddress to test with this IpV4Subnet + */ + public static void main(String[] args) + { + if (args.length != 0) + { + IpV4Subnet ipV4Subnet = null; + try + { + ipV4Subnet = new IpV4Subnet(args[0]); + } + catch (UnknownHostException e) + { + return; + } + if (args.length > 1) + { + try + { + System.out.println("Is IN: " + args[1] + " " + ipV4Subnet.contains(args[1])); + } + catch (UnknownHostException e) + { + } + } + } + } +} diff --git a/javaUtilities/yajsw/src/ahessian/org/jboss/netty/handler/ipfilter/IpV4SubnetFilterRule.java b/javaUtilities/yajsw/src/ahessian/org/jboss/netty/handler/ipfilter/IpV4SubnetFilterRule.java new file mode 100644 index 0000000000..0847f5b7fa --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/jboss/netty/handler/ipfilter/IpV4SubnetFilterRule.java @@ -0,0 +1,86 @@ +/* + * Copyright 2009 Red Hat, Inc. + * + * Red Hat licenses this file to you under the Apache License, version 2.0 + * (the "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package org.jboss.netty.handler.ipfilter; + +import java.net.InetAddress; +import java.net.UnknownHostException; + +/** + * IpV4 only Filter Rule + * @author frederic bregier + * + */ +public class IpV4SubnetFilterRule extends IpV4Subnet implements IpFilterRule +{ + /** + * Is this IpV4Subnet an ALLOW or DENY rule + */ + private boolean isAllowRule = true; + + /** + * Constructor for a ALLOW or DENY ALL + * @param allow True for ALLOW, False for DENY + */ + public IpV4SubnetFilterRule(boolean allow) + { + super(); + isAllowRule = allow; + } + + /** + * @param allow True for ALLOW, False for DENY + * @param inetAddress + * @param cidrNetMask + */ + public IpV4SubnetFilterRule(boolean allow, InetAddress inetAddress, int cidrNetMask) + { + super(inetAddress, cidrNetMask); + isAllowRule = allow; + } + + /** + * @param allow True for ALLOW, False for DENY + * @param inetAddress + * @param netMask + */ + public IpV4SubnetFilterRule(boolean allow, InetAddress inetAddress, String netMask) + { + super(inetAddress, netMask); + isAllowRule = allow; + } + + /** + * @param allow True for ALLOW, False for DENY + * @param netAddress + * @throws UnknownHostException + */ + public IpV4SubnetFilterRule(boolean allow, String netAddress) throws UnknownHostException + { + super(netAddress); + isAllowRule = allow; + } + + public boolean isAllowRule() + { + return isAllowRule; + } + + public boolean isDenyRule() + { + return !isAllowRule; + } + +} diff --git a/javaUtilities/yajsw/src/ahessian/org/jboss/netty/handler/ipfilter/OneIpFilterHandler.java b/javaUtilities/yajsw/src/ahessian/org/jboss/netty/handler/ipfilter/OneIpFilterHandler.java new file mode 100644 index 0000000000..968a9ede27 --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/jboss/netty/handler/ipfilter/OneIpFilterHandler.java @@ -0,0 +1,92 @@ +/* + * Copyright 2009 Red Hat, Inc. + * + * Red Hat licenses this file to you under the Apache License, version 2.0 + * (the "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package org.jboss.netty.handler.ipfilter; + +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +import org.jboss.netty.channel.ChannelEvent; +import org.jboss.netty.channel.ChannelHandlerContext; +import org.jboss.netty.channel.ChannelPipelineCoverage; +import org.jboss.netty.channel.ChannelState; +import org.jboss.netty.channel.ChannelStateEvent; + +/** + * Handler that block any new connection if there are already a currently active + * channel connected with the same InetAddress (IP).
+ *
+ * + * Take care to not change isBlocked method except if you know what you are doing + * since it is used to test if the current closed connection is to be removed + * or not from the map of currently connected channel. + * + * @author frederic bregier + * + */ +@ChannelPipelineCoverage("all") +public class OneIpFilterHandler extends IpFilteringHandlerImpl +{ + /** + * HashMap of current remote connected InetAddress + */ + private final ConcurrentMap connectedSet = new ConcurrentHashMap(); + + /* (non-Javadoc) + * @see org.jboss.netty.handler.ipfilter.IpFilteringHandler#accept(org.jboss.netty.channel.ChannelHandlerContext, org.jboss.netty.channel.ChannelEvent, java.net.InetSocketAddress) + */ + @Override + protected boolean accept(ChannelHandlerContext ctx, ChannelEvent e, InetSocketAddress inetSocketAddress) + throws Exception + { + InetAddress inetAddress = inetSocketAddress.getAddress(); + if (connectedSet.containsKey(inetAddress)) + { + return false; + } + connectedSet.put(inetAddress, Boolean.TRUE); + return true; + } + + /* (non-Javadoc) + * @see org.jboss.netty.handler.ipfilter.IpFilteringHandler#handleUpstream(org.jboss.netty.channel.ChannelHandlerContext, org.jboss.netty.channel.ChannelEvent) + */ + @Override + public void handleUpstream(ChannelHandlerContext ctx, ChannelEvent e) throws Exception + { + super.handleUpstream(ctx, e); + // Try to remove entry from Map if already exists + if (e instanceof ChannelStateEvent) + { + ChannelStateEvent evt = (ChannelStateEvent) e; + if (evt.getState() == ChannelState.CONNECTED) + { + if (evt.getValue() == null) + { + // DISCONNECTED but was this channel blocked or not + if (isBlocked(ctx)) + { + // remove inetsocketaddress from set since this channel was not blocked before + InetSocketAddress inetSocketAddress = (InetSocketAddress) e.getChannel().getRemoteAddress(); + connectedSet.remove(inetSocketAddress.getAddress()); + } + } + } + } + } + +} diff --git a/javaUtilities/yajsw/src/ahessian/org/jboss/netty/handler/ipfilter/PatternRule.java b/javaUtilities/yajsw/src/ahessian/org/jboss/netty/handler/ipfilter/PatternRule.java new file mode 100644 index 0000000000..717860aeb4 --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/jboss/netty/handler/ipfilter/PatternRule.java @@ -0,0 +1,207 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2010, Red Hat Middleware LLC, and individual contributors + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jboss.netty.handler.ipfilter; + +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.regex.Pattern; + +import org.jboss.netty.logging.InternalLogger; +import org.jboss.netty.logging.InternalLoggerFactory; + +/** + * The Class PatternRule represents an IP filter rule using string patterns. + *
+ * Rule Syntax: + *
+ *
+ * Rule ::= [n|i]:address          n stands for computer name, i for ip address
+ * address ::= <regex> | localhost
+ * regex is a regular expression with '*' as multi character and '?' as single character wild card
+ * 
+ *
+ * Example: allow localhost: + *
+ * new PatternRule(true, "n:localhost") + *
+ * Example: allow local lan: + *
+ * new PatternRule(true, "i:192.168.0.*") + *
+ * Example: block all + *
+ * new PatternRule(false, "n:*") + *
+ * + * @author Ron + */ +public class PatternRule implements IpFilterRule, Comparable +{ + private static final InternalLogger logger = InternalLoggerFactory.getInstance(PatternRule.class); + + private Pattern ipPattern = null; + + private Pattern namePattern = null; + + private boolean isAllowRule = true; + + private boolean localhost = false; + + private String pattern = null; + + /** + * Instantiates a new pattern rule. + * + * @param allow indicates if this is an allow or block rule + * @param pattern the filter pattern + */ + public PatternRule(boolean allow, String pattern) + { + this.isAllowRule = allow; + this.pattern = pattern; + parse(pattern); + } + + /** + * returns the pattern. + * + * @return the pattern + */ + public String getPattern() + { + return this.pattern; + } + + /* (non-Javadoc) + * @see org.jboss.netty.handler.ipfilter.IpFilterRule#isAllowRule() + */ + public boolean isAllowRule() + { + return isAllowRule; + } + + /* (non-Javadoc) + * @see org.jboss.netty.handler.ipfilter.IpFilterRule#isDenyRule() + */ + public boolean isDenyRule() + { + return !isAllowRule; + } + + /* (non-Javadoc) + * @see org.jboss.netty.handler.ipfilter.IpSet#contains(java.net.InetAddress) + */ + public boolean contains(InetAddress inetAddress) + { + if (localhost) + if (isLocalhost(inetAddress)) + return true; + if (ipPattern != null) + if (ipPattern.matcher(inetAddress.getHostAddress()).matches()) + return true; + if (namePattern != null) + if (namePattern.matcher(inetAddress.getHostName()).matches()) + return true; + return false; + } + + private void parse(String pattern) + { + if (pattern == null) + return; + + String[] acls = pattern.split(","); + + String ip = ""; + String name = ""; + for (String c : acls) + { + c = c.trim(); + if (c.equals("n:localhost")) + this.localhost = true; + else if (c.startsWith("n:")) + name = addRule(name, c.substring(2)); + else if (c.startsWith("i:")) + ip = addRule(ip, c.substring(2)); + } + if (ip.length() != 0) + ipPattern = Pattern.compile(ip); + if (name.length() != 0) + namePattern = Pattern.compile(name); + + } + + private String addRule(String pattern, String rule) + { + if (rule == null || rule.length() == 0) + return pattern; + if (pattern.length() != 0) + pattern += "|"; + rule = rule.replaceAll("\\.", "\\\\."); + rule = rule.replaceAll("\\*", ".*"); + rule = rule.replaceAll("\\?", "."); + pattern += "(" + rule + ")"; + return pattern; + } + + private boolean isLocalhost(InetAddress address) + { + try + { + if (address.equals(InetAddress.getLocalHost())) + return true; + } + catch (UnknownHostException e) + { + logger.info("error getting ip of localhost", e); + } + try + { + InetAddress[] addrs = InetAddress.getAllByName("127.0.0.1"); + for (InetAddress addr : addrs) + if (addr.equals(address)) + return true; + } + catch (UnknownHostException e) + { + logger.info("error getting ip of localhost", e); + } + return false; + + } + + /* (non-Javadoc) + * @see java.lang.Comparable#compareTo(java.lang.Object) + */ + public int compareTo(Object o) + { + if (o == null) + return -1; + if (!(o instanceof PatternRule)) + return -1; + PatternRule p = (PatternRule) o; + if (p.isAllowRule() && !this.isAllowRule) + return -1; + if (this.pattern == null && p.pattern == null) + return 0; + if (this.pattern != null) + return this.pattern.compareTo(p.getPattern()); + return -1; + } + +} diff --git a/javaUtilities/yajsw/src/ahessian/org/jboss/netty/handler/ipfilter/package-info.java b/javaUtilities/yajsw/src/ahessian/org/jboss/netty/handler/ipfilter/package-info.java new file mode 100644 index 0000000000..395ea022fc --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/jboss/netty/handler/ipfilter/package-info.java @@ -0,0 +1,93 @@ +/* + * Copyright 2009 Red Hat, Inc. + * + * Red Hat licenses this file to you under the Apache License, version 2.0 + * (the "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +/** + * Implementation of a Ip based Filter handlers.
+ *

+ * + * + *

The main goal of this package is to allow to filter connections based on IP rules. + * The main interface is {@link IpFilteringHandler} which all filters will extend.

+ * + *

Two IP filtering are proposed:
+ *

    + *
  • {@link OneIpFilterHandler}: This filter proposes to allow only one connection by client's IP Address. + * I.E. this filter will prevent two connections from the same client based on its IP address.


  • + * + *
  • {@link IpFilterRuleHandler}: This filter proposes to allow or block IP range (based on standard notation + * or on CIDR notation) when the connection is running. It relies on another class like + * IpV4SubnetFilterRule (IPV4 support only), IpSubnetFilterRule (IPV4 and IPV6 support) or PatternRule (string pattern support) + * which implements those Ip ranges.


  • + * + *

+ * + *

Standard use could be as follow: The accept method must be overridden (of course you can + * override others).

+ * + *

    + *
  • accept method allows to specify your way of choosing if a new connection is + * to be allowed or not.

  • + * In OneIpFilterHandler and IpFilterRuleHandler, + * this method is already implemented.
    + *
    + * + *
  • handleRefusedChannel method is executed when the accept method filters (blocks, so returning false) + * the new connection. This method allows you to implement specific actions to be taken before the channel is + * closed. After this method is called, the channel is immediately closed.

  • + * So if you want to send back a message to the client, don't forget to return a respectful ChannelFuture, + * otherwise the message could be missed since the channel will be closed immediately after this + * call and the waiting on this channelFuture (at least with respect of asynchronous operations).

    + * Per default implementation this method invokes an {@link IpFilterListener} or returns null if no listener has been set. + *

    + * + *
  • continues is called when any event appears after CONNECTED event and only for + * blocked channels.

  • + * It should return True if this new event has to go to next handlers + * in the pipeline if any, and False (default) if no events has to be passed to the next + * handlers when a channel is blocked. This is intend to prevent any unnecessary action since the connection is refused.
    + * However, you could change its behavior for instance because you don't want that any event + * will be blocked by this filter by returning always true or according to some events.
    + * Note that OPENED and BOUND events are still passed to the next entry in the pipeline since + * those events come out before the CONNECTED event, so there is no possibility to filter those two events + * before the CONNECTED event shows up. Therefore, you might want to let CLOSED and UNBOUND be passed + * to the next entry in the pipeline.

    + * Per default implementation this method invokes an {@link IpFilterListener} or returns false if no listener has been set. + *

    + * + *
  • Finally handleUpstream traps the CONNECTED and DISCONNECTED events.

  • + * If in the CONNECTED events the channel is blocked (accept refused the channel), + * then any new events on this channel will be blocked.
    + * However, you could change its behavior for instance because you don't want that all events + * will be blocked by this filter by testing the result of isBlocked, and if so, + * calling ctx.sendUpstream(e); after calling the super method or by changing the continues method.

    + + *



+ * + * A typical setup for ip filter for TCP/IP socket would be: + * + *
+ * {@link ChannelPipeline} pipeline = ...;
+ * 
+ * IpFilterRuleHandler firewall = new IpFilterRuleHandler();
+ * firewall.addAll(new IpFilterRuleList("+n:localhost, +c:192.168.0.0/27, -n:*"));
+ * pipeline.addFirst("firewall", firewall);
+ * 
+ * + * @apiviz.exclude ^java\.lang\. + */ +package org.jboss.netty.handler.ipfilter; + + diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/Constants.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/Constants.java new file mode 100644 index 0000000000..86edd0ad31 --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/Constants.java @@ -0,0 +1,38 @@ +package org.rzo.netty.ahessian; + +import org.jboss.netty.logging.InternalLogger; +import org.jboss.netty.logging.InternalLoggerFactory; + +public interface Constants +{ + public static final String HEADER_STRING = "H"; + public static final Integer GROUP_HEADER_KEY = 0; + public static final Integer CALL_ID_HEADER_KEY = 1; + public static final Integer SERVICE_ID_HEADER_KEY = 2; + + public static final Integer CALLBACK_ID_HEADER_KEY = 3; + public static final Integer CALLBACK_METHOD_HEADER_KEY = 4; + public static final Integer CALLBACK_ARGS_HEADER_KEY = 5; + public static final Integer CALLBACK_DONE_HEADER_KEY = 6; + + public static final Integer COMPLETED_HEADER_KEY = 7; + public static final Integer HAS_SESSION_FILTER_HEADER_KEY = 8; + + public static final int IGROUP_HEADER_KEY = 0; + public static final int ICALL_ID_HEADER_KEY = 1; + public static final int ISERVICE_ID_HEADER_KEY = 2; + + public static final int ICALLBACK_ID_HEADER_KEY = 3; + public static final int ICALLBACK_METHOD_HEADER_KEY = 4; + public static final int ICALLBACK_ARGS_HEADER_KEY = 5; + public static final int ICALLBACK_DONE_HEADER_KEY = 6; + + public static final int ICOMPLETED_HEADER_KEY = 7; + public static final int IHAS_SESSION_FILTER_HEADER_KEY = 8; + + + public static final InternalLogger ahessianLogger = + InternalLoggerFactory.getInstance("ahessian"); + + +} diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/application/file/remote/server/AsyncFileServiceImpl.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/application/file/remote/server/AsyncFileServiceImpl.java new file mode 100644 index 0000000000..2a879b62d0 --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/application/file/remote/server/AsyncFileServiceImpl.java @@ -0,0 +1,36 @@ +package org.rzo.netty.ahessian.application.file.remote.server; + +import java.io.File; +import java.io.InputStream; +import java.util.List; + +import org.rzo.netty.ahessian.application.file.remote.service.AsyncFileService; +import org.rzo.netty.ahessian.application.file.remote.service.FileObject; + +public class AsyncFileServiceImpl implements AsyncFileService +{ + + public FileObject getFile(String file) + { + File f = new File(file); + if (!f.exists()) + return null; + return toFileObject(f); + } + + private FileObject toFileObject(File f) + { + return new FileObjectImpl(f); + } + + public InputStream getInputStream(String file) + { + return null; + } + + public List list(String filter) + { + return null; + } + +} diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/application/file/remote/server/FileObjectImpl.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/application/file/remote/server/FileObjectImpl.java new file mode 100644 index 0000000000..70c637727f --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/application/file/remote/server/FileObjectImpl.java @@ -0,0 +1,75 @@ +package org.rzo.netty.ahessian.application.file.remote.server; + +import java.io.File; +import java.io.InputStream; + +import org.rzo.netty.ahessian.application.file.remote.service.AsyncFileService; +import org.rzo.netty.ahessian.application.file.remote.service.FileObject; + +public class FileObjectImpl implements FileObject +{ + long _created = -1; + String _path = null; + boolean _isDirectory = false; + boolean _isFile = false; + boolean _isHidden = false; + long _lastModified = -1; + long _length = -1; + transient AsyncFileService _fileService = null; + + public FileObjectImpl(File file) + { + _path = file.getAbsolutePath(); + _isDirectory = file.isDirectory(); + _isFile = file.isFile(); + _isHidden = file.isHidden(); + _lastModified = file.lastModified(); + _length = file.length(); + } + + public FileObjectImpl() + { + + } + + public long created() + { + return _created; + } + + public InputStream getInputStream() + { + return _fileService.getInputStream(_path); + } + + public String getPath() + { + return _path; + } + + public boolean isDirectory() + { + return _isDirectory; + } + + public boolean isFile() + { + return _isFile; + } + + public boolean isHidden() + { + return _isHidden; + } + + public long lastModified() + { + return _lastModified; + } + + public long length() + { + return _length; + } + +} diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/application/file/remote/service/AsyncFileService.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/application/file/remote/service/AsyncFileService.java new file mode 100644 index 0000000000..dbd4fbf939 --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/application/file/remote/service/AsyncFileService.java @@ -0,0 +1,32 @@ +package org.rzo.netty.ahessian.application.file.remote.service; + +import java.io.IOException; +import java.io.InputStream; +import java.util.List; + +import javax.management.Attribute; +import javax.management.AttributeList; +import javax.management.AttributeNotFoundException; +import javax.management.InstanceAlreadyExistsException; +import javax.management.InstanceNotFoundException; +import javax.management.IntrospectionException; +import javax.management.InvalidAttributeValueException; +import javax.management.ListenerNotFoundException; +import javax.management.MBeanException; +import javax.management.MBeanRegistration; +import javax.management.MBeanRegistrationException; +import javax.management.NotCompliantMBeanException; +import javax.management.NotificationFilter; +import javax.management.NotificationListener; +import javax.management.ObjectName; +import javax.management.QueryExp; +import javax.management.ReflectionException; + +public interface AsyncFileService +{ + public FileObject getFile(String file); + public List list(String filter); + public InputStream getInputStream(String file); + + +} diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/application/file/remote/service/FileObject.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/application/file/remote/service/FileObject.java new file mode 100644 index 0000000000..a13d35d3f4 --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/application/file/remote/service/FileObject.java @@ -0,0 +1,15 @@ +package org.rzo.netty.ahessian.application.file.remote.service; + +import java.io.InputStream; + +public interface FileObject +{ + public String getPath(); + public boolean isDirectory(); + public boolean isFile(); + public boolean isHidden(); + public long lastModified(); + public long created(); + public long length(); + public InputStream getInputStream(); +} diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/application/jmx/remote/client/Client.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/application/jmx/remote/client/Client.java new file mode 100644 index 0000000000..66ad895c15 --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/application/jmx/remote/client/Client.java @@ -0,0 +1,111 @@ +package org.rzo.netty.ahessian.application.jmx.remote.client; + +import java.net.InetSocketAddress; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +import javax.management.MBeanServerConnection; +import javax.management.ObjectName; + +import org.jboss.netty.bootstrap.ClientBootstrap; +import org.jboss.netty.channel.Channel; +import org.jboss.netty.channel.ChannelFuture; +import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory; +import org.rzo.netty.ahessian.application.jmx.remote.service.AsyncMBeanServerConnection; +import org.rzo.netty.ahessian.application.jmx.remote.service.MBeanServerConnectionAsyncAdapter; +import org.rzo.netty.ahessian.rpc.client.HessianProxyFactory; + + +public class Client +{ + static boolean stop = false; + static MBeanServerConnection server; + + public static void main(String[] args) throws Exception + { + + final ExecutorService executor = Executors.newCachedThreadPool(); + + // Configure the client. + ClientBootstrap bootstrap = new ClientBootstrap( + new NioClientSocketChannelFactory( + executor, + executor)); + + bootstrap.setOption( + "remoteAddress", new InetSocketAddress("localhost", 8080)); + + bootstrap.setOption("reuseAddress", true); + + + final HessianProxyFactory factory = new HessianProxyFactory(executor, "localhost:8080"); + bootstrap.setPipelineFactory( + new RPCClientSessionPipelineFactory(new RPCClientMixinPipelineFactory(executor, factory), bootstrap)); + + + factory.setDisconnectedListener(new Runnable() + { + public void run() + { + //stop = true; + } + }); + +factory.setNewSessionListener(new Runnable() +{ + public void run() + { + stop = false; + executor.execute(new Runnable() + { + public void run() + { + System.out.println("started work thread"); + Map options = new HashMap(); + options.put("sync", true); + options.put("timeout", (long)10000); + AsyncMBeanServerConnection service = (AsyncMBeanServerConnection) factory.create(AsyncMBeanServerConnection.class, Client.class.getClassLoader(), options); + server = new MBeanServerConnectionAsyncAdapter(service); + + while (!stop) + { + try + { + ObjectName on = new ObjectName("java.lang:type=ClassLoading"); + Object x = server.getAttribute(on, "LoadedClassCount"); + System.out.println(x); + } + catch(Exception ex) + { + ex.printStackTrace(); + System.out.println(ex); + } + try + { + Thread.sleep(1000); + } + catch (InterruptedException e) + { + e.printStackTrace(); + } + } + System.out.println("stopped work thread"); + } + }); + } +}); + + // Start the connection attempt. + ChannelFuture future = bootstrap.connect(new InetSocketAddress("localhost", 8080)); + // Wait until the connection attempt succeeds or fails. + Channel channel = future.awaitUninterruptibly().getChannel(); + if (future.isSuccess()) + System.out.println("connected"); + + // get a proxy + + } + +} diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/application/jmx/remote/client/RPCClientMixinPipelineFactory.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/application/jmx/remote/client/RPCClientMixinPipelineFactory.java new file mode 100644 index 0000000000..0fa1e6c9d4 --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/application/jmx/remote/client/RPCClientMixinPipelineFactory.java @@ -0,0 +1,55 @@ +package org.rzo.netty.ahessian.application.jmx.remote.client; + +import java.util.concurrent.Executor; + +import org.jboss.netty.channel.ChannelPipeline; +import org.jboss.netty.channel.ChannelPipelineFactory; +import org.rzo.netty.ahessian.application.jmx.remote.service.JmxSerializerFactory; +import org.rzo.netty.ahessian.io.InputStreamDecoder; +import org.rzo.netty.ahessian.io.OutputStreamEncoder; +import org.rzo.netty.ahessian.io.PullInputStreamConsumer; +import org.rzo.netty.ahessian.rpc.client.HessianProxyFactory; +import org.rzo.netty.ahessian.rpc.message.HessianRPCCallEncoder; +import org.rzo.netty.ahessian.rpc.message.HessianRPCReplyDecoder; +import org.rzo.netty.ahessian.rpc.message.OutputProducer; +import org.rzo.netty.ahessian.session.MixinPipeline; + +import com.caucho.hessian4.io.SerializerFactory; + +public class RPCClientMixinPipelineFactory implements ChannelPipelineFactory +{ + + Executor _executor; + HessianProxyFactory _factory; + SerializerFactory _serializerFactory = new JmxSerializerFactory(); + + RPCClientMixinPipelineFactory(Executor executor, HessianProxyFactory factory) + { + _executor = executor; + _factory = factory; + } + + + + public ChannelPipeline getPipeline() throws Exception + { + ChannelPipeline pipeline = new MixinPipeline(); + // InputStreamDecoder returns an input stream and calls the next handler in a separate thread + + pipeline.addLast("inputStream", new InputStreamDecoder()); + + //pipeline.addLast("logger2",new OutLogger1("2")); + pipeline.addLast("outputStream", new OutputStreamEncoder()); + + pipeline.addLast("hessianReplyDecoder", new PullInputStreamConsumer(new HessianRPCReplyDecoder(_factory, _serializerFactory), _executor)); + pipeline.addLast("hessianCallEncoder", new HessianRPCCallEncoder(_serializerFactory)); + pipeline.addLast("outputProducer", new OutputProducer(_executor)); + //pipeline.addLast("logger3",new OutLogger("3")); + pipeline.addLast("hessianHandler", _factory); + + return pipeline; + + } + + +} diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/application/jmx/remote/client/RPCClientSessionPipelineFactory.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/application/jmx/remote/client/RPCClientSessionPipelineFactory.java new file mode 100644 index 0000000000..97e5696d71 --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/application/jmx/remote/client/RPCClientSessionPipelineFactory.java @@ -0,0 +1,92 @@ +package org.rzo.netty.ahessian.application.jmx.remote.client; + +import static org.jboss.netty.channel.Channels.pipeline; + +import java.net.ConnectException; +import java.util.Timer; +import java.util.TimerTask; + +import org.jboss.netty.bootstrap.ClientBootstrap; +import org.jboss.netty.channel.ChannelFuture; +import org.jboss.netty.channel.ChannelHandlerContext; +import org.jboss.netty.channel.ChannelPipeline; +import org.jboss.netty.channel.ChannelPipelineFactory; +import org.jboss.netty.channel.ChannelStateEvent; +import org.jboss.netty.channel.ExceptionEvent; +import org.jboss.netty.channel.SimpleChannelUpstreamHandler; +import org.rzo.netty.ahessian.log.OutLogger; +import org.rzo.netty.ahessian.session.ClientSessionFilter; + +public class RPCClientSessionPipelineFactory implements ChannelPipelineFactory +{ + + ChannelPipelineFactory _mixinFactory; + ClientSessionFilter _sessionFilter; + private static Timer timer = new Timer(); + private static long RECONNECT_DELAY = 5000; + ClientBootstrap _bootstrap; + + + + RPCClientSessionPipelineFactory(ChannelPipelineFactory mixinFactory, ClientBootstrap bootstrap) + { + _mixinFactory = mixinFactory; + _sessionFilter = new ClientSessionFilter(_mixinFactory); + _bootstrap = bootstrap; + } + + public ChannelPipeline getPipeline() throws Exception + { + ChannelPipeline pipeline = pipeline(); // Note the static import. + pipeline.addLast("logger",new OutLogger("1")); + pipeline.addLast("reconnector", new SimpleChannelUpstreamHandler() + { + + @Override + public void channelClosed(ChannelHandlerContext ctx, ChannelStateEvent e) { + ctx.sendUpstream(e); + System.out.println("channel closed wait to reconnect ..."); + timer.schedule(new TimerTask() { + public void run() { + System.out.println("reconnecting..."); + ChannelFuture f = _bootstrap.connect(); + try + { + System.out.println("future wait"); + f.awaitUninterruptibly(); + System.out.println("future wait terminated"); + } + catch (Exception e) + { + // TODO Auto-generated catch block + e.printStackTrace(); + } + if (f.isSuccess()) + System.out.println("connected"); + else + { + System.out.println("not connected"); + // f.getChannel().close(); + } + + } + }, RECONNECT_DELAY); + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) { + Throwable cause = e.getCause(); + if (cause instanceof ConnectException) + { + System.out.println("conection lost"); + } + ctx.getChannel().close(); + } + } +); + pipeline.addLast("sessionFilter", _sessionFilter); + + return pipeline; + } + +} diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/application/jmx/remote/server/RPCServerMixinPipelineFactory.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/application/jmx/remote/server/RPCServerMixinPipelineFactory.java new file mode 100644 index 0000000000..3f1eab0d4e --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/application/jmx/remote/server/RPCServerMixinPipelineFactory.java @@ -0,0 +1,63 @@ +package org.rzo.netty.ahessian.application.jmx.remote.server; + +import java.util.ArrayList; +import java.util.concurrent.Executor; + +import javax.management.MBeanServer; +import javax.management.MBeanServerConnection; +import javax.management.MBeanServerFactory; + +import org.jboss.netty.channel.ChannelPipeline; +import org.jboss.netty.channel.ChannelPipelineFactory; +import org.rzo.netty.ahessian.application.jmx.remote.service.JmxSerializerFactory; +import org.rzo.netty.ahessian.io.InputStreamDecoder; +import org.rzo.netty.ahessian.io.OutputStreamEncoder; +import org.rzo.netty.ahessian.io.PullInputStreamConsumer; +import org.rzo.netty.ahessian.rpc.message.HessianRPCCallDecoder; +import org.rzo.netty.ahessian.rpc.message.HessianRPCReplyEncoder; +import org.rzo.netty.ahessian.rpc.message.OutputProducer; +import org.rzo.netty.ahessian.rpc.server.HessianRPCServiceHandler; +import org.rzo.netty.ahessian.rpc.server.ImmediateInvokeService; +import org.rzo.netty.ahessian.session.MixinPipeline; + +import com.caucho.hessian4.io.SerializerFactory; + +public class RPCServerMixinPipelineFactory implements ChannelPipelineFactory +{ + + Executor _executor; + SerializerFactory _serializerFactory = new JmxSerializerFactory(); + + RPCServerMixinPipelineFactory(Executor executor) + { + _executor = executor; + } + + public ChannelPipeline getPipeline() throws Exception + { + ChannelPipeline pipeline = new MixinPipeline(); + pipeline.addLast("inputStream", new InputStreamDecoder()); + //pipeline.addLast("logger2",new OutLogger("2")); + pipeline.addLast("outputStream", new OutputStreamEncoder()); + pipeline.addLast("callDecoder", new PullInputStreamConsumer(new HessianRPCCallDecoder(_serializerFactory), _executor )); + pipeline.addLast("replyEncoder", new HessianRPCReplyEncoder(_serializerFactory)); + pipeline.addLast("outputProducer", new OutputProducer(_executor)); + //pipeline.addLast("logger3",new OutLogger("3")); + HessianRPCServiceHandler factory = new HessianRPCServiceHandler(_executor); + ArrayList servers = MBeanServerFactory.findMBeanServer(null); + MBeanServer server = null; + if (servers != null && servers.size() > 0) + server = (MBeanServer) servers.get(0); + if (server == null) + server = MBeanServerFactory.createMBeanServer(); + + + //factory.addService("default", new ContinuationService(new ContinuationHalloWorldService(), HelloWorldServiceInterface.class, factory, _executor)); + factory.addService("default", new ImmediateInvokeService(server, MBeanServerConnection.class, factory)); + pipeline.addLast("hessianRPCServer", factory); + + //bootstrap.getPipeline().addLast("logger4",new OutLogger("4")); + return pipeline; + } + +} diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/application/jmx/remote/server/RPCServerSessionPipelineFactory.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/application/jmx/remote/server/RPCServerSessionPipelineFactory.java new file mode 100644 index 0000000000..822bcfd654 --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/application/jmx/remote/server/RPCServerSessionPipelineFactory.java @@ -0,0 +1,28 @@ +package org.rzo.netty.ahessian.application.jmx.remote.server; + +import static org.jboss.netty.channel.Channels.pipeline; + +import org.jboss.netty.channel.ChannelPipeline; +import org.jboss.netty.channel.ChannelPipelineFactory; +import org.rzo.netty.ahessian.log.OutLogger; +import org.rzo.netty.ahessian.session.ServerSessionFilter; + +public class RPCServerSessionPipelineFactory implements ChannelPipelineFactory +{ + + ChannelPipelineFactory _mixinFactory; + + RPCServerSessionPipelineFactory(ChannelPipelineFactory mixinFactory) + { + _mixinFactory = mixinFactory; + } + + public ChannelPipeline getPipeline() throws Exception + { + ChannelPipeline pipeline = pipeline(); // Note the static import. + pipeline.addLast("logger",new OutLogger("1")); + pipeline.addLast("sessionFilter", new ServerSessionFilter(_mixinFactory)); + return pipeline; + } + +} diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/application/jmx/remote/server/Server.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/application/jmx/remote/server/Server.java new file mode 100644 index 0000000000..4f54af008c --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/application/jmx/remote/server/Server.java @@ -0,0 +1,30 @@ +package org.rzo.netty.ahessian.application.jmx.remote.server; + +import java.net.InetSocketAddress; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; + +import org.jboss.netty.bootstrap.ServerBootstrap; +import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory; + +public class Server +{ + public static void main(String[] args) + { + Executor executor = Executors.newFixedThreadPool(200); + + // Configure the server. + ServerBootstrap bootstrap = new ServerBootstrap( + new NioServerSocketChannelFactory( + executor, + executor)); + + bootstrap.setPipelineFactory( + new RPCServerSessionPipelineFactory( new RPCServerMixinPipelineFactory(executor))); + + // Bind and start to accept incoming connections. + bootstrap.bind(new InetSocketAddress(8080)); + + } + +} diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/application/jmx/remote/service/AsyncMBeanServerConnection.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/application/jmx/remote/service/AsyncMBeanServerConnection.java new file mode 100644 index 0000000000..0b142ed16c --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/application/jmx/remote/service/AsyncMBeanServerConnection.java @@ -0,0 +1,378 @@ +package org.rzo.netty.ahessian.application.jmx.remote.service; + +import java.io.IOException; + +import javax.management.Attribute; +import javax.management.AttributeList; +import javax.management.AttributeNotFoundException; +import javax.management.InstanceAlreadyExistsException; +import javax.management.InstanceNotFoundException; +import javax.management.IntrospectionException; +import javax.management.InvalidAttributeValueException; +import javax.management.ListenerNotFoundException; +import javax.management.MBeanException; +import javax.management.MBeanRegistration; +import javax.management.MBeanRegistrationException; +import javax.management.NotCompliantMBeanException; +import javax.management.NotificationFilter; +import javax.management.NotificationListener; +import javax.management.ObjectName; +import javax.management.QueryExp; +import javax.management.ReflectionException; + +public interface AsyncMBeanServerConnection +{ + /** + * Adds a NotificationListener to a registered MBean. + * A notification emitted by the specified source MBean will be forwarded by the MBeanServer to the given listener, + * if the given NotificationFilter allows so. If the filter is null, every notification will be sent to the + * listener. + * The handback object is transparently passed to the listener by the MBeanServer. + * The source of the notification is the source MBean ObjectName. + * + * @param observed The ObjectName of the source MBean on which the listener should be added. + * @param listener The listener which will handle the notifications emitted by the source MBean. + * @param filter The filter which will allow the notification to be forwarded to the listener. + * @param handback The context to be sent to the listener when a notification is emitted. + * @throws InstanceNotFoundException If the source MBean is not registered in the MBeanServer. + * @throws IOException If a communication problem occurred. + * @see #removeNotificationListener(ObjectName, NotificationListener, NotificationFilter, Object) + */ + public Object addNotificationListener(ObjectName observed, NotificationListener listener, NotificationFilter filter, Object handback) + throws InstanceNotFoundException, IOException; + + /** + * Adds a NotificationListener to a registered MBean. + * A notification emitted by the specified source MBean will be forwarded by the MBeanServer to the given listener MBean, + * if the given NotificationFilter allows so. If the filter is null, every notification will be sent to the + * listener. + * The handback object is transparently passed to the listener by the MBeanServer. + * The source of the notification is the source MBean ObjectName. + * If the listener MBean is unregistered, it will continue to receive notifications. + * + * @param observed The ObjectName of the source MBean on which the listener should be added. + * @param listener The ObjectName of the listener MBean which will handle the notifications emitted by the source MBean. + * @param filter The filter which will allow the notification to be forwarded to the listener. + * @param handback The context to be sent to the listener when a notification is emitted. + * @throws InstanceNotFoundException If the source or listener MBean are not registered in the MBeanServer. + * @throws IOException If a communication problem occurred. + * @see #removeNotificationListener(ObjectName, ObjectName, NotificationFilter, Object) + */ + public Object addNotificationListener(ObjectName observed, ObjectName listener, NotificationFilter filter, Object handback) + throws InstanceNotFoundException, IOException; + + /** + * Removes the specified listener from the named source MBean. + * If the listener is registered more than once, for example with different filters or handbacks, + * this method will remove all those registrations. + * + * @param observed The ObjectName of the source MBean on which the listener should be removed. + * @param listener The listener to be removed. + * @throws InstanceNotFoundException If the source MBean is not registered in the MBeanServer. + * @throws ListenerNotFoundException If the listener is not registered in the MBean. + * @throws IOException If a communication problem occurred. + * @see #addNotificationListener(ObjectName, NotificationListener, NotificationFilter, Object) + */ + public Object removeNotificationListener(ObjectName observed, NotificationListener listener) + throws InstanceNotFoundException, ListenerNotFoundException, IOException; + + /** + * Removes the specified listener MBean from the named source MBean. + * If the listener is registered more than once, for example with different filters or handbacks, + * this method will remove all those registrations. + * + * @param observed The ObjectName of the source MBean on which the listener should be removed. + * @param listener The ObjectName of the listener MBean to be removed. + * @throws InstanceNotFoundException If the source or listener MBean are not registered in the MBeanServer. + * @throws ListenerNotFoundException The listener is not registered in the MBean. + * @throws IOException If a communication problem occurred. + * @see #addNotificationListener(ObjectName, ObjectName, NotificationFilter, Object) + */ + public Object removeNotificationListener(ObjectName observed, ObjectName listener) + throws InstanceNotFoundException, ListenerNotFoundException, IOException; + + /** + * Removes the specified listener from the named source MBean. + * The MBean must have a listener that exactly matches the given listener, filter, and handback parameters. + * + * @param observed The ObjectName of the source MBean on which the listener should be removed. + * @param listener The listener to be removed. + * @param filter The filter that was specified when the listener was added. + * @param handback The handback that was specified when the listener was added. + * @throws InstanceNotFoundException If the source MBean is not registered in the MBeanServer. + * @throws ListenerNotFoundException If the listener (along with filter and handback) is not registered in the MBean. + * @throws IOException If a communication problem occurred. + * @see #addNotificationListener(ObjectName, NotificationListener, NotificationFilter, Object) + * @since JMX 1.2 + */ + public Object removeNotificationListener(ObjectName observed, NotificationListener listener, NotificationFilter filter, Object handback) + throws InstanceNotFoundException, ListenerNotFoundException, IOException; + + /** + * Removes the specified listener MBean from the named source MBean. + * The MBean must have a listener that exactly matches the given listener, filter, and handback parameters. + * + * @param observed The ObjectName of the source MBean on which the listener should be removed. + * @param listener The ObjectName of the listener MBean to be removed. + * @param filter The filter that was specified when the listener was added. + * @param handback The handback that was specified when the listener was added. + * @throws InstanceNotFoundException If the source MBean is not registered in the MBeanServer. + * @throws ListenerNotFoundException If the listener (along with filter and handback) is not registered in the MBean. + * @throws IOException If a communication problem occurred. + * @see #addNotificationListener(ObjectName, NotificationListener, NotificationFilter, Object) + * @since JMX 1.2 + */ + public Object removeNotificationListener(ObjectName observed, ObjectName listener, NotificationFilter filter, Object handback) + throws InstanceNotFoundException, ListenerNotFoundException, IOException; + + /** + * Returns the metadata information exposed for management about the named MBean. + * + * @param objectName The name of the MBean for which retrieve the metadata. + * @return An instance of MBeanInfo allowing the retrieval of constructors, attributes, operations and notifications of this MBean. + * @throws IntrospectionException If an exception occured during introspection of the MBean. + * @throws InstanceNotFoundException If the named MBean is not registered in the MBeanServer. + * @throws ReflectionException If a reflection-type exception occurred + * @throws IOException If a communication problem occurred. + */ + public Object getMBeanInfo(ObjectName objectName) + throws InstanceNotFoundException, IntrospectionException, ReflectionException, IOException; + + /** + * Returns whether the MBean specified is an instance of the specified class. + * + * @param objectName The ObjectName of the MBean. + * @param className The name of the class. + * @return True if the MBean specified is an instance of the specified class. + * @throws InstanceNotFoundException If the named MBean is not registered in the MBeanServer. + * @throws IOException If a communication problem occurred. + */ + public Object isInstanceOf(ObjectName objectName, String className) + throws InstanceNotFoundException, IOException; + + /** + * Returns the list of different ObjectName domains under which the MBeans in this MBeanServer are registered. + * + * @return The array of different ObjectName domains present in this MBeanServer. + * @throws IOException If a communication problem occurred. + * @since JMX 1.2 + */ + public Object getDomains() + throws IOException; + + /** + * Returns the default domain for this MBeanServer used in case ObjectName domain are not specified. + * + * @return The default domain of this MBeanServer. + * @throws IOException If a communication problem occurred. + */ + public Object getDefaultDomain() + throws IOException; + + /** + * A facility method for createMBean(className, objectName, null, null). + * + * @see #createMBean(String, ObjectName, Object[], String[]) + */ + public Object createMBean(String className, ObjectName objectName) + throws ReflectionException, InstanceAlreadyExistsException, MBeanRegistrationException, MBeanException, NotCompliantMBeanException, IOException; + + /** + * Instantiates and registers an MBean of the specified class with the given ObjectName in the MBeanServer. + * The MBeanServer will use its ClassLoaderRepository to load the class of the MBean and the specified + * constructor's parameter classes, and creates the instance passing the specified arguments. + * The ObjectName may be null if the MBean implements {@link MBeanRegistration} + * + * @param className The class name of the MBean to be instantiated. + * @param objectName The ObjectName of the MBean, may be null. + * @param args An array containing the arguments to pass to the constructor. + * @param parameters An array containing the signature of the constructor. + * @return An ObjectInstance, containing the ObjectName and the Java class name of the newly instantiated MBean. + * @throws ReflectionException If a reflection exception is thrown. + * @throws InstanceAlreadyExistsException If another MBean with the same ObjectName is already registered in the MBeanServer. + * @throws MBeanRegistrationException If an exception is thrown during MBean's registration. + * @throws MBeanException If the constructor of the MBean has thrown an exception + * @throws NotCompliantMBeanException If the MBean is not a JMX compliant MBean + * @throws IOException If a communication problem occurred. + */ + public Object createMBean(String className, ObjectName objectName, Object[] args, String[] parameters) + throws ReflectionException, InstanceAlreadyExistsException, MBeanRegistrationException, MBeanException, NotCompliantMBeanException, IOException; + + /** + * A facility method for createMBean(className, objectName, loaderName, null, null). + * + * @see #createMBean(String, ObjectName, ObjectName, Object[], String[]) + */ + public Object createMBean(String className, ObjectName objectName, ObjectName loaderName) + throws ReflectionException, InstanceAlreadyExistsException, MBeanRegistrationException, MBeanException, NotCompliantMBeanException, InstanceNotFoundException, IOException; + + /** + * Instantiates and registers an MBean of the specified class with the given ObjectName in the MBeanServer. + * The MBeanServer will use the specified classloader MBean to load the class of the MBean and the specified + * constructor's parameter classes, and creates the instance passing the specified arguments, or the classloader + * of the MBeanServer if the classloader ObjectName is null. + * The ObjectName may be null if the MBean implements {@link MBeanRegistration} + * + * @param className The class name of the MBean to be instantiated. + * @param objectName The ObjectName of the MBean, may be null. + * @param loaderName The ObjectName of the classloader MBean to be used. + * @param args An array containing the arguments to pass to the constructor. + * @param parameters An array containing the signature of the constructor. + * @return An ObjectInstance, containing the ObjectName and the Java class name of the newly instantiated MBean. + * @throws ReflectionException If a reflection exception is thrown. + * @throws InstanceAlreadyExistsException If another MBean with the same ObjectName is already registered in the MBeanServer. + * @throws MBeanRegistrationException If an exception is thrown during MBean's registration. + * @throws MBeanException If the constructor of the MBean has thrown an exception + * @throws NotCompliantMBeanException If the MBean is not a JMX compliant MBean + * @throws InstanceNotFoundException If the specified classloader MBean is not registered in the MBeanServer. + * @throws IOException If a communication problem occurred. + */ + public Object createMBean(String className, ObjectName objectName, ObjectName loaderName, Object[] args, String[] parameters) + throws ReflectionException, InstanceAlreadyExistsException, MBeanRegistrationException, MBeanException, NotCompliantMBeanException, InstanceNotFoundException, IOException; + + /** + * Unregisters the MBean with the specified ObjectName from this MBeanServer. + * + * @param objectName The ObjectName of the MBean to be unregistered. + * @throws InstanceNotFoundException If the specified MBean is not registered in the MBeanServer. + * @throws MBeanRegistrationException If an exception is thrown during MBean's unregistration. + * @throws IOException If a communication problem occurred. + */ + public Object unregisterMBean(ObjectName objectName) + throws InstanceNotFoundException, MBeanRegistrationException, IOException; + + /** + * Gets the value of the specified attribute of the named MBean. + * + * @param objectName The ObjectName of the MBean from which the attribute is to be retrieved. + * @param attribute The attribute name. + * @return The value of the specified attribute. + * @throws AttributeNotFoundException If the specified attribute does not belong to the management interface of the MBean. + * @throws MBeanException If the MBean's getter method throws an exception. + * @throws InstanceNotFoundException If the specified MBean is not registered in the MBeanServer. + * @throws ReflectionException If a reflection exception is thrown. + * @throws IOException If a communication problem occurred. + * @see #setAttribute + */ + public Object getAttribute(ObjectName objectName, String attribute) + throws MBeanException, AttributeNotFoundException, InstanceNotFoundException, ReflectionException, IOException; + + /** + * Sets the value of the specified attribute of the named MBean. + * + * @param objectName The name of the MBean within which the attribute is to be set. + * @param attribute The Attribute to be set. + * @throws InstanceNotFoundException If the specified MBean is not registered in the MBeanServer. + * @throws AttributeNotFoundException If the specified attribute does not belong to the management interface of the MBean. + * @throws InvalidAttributeValueException If the value specified for the attribute does not match the attribute's type + * @throws MBeanException If the MBean's setter method throws an exception. + * @throws ReflectionException If a reflection exception is thrown. + * @throws IOException If a communication problem occurred. + * @see #getAttribute + */ + public Object setAttribute(ObjectName objectName, Attribute attribute) + throws InstanceNotFoundException, AttributeNotFoundException, InvalidAttributeValueException, MBeanException, ReflectionException, IOException; + + /** + * Gets the values of several attributes of the named MBean. + * + * @param objectName The ObjectName of the MBean from which the attributes are to be retrieved. + * @param attributes The attribute names. + * @return An AttributeList containing the values of the attributes that it has been possible to retrieve. + * @throws InstanceNotFoundException If the specified MBean is not registered in the MBeanServer. + * @throws ReflectionException If a reflection exception is thrown. + * @throws IOException If a communication problem occurred. + * @see #setAttributes + */ + public Object getAttributes(ObjectName objectName, String[] attributes) + throws InstanceNotFoundException, ReflectionException, IOException; + + /** + * Sets the values of several attributes of the named MBean. + * + * @param objectName The name of the MBean within which the attribute is to be set. + * @param attributes The AttributeList containing the Attributes to be set. + * @return The AttributeList containing the attributes that it has been possible to set. + * @throws InstanceNotFoundException If the specified MBean is not registered in the MBeanServer. + * @throws ReflectionException If a reflection exception is thrown. + * @throws IOException If a communication problem occurred. + * @see #getAttributes + */ + public Object setAttributes(ObjectName objectName, AttributeList attributes) + throws InstanceNotFoundException, ReflectionException, IOException; + + /** + * Invokes the specified operation on the named MBean. + * + * @param objectName The ObjectName of the MBean on which the method is to be invoked. + * @param methodName The name of the operation to be invoked. + * @param args An array containing the arguments to pass to the operation. + * @param parameters An array containing the signature of the operation. + * @return The return value of the operation, or null if the operation returns void. + * @throws InstanceNotFoundException If the specified MBean is not registered in the MBeanServer. + * @throws MBeanException If the MBean's operation method throws an exception. + * @throws ReflectionException If a reflection exception is thrown. + * @throws IOException If a communication problem occurred. + */ + public Object invoke(ObjectName objectName, String methodName, Object[] args, String[] parameters) + throws InstanceNotFoundException, MBeanException, ReflectionException, IOException; + + /** + * Returns the number of MBeans registered in this MBeanServer. + * + * @throws IOException If a communication problem occurred. + */ + public Object getMBeanCount() + throws IOException; + + /** + * Checks whether the given ObjectName identifies an MBean registered in this MBeanServer. + * + * @param objectName The ObjectName to be checked. + * @return True if an MBean with the specified ObjectName is already registered in the MBeanServer. + * @throws IOException If a communication problem occurred. + */ + public Object isRegistered(ObjectName objectName) + throws IOException; + + /** + * Gets the ObjectInstance for the named MBean registered with the MBeanServer. + * + * @param objectName The ObjectName of the MBean. + * @return The ObjectInstance associated with the named MBean. + * @throws InstanceNotFoundException If the specified MBean is not registered in the MBeanServer. + * @throws IOException If a communication problem occurred. + */ + public Object getObjectInstance(ObjectName objectName) + throws InstanceNotFoundException, IOException; + + /** + * Gets a subset of the ObjectInstances belonging to MBeans registered in this MBeanServer. + * It is possible to filter the set of MBeans by specifying a pattern for MBean's ObjectNames, and a query expression + * to be evaluated to further filter the set of MBeans. + * The set can be further restricted if any exception is thrown during retrieval of MBean (for example for + * security reasons): the failing MBean will not be included. + * + * @param patternName The ObjectName pattern identifying the MBeans to be retrieved, or null to retrieve all MBeans. + * @param filter The query expression to be evaluated for selecting MBeans, or null. + * @return A set containing the ObjectInstance objects for the selected MBeans. + * @throws IOException If a communication problem occurred. + */ + public Object queryMBeans(ObjectName patternName, QueryExp filter) + throws IOException; + + /** + * Gets a subset of the ObjectNames belonging to MBeans registered in this MBeanServer. + * It is possible to filter the set of MBeans by specifying a pattern for MBean's ObjectNames, and a query expression + * to be evaluated to further filter the set of MBeans. + * The set can be further restricted if any exception is thrown during retrieval of MBean (for example for + * security reasons): the failing MBean will not be included. + * + * @param patternName The ObjectName pattern identifying the MBeans to be retrieved, or null to retrieve all MBeans. + * @param filter The query expression to be evaluated for selecting MBeans, or null. + * @return A set containing the ObjectNames for the selected MBeans. + * @throws IOException If a communication problem occurred. + */ + public Object queryNames(ObjectName patternName, QueryExp filter) + throws IOException; +} diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/application/jmx/remote/service/JmxSerializerFactory.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/application/jmx/remote/service/JmxSerializerFactory.java new file mode 100644 index 0000000000..011fa59353 --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/application/jmx/remote/service/JmxSerializerFactory.java @@ -0,0 +1,30 @@ +package org.rzo.netty.ahessian.application.jmx.remote.service; + +import java.util.HashMap; +import java.util.Map; + +import javax.management.ObjectName; + +import org.rzo.netty.ahessian.rpc.message.MappingSerializerFactory; + +import com.caucho.hessian4.io.ObjectNameDeserializer; +import com.caucho.hessian4.io.StringValueSerializer; + +public class JmxSerializerFactory extends MappingSerializerFactory +{ + static Map serializers = new HashMap(); + static Map deserializers = new HashMap(); + + static + { + serializers.put(ObjectName.class.getName(), StringValueSerializer.class.getName()); + deserializers.put(ObjectName.class.getName(), ObjectNameDeserializer.class.getName()); + } + + public JmxSerializerFactory() + { + super(serializers, deserializers); + } + + +} diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/application/jmx/remote/service/MBeanServerConnectionAsyncAdapter.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/application/jmx/remote/service/MBeanServerConnectionAsyncAdapter.java new file mode 100644 index 0000000000..d78eae165b --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/application/jmx/remote/service/MBeanServerConnectionAsyncAdapter.java @@ -0,0 +1,174 @@ +package org.rzo.netty.ahessian.application.jmx.remote.service; + +import java.io.IOException; +import java.util.Set; + +import javax.management.Attribute; +import javax.management.AttributeList; +import javax.management.AttributeNotFoundException; +import javax.management.InstanceAlreadyExistsException; +import javax.management.InstanceNotFoundException; +import javax.management.IntrospectionException; +import javax.management.InvalidAttributeValueException; +import javax.management.ListenerNotFoundException; +import javax.management.MBeanException; +import javax.management.MBeanInfo; +import javax.management.MBeanRegistrationException; +import javax.management.MBeanServerConnection; +import javax.management.NotCompliantMBeanException; +import javax.management.NotificationFilter; +import javax.management.NotificationListener; +import javax.management.ObjectInstance; +import javax.management.ObjectName; +import javax.management.QueryExp; +import javax.management.ReflectionException; + +public class MBeanServerConnectionAsyncAdapter implements MBeanServerConnection +{ + AsyncMBeanServerConnection root; + public MBeanServerConnectionAsyncAdapter(AsyncMBeanServerConnection root) + { + this.root = root; + } + + public void addNotificationListener(ObjectName name, NotificationListener listener, NotificationFilter filter, Object handback) + throws InstanceNotFoundException, IOException + { + root.addNotificationListener(name, listener, filter, handback); + } + + public void addNotificationListener(ObjectName name, ObjectName listener, NotificationFilter filter, Object handback) + throws InstanceNotFoundException, IOException + { + root.addNotificationListener(name, listener, filter, handback); + } + + public ObjectInstance createMBean(String className, ObjectName name) throws ReflectionException, InstanceAlreadyExistsException, + MBeanRegistrationException, MBeanException, NotCompliantMBeanException, IOException + { + return (ObjectInstance) root.createMBean(className, name); + } + + public ObjectInstance createMBean(String className, ObjectName name, ObjectName loaderName) throws ReflectionException, + InstanceAlreadyExistsException, MBeanRegistrationException, MBeanException, NotCompliantMBeanException, InstanceNotFoundException, + IOException + { + return (ObjectInstance) root.createMBean(className, name, loaderName); + } + + public ObjectInstance createMBean(String className, ObjectName name, Object[] params, String[] signature) throws ReflectionException, + InstanceAlreadyExistsException, MBeanRegistrationException, MBeanException, NotCompliantMBeanException, IOException + { + return (ObjectInstance) root.createMBean(className, name, params, signature); + } + + public ObjectInstance createMBean(String className, ObjectName name, ObjectName loaderName, Object[] params, String[] signature) + throws ReflectionException, InstanceAlreadyExistsException, MBeanRegistrationException, MBeanException, NotCompliantMBeanException, + InstanceNotFoundException, IOException + { + return (ObjectInstance) root.createMBean(className, name, loaderName, params, signature); + } + + public Object getAttribute(ObjectName name, String attribute) throws MBeanException, AttributeNotFoundException, InstanceNotFoundException, + ReflectionException, IOException + { + return root.getAttribute(name, attribute); + } + + public AttributeList getAttributes(ObjectName name, String[] attributes) throws InstanceNotFoundException, ReflectionException, IOException + { + return (AttributeList) root.getAttributes(name, attributes); + } + + public String getDefaultDomain() throws IOException + { + return (String) root.getDefaultDomain(); + } + + public String[] getDomains() throws IOException + { + return (String[]) root.getDomains(); + } + + public Integer getMBeanCount() throws IOException + { + return (Integer) root.getMBeanCount(); + } + + public MBeanInfo getMBeanInfo(ObjectName name) throws InstanceNotFoundException, IntrospectionException, ReflectionException, IOException + { + return (MBeanInfo) root.getMBeanInfo(name); + } + + public ObjectInstance getObjectInstance(ObjectName name) throws InstanceNotFoundException, IOException + { + return (ObjectInstance)root.getObjectInstance(name); + } + + public Object invoke(ObjectName name, String operationName, Object[] params, String[] signature) throws InstanceNotFoundException, + MBeanException, ReflectionException, IOException + { + return root.invoke(name, operationName, params, signature); + } + + public boolean isInstanceOf(ObjectName name, String className) throws InstanceNotFoundException, IOException + { + return ((Boolean)root.isInstanceOf(name, className)).booleanValue(); + } + + public boolean isRegistered(ObjectName name) throws IOException + { + return ((Boolean)root.isRegistered(name)).booleanValue(); + } + + public Set queryMBeans(ObjectName name, QueryExp query) throws IOException + { + return (Set)root.queryMBeans(name, query); + } + + public Set queryNames(ObjectName name, QueryExp query) throws IOException + { + return (Set)root.queryNames(name, query); + } + + public void removeNotificationListener(ObjectName name, ObjectName listener) throws InstanceNotFoundException, ListenerNotFoundException, + IOException + { + root.removeNotificationListener(name, listener); + } + + public void removeNotificationListener(ObjectName name, NotificationListener listener) throws InstanceNotFoundException, + ListenerNotFoundException, IOException + { + root.removeNotificationListener(name, listener); + } + + public void removeNotificationListener(ObjectName name, ObjectName listener, NotificationFilter filter, Object handback) + throws InstanceNotFoundException, ListenerNotFoundException, IOException + { + root.removeNotificationListener(name, listener, filter, handback); + } + + public void removeNotificationListener(ObjectName name, NotificationListener listener, NotificationFilter filter, Object handback) + throws InstanceNotFoundException, ListenerNotFoundException, IOException + { + root.removeNotificationListener(name, listener, filter, handback); + } + + public void setAttribute(ObjectName name, Attribute attribute) throws InstanceNotFoundException, AttributeNotFoundException, + InvalidAttributeValueException, MBeanException, ReflectionException, IOException + { + root.setAttribute(name, attribute); + } + + public AttributeList setAttributes(ObjectName name, AttributeList attributes) throws InstanceNotFoundException, ReflectionException, IOException + { + return (AttributeList)root.setAttributes(name, attributes); + } + + public void unregisterMBean(ObjectName name) throws InstanceNotFoundException, MBeanRegistrationException, IOException + { + root.unregisterMBean(name); + } + +} diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/application/jmx/remote/service/ObjectNameDeserializer.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/application/jmx/remote/service/ObjectNameDeserializer.java new file mode 100644 index 0000000000..c9676191ce --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/application/jmx/remote/service/ObjectNameDeserializer.java @@ -0,0 +1,31 @@ +package org.rzo.netty.ahessian.application.jmx.remote.service; + +import java.io.IOException; + +import javax.management.ObjectName; + +import com.caucho.hessian4.io.AbstractDeserializer; +import com.caucho.hessian4.io.AbstractHessianInput; + +public class ObjectNameDeserializer extends AbstractDeserializer +{ + + public Object readObject(AbstractHessianInput in, + Object []fields) +throws IOException +{ + String on = in.readString(); + try + { + Object result = new ObjectName(on); + in.addRef(result); + return result; + } + catch (Exception e) + { + e.printStackTrace(); + } + return null; + } + +} diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/application/jmx/remote/service/ObjectNameSerializer.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/application/jmx/remote/service/ObjectNameSerializer.java new file mode 100644 index 0000000000..bfbc62c356 --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/application/jmx/remote/service/ObjectNameSerializer.java @@ -0,0 +1,25 @@ +package org.rzo.netty.ahessian.application.jmx.remote.service; + +import java.io.IOException; + +import javax.management.ObjectName; + +import com.caucho.hessian4.io.AbstractHessianOutput; +import com.caucho.hessian4.io.AbstractSerializer; + +public class ObjectNameSerializer extends AbstractSerializer +{ + + public void writeInstance(Object obj, AbstractHessianOutput out) throws IOException + { + ObjectName on = (ObjectName)obj; + out.writeString(on.getCanonicalName()); + } + + protected void writeDefinition20(Class cl, + AbstractHessianOutput out) +throws IOException +{ + out.writeInt(0); +} +} diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/auth/AuthToken.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/auth/AuthToken.java new file mode 100644 index 0000000000..66341f4d62 --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/auth/AuthToken.java @@ -0,0 +1,42 @@ +package org.rzo.netty.ahessian.auth; + +import org.jboss.netty.channel.ChannelHandlerContext; +import org.jboss.netty.channel.MessageEvent; + +/** + * The Interface AuthToken. + */ +public interface AuthToken +{ + + /** Password not completely received */ + public static int NOT_COMPLETE = 0; + + /** Authentication passed */ + public static int PASSED = 1; + + /** Authentication failed */ + public static int FAILED = 2; + + /** + * Authenticate the received password + * + * @param ctx the ChannelHandlerContext + * @param e the MessageEvent + * + * @return the state: NOT_COMPLETE, PASSED, FAILED + */ + public int authenticate(ChannelHandlerContext ctx, MessageEvent e); + + /** + * Send the password to the server + * + * @param ctx ChannelHandlerContext + */ + public void sendPassword(ChannelHandlerContext ctx); + + public boolean isLoggedOn(); + + public void disconnected(); + +} diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/auth/AuthTokenList.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/auth/AuthTokenList.java new file mode 100644 index 0000000000..e1f0029d68 --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/auth/AuthTokenList.java @@ -0,0 +1,119 @@ +package org.rzo.netty.ahessian.auth; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.jboss.netty.channel.ChannelHandlerContext; +import org.jboss.netty.channel.MessageEvent; +import org.jboss.netty.logging.InternalLogger; +import org.jboss.netty.logging.InternalLoggerFactory; + +public class AuthTokenList implements AuthToken +{ + + private Map _tokens; + int _receivedLength = 0; + byte [] _receivedBytes; + AuthToken _currentToken; + boolean _uniqueLogon; + private static final InternalLogger logger = + InternalLoggerFactory.getInstance(SimpleAuthToken.class); + + public AuthTokenList(Map tokens, int bytesLength, boolean uniqueLogon) + { + _tokens = tokens; + _receivedBytes = new byte[bytesLength]; + _uniqueLogon = uniqueLogon; + } + + public static AuthTokenList fromList(List tokens, boolean uniqueLogon) + { + Map tks = new HashMap(); + int bytesLength = 0; + for (AuthToken token : tokens) + { + byte[] pwd = ((SimpleAuthToken)token).getPassword(); + tks.put(new ByteArrayWrapper(pwd), token); + if (bytesLength < pwd.length) + bytesLength = pwd.length; + } + return new AuthTokenList(tks, bytesLength, uniqueLogon); + } + + public static AuthTokenList fromList(List tokens) + { + return fromList(tokens, false); + } + + + public int authenticate(ChannelHandlerContext ctx, MessageEvent e) + { + ChannelBuffer b = (ChannelBuffer) e.getMessage(); + int toCopy = Math.min(_receivedBytes.length-_receivedLength, b.readableBytes()); + byte[] bytes = new byte[toCopy]; + b.readBytes(bytes); + System.arraycopy(bytes, 0, _receivedBytes, _receivedLength, bytes.length); + _receivedLength += toCopy; + if (_receivedLength == _receivedBytes.length) + { + _currentToken = _tokens.get(new ByteArrayWrapper(_receivedBytes)); + if (_currentToken != null && (_uniqueLogon || _currentToken.isLoggedOn())) + { + logger.info("authenticated"); + ((SimpleAuthToken)_currentToken).setLoggedOn(true); + if (b.readableBytes() != 0) + ctx.sendUpstream(e); + return PASSED; + } + else + { + _currentToken = null; + return FAILED; + } + } + else + return NOT_COMPLETE; + + } + + public AuthToken authenticate(String password) throws Exception + { + ByteArrayWrapper input = new ByteArrayWrapper(password.getBytes("UTF-8")); + AuthToken result = _tokens.get(input); + if (result == null) + return null; + if (_uniqueLogon && result.isLoggedOn()) + return null; + ((SimpleAuthToken)result).setLoggedOn(true); + return result; + } + + public void sendPassword(ChannelHandlerContext ctx) + { + ; + } + + public boolean isLoggedOn() + { + return _currentToken != null; + } + + public void setLoggedOn(boolean value) + { + if (!value && _currentToken != null) + { + ((SimpleAuthToken)_currentToken).setLoggedOn(false); + _currentToken = null; + } + } + + public void disconnected() + { + setLoggedOn(false); + } + + + +} diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/auth/AuthTokenListFactory.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/auth/AuthTokenListFactory.java new file mode 100644 index 0000000000..e40ffaaf93 --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/auth/AuthTokenListFactory.java @@ -0,0 +1,58 @@ +package org.rzo.netty.ahessian.auth; + +import java.security.NoSuchAlgorithmException; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +public class AuthTokenListFactory +{ + private Map _tokens = new HashMap(); + private int _length; + private boolean _unique = true; + + public void addList(Collection list, int length) + { + _length = length; + for (String pwd : list) + { + SimpleAuthToken token = new SimpleAuthToken(); + token.setLength(length); + token.setPassword(pwd); + _tokens.put(new ByteArrayWrapper(token._password), token); + } + } + + public void addList(Collection list, int length, String encryptionAlgorithm) + { + _length = length; + for (String pwd : list) + { + EncryptedAuthToken token = new EncryptedAuthToken(); + token.setLength(length); + try + { + token.setAlgorithm(encryptionAlgorithm); + } + catch (NoSuchAlgorithmException e) + { + e.printStackTrace(); + } + token.setPassword(pwd); + _tokens.put(new ByteArrayWrapper(token._password), token); + } + } + + public void setUnique(boolean value) + { + _unique = value; + } + + public AuthTokenList getAuthTokenList() + { + return new AuthTokenList(_tokens, _length, _unique); + } + + + +} diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/auth/Base64AuthToken.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/auth/Base64AuthToken.java new file mode 100644 index 0000000000..b95a030466 --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/auth/Base64AuthToken.java @@ -0,0 +1,28 @@ +package org.rzo.netty.ahessian.auth; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.jboss.netty.buffer.ChannelBuffers; +import org.jboss.netty.handler.codec.base64.Base64; + + +public class Base64AuthToken extends SimpleAuthToken +{ + + private String _user; + + public Base64AuthToken(String user, String password) throws Exception + { + _user = user; + String data = user+":"+password; + ChannelBuffer digest = Base64.encode(ChannelBuffers.wrappedBuffer(data.getBytes("UTF-8"))); + byte[] digestBytes = new byte[digest.readableBytes()]; + digest.readBytes(digestBytes); + super.setPassword(new String(digestBytes)); + } + + public String getUser() + { + return _user; + } + +} diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/auth/ByteArrayWrapper.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/auth/ByteArrayWrapper.java new file mode 100644 index 0000000000..56f9995a10 --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/auth/ByteArrayWrapper.java @@ -0,0 +1,35 @@ +package org.rzo.netty.ahessian.auth; + +import java.util.Arrays; + +public class ByteArrayWrapper +{ + + private final byte[] data; + + public ByteArrayWrapper(byte[] data) + { + if (data == null) + { + throw new NullPointerException(); + } + this.data = data; + } + + @Override + public boolean equals(Object other) + { + if (!(other instanceof ByteArrayWrapper)) + { + return false; + } + return Arrays.equals(data, ((ByteArrayWrapper)other).data); + } + + @Override + public int hashCode() + { + return Arrays.hashCode(data); + } + +} diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/auth/ClientAuthFilter.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/auth/ClientAuthFilter.java new file mode 100644 index 0000000000..8ff5c0216e --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/auth/ClientAuthFilter.java @@ -0,0 +1,54 @@ +package org.rzo.netty.ahessian.auth; + +import org.jboss.netty.channel.ChannelHandlerContext; +import org.jboss.netty.channel.ChannelPipeline; +import org.jboss.netty.channel.ChannelPipelineCoverage; +import org.jboss.netty.channel.ChannelStateEvent; +import org.jboss.netty.channel.SimpleChannelUpstreamHandler; + +/** + * Client side authentication handler. + *
+ * This must be the first handler in the pipeline. + * + *
+ * A typical setup for ClientAuthFilter for TCP/IP socket would be: + * + *
+ * {@link ChannelPipeline} pipeline = ...;
+ * 
+ *   EncryptedAuthToken token = new EncryptedAuthToken();
+ *   token.setAlgorithm("SHA-1");
+ *   token.setPassword("test");
+ *   ClientAuthFilter auth = new ClientAuthFilter(token);
+ *   pipeline.addLast("auth", auth);
+ * 
+ * + */ +@ChannelPipelineCoverage("one") +public class ClientAuthFilter extends SimpleChannelUpstreamHandler +{ + + /** The authentication token. */ + AuthToken _token; + + /** + * Instantiates a new client authentication handler. + * + * @param token the token + */ + public ClientAuthFilter(AuthToken token) + { + _token = token; + } + + /* (non-Javadoc) + * @see org.jboss.netty.channel.SimpleChannelUpstreamHandler#channelConnected(org.jboss.netty.channel.ChannelHandlerContext, org.jboss.netty.channel.ChannelStateEvent) + */ + @Override + public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception { + _token.sendPassword(ctx); + ctx.sendUpstream(e); + } + +} diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/auth/EncryptedAuthToken.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/auth/EncryptedAuthToken.java new file mode 100644 index 0000000000..3dd41b642a --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/auth/EncryptedAuthToken.java @@ -0,0 +1,42 @@ +package org.rzo.netty.ahessian.auth; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +/** + * An Encrypted Authentication Token. + * The password is transmitted encrypted. + */ +public class EncryptedAuthToken extends SimpleAuthToken +{ + + /** The _algorithm. */ + MessageDigest _algorithm = null; + + /** + * Sets the algorithm. + * + * @param algorithm the encryption algorithm. + * @see java.security.MessageDigest + * + * @throws NoSuchAlgorithmException + */ + public void setAlgorithm(String algorithm) throws NoSuchAlgorithmException + { + _algorithm = MessageDigest.getInstance(algorithm); + + } + + /* (non-Javadoc) + * @see org.rzo.netty.ahessian.auth.SimpleAuthToken#setPassword(java.lang.String) + */ + public void setPassword(String password) + { + _algorithm.reset(); + _algorithm.update(password.getBytes()); + _password = ensureLength(_algorithm.digest()); + _receivedBytes = new byte[_password.length]; + + } + +} diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/auth/ServerAuthFilter.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/auth/ServerAuthFilter.java new file mode 100644 index 0000000000..b26df2cfcf --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/auth/ServerAuthFilter.java @@ -0,0 +1,91 @@ +package org.rzo.netty.ahessian.auth; + +import org.jboss.netty.channel.ChannelHandlerContext; +import org.jboss.netty.channel.ChannelPipeline; +import org.jboss.netty.channel.ChannelPipelineCoverage; +import org.jboss.netty.channel.ChannelStateEvent; +import org.jboss.netty.channel.MessageEvent; +import org.jboss.netty.channel.SimpleChannelUpstreamHandler; +import org.jboss.netty.logging.InternalLogger; +import org.jboss.netty.logging.InternalLoggerFactory; + +/** + * Server side authentication handler. + *
+ * This must be the first handler in the pipeline. + * + *
+ * A typical setup for ServerAuthFilter for TCP/IP socket would be: + * + *
+ * {@link ChannelPipeline} pipeline = ...;
+ * 
+ *   EncryptedAuthToken token = new EncryptedAuthToken();
+ *   token.setAlgorithm("SHA-1");
+ *   token.setPassword("test");
+ *   ServerAuthFilter auth = new ServerAuthFilter(token);
+ *   pipeline.addLast("auth", auth);
+ * 
+ * + */ +@ChannelPipelineCoverage("one") +public class ServerAuthFilter extends SimpleChannelUpstreamHandler +{ + private AuthToken _token = null; + private boolean _authenticated = false; + private static final InternalLogger logger = + InternalLoggerFactory.getInstance(ServerAuthFilter.class); + + /** + * Instantiates a new server auth filter. + * + * @param token the token + */ + public ServerAuthFilter(AuthToken token) + { + setToken(token); + } + + /** + * Sets the token. + * + * @param token the new token + */ + public void setToken(AuthToken token) + { + _token = token; + } + + /* (non-Javadoc) + * @see org.jboss.netty.channel.SimpleChannelUpstreamHandler#messageReceived(org.jboss.netty.channel.ChannelHandlerContext, org.jboss.netty.channel.MessageEvent) + */ + @Override + public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception + { + if (!_authenticated) + { + int result = _token.authenticate(ctx, e); + if ( result == AuthToken.FAILED) + { + logger.warn("authentication failed -> close connection"); + ctx.getChannel().close(); + } + else if (result == AuthToken.PASSED) + { + _authenticated = true; + } + } + else + ctx.sendUpstream(e); + } + + @Override + public void channelDisconnected( + ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception + { + _token.disconnected(); + ctx.sendUpstream(e); + } + + +} diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/auth/SimpleAuthToken.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/auth/SimpleAuthToken.java new file mode 100644 index 0000000000..65235201e8 --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/auth/SimpleAuthToken.java @@ -0,0 +1,119 @@ +package org.rzo.netty.ahessian.auth; + +import java.util.Arrays; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.jboss.netty.buffer.ChannelBuffers; +import org.jboss.netty.channel.ChannelHandlerContext; +import org.jboss.netty.channel.Channels; +import org.jboss.netty.channel.MessageEvent; +import org.jboss.netty.logging.InternalLogger; +import org.jboss.netty.logging.InternalLoggerFactory; + +/** + * A Simple Authentication Token. + * The password is sent unencrypted. + */ +public class SimpleAuthToken implements AuthToken +{ + + /** The _password. */ + byte[] _password; + + /** The _received bytes. */ + byte[] _receivedBytes; + + /** The _received length. */ + int _receivedLength = 0; + + boolean _loggedOn = false; + + int _length = -1; + + private static final InternalLogger logger = + InternalLoggerFactory.getInstance(SimpleAuthToken.class); + + + /** + * Sets the password. + * + * @param password the new password + */ + public void setPassword(String password) + { + _password = ensureLength(password.getBytes()); + _receivedBytes = new byte[_password.length]; + } + + public void setLength(int length) + { + _length = length; + } + + /* (non-Javadoc) + * @see org.rzo.netty.ahessian.auth.AuthToken#authenticate(org.jboss.netty.channel.ChannelHandlerContext, org.jboss.netty.channel.MessageEvent) + */ + public int authenticate(ChannelHandlerContext ctx, MessageEvent e) + { + ChannelBuffer b = (ChannelBuffer) e.getMessage(); + int toCopy = Math.min(_receivedBytes.length-_receivedLength, b.readableBytes()); + byte[] bytes = new byte[toCopy]; + b.readBytes(bytes); + System.arraycopy(bytes, 0, _receivedBytes, _receivedLength, bytes.length); + _receivedLength += toCopy; + if (_receivedLength == _password.length) + { + if (Arrays.equals(_receivedBytes, _password)) + { + logger.info("authenticated"); + if (b.readableBytes() != 0) + ctx.sendUpstream(e); + return PASSED; + } + else + return FAILED; + } + else + return NOT_COMPLETE; + } + + /* (non-Javadoc) + * @see org.rzo.netty.ahessian.auth.AuthToken#sendPassword(org.jboss.netty.channel.ChannelHandlerContext) + */ + public void sendPassword(ChannelHandlerContext ctx) + { + Channels.write(ctx, Channels.future(ctx.getChannel()), ChannelBuffers.wrappedBuffer(_password)); + } + + public boolean isLoggedOn() + { + return _loggedOn; + } + + void setLoggedOn(boolean loggedOn) + { + _loggedOn = loggedOn; + } + + public void disconnected() + { + setLoggedOn(false); + } + + byte[] ensureLength(byte[] bytes) + { + if (bytes.length == _length || _length <= 0) + return bytes; + else + { + return Arrays.copyOf(bytes, _length); + } + } + + byte[] getPassword() + { + return _password; + } + + +} diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/crypto/ClientCryptoFilter.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/crypto/ClientCryptoFilter.java new file mode 100644 index 0000000000..a9bb9b69a1 --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/crypto/ClientCryptoFilter.java @@ -0,0 +1,221 @@ +package org.rzo.netty.ahessian.crypto; + +import java.io.ByteArrayOutputStream; +import java.security.Key; +import java.security.KeyFactory; +import java.security.SecureRandom; +import java.security.spec.KeySpec; +import java.security.spec.X509EncodedKeySpec; +import java.util.Arrays; + +import javax.crypto.Cipher; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.jboss.netty.buffer.ChannelBuffers; +import org.jboss.netty.channel.Channel; +import org.jboss.netty.channel.ChannelEvent; +import org.jboss.netty.channel.ChannelFuture; +import org.jboss.netty.channel.ChannelHandlerContext; +import org.jboss.netty.channel.ChannelStateEvent; +import org.jboss.netty.channel.Channels; +import org.jboss.netty.channel.MessageEvent; +import org.jboss.netty.channel.SimpleChannelHandler; +import org.rzo.netty.ahessian.log.OutLogger; + +public class ClientCryptoFilter extends SimpleChannelHandler implements CryptoConstants +{ + private StreamCipher _encodeCipher; + private StreamCipher _decodeCipher; + private byte[] _encodedPublicKey; + private int _bytesRead; + private SecureRandom _secureRandom = new SecureRandom(); + private ChannelEvent _connectedEvent; + private byte[] _password = new byte[PASSWORD_SIZE]; + + public ClientCryptoFilter() + { + super(); + Arrays.fill(_password, (byte)0); + } + + + + + public void messageReceived( + ChannelHandlerContext ctx, MessageEvent e) throws Exception + { + // have we sent our secret key ? + if (_decodeCipher != null) + { + // decode and send upstream + MessageEvent m = Util.code(_decodeCipher, e, true); + ctx.sendUpstream(m); + } + // we are still in the crypto protocol + else + { + ChannelBuffer b = (ChannelBuffer) e.getMessage(); + // is this our first message ? + if (_encodedPublicKey == null) + { + int size = b.readInt(); + _encodedPublicKey = new byte[size]; + } + // readin the server's public key + // it may come in multiple chunks + int available = b.readableBytes(); + int toRead = Math.min(_encodedPublicKey.length - _bytesRead, available); + b.readBytes(_encodedPublicKey, _bytesRead, toRead); + _bytesRead += toRead; + // we have completed reception of the public key ? + if (_bytesRead == _encodedPublicKey.length) + { + // generate our secret key and send it to the server + sendSecretKey(ctx); + } + } + } + + private Cipher getAsymCipher() + { + try + { + // generate Cipher using the server's public key + KeyFactory fact = KeyFactory.getInstance(ASYM_KEY_TYPE); + KeySpec ks = new X509EncodedKeySpec(_encodedPublicKey); + Key pubKey = fact.generatePublic(ks); + String type = "".equals(ASYM_CIPHER_TYPE) ? ASYM_KEY_TYPE : ASYM_KEY_TYPE+"/"+ASYM_CIPHER_TYPE; + Cipher result = Cipher.getInstance(type); + result.init(Cipher.ENCRYPT_MODE, pubKey); + return result; + } + catch (Exception ex) + { + ex.printStackTrace(); + } + return null; + } + + private byte[] getSymKey() + //private Key getSymKey() + { + // generate a random secret key + try + { +// KeyGenerator keyGenerator = KeyGenerator.getInstance(SYM_KEY_TYPE); +// keyGenerator.init(SYM_KEY_SIZE); +// return keyGenerator.generateKey(); + byte[] key = new byte[SYM_KEY_SIZE]; + _secureRandom.nextBytes(key); + return key; + + } + catch (Exception ex) + { + ex.printStackTrace(); + } + return null; + } + + private byte[] getIv() + { + byte[] iv = new byte[SYM_IV_SIZE]; + _secureRandom.nextBytes(iv); + return iv; + } + + private void sendSecretKey(ChannelHandlerContext ctx) + { + try + { + // generate our secret key and iv and write it to a buffer + byte[] symKeyEncoded = getSymKey(); + byte[] ivEncoded = getIv(); + ByteArrayOutputStream b = new ByteArrayOutputStream(); + b.write(ivEncoded); + b.write(symKeyEncoded); + if (_password != null) + b.write(_password); + b.flush(); + + System.out.println("generated iv+key: "+OutLogger.asString(b.toByteArray())); + + // encode it using the server's public key + Cipher asymCipher = getAsymCipher(); + byte[] encryptedIvSymKey = asymCipher.doFinal(b.toByteArray()); + ChannelBuffer cb = ChannelBuffers.dynamicBuffer(); + cb.writeInt(encryptedIvSymKey.length); + cb.writeBytes(encryptedIvSymKey); + + // send it to the server + Channel channel = ctx.getChannel(); + ChannelFuture future = Channels.future(ctx.getChannel()); + + Channels.write(ctx, future, cb); + + // wait for the message transmission + future.await(); + + // we can now accept in/out messages encrypted with our key + // first create symmetric ciphers + _encodeCipher = StreamCipherFactory.createCipher(SYM_KEY_TYPE); + _encodeCipher.engineInitEncrypt(symKeyEncoded, ivEncoded); + + _decodeCipher = StreamCipherFactory.createCipher(SYM_KEY_TYPE); + _decodeCipher.engineInitDecrypt(symKeyEncoded, ivEncoded); + + // inform others in the pipeline that a secure connection has been established + ctx.sendUpstream(_connectedEvent); + + } + catch (Exception ex) + { + ex.printStackTrace(); + } + + } + + + + @Override + public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception { + // remember this event, so that we can propagate it to the rest of the pipeline once we have + // encryption and decryption ciphers in place + _connectedEvent = e; + } + + @Override + public void writeRequested( + ChannelHandlerContext ctx, MessageEvent e) throws Exception + { + // if we can encode + if (_encodeCipher != null) + { + // encode the message and send it downstream + MessageEvent m = Util.code(_encodeCipher, e, false); + ctx.sendDownstream(m); + } + // else ignore. this should not happen, since we have not yet propagated the connected event. + + } + + public void setPassword(byte[] password) + { + if (password == null || password.length == 0) + return; + int length = Math.min(PASSWORD_SIZE, password.length); + System.arraycopy(password, 0, _password, 0, length); + } + + + + public static void main(String[] args) + { + ServerCryptoFilter s = new ServerCryptoFilter(); + ClientCryptoFilter c = new ClientCryptoFilter(); + c._encodedPublicKey = s.getPublicKeyEncoded(); + c.sendSecretKey(null); + } + + +} diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/crypto/CryptoConstants.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/crypto/CryptoConstants.java new file mode 100644 index 0000000000..0c879a16b6 --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/crypto/CryptoConstants.java @@ -0,0 +1,21 @@ +package org.rzo.netty.ahessian.crypto; + +public interface CryptoConstants +{ + public static String ASYM_KEY_TYPE = "RSA"; + public static String ASYM_CIPHER_TYPE = "ECB/NOPADDING"; + public static String SYM_KEY_TYPE = "RC4";//"Salsa20";//"RC4";// + // if lower than 512 java throws an exception + // size in bits + public static int ASYM_KEY_SIZE = 512; + // size in bytes + public static int SYM_KEY_SIZE = 16; + // only required for salsa20, must be 8 byte + // size in bytes + public static int SYM_IV_SIZE = 8; + + // size of password in bytes + // password is used to avoid MITM attacks + public static int PASSWORD_SIZE = 15; + +} diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/crypto/CryptoException.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/crypto/CryptoException.java new file mode 100644 index 0000000000..a0d78d5a40 --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/crypto/CryptoException.java @@ -0,0 +1,27 @@ +package org.rzo.netty.ahessian.crypto; + +/** + * This class is for any unexpected exception in the crypto library. + *

+ * Copyright © 1997 + * Systemics Ltd on behalf of the + * Cryptix Development Team. + *
All rights reserved. + *

+ * $Revision: 1.6 $ + * @author David Hopwood + * @since Cryptix 2.2.2 + */ + + + public class CryptoException extends Exception { + public CryptoException() { + super (); + } + + /** @param reason the reason why the exception was thrown. */ + public CryptoException(String reason) { + super(reason); + } + } + diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/crypto/RC4Cipher.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/crypto/RC4Cipher.java new file mode 100644 index 0000000000..eb966d9232 --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/crypto/RC4Cipher.java @@ -0,0 +1,334 @@ +package org.rzo.netty.ahessian.crypto; + +import java.util.Arrays; + +/** + * Taken from the cryptix project + * This class implements the RC4 (TM) stream cipher. + *

+ * The source code (C version) from which this port was done, is the one + * posted to the sci.crypt, alt.security, comp.security.misc, and + * alt.privacy newsgroups on Wed, 14 Sep 1994 06:35:31 GMT by + * "David Sterndark" <sterndark@netcom.com> + * (Message-ID: <sternCvKL4B.Hyy@netcom.com>) + *

+ * RC4 (TM) was designed by Ron Rivest, and was previously a trade secret of + * RSA Data Security, Inc. The algorithm is now in the public domain. The name + * "RC4" is a trademark of RSA Data Security, Inc. + *

+ * References: + *

    + *
  1. Bruce Schneier, + * "Section 17.1 RC4," + * Applied Cryptography, 2nd edition, + * John Wiley & Sons, 1996. + *
+ *

+ * Copyright © 1997 + * Systemics Ltd on behalf of the + * Cryptix Development Team. + *
All rights reserved. + *

+ * $Revision: 1.6 $ + * @author Raif S. Naffah + * @author David Hopwood + * @since Cryptix 2.2.2 + */ + + +public class RC4Cipher implements StreamCipher +{ + // RC4 constants and variables + //............................................................................ + + /** + * The state of the cipher object when it is uninitialized, + * that is, the state it is in right after it has been created. + */ + public static final int UNINITIALIZED = 0; + + /** + * The state of the cipher when it is ready to encrypt, that is, + * the state it is in right after a call to initEncrypt. + * + * @see #initEncrypt + */ + public static final int ENCRYPT = 1; + + /** + * The state of the cipher when it is ready to decrypt, that is, + * the state it is in right after a call to initDecrypt. + * + * @see #initDecrypt + */ + public static final int DECRYPT = 2; + + /** + * Will hold the contents of the current set S-box. + */ + private int[] sBox = new int[256]; + + /** + * The two indices for the S-box computation referred to as i and j + * in Schneier. + */ + private int x, y; + + /** + * The block size of this cipher. Being a stream cipher this value + * is 1! + */ + private static final int BLOCK_SIZE = 1; + + private int state; // defaults to UNINITIALIZED = 0 + private String cipherName = "RC4"; + + // Constructor, finalizer, and clone() + //............................................................................ + + /** + * Constructs an RC4 cipher object, in the UNINITIALIZED state. + * This calls the Cipher constructor with implBuffering false, + * implPadding false and the provider set to "Cryptix". + */ + public RC4Cipher() { + //super(false, false, "Cryptix"); + } + + /** + * Always throws a CloneNotSupportedException (cloning of ciphers is not + * supported for security reasons). + */ + public final Object clone() throws CloneNotSupportedException { + throw new CloneNotSupportedException(); + } + + // Implementation of JCE methods + //............................................................................ + + /** + * SPI: Returns the length of an input block, in bytes. + * + * @return the length in bytes of an input block for this cipher. + */ + public int engineBlockSize() { + return BLOCK_SIZE; + } + + /** + * SPI: Initializes this cipher for encryption, using the + * specified key. + * + * @param key the key to use for encryption. + * @exception CryptoException if the key is invalid. + */ + public void engineInitEncrypt(byte[] key, byte[] iv) throws CryptoException { + makeKey(key); + state = ENCRYPT; + } + + /** + * SPI: Initializes this cipher for decryption, using the + * specified key. + * + * @param key the key to use for decryption. + * @exception CryptoException if the key is invalid. + */ + public void engineInitDecrypt(byte[] key, byte[] iv) throws CryptoException { + makeKey(key); + state = ENCRYPT; + } + + /** + * SPI: This is the main engine method for updating data. + *

+ * in and out may be the same array, and the input and output + * regions may overlap. + * + * @param in the input data. + * @param inOffset the offset into in specifying where the data starts. + * @param inLen the length of the subarray. + * @param out the output array. + * @param outOffset the offset indicating where to start writing into + * the out array. + * @return the number of bytes written. + * reports an error. + */ + protected int engineUpdate(byte[] in, int inOffset, int inLen, + byte[] out, int outOffset) { + if (inLen < 0) + throw new IllegalArgumentException("inLen < 0"); + + boolean doEncrypt = (getState() == ENCRYPT); + + // Avoid overlapping input and output regions. + if (in == out + && (outOffset >= inOffset + && outOffset < inOffset + inLen || inOffset >= outOffset + && inOffset < outOffset + inLen)) { + byte[] newin = new byte[inLen]; + System.arraycopy(in, inOffset, newin, 0, inLen); + in = newin; + inOffset = 0; + } + + rc4(in, inOffset, inLen, out, outOffset); + + return inLen; + } + + // Own methods + //............................................................................ + + /** + * RC4 encryption/decryption. The input and output regions are assumed not to + * overlap. + * + * @param in the input data. + * @param inOffset the offset into in specifying where the data starts. + * @param inLen the length of the subarray. + * @param out the output array. + * @param outOffset the offset indicating where to start writing into + * the out array. + */ + private void rc4(byte[] in, int inOffset, int inLen, byte[] out, + int outOffset) { + int xorIndex, t; + + for (int i = 0; i < inLen; i++) { + x = (x + 1) & 0xFF; + y = (sBox[x] + y) & 0xFF; + + t = sBox[x]; + sBox[x] = sBox[y]; + sBox[y] = t; + + xorIndex = (sBox[x] + sBox[y]) & 0xFF; + out[outOffset++] = (byte) (in[inOffset++] ^ sBox[xorIndex]); + } + } + + /** + * Expands a user-key to a working key schedule. + *

+ * The key bytes are first extracted from the user-key and then + * used to build the contents of this key schedule. + *

+ * The method's only exceptions are when the user-key's contents + * are null, or a byte array of zero length. + * + * @param key the user-key object to use. + * @exception CryptoException if one of the following occurs:

    + *
  • key.getEncoded() == null; + *
  • The encoded byte array form of the key is zero-length; + *
+ */ + private void makeKey(byte[] userkey) throws CryptoException { + + if (userkey == null) + throw new CryptoException(getAlgorithm() + + ": Null user key"); + + int len = userkey.length; + if (len == 0) + throw new CryptoException(getAlgorithm() + + ": Invalid user key length"); + + x = y = 0; + for (int i = 0; i < 256; i++) + sBox[i] = i; + + int i1 = 0, i2 = 0, t; + + for (int i = 0; i < 256; i++) { + i2 = ((userkey[i1] & 0xFF) + sBox[i] + i2) & 0xFF; + + t = sBox[i]; + sBox[i] = sBox[i2]; + sBox[i2] = t; + + i1 = (i1 + 1) % len; + } + } + + /** + * Returns this algorithm's standard cipher name (not including + * mode and padding). + *

+ * See + * International JCE Standard Algorithm Names for a list + * of Cipher algorithm names. + * + * @return the standard cipher name (such as "DES"). + */ + public final String getAlgorithm() { + return cipherName; + } + + /** + * Returns the state of this Cipher object. Possible states are: + *

+ *

+ *
UNINITIALIZED + *
The cipher has not been initialized. + *
ENCRYPT + *
The cipher has been initialized for encryption. It may be + * used for encryption only. + *
DECRYPT + *
The cipher has been initialized for decryption. It may be + * used for decryption only. + *
+ * + * @return the state of this cipher object. + * + * @see #UNINITIALIZED + * @see #ENCRYPT + * @see #DECRYPT + */ + public final int getState() { + return state; + } + + public final byte[] crypt(byte[] data, int position, int length) { + byte[] buffer = new byte[length]; + engineUpdate(data, position, length, buffer, 0); + return buffer; + } + + public final byte[] crypt(byte[] data) { + byte[] buffer = new byte[data.length]; + engineUpdate(data, 0, data.length, buffer, 0); + return buffer; + } + + public final void crypt(byte[] in, int in_offset, int length, + byte[] out, int out_offset) { + engineUpdate(in, in_offset, length, out, out_offset); + } + + public static void main(String[] args) throws Exception + { + byte[] iv = new byte[CryptoConstants.SYM_IV_SIZE]; + byte[] key = new byte[CryptoConstants.SYM_KEY_SIZE]; + StreamCipher eCipher = StreamCipherFactory.createCipher("RC4"); + StreamCipher dCipher = StreamCipherFactory.createCipher("RC4"); + eCipher.engineInitEncrypt(key, iv); + dCipher.engineInitDecrypt(key, iv); + byte[] msg = "this is a secret message".getBytes(); + byte[] cMsg; + byte[] eMsg = new byte[0]; + long t = System.currentTimeMillis(); + for (int i=0; i<1000000; i++) + { + cMsg = eCipher.crypt(msg, 0, msg.length); + eMsg = dCipher.crypt(cMsg, 0, cMsg.length); + } + System.out.println(System.currentTimeMillis() - t); + if (!Arrays.equals(msg, eMsg)) + System.out.println("error"); + + + } + + } + diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/crypto/Salsa20.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/crypto/Salsa20.java new file mode 100644 index 0000000000..2533ad97fb --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/crypto/Salsa20.java @@ -0,0 +1,387 @@ +package org.rzo.netty.ahessian.crypto; + +import java.util.Arrays; + +/** + * Implementation of Daniel J. Bernstein's Salsa20 stream cipher, Snuffle 2005 + * Taken from Bouncycastle + */ + + +public class Salsa20 implements StreamCipher +{ + + private final static int stateSize = 16; // 16, 32 bit ints = 64 bytes + + private final static byte[] + sigma = "expand 32-byte k".getBytes(), + tau = "expand 16-byte k".getBytes(); + + /* + * variables to hold the state of the engine + * during encryption and decryption + */ + private int index = 0; + private int[] engineState = new int[stateSize]; // state + private int[] x = new int[stateSize] ; // internal buffer + private byte[] keyStream = new byte[stateSize * 4], // expanded state, 64 bytes + workingKey = null, + workingIV = null; + private boolean initialised = false; + + /* + * internal counter + */ + private int cW0, cW1, cW2; + + public void engineInitEncrypt(byte[] key, byte[] iv) throws CryptoException { + init(true, key, iv); + } + + public void engineInitDecrypt(byte[] key, byte[] iv) throws CryptoException { + init(false, key, iv); + } + + + /** + * initialise a Salsa20 cipher. + * + * @param forEncryption whether or not we are for encryption. + * @param params the parameters required to set up the cipher. + * @exception IllegalArgumentException if the params argument is + * inappropriate. + */ + private void init( + boolean forEncryption, + byte[] key, byte[] iv) + { + /* + * Salsa20 encryption and decryption is completely + * symmetrical, so the 'forEncryption' is + * irrelevant. (Like 90% of stream ciphers) + */ + + if (iv == null || iv.length != 8) + { + throw new IllegalArgumentException("Salsa20 requires exactly 8 bytes of IV"); + } + + + workingKey = key; + workingIV = iv; + + setKey(workingKey, workingIV); + } + + public String getAlgorithmName() + { + return "Salsa20"; + } + + public byte returnByte(byte in) throws CryptoException + { + if (limitExceeded()) + { + throw new CryptoException("2^70 byte limit per IV; Change IV"); + } + + if (index == 0) + { + salsa20WordToByte(engineState, keyStream); + engineState[8]++; + if (engineState[8] == 0) + { + engineState[9]++; + } + } + byte out = (byte)(keyStream[index]^in); + index = (index + 1) & 63; + + return out; + } + + public final byte[] crypt(byte[] data, int position, int length) throws CryptoException + { + byte[] buffer = new byte[length]; + crypt(data, position, length, buffer, 0); + return buffer; + } + + + public void crypt + ( + byte[] in, + int inOff, + int len, + byte[] out, + int outOff) throws CryptoException + { + if (!initialised) + { + throw new IllegalStateException(getAlgorithmName()+" not initialised"); + } + + if ((inOff + len) > in.length) + { + throw new CryptoException("input buffer too short"); + } + + if ((outOff + len) > out.length) + { + throw new CryptoException("output buffer too short"); + } + + if (limitExceeded(len)) + { + throw new CryptoException("2^70 byte limit per IV would be exceeded; Change IV"); + } + + for (int i = 0; i < len; i++) + { + if (index == 0) + { + salsa20WordToByte(engineState, keyStream); + engineState[8]++; + if (engineState[8] == 0) + { + engineState[9]++; + } + } + out[i+outOff] = (byte)(keyStream[index]^in[i+inOff]); + index = (index + 1) & 63; + } + } + + public void reset() + { + setKey(workingKey, workingIV); + } + + // Private implementation + + private void setKey(byte[] keyBytes, byte[] ivBytes) + { + workingKey = keyBytes; + workingIV = ivBytes; + + index = 0; + resetCounter(); + int offset = 0; + byte[] constants; + + // Key + engineState[1] = byteToIntLittle(workingKey, 0); + engineState[2] = byteToIntLittle(workingKey, 4); + engineState[3] = byteToIntLittle(workingKey, 8); + engineState[4] = byteToIntLittle(workingKey, 12); + + if (workingKey.length == 32) + { + constants = sigma; + offset = 16; + } + else + { + constants = tau; + } + + engineState[11] = byteToIntLittle(workingKey, offset); + engineState[12] = byteToIntLittle(workingKey, offset+4); + engineState[13] = byteToIntLittle(workingKey, offset+8); + engineState[14] = byteToIntLittle(workingKey, offset+12); + engineState[0 ] = byteToIntLittle(constants, 0); + engineState[5 ] = byteToIntLittle(constants, 4); + engineState[10] = byteToIntLittle(constants, 8); + engineState[15] = byteToIntLittle(constants, 12); + + // IV + engineState[6] = byteToIntLittle(workingIV, 0); + engineState[7] = byteToIntLittle(workingIV, 4); + engineState[8] = engineState[9] = 0; + + initialised = true; + } + + /** + * Salsa20 function + * + * @param input input data + * + * @return keystream + */ + private void salsa20WordToByte(int[] input, byte[] output) + { + System.arraycopy(input, 0, x, 0, input.length); + + for (int i = 0; i < 10; i++) + { + x[ 4] ^= rotl((x[ 0]+x[12]), 7); + x[ 8] ^= rotl((x[ 4]+x[ 0]), 9); + x[12] ^= rotl((x[ 8]+x[ 4]),13); + x[ 0] ^= rotl((x[12]+x[ 8]),18); + x[ 9] ^= rotl((x[ 5]+x[ 1]), 7); + x[13] ^= rotl((x[ 9]+x[ 5]), 9); + x[ 1] ^= rotl((x[13]+x[ 9]),13); + x[ 5] ^= rotl((x[ 1]+x[13]),18); + x[14] ^= rotl((x[10]+x[ 6]), 7); + x[ 2] ^= rotl((x[14]+x[10]), 9); + x[ 6] ^= rotl((x[ 2]+x[14]),13); + x[10] ^= rotl((x[ 6]+x[ 2]),18); + x[ 3] ^= rotl((x[15]+x[11]), 7); + x[ 7] ^= rotl((x[ 3]+x[15]), 9); + x[11] ^= rotl((x[ 7]+x[ 3]),13); + x[15] ^= rotl((x[11]+x[ 7]),18); + x[ 1] ^= rotl((x[ 0]+x[ 3]), 7); + x[ 2] ^= rotl((x[ 1]+x[ 0]), 9); + x[ 3] ^= rotl((x[ 2]+x[ 1]),13); + x[ 0] ^= rotl((x[ 3]+x[ 2]),18); + x[ 6] ^= rotl((x[ 5]+x[ 4]), 7); + x[ 7] ^= rotl((x[ 6]+x[ 5]), 9); + x[ 4] ^= rotl((x[ 7]+x[ 6]),13); + x[ 5] ^= rotl((x[ 4]+x[ 7]),18); + x[11] ^= rotl((x[10]+x[ 9]), 7); + x[ 8] ^= rotl((x[11]+x[10]), 9); + x[ 9] ^= rotl((x[ 8]+x[11]),13); + x[10] ^= rotl((x[ 9]+x[ 8]),18); + x[12] ^= rotl((x[15]+x[14]), 7); + x[13] ^= rotl((x[12]+x[15]), 9); + x[14] ^= rotl((x[13]+x[12]),13); + x[15] ^= rotl((x[14]+x[13]),18); + } + + int offset = 0; + for (int i = 0; i < stateSize; i++) + { + intToByteLittle(x[i] + input[i], output, offset); + offset += 4; + } + + for (int i = stateSize; i < x.length; i++) + { + intToByteLittle(x[i], output, offset); + offset += 4; + } + } + + /** + * 32 bit word to 4 byte array in little endian order + * + * @param x value to 'unpack' + * + * @return value of x expressed as a byte[] array in little endian order + */ + private byte[] intToByteLittle(int x, byte[] out, int off) + { + out[off] = (byte)x; + out[off + 1] = (byte)(x >>> 8); + out[off + 2] = (byte)(x >>> 16); + out[off + 3] = (byte)(x >>> 24); + return out; + } + + /** + * Rotate left + * + * @param x value to rotate + * @param y amount to rotate x + * + * @return rotated x + */ + private int rotl(int x, int y) + { + return (x << y) | (x >>> -y); + } + + /** + * Pack byte[] array into an int in little endian order + * + * @param x byte array to 'pack' + * @param offset only x[offset]..x[offset+3] will be packed + * + * @return x[offset]..x[offset+3] 'packed' into an int in little-endian order + */ + private int byteToIntLittle(byte[] x, int offset) + { + return ((x[offset] & 255)) | + ((x[offset + 1] & 255) << 8) | + ((x[offset + 2] & 255) << 16) | + (x[offset + 3] << 24); + } + + private void resetCounter() + { + cW0 = 0; + cW1 = 0; + cW2 = 0; + } + + private boolean limitExceeded() + { + cW0++; + if (cW0 == 0) + { + cW1++; + if (cW1 == 0) + { + cW2++; + return (cW2 & 0x20) != 0; // 2^(32 + 32 + 6) + } + } + + return false; + } + + /* + * this relies on the fact len will always be positive. + */ + private boolean limitExceeded(int len) + { + if (cW0 >= 0) + { + cW0 += len; + } + else + { + cW0 += len; + if (cW0 >= 0) + { + cW1++; + if (cW1 == 0) + { + cW2++; + return (cW2 & 0x20) != 0; // 2^(32 + 32 + 6) + } + } + } + + return false; + } + + public static void main(String[] args) throws Exception + { + byte[] iv = new byte[CryptoConstants.SYM_IV_SIZE]; + byte[] key = new byte[CryptoConstants.SYM_KEY_SIZE]; + StreamCipher eCipher = StreamCipherFactory.createCipher("Salsa20"); + StreamCipher dCipher = StreamCipherFactory.createCipher("Salsa20"); + eCipher.engineInitEncrypt(key, iv); + dCipher.engineInitDecrypt(key, iv); + byte[] msg = "this is a secret message".getBytes(); + byte[] cMsg; + byte[] eMsg = new byte[0]; + long t = System.currentTimeMillis(); + System.out.println("message size "+msg.length); + for (int i=0; i<1000000; i++) + { + cMsg = eCipher.crypt(msg, 0, msg.length); + eMsg = dCipher.crypt(cMsg, 0, cMsg.length); + } + System.out.println(System.currentTimeMillis() - t); + if (!Arrays.equals(msg, eMsg)) + System.out.println("error"); + + + } + + + +} diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/crypto/ServerCryptoFilter.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/crypto/ServerCryptoFilter.java new file mode 100644 index 0000000000..8de18e066f --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/crypto/ServerCryptoFilter.java @@ -0,0 +1,208 @@ +package org.rzo.netty.ahessian.crypto; + +import java.security.Key; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.SecureRandom; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import javax.crypto.Cipher; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.jboss.netty.buffer.ChannelBuffers; +import org.jboss.netty.channel.Channel; +import org.jboss.netty.channel.ChannelFuture; +import org.jboss.netty.channel.ChannelHandlerContext; +import org.jboss.netty.channel.ChannelStateEvent; +import org.jboss.netty.channel.Channels; +import org.jboss.netty.channel.MessageEvent; +import org.jboss.netty.channel.SimpleChannelHandler; +import org.rzo.netty.ahessian.log.OutLogger; + +public class ServerCryptoFilter extends SimpleChannelHandler implements CryptoConstants +{ + KeyPair _serverKeyPair; + Key _clientKey; + ChannelStateEvent _connectedEvent; + private StreamCipher _encodeCipher; + private StreamCipher _decodeCipher; + private byte[] _cryptedIvKeyMessage; + private int _bytesRead; + private List _passwords = new ArrayList(); + + + @Override + public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception { + // send public key + sendByteArray(ctx, getPublicKeyEncoded()); + // remember this event, so that we can propagate it to the rest of the pipeline once we have + // the client's secret key + _connectedEvent = e; + } + + private void sendByteArray(ChannelHandlerContext ctx, byte[] buffer) + { + try + { + Channel channel = ctx.getChannel(); + ChannelFuture future = Channels.future(ctx.getChannel()); + ChannelBuffer b = ChannelBuffers.dynamicBuffer(); + // first send encoded key bytes size + b.writeInt(buffer.length); + // then the public key + b.writeBytes(buffer); + Channels.write(ctx, future, b); + } + catch (Exception e) + { + e.printStackTrace(); + } + } + + byte[] getPublicKeyEncoded() + { + try + { + // generate a key pair + SecureRandom random = new SecureRandom(); + KeyPairGenerator generator = KeyPairGenerator.getInstance(ASYM_KEY_TYPE); + generator.initialize(ASYM_KEY_SIZE, random); + + _serverKeyPair = generator.generateKeyPair(); + Key pubKey = _serverKeyPair.getPublic(); + return pubKey.getEncoded(); + } + catch (Exception e) + { + e.printStackTrace(); + } + return null; + + } + + @Override + public void messageReceived( + ChannelHandlerContext ctx, MessageEvent e) throws Exception + { + // have we sent our secret key ? + if (_decodeCipher != null) + { + MessageEvent m = Util.code(_decodeCipher, e, true); + ctx.sendUpstream(m); + } + else + { + ChannelBuffer b = (ChannelBuffer) e.getMessage(); + // is this our first message ? + if (_cryptedIvKeyMessage == null) + { + int size = b.readInt(); + // consistency check, so we do not get an out of memory exception + if (size > 1024) + { + ctx.getChannel().close(); + return; + } + _cryptedIvKeyMessage = new byte[size]; + } + // readin the client's secret key and iv + int available = b.readableBytes(); + int toRead = Math.min(_cryptedIvKeyMessage.length - _bytesRead, available); + b.readBytes(_cryptedIvKeyMessage, _bytesRead, toRead); + _bytesRead += toRead; + // we have completed receiption ? + if (_bytesRead == _cryptedIvKeyMessage.length) + { + boolean ok = false; + try + { + createCiphers(); + ok = true; + } + catch (Exception ex) + { + ex.printStackTrace(); + ctx.getChannel().close(); + } + // inform pipline that we are ready for encrypted communication + if (ok) + ctx.sendUpstream(_connectedEvent); + } + } + } + + private void createCiphers() throws Exception + { + // first decode the received data + String type = "".equals(ASYM_CIPHER_TYPE) ? ASYM_KEY_TYPE : ASYM_KEY_TYPE+"/"+ASYM_CIPHER_TYPE; + Cipher asymCipher = Cipher.getInstance(type); + asymCipher.init(Cipher.DECRYPT_MODE, _serverKeyPair.getPrivate()); + byte[] data = asymCipher.doFinal(_cryptedIvKeyMessage); + + System.out.println("received iv+key: "+OutLogger.asString(data)); + + byte[] iv = new byte[SYM_IV_SIZE]; + System.arraycopy(data, data.length-(SYM_IV_SIZE+SYM_KEY_SIZE+PASSWORD_SIZE+PASSWORD_SIZE), iv, 0, iv.length); + System.out.println("received iv: "+OutLogger.asString(iv)); + + byte[] key = new byte[SYM_KEY_SIZE]; + System.arraycopy(data, data.length-(SYM_KEY_SIZE+PASSWORD_SIZE), key, 0, key.length); + System.out.println("received key: "+OutLogger.asString(key)); + + byte[] password = new byte[PASSWORD_SIZE]; + System.arraycopy(data, data.length-PASSWORD_SIZE, password, 0, password.length); + if (!checkPassword(password)) + throw new RuntimeException("password mismatch"); + + _encodeCipher = StreamCipherFactory.createCipher(SYM_KEY_TYPE); + _encodeCipher.engineInitEncrypt(key, iv); + + _decodeCipher = StreamCipherFactory.createCipher(SYM_KEY_TYPE); + _decodeCipher.engineInitDecrypt(key, iv); + } + + private boolean checkPassword(byte[] password) + { + if (password == null || password.length != PASSWORD_SIZE) + return false; + else for (byte[] pwd : _passwords) + if (Arrays.equals(password, pwd)) + return true; + return false; + } + + public void addPassword(byte[] password) + { + if (password == null || password.length == 0 || PASSWORD_SIZE == 0) + return; + byte[]mPassword = new byte[PASSWORD_SIZE]; + Arrays.fill(mPassword, (byte)0); + int length = Math.min(PASSWORD_SIZE, password.length); + System.arraycopy(password, 0, mPassword, 0, length); + _passwords.add(mPassword); + } + + + @Override + public void writeRequested( + ChannelHandlerContext ctx, MessageEvent e) throws Exception + { + if (_encodeCipher != null) + { + MessageEvent m = Util.code(_encodeCipher, e, false); + ctx.sendDownstream(m); + } + + } + + + + public static void main(String[] args) + { + ServerCryptoFilter h = new ServerCryptoFilter(); + h.getPublicKeyEncoded(); + } + +} diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/crypto/StreamCipher.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/crypto/StreamCipher.java new file mode 100644 index 0000000000..5953e1b869 --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/crypto/StreamCipher.java @@ -0,0 +1,15 @@ +package org.rzo.netty.ahessian.crypto; + +public interface StreamCipher +{ + public void engineInitEncrypt(byte[] key, byte[] iv) throws CryptoException; + + public void engineInitDecrypt(byte[] key, byte[] iv) throws CryptoException; + + public void crypt(byte[] in, int inOffset, int length, byte[] out, int outOffset) throws CryptoException; + + public byte[] crypt(byte[] data, int position, int length) throws CryptoException; + + + +} diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/crypto/StreamCipherFactory.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/crypto/StreamCipherFactory.java new file mode 100644 index 0000000000..8a0b840d4c --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/crypto/StreamCipherFactory.java @@ -0,0 +1,16 @@ +package org.rzo.netty.ahessian.crypto; + +public class StreamCipherFactory +{ + + public static StreamCipher createCipher(String algorithm) + { + if ("RC4".equals(algorithm)) + return new RC4Cipher(); + else if ("Salsa20".equals(algorithm)) + return new Salsa20(); + else + return null; + } + +} diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/crypto/Util.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/crypto/Util.java new file mode 100644 index 0000000000..d21dc2fb05 --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/crypto/Util.java @@ -0,0 +1,45 @@ +package org.rzo.netty.ahessian.crypto; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.jboss.netty.buffer.ChannelBuffers; +import org.jboss.netty.channel.DownstreamMessageEvent; +import org.jboss.netty.channel.MessageEvent; +import org.jboss.netty.channel.UpstreamMessageEvent; + +public class Util implements CryptoConstants +{ + static MessageEvent code(StreamCipher cipher, MessageEvent e, boolean decode) throws Exception + { + try + { + ChannelBuffer b = (ChannelBuffer) e.getMessage(); + byte[] encodedData = cipher.crypt(b.array(), b.readerIndex(), b.readableBytes()); + return toMessageEvent(e, ChannelBuffers.wrappedBuffer(encodedData)); + } + catch (Exception ex) + { + ex.printStackTrace(); + throw ex; + } + + } + + static MessageEvent toMessageEvent(MessageEvent e, ChannelBuffer data) + { + if (e instanceof DownstreamMessageEvent) + { + return new DownstreamMessageEvent(e.getChannel(), e.getFuture(), data, e.getRemoteAddress()); + } + else if (e instanceof UpstreamMessageEvent) + { + return new UpstreamMessageEvent(e.getChannel(), data, e.getRemoteAddress()); + } + else + { + System.out.println("unxepected message type in Util.toMessageEvent: " + e.getMessage()); + return e; + } + } + + +} diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/io/CRCInputStream.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/io/CRCInputStream.java new file mode 100644 index 0000000000..0651dbb807 --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/io/CRCInputStream.java @@ -0,0 +1,40 @@ +package org.rzo.netty.ahessian.io; + +import java.io.IOException; + +public class CRCInputStream extends InputStreamBuffer +{ + byte _crc = 0; + + public void resetCRC() + { + _crc = 0; + } + + public byte getCRC() + { + byte result = _crc; + resetCRC(); + return result; + } + + @Override + public int read() throws IOException + { + int result = super.read(); + _crc ^= (byte)result; + return result; + } + + @Override + public int read(byte[] b, int off, int len) throws IOException + { + int result = super.read(b, off, len); + for (int i=off; i _bufs = new LinkedList(); + + /** Indicates if the stream has been closed */ + private volatile boolean _closed = false; + final private Lock _lock = new MyReentrantLock(); + /** Sync condition indicating that buffer is not empty. */ + final private Condition _notEmpty = _lock.newCondition(); + private volatile int _available = 0; + boolean blocking = false; + long _readTimeout = 3000; + // close stream on empty buffer + boolean _closeOnEmpty = false; + + /* + * (non-Javadoc) + * + * @see java.io.InputStream#read() + */ + @Override + public int read() throws IOException + { + int result = -1; + if (_closed) + return -1; + checkCloseOnEmpty(); + _lock.lock(); + try + { + while (!_closed && available() == 0) + if (blocking) + { + if (_readTimeout > 0) + { + if (!_notEmpty.await(_readTimeout, TimeUnit.MILLISECONDS)) + throw new IOException("read timeout"); + } + else + _notEmpty.await(); + } + else + { + throw new IOException("no data"); + } + if (!_closed) + { + result = (int) _bufs.getFirst().readByte() & 0xFF; + _available--; + checkBufs(); + } + } + catch (Exception ex) + { + throw new IOException(ex.getMessage()); + } + finally + { + _lock.unlock(); + } + + //System.out.println("read "+_available); + checkCloseOnEmpty(); + return result; + } + + private void checkBufs() + { + if (!_bufs.isEmpty() && _bufs.getFirst().readableBytes() == 0) + { + _bufs.removeFirst(); + } + } + + /* + * (non-Javadoc) + * + * @see java.io.InputStream#close() + */ + @Override + public void close() throws IOException + { + _lock.lock(); + try + { + _closed = true; + _notEmpty.signal(); + super.close(); + } + catch (Exception ex) + { + Constants.ahessianLogger.warn("error closing input stream", ex); + } + finally + { + _lock.unlock(); + } + } + + /** + * Insert bytes to the input stream + * + * @param buf + * bytes received from previous upstream handler + * + * @throws IOException + * Signals that an I/O exception has occurred. + */ + public void write(ChannelBuffer buf) throws IOException + { + if (_closed) + throw new IOException("stream closed"); + _lock.lock(); + // if (_available == 0) + // System.out.println("input not empty"); + try + { + if (_bufs.isEmpty() || buf != _bufs.getLast()) + { + _bufs.addLast(buf); + } + _available += buf.readableBytes(); + _notEmpty.signal(); + } + catch (Exception ex) + { + Constants.ahessianLogger.warn("", ex); + } + finally + { + _lock.unlock(); + } + } + + /* + * (non-Javadoc) + * + * @see java.io.InputStream#available() + */ + public int available() throws IOException + { + if (_closed) + throw new IOException("stream closed"); + return _available; + } + + /* + * (non-Javadoc) + * + * @see java.io.InputStream#read(byte[], int, int) + */ + @Override + public int read(byte[] b, int off, int len) throws IOException + { + int result = -1; + if (_closed) + return -1; + _lock.lock(); + try + { + while (!_closed && available() == 0) + { + checkCloseOnEmpty(); + if (_readTimeout > 0) + { + if (!_notEmpty.await(_readTimeout, TimeUnit.MILLISECONDS)) + throw new IOException("read timeout"); + } + else + _notEmpty.awaitUninterruptibly(); + } + if (!_closed) + { + int length = Math.min(len, _bufs.getFirst().readableBytes()); + _bufs.getFirst().readBytes(b, off, length); + result = length; + _available -= length; + checkBufs(); + // if (_available == 0) + // System.out.println("input empty"); + } + } + catch (Exception ex) + { + throw new IOException(ex.getMessage()); + } + finally + { + _lock.unlock(); + } + checkCloseOnEmpty(); + return result; + } + + /** + * Checks if the stream is closed. + * + * @return true, if is closed + */ + public boolean isClosed() + { + return _closed; + } + + public void setReadTimeout(long timeout) + { + _readTimeout = timeout; + } + + public boolean isBlocking() + { + return blocking; + } + + public void setBlocking(boolean blocking) + { + this.blocking = blocking; + } + + private void checkCloseOnEmpty() throws IOException + { + if (_closeOnEmpty && !_closed && available() == 0) + close(); + } + + public void closeOnEmpty() throws IOException + { + _closeOnEmpty = true; + checkCloseOnEmpty(); + } + +} diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/io/InputStreamConsumer.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/io/InputStreamConsumer.java new file mode 100644 index 0000000000..dc745fcf91 --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/io/InputStreamConsumer.java @@ -0,0 +1,15 @@ +package org.rzo.netty.ahessian.io; + +import java.io.InputStream; + +import org.jboss.netty.channel.ChannelHandlerContext; + +public interface InputStreamConsumer +{ + public void consume(ChannelHandlerContext ctx, InputStream message); + + public boolean isBufferEmpty(); + + public void setContext(ChannelHandlerContext ctx); + +} diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/io/InputStreamDecoder.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/io/InputStreamDecoder.java new file mode 100644 index 0000000000..9ae6fbecb9 --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/io/InputStreamDecoder.java @@ -0,0 +1,159 @@ +package org.rzo.netty.ahessian.io; + +import static org.jboss.netty.channel.Channels.fireMessageReceived; + +import java.io.IOException; +import java.io.InputStream; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.jboss.netty.channel.ChannelHandlerContext; +import org.jboss.netty.channel.ChannelPipeline; +import org.jboss.netty.channel.ChannelStateEvent; +import org.jboss.netty.channel.MessageEvent; +import org.jboss.netty.channel.SimpleChannelUpstreamHandler; +import org.rzo.netty.ahessian.stopable.StopableHandler; + +//import com.caucho.services.server.ServiceContext; + +/** + * Encodes bytes read by a {@link Channel} into an {@link InputStream}.
+ * Once created the InputStream is passed as a message to the next handler + * within a separate thread. From there on, no further messages are passed + * through the pipeline.
+ * A typical setup for a serialization protocol in a TCP/IP socket would be: + * + *
+ * {@link ChannelPipeline} pipeline = ...;
+ * 
+ * // Encoder
+ * pipeline.addLast("outputStream", new {@link handler.io.OutputStream}());
+ * pipeline.addLast("outputHandler", new MyOutputHandler());
+ * 
+ * // Decoder
+ * pipeline.addLast("inputStream", new {@link handler.io.InputStream}());
+ * pipeline.addLast("inputHandler", new MyInputHandler());
+ * 
+ * + * and then, within the handler you can use a {@link java.io.InputStream} or + * {@link java.io.OutputStream} instead of a {@link ChannelBuffer} as a message:
+ * Writing to OutputStream: + * + *
+ * // synchronized for multithreaded environment to avoid messages mixing
+ * synchronized public void writeRequested(ChannelHandlerContext ctx, MessageEvent e) throws Exception
+ * {
+ * 	byte[] message = (byte[]) e.getMessage();
+ * 	OutputStream out = OutputStreamEncoder.getOutputStream(ctx);
+ * 	out.write(message);
+ * 	// if this is the last chunk of bytes we should flush the output
+ * 	out.flush();
+ * 	// netty seems to require this, so that the boss thread may read input from the channel
+ * 	Thread.yield();
+ * }
+ * 
+ * 
+ * + *
+ * Reading from InputStream: + * + *
+ * void messageReceived(ChannelHandlerContext ctx, MessageEvent e)
+ * {
+ * 	// message received is called only once to deliver the input stream
+ * 	// it is called in a separate thread and not in the netty worker thread.
+ * 	// incoming bytes are consumed in this method.
+ * 	// the stream is closed once the channel is disconnected
+ * 	InputStream in = (InputStream) evt.getMessage();
+ * 
+ * 	while (ctx.getChannel().isConnected())
+ * 	{
+ * 		// parse the incoming stream and forward the result to the next handler
+ * 		Channels.fireMessageReceived(ctx, parseReply(in));
+ * 	}
+ * }
+ * 
+ */ +public class InputStreamDecoder extends SimpleChannelUpstreamHandler implements StopableHandler +{ + + /** Thread pool for getting a thread for calling the next handler */ + InputStreamBuffer _in = null; + boolean _stopEnabled = true; + boolean _crcCheck = false; + + public InputStreamDecoder() + { + + } + + public InputStreamDecoder (boolean crcCheck) + { + _crcCheck = crcCheck; + } + + /** + * Instantiates a new input stream decoder. + * + * @param executor + * the thread pool + */ + /* + * (non-Javadoc) + * + * @see + * org.jboss.netty.channel.SimpleChannelUpstreamHandler#messageReceived( + * org.jboss.netty.channel.ChannelHandlerContext, + * org.jboss.netty.channel.MessageEvent) + */ + @Override + public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception + { + _in.write((ChannelBuffer) e.getMessage()); + + fireMessageReceived(ctx, _in, e.getRemoteAddress()); + } + + @Override + public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception + { + if (_in == null) + { + if (_crcCheck) + _in = new CRCInputStream(); + else + _in = new InputStreamBuffer(); + ctx.setAttachment(_in); + } + ctx.sendUpstream(e); + } + + public static InputStreamBuffer getInputStream(ChannelHandlerContext ctx) + { + return (InputStreamBuffer) ctx.getPipeline().getContext(InputStreamDecoder.class).getAttachment(); + } + + public boolean isStopEnabled() + { + return _stopEnabled; + } + + public void setStopEnabled(boolean stopEnabled) + { + _stopEnabled = stopEnabled; + } + + public void stop() + { + try + { + _in.close(); + } + catch (IOException e) + { + // TODO Auto-generated catch block + e.printStackTrace(); + } + _in = null; + } + +} diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/io/OutputStreamBuffer.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/io/OutputStreamBuffer.java new file mode 100644 index 0000000000..e423b60021 --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/io/OutputStreamBuffer.java @@ -0,0 +1,188 @@ +package org.rzo.netty.ahessian.io; + +import static org.jboss.netty.buffer.ChannelBuffers.dynamicBuffer; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.jboss.netty.channel.ChannelFuture; +import org.jboss.netty.channel.ChannelHandlerContext; +import org.jboss.netty.channel.Channels; +import org.jboss.netty.channel.DownstreamMessageEvent; +import org.rzo.netty.ahessian.Constants; +import org.rzo.netty.ahessian.utils.MyReentrantLock; + +import com.caucho.hessian4.io.FlushableOutput; + +/** + * A buffer for storing outgoing bytes. Bytes are sent upstream if + * The number of written bytes is > than a watermark, or if flush is called + */ +public class OutputStreamBuffer extends OutputStream implements FlushableOutput +{ + + /** The context of the channel on which to send the bytes downstream */ + private volatile ChannelHandlerContext _ctx; + + /** Indicates if the stream has been closed */ + private volatile boolean _closed = false; + private Lock _lock = new MyReentrantLock(); + + /** If written bytes > watermark, the bytes are sent downstream */ + int _watermark = 1024*1024; + int _initialBuffSize = 1024; + + /** The buffer for storing outgoing bytes. Once the bytes have been sent downstream a new buffer is created */ + private ChannelBuffer _buf = dynamicBuffer(_initialBuffSize); + + + /** + * Instantiates a new output stream buffer. + * + * @param ctx the context in which bytes are sent downstream + */ + OutputStreamBuffer(ChannelHandlerContext ctx) + { + super(); + _ctx = ctx; + } + + /* (non-Javadoc) + * @see java.io.OutputStream#write(int) + */ + @Override + public void write(int b) throws IOException + { + if (_closed) + throw new IOException("stream closed"); + _lock.lock(); + try + { + //System.out.println("write "+_buf.readableBytes()); + _buf.writeByte((byte)b); + if (_buf.writerIndex() >= _watermark) + sendDownstream(null); + } + finally + { + _lock.unlock(); + } + } + + /* (non-Javadoc) + * @see java.io.OutputStream#write(byte[], int, int) + */ + @Override + public void write(byte b[], int off, int len) throws IOException + { + if (_closed) + throw new IOException("stream closed"); + _lock.lock(); + try + { + _buf.writeBytes(b, off, len); + //System.out.println("write "+len+" "+_buf.readableBytes()); + if (_buf.writerIndex() >= _watermark) + sendDownstream(null); + } + finally + { + _lock.unlock(); + } + + } + + /* (non-Javadoc) + * @see java.io.OutputStream#flush() + */ + @Override + public void flush() throws IOException + { + flush(null); + } + + + public void flush(ChannelFuture future) throws IOException + { + _lock.lock(); + if (_buf.readableBytes() > 0) + try + { + super.flush(); + if (future == null) + { + ChannelFuture f = sendDownstream(null); + f.await(20000); + //if (!f.await(10000)) + // throw new IOException("write longer than 10 secs"); + } + else + { + sendDownstream(future); + } + } + catch (Exception e) + { + throw new IOException(e); + } + finally + { + _lock.unlock(); + } + } + + private ChannelFuture sendDownstream(ChannelFuture future) throws IOException + { + if (! _ctx.getChannel().isConnected()) + throw new IOException("channel disconnected"); + while (!_ctx.getChannel().isWritable()) + try + { + Thread.sleep(100); + } + catch (Exception ex) + { + Constants.ahessianLogger.warn("",ex); + } + if (future == null) + future = Channels.future(_ctx.getChannel()); + _ctx.sendDownstream(new DownstreamMessageEvent(_ctx.getChannel(), future, _buf, _ctx.getChannel().getRemoteAddress())); + _buf = dynamicBuffer(1024); + _buf.clear(); + return future; + } + + /* (non-Javadoc) + * @see java.io.OutputStream#close() + */ + @Override + public void close() throws IOException + { + _lock.lock(); + _closed = true; + _lock.unlock(); + } + + public void setContext(ChannelHandlerContext ctx) + { + _ctx = ctx; + reset(); + } + + public ChannelHandlerContext getContext() + { + return _ctx; + } + + public void reset() + { + _lock.lock(); + _buf = dynamicBuffer(); + _closed = false; + _lock.unlock(); + } + +} diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/io/OutputStreamEncoder.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/io/OutputStreamEncoder.java new file mode 100644 index 0000000000..60eb2a53fa --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/io/OutputStreamEncoder.java @@ -0,0 +1,163 @@ +package org.rzo.netty.ahessian.io; + +import java.io.IOException; +import java.io.OutputStream; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.jboss.netty.channel.ChannelHandlerContext; +import org.jboss.netty.channel.ChannelPipeline; +import org.jboss.netty.channel.ChannelStateEvent; +import org.jboss.netty.channel.SimpleChannelHandler; +import org.rzo.netty.ahessian.stopable.StopableHandler; + +/** + * Encodes bytes written to an {@link OutputStream} into a {@link ChannelBuffer} + * . A typical setup for a serialization protocol in a TCP/IP socket would be: + * + *
+ * {@link ChannelPipeline} pipeline = ...;
+ * 
+ * // Encoder
+ * pipeline.addLast("outputStream", new {@link handler.io.OutputStream}());
+ * pipeline.addLast("outputHandler", new MyOutputHandler());
+ * 
+ * // Decoder
+ * pipeline.addLast("inputStream", new {@link handler.io.InputStream}());
+ * pipeline.addLast("inputHandler", new MyInputHandler());
+ * 
+ * + * and then, within the handler you can use a {@link java.io.InputStream} or + * {@link java.io.OutputStream} instead of a {@link ChannelBuffer} as a message:
+ * Writing to OutputStream: + * + *
+ * // synchronized for multithreaded environment to avoid messages mixing
+ * synchronized public void writeRequested(ChannelHandlerContext ctx, MessageEvent e) throws Exception
+ * {
+ * byte[] message = (byte[]) e.getMessage();
+ * OutputStream out = OutputStreamEncoder.getOutputStream(ctx);
+ * out.write(message);
+ * // if this is the last chunk of bytes we should flush the output
+ * out.flush();
+ * // netty seems to require this, so that the boss thread may read input from the channel
+ * Thread.yield();
+ * }
+ * 
+ * 
+ * + *
+ * Reading from InputStream: + * + *
+ * void messageReceived(ChannelHandlerContext ctx, MessageEvent e)
+ * {
+ * // message received is called only once to deliver the input stream
+ * // it is called in a separate thread and not in the netty worker thread.
+ * // incoming bytes are consumed in this method.
+ * // the stream is closed once the channel is disconnected
+ * InputStream in = (InputStream) evt.getMessage();
+ * 
+ * while (ctx.getChannel().isConnected())
+ * {
+ * // parse the incoming stream and forward the result to the next handler
+ * Channels.fireMessageReceived(ctx, parseReply(in));
+ * }
+ * }
+ * 
+ */ +public class OutputStreamEncoder extends SimpleChannelHandler implements StopableHandler +{ + volatile OutputStreamBuffer _buffer = null; + private boolean _stopEnabled = true; + boolean _crcCheck = false; + + public OutputStreamEncoder() + { + + } + + public OutputStreamEncoder(boolean crcCheck) + { + _crcCheck = crcCheck; + } + + /* (non-Javadoc) + * @see org.jboss.netty.channel.SimpleChannelHandler#channelConnected(org.jboss.netty.channel.ChannelHandlerContext, org.jboss.netty.channel.ChannelStateEvent) + */ + @Override + public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception + { + if (_buffer == null) + { + if (_crcCheck) + _buffer = new CRCOutputStream(ctx); + else + _buffer = new OutputStreamBuffer(ctx); + ctx.setAttachment(_buffer); + } + else + _buffer.setContext(ctx); + ctx.sendUpstream(e); + } + + /* (non-Javadoc) + * @see org.jboss.netty.channel.SimpleChannelHandler#channelDisconnected(org.jboss.netty.channel.ChannelHandlerContext, org.jboss.netty.channel.ChannelStateEvent) + */ + @Override + public void channelDisconnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception + { + if (_buffer != null) + { + _buffer.close(); + } + ctx.sendUpstream(e); + } + + /** + * Helper method: Gets the output stream from the pipeline of a given context. + * + * @param ctx the context + * + * @return the output stream + */ + public static OutputStream getOutputStream(ChannelHandlerContext ctx) + { + return (OutputStream) ctx.getPipeline().getContext(OutputStreamEncoder.class).getAttachment(); + } + + public static OutputStreamEncoder getOutputEncoder(ChannelHandlerContext ctx) + { + return (OutputStreamEncoder) ctx.getPipeline().getContext(OutputStreamEncoder.class).getHandler(); + } + + public OutputStreamBuffer getBuffer() + { + return _buffer; + } + + public boolean isStopEnabled() + { + return _stopEnabled ; + } + + public void setStopEnabled(boolean stopEnabled) + { + _stopEnabled = stopEnabled; + } + + public void stop() + { + try + { + _buffer.close(); + } + catch (IOException e) + { + // TODO Auto-generated catch block + e.printStackTrace(); + } + _buffer = null; + } + + +} diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/io/PullInputStreamConsumer.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/io/PullInputStreamConsumer.java new file mode 100644 index 0000000000..e9df7b30a0 --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/io/PullInputStreamConsumer.java @@ -0,0 +1,149 @@ +package org.rzo.netty.ahessian.io; + +import java.io.InputStream; +import java.util.concurrent.Executor; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +import org.jboss.netty.channel.ChannelHandlerContext; +import org.jboss.netty.channel.ChannelStateEvent; +import org.jboss.netty.channel.MessageEvent; +import org.jboss.netty.channel.SimpleChannelUpstreamHandler; +import org.rzo.netty.ahessian.Constants; +import org.rzo.netty.ahessian.stopable.StopableHandler; +import org.rzo.netty.ahessian.utils.MyReentrantLock; + +public class PullInputStreamConsumer extends SimpleChannelUpstreamHandler implements StopableHandler +{ + final InputStreamConsumer _consumer; + final Executor _executor; + final Lock _lock = new MyReentrantLock(); + final Condition _hasData = _lock.newCondition(); + volatile boolean _stop = false; + volatile ChannelHandlerContext _ctx; + volatile InputStream _inputStream; + volatile boolean _waiting = false; + static AtomicInteger _threadCounter = new AtomicInteger(0); + private boolean _stopEnabled = true; + + + public PullInputStreamConsumer(InputStreamConsumer consumer, Executor executor) + { + _consumer = consumer; + _executor = executor; + + _executor.execute(new Runnable() + { + public void run() + { + String tName = Thread.currentThread().getName(); + Thread.currentThread().setName("ahessian-PullInputStreamConsumer-#"+_threadCounter.incrementAndGet()); + try + { + waitForData(); + while (!_stop) + { + _consumer.consume(_ctx, _inputStream); + waitForData(); + } + } + finally + { + Thread.currentThread().setName(tName); + _threadCounter.decrementAndGet(); + } + } + }); + } + + private void waitForData() + { + //System.out.println("wait for data"); + while (! _stop &&( _consumer == null || _consumer.isBufferEmpty() || _ctx == null || !_ctx.getChannel().isConnected())) + { + + _lock.lock(); + try + { + _waiting = true; + _hasData.await(500, TimeUnit.MILLISECONDS); + } + catch (InterruptedException e) + { + Constants.ahessianLogger.warn("", e); + } + finally + { + _waiting = false; + _lock.unlock(); + } + } + //System.out.println("got data"); + } + + + + @Override + public void messageReceived(ChannelHandlerContext ctx, MessageEvent evnt) throws Exception + { + if (_ctx != ctx) + _ctx = ctx; + if (_inputStream != evnt.getMessage()) + { + _inputStream = (InputStream) evnt.getMessage(); + ((InputStreamBuffer)_inputStream).setReadTimeout(-1); + } + if (_waiting) + { + _lock.lock(); + try + { + _hasData.signal(); + } + finally + { + _lock.unlock(); + } + } + } + + public void channelConnected( + ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception + { + _lock.lock(); + try + { + _consumer.setContext(ctx); + _ctx = ctx; + } + finally + { + _lock.unlock(); + } + ctx.sendUpstream(e); + } + + public boolean isStopEnabled() + { + return _stopEnabled ; + } + + public void setStopEnabled(boolean stopEnabled) + { + _stopEnabled = stopEnabled; + } + + public void stop() + { + _stop = true; + } + + + + + + +} diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/io/PushInputStreamConsumer.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/io/PushInputStreamConsumer.java new file mode 100644 index 0000000000..be59c7c439 --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/io/PushInputStreamConsumer.java @@ -0,0 +1,108 @@ +package org.rzo.netty.ahessian.io; + +import java.io.InputStream; +import java.util.concurrent.Executor; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +import org.jboss.netty.channel.ChannelHandlerContext; +import org.jboss.netty.channel.ChannelStateEvent; +import org.jboss.netty.channel.MessageEvent; +import org.jboss.netty.channel.SimpleChannelUpstreamHandler; +import org.rzo.netty.ahessian.Constants; +import org.rzo.netty.ahessian.utils.MyReentrantLock; + +public class PushInputStreamConsumer extends SimpleChannelUpstreamHandler +{ + + volatile Lock _lock = new MyReentrantLock(); + AtomicInteger _consumerThreadsCount = new AtomicInteger(0); + + volatile InputStreamConsumer _consumer; + volatile Executor _executor; + + public PushInputStreamConsumer(InputStreamConsumer consumer, Executor executor) + { + _consumer = consumer; + _executor = executor; + } + + @Override + public void messageReceived(final ChannelHandlerContext ctx, final MessageEvent evt) throws Exception + { + // input stream is consumed within a separate thread + // we return the current worker thread to netty, so that it may continue feeding the input stream + _executor.execute(new Runnable() + { + public void run() + { + String tName = Thread.currentThread().getName(); + try + { + PushInputStreamConsumer.this.run(ctx, evt); + } + finally + { + Thread.currentThread().setName(tName); + _consumerThreadsCount.decrementAndGet(); + } + } + }); + + } + + private void run(ChannelHandlerContext ctx, MessageEvent evt) + { + if (_consumer.isBufferEmpty()) + { + // we have nothing to consume + return; + } + + if (_consumerThreadsCount.incrementAndGet() > 2) + { + // there is already a thread consuming and another at the gate to consume the last chunk + _consumerThreadsCount.decrementAndGet(); + return; + } + + Thread.currentThread().setName("ahessian-PushInputStreamConsumer-#"+_consumerThreadsCount.get()); + + + // consume only with one thread at a time + _lock.lock(); + try + { + _consumer.consume(ctx, (InputStream)evt.getMessage()); + } + catch (Exception ex) + { + Constants.ahessianLogger.warn("", ex); + } + finally + { + _consumerThreadsCount.decrementAndGet(); + _lock.unlock(); + } + +} + + public void channelConnected( + ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception + { + _lock.lock(); + try + { + _consumer.setContext(ctx); + } + finally + { + _lock.unlock(); + } + ctx.sendUpstream(e); + } + + + +} diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/io/package-info.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/io/package-info.java new file mode 100644 index 0000000000..9dcce3d495 --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/io/package-info.java @@ -0,0 +1,9 @@ +/** + * Encoder, decoder which + * transforms a {@link ChannelBuffer} into a Stream and + * vice versa. + *
+ * Legacy frameworks which require InputStream and OutputStream, such as xml parsers, + * serializers, etc. can thus be easily integrated into netty. + */ +package org.rzo.netty.ahessian.io; \ No newline at end of file diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/log/OutLogger.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/log/OutLogger.java new file mode 100644 index 0000000000..4ce398878c --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/log/OutLogger.java @@ -0,0 +1,93 @@ +package org.rzo.netty.ahessian.log; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.jboss.netty.channel.ChannelEvent; +import org.jboss.netty.channel.DownstreamMessageEvent; +import org.jboss.netty.channel.ExceptionEvent; +import org.jboss.netty.channel.UpstreamMessageEvent; +import org.jboss.netty.handler.logging.LoggingHandler; +import org.rzo.netty.ahessian.Constants; + +public class OutLogger extends LoggingHandler +{ + String _name; + public OutLogger(String name) + { + super(name); + _name = name; + } + @Override + public void log(ChannelEvent e) + { + StringBuilder sb = new StringBuilder(); + + //System.out.println(e); + if (e instanceof DownstreamMessageEvent) + { + DownstreamMessageEvent devm = (DownstreamMessageEvent) e; + Object mes = devm.getMessage(); + sb.append('['); + sb.append(e.getChannel().getId()); + sb.append(" >out> "); + if (mes instanceof ChannelBuffer) + encodeBuffer((ChannelBuffer)((DownstreamMessageEvent)e).getMessage(), sb); + else + sb.append(mes.toString()); + } else + if (e instanceof UpstreamMessageEvent) + { + sb.append(e.getChannel().getId()); + sb.append(" = 0x20 && ch < 0x7f) { + sb.append((char) ch); + } + else + sb.append(String.format("\\x%02x", ch & 0xff)); + } + + public static String asString(byte[] buffer) + { + StringBuilder sb = new StringBuilder(); + if (buffer == null) + return "null"; + sb.append("("+buffer.length+") "); + for (int i=0; ihessian +serialization and rpc by using netty +as framework for the transport layer.
+Currently hessian2 rpc is supported.
+
+Dependencies:
+
+
    +
  • netty-3.2.0ALPHA2
  • +
  • hessian-4.0.2
  • +
  • servlet-api.jar (required by hessian, but not used)
  • +
+
+
+Major features
+
+
    +
  • transport of hessian serialized objects
  • +
  • synchronous & asynchronous client RPC proxy
  • +
  • single or multiple RPC execution threads per connection
  • +
  • support for server side continuations allowing architecture +similar to jetty continuations or servlet 3.0 suspend/resume. The +client may thus receive multiple results with a single rpc invoke
  • +
  • support for sessions allowing clients to invoke long +running RPC requests or continuations, disconnect and then reconnect to +get the results.
  • +
+
+
+TODO
+
+
    +
  • session timeout
  • +
  • invocation timeout
  • +
  • options for storing invocation results on the server and referring to them within subsequent invocations
  • +
+
+

+
+ */ +package org.rzo.netty.ahessian; \ No newline at end of file diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/callback/Callback.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/callback/Callback.java new file mode 100644 index 0000000000..8975131614 --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/callback/Callback.java @@ -0,0 +1,8 @@ +package org.rzo.netty.ahessian.rpc.callback; + +public interface Callback +{ + public void setDone(boolean value); + public boolean isDone(); + public boolean isValid(); +} diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/callback/CallbackReplyMessage.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/callback/CallbackReplyMessage.java new file mode 100644 index 0000000000..9769dca426 --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/callback/CallbackReplyMessage.java @@ -0,0 +1,44 @@ +package org.rzo.netty.ahessian.rpc.callback; + +import org.rzo.netty.ahessian.Constants; +import org.rzo.netty.ahessian.rpc.message.HessianRPCCallMessage; +import org.rzo.netty.ahessian.rpc.message.HessianRPCReplyMessage; + +public class CallbackReplyMessage extends HessianRPCReplyMessage implements Constants +{ + + private Object[] _args; + private String _method; + private boolean _done = false; + + + public CallbackReplyMessage(String method, Object[] args, Object fault, HessianRPCCallMessage message) + { + super(null, fault, message); + _args = args; + _method = method; + } + + public void setDone(Boolean done) + { + _done = done; + } + + + public Object[] getArgs() + { + return _args; + } + + + public String getMethod() + { + return _method; + } + + public boolean isDone() + { + return _done; + } + +} diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/callback/ClientCallback.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/callback/ClientCallback.java new file mode 100644 index 0000000000..ea91d08873 --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/callback/ClientCallback.java @@ -0,0 +1,64 @@ +package org.rzo.netty.ahessian.rpc.callback; + +import java.io.Serializable; +import java.lang.reflect.Method; +import java.util.concurrent.atomic.AtomicLong; + +import org.rzo.netty.ahessian.Constants; + + +public class ClientCallback implements Serializable +{ + + private transient Callback _callback; + private Long _id; + private String _callbackClass; + private static final AtomicLong _idCounter = new AtomicLong(); + private transient boolean _done = false; + + public ClientCallback() + { + + } + + + public ClientCallback(Callback callback) + { + _callback = callback; + _id = _idCounter.getAndIncrement(); + _callbackClass = _callback.getClass().getName(); + } + + public Long getId() + { + return _id; + } + + public void invoke(CallbackReplyMessage message) + { + try + { + String methodName = message.getMethod(); + Object[] args = message.getArgs(); + Method[] methods = _callback.getClass().getMethods(); + for (Method method : methods) + { + if (methodName.equals(method.getName()) && method.getParameterTypes().length == args.length) + { + method.invoke(_callback, args); + break; + } + } + } + catch (Exception ex) + { + Constants.ahessianLogger.warn("", ex); + } + } + + public String getCallbackClass() + { + return _callbackClass; + } + +} diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/callback/ServerCallbackProxy.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/callback/ServerCallbackProxy.java new file mode 100644 index 0000000000..1073765ff3 --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/callback/ServerCallbackProxy.java @@ -0,0 +1,59 @@ +package org.rzo.netty.ahessian.rpc.callback; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; + +import org.rzo.netty.ahessian.Constants; +import org.rzo.netty.ahessian.rpc.message.HessianRPCCallMessage; +import org.rzo.netty.ahessian.rpc.server.HessianRPCServiceHandler; + +public class ServerCallbackProxy implements InvocationHandler, Constants +{ + private boolean _done = false; + private HessianRPCServiceHandler _handler; + private HessianRPCCallMessage _message; + private ClientCallback _clientCallback; + boolean _closed = false; + + public ServerCallbackProxy(HessianRPCServiceHandler handler, HessianRPCCallMessage message, ClientCallback clientCallback) + { + _message = message; + _clientCallback = clientCallback; + _handler = handler; + } + + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable + { + if (_closed) + throw new RuntimeException("cannot invoke callback after call to setDone(true)"); + String methodName = method.getName(); + if ("setDone".equals(methodName) && args.length == 1 && (args[0] instanceof Boolean)) + { + _done = ((Boolean)args[0]).booleanValue(); + return null; + } + if ("isDone".equals(method.getName()) && (args == null || args.length == 0)) + { + return (Boolean)_done; + } + + if ("isValid".equals(method.getName()) && (args == null || args.length == 0)) + { + return (Boolean)_message.isValid(); + } + + CallbackReplyMessage reply = new CallbackReplyMessage(methodName, args, null, _message); + reply.setCallId((Long) _message.getHeaders().get(CALL_ID_HEADER_KEY)); + reply.setGroup((Integer) _message.getHeaders().get(GROUP_HEADER_KEY)); + reply.setCallbackId(_clientCallback.getId()); + reply.setCallbackArgs(args); + reply.setCallbackMethod(methodName); + if (_done) + reply.setCallbackDone(true); + _handler.writeResult(reply); + if (_done) + _closed = true; + return null; + } + +} diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/client/AsyncHessianProxy.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/client/AsyncHessianProxy.java new file mode 100644 index 0000000000..84cbe5346b --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/client/AsyncHessianProxy.java @@ -0,0 +1,190 @@ +package org.rzo.netty.ahessian.rpc.client; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.net.InetSocketAddress; +import java.util.HashMap; +import java.util.Map; +import java.util.WeakHashMap; +import java.util.concurrent.Future; + +import org.jboss.netty.channel.Channel; +import org.rzo.netty.ahessian.Constants; +import org.rzo.netty.ahessian.rpc.message.HessianRPCReplyMessage; + +import com.caucho.hessian4.services.server.AbstractSkeleton; + +/** + * A proxy object implementing asynchronous invocations. + * All invocations return a HessianProxyFuture + */ +public class AsyncHessianProxy implements InvocationHandler, Constants +{ + + private WeakHashMap _mangleMap = new WeakHashMap(); + private HessianProxyFactory _factory; + private Class _api; + private boolean _valid = true; + Map _options; + private Map _groups = new HashMap(); + + + /** + * Instantiates a new async hessian proxy. + * + * @param factory the factory + * @param api the api + * @param options the options + */ + AsyncHessianProxy(HessianProxyFactory factory, Class api, Map options) + { + _factory = factory; + _api = api; + _options = options; + } + + /** + * Gets the channel. + * + * @return the channel + */ + Channel getChannel() + { + if (!_valid) + throw new RuntimeException("invalidated proxy"); + return _factory.getChannel(); + } + + /* (non-Javadoc) + * @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object[]) + */ + public Object invoke(Object proxy, Method method, Object []args) + throws Throwable + { + String mangleName; + Channel channel = getChannel(); + + synchronized (_mangleMap) { + mangleName = _mangleMap.get(method); + } + + if (mangleName == null) { + String methodName = method.getName(); + Class []params = method.getParameterTypes(); + + // equals and hashCode are special cased + if (methodName.equals("equals") + && params.length == 1 && params[0].equals(Object.class)) { + Object value = args[0]; + if (value == null || ! Proxy.isProxyClass(value.getClass())) + return Boolean.FALSE; + + Object proxyHandler = Proxy.getInvocationHandler(value); + + if (! (proxyHandler instanceof AsyncHessianProxy)) + return Boolean.FALSE; + + AsyncHessianProxy handler = (AsyncHessianProxy) proxyHandler; + + return new Boolean(this.equals(handler)); + } + else if (methodName.equals("hashCode") && params.length == 0) + return new Integer(System.identityHashCode(this)); + else if (methodName.equals("getHessianType")) + return proxy.getClass().getInterfaces()[0].getName(); + else if (methodName.equals("getHessianURL")) + return channel == null ? "?" : channel.toString(); + else if (methodName.equals("toString") && params.length == 0) + return "HessianProxy[" + _api + "]"; + + if (! _factory.isOverloadEnabled()) + mangleName = method.getName(); + else + mangleName = mangleName(method); + + synchronized (_mangleMap) { + _mangleMap.put(method, mangleName); + } + } + Integer group = getGroup(mangleName, method.getName()); + return sendRequest(mangleName, args, group); + } + + private Integer getGroup(String mangleName, String name) + { + Integer result = _groups.get(mangleName); + if (result == null) + { + // get the group for the method + result = (Integer) _options.get("method.group."+mangleName); + if (result == null) + result = (Integer) _options.get("method.group."+name); + // if no group found set to default group 0 + if (result == null) + result = 0; + _groups.put(mangleName, result); + } + return result; + } + + protected Future sendRequest(String methodName, Object []args, Integer group) + throws InterruptedException + { + if (!_valid) + throw new RuntimeException("invalidated proxy"); + //Map options = new HashMap(_options); + Map options = new HashMap(); + options.put(GROUP_HEADER_KEY, group); + options.put(SERVICE_ID_HEADER_KEY, _options.get(SERVICE_ID_HEADER_KEY)); + return _factory.sendRequest(methodName, args, options); + } + + /** The id. */ + static volatile long id = 0; + + protected Map getHeaders() + { + Map result = new HashMap(); + result.put(CALL_ID_HEADER_KEY, id++); + return result; + + } + + + protected String mangleName(Method method) + { + Class []param = method.getParameterTypes(); + + if (param == null || param.length == 0) + return method.getName(); + else + return AbstractSkeleton.mangleName(method, false); + } + + private Future immediateFuture(Object result) + { + HessianProxyFuture future = new HessianProxyFuture(); + future.set(new HessianRPCReplyMessage(result, null, null)); + return future; + } + + protected void invalidate() + { + _valid = false; + } + + public String getHost() + { + Channel c = getChannel(); + if (c == null) + return null; + InetSocketAddress addr = (InetSocketAddress) c.getRemoteAddress(); + return addr.getHostName(); + } + + + + + +} diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/client/BootstrapProvider.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/client/BootstrapProvider.java new file mode 100644 index 0000000000..ba2e87d54b --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/client/BootstrapProvider.java @@ -0,0 +1,8 @@ +package org.rzo.netty.ahessian.rpc.client; + +import org.jboss.netty.bootstrap.ClientBootstrap; + +public interface BootstrapProvider +{ + public ClientBootstrap getBootstrap(); +} diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/client/HessianProxyFactory.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/client/HessianProxyFactory.java new file mode 100644 index 0000000000..d3bf9eb66c --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/client/HessianProxyFactory.java @@ -0,0 +1,626 @@ +package org.rzo.netty.ahessian.rpc.client; + +import java.io.InputStream; +import java.io.OutputStream; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Proxy; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.concurrent.Executor; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +import org.jboss.netty.channel.Channel; +import org.jboss.netty.channel.ChannelHandlerContext; +import org.jboss.netty.channel.ChannelPipeline; +import org.jboss.netty.channel.ChannelPipelineCoverage; +import org.jboss.netty.channel.ChannelStateEvent; +import org.jboss.netty.channel.ExceptionEvent; +import org.jboss.netty.channel.MessageEvent; +import org.jboss.netty.channel.SimpleChannelHandler; +import org.jboss.netty.util.HashedWheelTimer; +import org.jboss.netty.util.Timeout; +import org.jboss.netty.util.Timer; +import org.jboss.netty.util.TimerTask; +import org.rzo.netty.ahessian.Constants; +import org.rzo.netty.ahessian.rpc.io.Hessian2Input; +import org.rzo.netty.ahessian.rpc.io.Hessian2Output; +import org.rzo.netty.ahessian.rpc.message.HessianRPCCallMessage; +import org.rzo.netty.ahessian.rpc.message.HessianRPCReplyMessage; +import org.rzo.netty.ahessian.rpc.stream.ClientStreamManager; +import org.rzo.netty.ahessian.rpc.stream.InputStreamReplyMessage; +import org.rzo.netty.ahessian.session.ClientSessionFilter; +import org.rzo.netty.ahessian.utils.MyBlockingQueue; +import org.rzo.netty.ahessian.utils.MyLinkedBlockingQueue; +import org.rzo.netty.ahessian.utils.MyReentrantLock; +import org.rzo.netty.ahessian.utils.TimedBlockingPriorityQueue; + +import com.caucho.hessian4.io.AbstractHessianInput; +import com.caucho.hessian4.io.AbstractHessianOutput; +import com.caucho.hessian4.io.HessianRemoteObject; + +/** + * Handles client side hessian rpc proxy invocations and is a factory for + * service proxies.
+ * A typical setup for a protocol in a TCP/IP socket would be:
+ * + *
+ * Executor executor = ...
+ * HessianProxyFactory proxyFactory = ...
+ * 
+ * {@link ChannelPipeline} pipeline = ...;
+ * pipeline.addLast("inputStream", new InputStreamDecoder(_executor));
+ * pipeline.addLast("outputStream", new OutputStreamEncoder());        
+ * pipeline.addLast("hessianReplyDecoder", new HessianRPCReplyDecoder(_factory));
+ * pipeline.addLast("hessianCallEncoder", new HessianRPCCallEncoder());
+ * pipeline.addLast("hessianHandler", proxyFactory);
+ * 
+ * + *
+ * Typical usage within the client would be: + * + *
+ * 
+ * ClientBootstrap bootstrap = ...
+ * ChannelPipelineFactory pipelinetFactory = new ...(proxyFactory)
+ * bootstrap.setPipelineFactory(...)
+ * bootstrap.connect(...)
+ * 
+ * // get a service proxy 
+ * Map options = new HashMap();
+ * options.put("id", "myServiceName");
+ * // AsynchMyServiceInterface is an interface including the same methods as MyServiceInterface 
+ * //except that the return type is always of type HessianProxyFuture
+ * AsynchMyServiceInterface service = (AsynchMyServiceInterface) factory.create(AsynchMyServiceInterface.class, getClassLoader(), options);
+ * 
+ * // invoke a service method
+ * HessianProxyFuture future = service.myMethod();
+ * // wait for the result
+ * // if an exception is thrown by the server the exception is thrown by the call to the get() method 
+ * Object result = future.get();
+ * 
+ */ + +@ChannelPipelineCoverage("all") +public class HessianProxyFactory extends SimpleChannelHandler implements Constants +{ + private volatile Map> _openCalls = Collections + .synchronizedMap(new HashMap>()); + private volatile int _id = 0; + private volatile Channel _channel = null; + private volatile com.caucho.hessian4.client.HessianProxyFactory _factory = null; + private volatile MyBlockingQueue _pendingCalls; + + /** The _done listener. */ + Runnable _doneListener; + + /** The _executor. */ + Executor _executor; + private Lock _lock = new MyReentrantLock(); + private Condition _connected = _lock.newCondition(); + + /** The _stop. */ + boolean _stop = false; + private String _name; + private boolean _sessionListenerAdded = false; + private Runnable _closedSessionListener; + private Runnable _newSessionListener; + private Runnable _disconnectedListener; + private Runnable _connectedListener; + + Map _proxies = Collections + .synchronizedMap(new HashMap()); + + Timer _timer = new HashedWheelTimer(); + + private volatile boolean _blocked = false; + + ClientStreamManager _clientStreamManager; + /** + * Instantiates a new hessian proxy factory. + * + * @param executor + * the executor + */ + public HessianProxyFactory(Executor executor, String name) + { + this(executor, name, null, new HashMap()); + } + + public HessianProxyFactory(Executor executor, String name, Map options) + { + this(executor, name, null, options); + } + + public HessianProxyFactory(Executor executor, String name, ClassLoader loader, Map options) + { + _executor = executor; + _name = name; + if (options != null) + _pendingCalls = new TimedBlockingPriorityQueue(options, null, "HessianProxyFactory-PendingCalls"); + else + _pendingCalls = new MyLinkedBlockingQueue(); + if (loader == null) + _factory = new com.caucho.hessian4.client.HessianProxyFactory(); + else + _factory = new com.caucho.hessian4.client.HessianProxyFactory(loader); + /* + _executor.execute(new Runnable() + { + public void run() + { + Thread.currentThread().setName("HessianProxyFactory-Call-Tx"); + HessianRPCCallMessage message = null; + while (!_stop) + { + // if previous message sent + if (message == null) + try + { + message = _pendingCalls.take(); + } + catch (InterruptedException e1) + { + Constants.ahessianLogger.warn("", e1); + } + if (message == null) + continue; + _lock.lock(); + Channel channel = getChannel(); + while (channel == null || !channel.isConnected() && !_stop) + try + { + _connected.await(1000, TimeUnit.MILLISECONDS); + channel = getChannel(); + } + catch (InterruptedException e) + { + Constants.ahessianLogger.warn("", e); + } + _lock.unlock(); + if (!_stop && message != null && message.getMethod() != null) + try + { + ChannelFuture future = channel.write(message); + future.await(); + if (future.isSuccess()) + { +// if (_pendingCalls.size() == 0) +// channel.write(new Integer(0)); + message = null; + } + else + ahessianLogger.warn("cannot send message, will retry"); + } + catch (Exception ex) + { + Constants.ahessianLogger.warn("", ex); + } + else if (message.getMethod() == null) + message = null; + + } + + } + }); + */ + } + + /** + * Gets the hessian2 input. + * + * @param is + * the is + * + * @return the hessian2 input + */ + public AbstractHessianInput getHessian2Input(InputStream is) + { + return new Hessian2Input(is); + } + + public AbstractHessianOutput getHessian2Output(OutputStream out) + { + Hessian2Output out2 = new Hessian2Output(out); + out2.setSerializerFactory(_factory.getSerializerFactory()); + return out2; + } + + /** + * Checks if is overload enabled. + * + * @return true, if is overload enabled + */ + public boolean isOverloadEnabled() + { + return _factory.isOverloadEnabled(); + } + + /** + * Send request. + * + * @param methodName + * the method name + * @param args + * the args + * @param options + * the options + * + * @return the future< object> + * + * @throws InterruptedException + * the interrupted exception + */ + synchronized Future sendRequest(String methodName, Object[] args, Map options) throws InterruptedException + { + if (_blocked) + throw new RuntimeException("send blocked"); + if (_stop) + return null; + Map headers = options; + final Long id = new Long(_id); + _id++; + headers.put(CALL_ID_HEADER_KEY, id); + final HessianProxyFuture future = new HessianProxyFuture(); + future.handleCallbacks(args); + final HessianRPCCallMessage message = new HessianRPCCallMessage(methodName, args, headers, null); + _openCalls.put(id, future); + Integer g = (Integer) options.get("group"); + final Integer group = g == null ? 0 : g; + long timeout = _pendingCalls.getTimeout(group); + if (timeout > 0) + { + TimerTask task = new TimerTask() + { + + public void run(Timeout arg0) throws Exception + { + _openCalls.remove(id); + future.timedOut(); + } + + }; + future.setTimeout(_timer.newTimeout(task, timeout, TimeUnit.MILLISECONDS)); + } + while (getChannel() == null) + { + _lock.lock(); + try + { + _connected.await(1000, TimeUnit.MILLISECONDS); + } + finally + { + _lock.unlock(); + } + } + getChannel().write(message); + + return future; + } + + /* + * (non-Javadoc) + * + * @see + * org.jboss.netty.channel.SimpleChannelHandler#messageReceived(org.jboss + * .netty.channel.ChannelHandlerContext, + * org.jboss.netty.channel.MessageEvent) + */ + @Override + public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception + { + if (e.getMessage() instanceof HessianRPCReplyMessage) + { + final HessianRPCReplyMessage message = (HessianRPCReplyMessage) e.getMessage(); + { + final Long id = message.getCallId(); + if (id != null) + { + final HessianProxyFuture future = (HessianProxyFuture) _openCalls.get(id); + if (future == null) + { + ahessianLogger.warn("no future found for call-id " + id); + return; + } + if (message.getCompleted() == null || Boolean.TRUE.equals(message.getCompleted())) + if ((!future.hasCallbacks())) + { + _openCalls.remove(id); + } + if (_doneListener != null && _openCalls.isEmpty()) + _doneListener.run(); + if (future != null) + // setting message in future may fire listeners -> run + // in separate thread + _executor.execute(new Runnable() + { + public void run() + { + if (message.getValue() instanceof InputStreamReplyMessage) + { + InputStream stream = _clientStreamManager.newInputStream(((InputStreamReplyMessage)message.getValue()).getId()); + // caller should get a stream, not the reply + message.setValue(stream); + } + future.set(message); + // check in case this was a callback + if (future.isDone()) + if (!future.hasCallbacks()) + { + _openCalls.remove(id); + + } + } + }); + else + ahessianLogger.warn("no future for call reply " + id + " " + message.getValue()); + } + else + ahessianLogger.warn("message missing id " + message); + + } + } + else if (e.getMessage() instanceof InputStreamReplyMessage) + { + _clientStreamManager.messageReceived((InputStreamReplyMessage)e.getMessage()); + } + ctx.sendUpstream(e); + } + + /** + * Creates a service proxy. + * + * @param api + * the "asynched" api of the service + * @param loader + * the class loader for creating the proxy + * @param options + * the options + * + * @return the object + */ + public Object create(Class api, ClassLoader loader, Map options) + { + if (api == null) + throw new NullPointerException("api must not be null for HessianProxyFactory.create()"); + InvocationHandler handler = null; + + handler = new AsyncHessianProxy(this, api, options); + if (options.get("sync") != null) + handler = new SyncHessianProxy(handler); + + Object result = Proxy.newProxyInstance(loader, new Class[] + { api, HessianRemoteObject.class }, handler); + _proxies.put(result, handler); + return result; + + } + + public void returnProxy(Object proxy) + { + Object handler = _proxies.remove(proxy); + if (handler != null) + if (handler instanceof SyncHessianProxy) + handler = ((SyncHessianProxy) handler)._handler; + ((AsyncHessianProxy) handler).invalidate(); + } + + /** + * Gets the channel. + * + * @return the channel + */ + public Channel getChannel() + { + return _channel; + } + + /* + * (non-Javadoc) + * + * @see + * org.jboss.netty.channel.SimpleChannelHandler#channelConnected(org.jboss + * .netty.channel.ChannelHandlerContext, + * org.jboss.netty.channel.ChannelStateEvent) + */ + public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception + { + if (_connectedListener != null) + try + { + _connectedListener.run(); + } + catch (Throwable ex) + { + Constants.ahessianLogger.warn("", ex); + } + _lock.lock(); + try + { + if (!_sessionListenerAdded) + { + if (ctx.getPipeline().getContext(ClientSessionFilter.class) != null) + { + ClientSessionFilter sessionHandler = (ClientSessionFilter) ctx.getPipeline().getContext(ClientSessionFilter.class).getHandler(); + sessionHandler.addSessionClosedListener(new Runnable() + { + public void run() + { + _lock.lock(); + try + { + invalidateProxies(); + _openCalls.clear(); + + _pendingCalls.clear(); + } + finally + { + _lock.unlock(); + } + if (_closedSessionListener != null) + try + { + _closedSessionListener.run(); + } + catch (Throwable ex) + { + Constants.ahessianLogger.warn("", ex); + } + } + }); + sessionHandler.addSessionNewListener(new Runnable() + { + public void run() + { + if (_newSessionListener != null) + try + { + _newSessionListener.run(); + } + catch (Throwable ex) + { + Constants.ahessianLogger.warn("", ex); + } + } + }); + _sessionListenerAdded = true; + } + } + _channel = ctx.getChannel(); + super.channelConnected(ctx, e); + _connected.signal(); + } + finally + { + _lock.unlock(); + } + } + + /* + * (non-Javadoc) + * + * @see + * org.jboss.netty.channel.SimpleChannelHandler#channelDisconnected(org. + * jboss.netty.channel.ChannelHandlerContext, + * org.jboss.netty.channel.ChannelStateEvent) + */ + public void channelDisconnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception + { + _channel = null; + // _stop = true; + _lock.lock(); + try + { + _connected.signal(); + } + finally + { + _lock.unlock(); + } + // put something in the queue in case the worker thread hangs in + // _pendingCalls.take() + _pendingCalls.offer(new HessianRPCCallMessage(null, null, null, null)); + super.channelDisconnected(ctx, e); + if (_disconnectedListener != null) + try + { + _disconnectedListener.run(); + } + catch (Throwable ex) + { + Constants.ahessianLogger.warn("", ex); + } + + } + + /** + * Sets the done listener. This listener is fired whenever all requests have + * been completed + * + * @param listener + * the new listener + */ + public void setDoneListener(Runnable listener) + { + _doneListener = listener; + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) + { + ahessianLogger.warn("error accessing service " + _name + " Exception " + e.getCause().getClass() + " " + e.getCause().getMessage()); + ctx.getChannel().disconnect(); + ctx.getChannel().close(); + if (!_stop) + { + _channel = null; + // _stop = true; + _lock.lock(); + try + { + _connected.signal(); + } + finally + { + _lock.unlock(); + } + // put something in the queue in case the worker thread hangs in + // _pendingCalls.take() + _pendingCalls.offer(new HessianRPCCallMessage(null, null, null, null)); + } + } + + public void invalidateProxies() + { + for (Object proxy : new HashSet(_proxies.keySet())) + { + returnProxy(proxy); + } + } + + public void setClosedSessionListener(Runnable listener) + { + _closedSessionListener = listener; + } + + public void setDisconnectedListener(Runnable listener) + { + _disconnectedListener = listener; + } + + public void setConnectedListener(Runnable listener) + { + _connectedListener = listener; + } + + public void setNewSessionListener(Runnable listener) + { + _newSessionListener = listener; + } + + public void invalidateAllPendingCalls() + { + + final HessianRPCReplyMessage message = new HessianRPCReplyMessage(null, new RuntimeException("connection closed"), null); + for (Future future : new ArrayList(_openCalls.values())) + { + ((HessianProxyFuture) future).set(message); + } + _openCalls.clear(); + _pendingCalls.clear(); + } + + public void setBlocked(boolean blocked) + { + _blocked = blocked; + } + +} diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/client/HessianProxyFuture.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/client/HessianProxyFuture.java new file mode 100644 index 0000000000..be525a5418 --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/client/HessianProxyFuture.java @@ -0,0 +1,251 @@ +package org.rzo.netty.ahessian.rpc.client; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +import org.jboss.netty.util.Timeout; +import org.rzo.netty.ahessian.Constants; +import org.rzo.netty.ahessian.rpc.callback.Callback; +import org.rzo.netty.ahessian.rpc.callback.CallbackReplyMessage; +import org.rzo.netty.ahessian.rpc.callback.ClientCallback; +import org.rzo.netty.ahessian.rpc.message.HessianRPCReplyMessage; +import org.rzo.netty.ahessian.utils.MyReentrantLock; + +/** + * Future object returned when executing a remote method invocation. + *
+ * Note: within a continuation scenario, get() will return the last result received. + * Results sent by the server will override the result available on the client. + */ +public class HessianProxyFuture implements Future, Constants +{ + + /** indicates if the invocation has been completed */ + private boolean _done = false; + + /** TODO indicates if the invocation has been canceled or timed out. */ + private boolean _canceled = false; + + /** result of the invocation. */ + private HessianRPCReplyMessage _result = null; + + private Lock _lock = new MyReentrantLock(); + private Condition _resultReceived = _lock.newCondition(); + private Collection _listeners = Collections.synchronizedCollection(new ArrayList()); + private volatile Map _callbacks = Collections.synchronizedMap(new HashMap()); + private volatile Timeout _timeout = null; + + /* (non-Javadoc) + * @see java.util.concurrent.Future#cancel(boolean) + */ + + public boolean cancel(boolean mayInterruptIfRunning) + { + _canceled = true; + return true; + } + + /* (non-Javadoc) + * @see java.util.concurrent.Future#get() + */ + + public Object get() throws InterruptedException, ExecutionException + { + Object result = null; + _lock.lock(); + try + { + while (_result == null) + { + _resultReceived.await(); + } + if (_result.getFault() != null) + throw new ExecutionException(_result.getFault()); + else + return _result.getValue(); + } + finally + { + _lock.unlock(); + } + } + + /* (non-Javadoc) + * @see java.util.concurrent.Future#get(long, java.util.concurrent.TimeUnit) + */ + + public Object get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException + { + Object result = null; + _lock.lock(); + try + { + if (_result == null) + _resultReceived.await(timeout, unit); + if (_result == null) + throw new TimeoutException(); + if (_result.getFault() != null) + throw new ExecutionException(_result.getFault()); + else + return _result.getValue(); + } + finally + { + _lock.unlock(); + } + } + + /* (non-Javadoc) + * @see java.util.concurrent.Future#isCancelled() + */ + + public boolean isCancelled() + { + return _canceled; + } + + /* (non-Javadoc) + * @see java.util.concurrent.Future#isDone() + */ + + public boolean isDone() + { + return _done; + } + + protected synchronized void set(HessianRPCReplyMessage message) + { + _lock.lock(); + if (_timeout != null) + _timeout.cancel(); + try + { + if (message instanceof CallbackReplyMessage) + { + handleCallbackReply((CallbackReplyMessage)message); + } + else + { + _done = true; + _result = message; + _resultReceived.signal(); + callListners(); + } + } + finally + { + _lock.unlock(); + } + } + + private void handleCallbackReply(CallbackReplyMessage message) + { + Long callbackId = message.getCallbackId(); + if (callbackId == null) + return; + ClientCallback callback = _callbacks.get(callbackId); + if (callback == null) + { + System.out.println("no callback found for "+callbackId); + return; + } + callback.invoke(message); + if (message.isDone()) + { + _callbacks.remove(callbackId); + //System.out.println("removed callback "+callbackId); + } + + } + + private void callListners() + { + synchronized(_listeners) + { + for (Runnable listener : _listeners) + listener.run(); + } + } + + /** + * Adds the listener. + * + * @param listener the listener + */ + public void addListener(Runnable listener) + { + _lock.lock(); + try + { + if (isDone()) + listener.run(); + else + _listeners.add(listener); + } + finally + { + _lock.unlock(); + } + } + + /** + * Removes the listener. + * + * @param listener the listener + */ + public void removeListener(Runnable listener) + { + _listeners.remove(listener); + } + + public void handleCallbacks(Object[] args) + { + if (args == null) + return; + for (int i=0; i + * Typical usage: + * + *
+ * HessianProxyFactory proxyFactory = ...
+ * 
+ * Map options = new HashMap();
+ * options.put("id", "myServiceName");
+ * options.put("synch", Boolean.TRUE);
+ * 
+ * // AsynchMyServiceInterface is an interface including the same methods as MyServiceInterface 
+ * //except that the return type is always of type HessianProxyFuture
+ * AsynchMyServiceInterface service = (AsynchMyServiceInterface) factory.create(AsynchMyServiceInterface.class, getClassLoader(), options);
+ * 
+ * // invoke a service method and wait for the result
+ * Object result = service.myMethod();
+ * 
+ */ +public class SyncHessianProxy implements InvocationHandler +{ + + /** The _handler. */ + AsyncHessianProxy _handler; + long _defaultTimeout = -1; + + /** + * Instantiates a new sync hessian proxy. + * + * @param handler + * the handler + */ + SyncHessianProxy(InvocationHandler handler) + { + _handler = (AsyncHessianProxy) handler; + if (_handler._options != null && _handler._options.get("timeout") != null) + _defaultTimeout = ((Long)_handler._options.get("timeout")).longValue(); + } + + /* + * (non-Javadoc) + * + * @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object, + * java.lang.reflect.Method, java.lang.Object[]) + */ + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable + { + if ("equals".equals(method.getName()) || "hashCode".equals(method.getName()) || "getHessianType".equals(method.getName()) || "getHessianURL".equals(method.getName())) + return _handler.invoke(proxy, method, args); + if ("toString".equals(method.getName())) + return "Sync:"+_handler.invoke(proxy, method, args); + long timeout = getTimeout(method.getName()); + if (timeout > 0) + return ((Future)_handler.invoke(proxy, method, args)).get(timeout, TimeUnit.MILLISECONDS); + else + return ((Future)_handler.invoke(proxy, method, args)).get(); + } + + private long getTimeout(String method) + { + if (_handler._options != null && _handler._options.get("timeout."+method) != null) + return ((Long)_handler._options.get("timeout."+method)).longValue(); + return _defaultTimeout; + } + + +} diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/client/package-info.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/client/package-info.java new file mode 100644 index 0000000000..57ddc92cd9 --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/client/package-info.java @@ -0,0 +1,7 @@ +/** + * Provides hessian rpc client side handling + *
+ * {@link HessianProxyFactory} is the netty handler which handles + * client side hessian RPC proxy requests. + */ +package org.rzo.netty.ahessian.rpc.client; \ No newline at end of file diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/io/Hessian2Input.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/io/Hessian2Input.java new file mode 100644 index 0000000000..cff3146388 --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/io/Hessian2Input.java @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2001-2008 Caucho Technology, Inc. All rights reserved. + * + * The Apache Software License, Version 1.1 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Caucho Technology (http://www.caucho.com/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Hessian", "Resin", and "Caucho" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * info@caucho.com. + * + * 5. Products derived from this software may not be called "Resin" + * nor may "Resin" appear in their names without prior written + * permission of Caucho Technology. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Scott Ferguson + */ + +package org.rzo.netty.ahessian.rpc.io; + +import java.io.IOException; +import java.io.InputStream; +import java.util.HashMap; +import java.util.Map; + +import org.rzo.netty.ahessian.Constants; +import org.rzo.netty.ahessian.io.InputStreamBuffer; + + +/** + * The Class Hessian2Input. + */ +public class Hessian2Input + extends com.caucho.hessian4.io.Hessian2Input +{ + + boolean _closed = false; + /** + * Instantiates a new hessian2 input. + * + * @param is the is + */ + volatile InputStreamBuffer _isb; + public Hessian2Input(InputStream is) + { + super(is); + _isb = (InputStreamBuffer) is; + } + + public boolean bufferEmpty() + { + if (_isb == null) + return true; + try + { + return _isb.available() == 0 && _length <= _offset; + } + catch (IOException e) + { + Constants.ahessianLogger.warn("", e); + return true; + } + } + + public void close() throws IOException + { + _closed = true; + super.close(); + } + + public boolean isClosed() + { + return _closed; + } + + /** + * Read headers. + * + * @return the map + */ + public Map readHeaders() + { + Map result = new HashMap(); + String header = null; + try + { + header = readHeader(); + } + catch (IOException e) + { + Constants.ahessianLogger.warn("", e); + } + while (header != null) + { + try + { + result.put(header, readObject()); + header = readHeader(); + } + catch (IOException e) + { + Constants.ahessianLogger.warn("", e); + header = null; + } + } + return result; + } + + public InputStream getInputStream() + { + return _isb; + } + + + + +} diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/io/Hessian2Output.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/io/Hessian2Output.java new file mode 100644 index 0000000000..88517bb14d --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/io/Hessian2Output.java @@ -0,0 +1,169 @@ +package org.rzo.netty.ahessian.rpc.io; + +import java.io.IOException; +import java.io.OutputStream; +import java.lang.reflect.InvocationTargetException; +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; + +import org.rzo.netty.ahessian.Constants; +import org.rzo.netty.ahessian.rpc.message.HessianRPCCallMessage; +import org.rzo.netty.ahessian.rpc.message.HessianRPCReplyMessage; + +/** + * The Class HessianOutput. + */ +public class Hessian2Output extends com.caucho.hessian4.io.Hessian2Output implements Constants +{ + + /** + * Instantiates a new hessian output. + * + * @param out + * the out + */ + public Hessian2Output(OutputStream out) + { + super(out); + setCloseStreamOnClose(true); + } + + /** + * Write reply. + * + * @param message + * the message + * + * @throws IOException + * Signals that an I/O exception has occurred. + */ + public void writeReply(HessianRPCReplyMessage message) throws IOException + { + writeVersion(); + startEnvelope(HEADER_STRING); + writeHeaders(message); + if (message.getFault() == null) + { + startReply(); + writeObject(message.getValue()); + completeReply(); + } + else + { + Throwable fault = message.getFault(); + if (fault instanceof InvocationTargetException) + { + InvocationTargetException invi = (InvocationTargetException) fault; + Throwable inviFault = invi.getTargetException(); + if (inviFault != null) + this.writeFault(inviFault.getClass().getSimpleName(), inviFault.getMessage(), inviFault); + else + this.writeFault(fault.getClass().getSimpleName(), fault.getMessage(), fault); + } + else + this.writeFault(fault.getClass().getSimpleName(), fault.getMessage(), fault); + } + + completeEnvelope(); + + } + + /** + * Call. + * + * @param message + * the message + * + * @throws IOException + * Signals that an I/O exception has occurred. + */ + public void call(HessianRPCCallMessage message) throws IOException + { + String method = message.getMethod(); + Object[] args = message.getArgs(); + if (args == null) + args = new Object[0]; + Map headers = message.getHeaders(); + int length = args != null ? args.length : 0; + + writeVersion(); + startEnvelope(HEADER_STRING); + writeHeaders(message.getHeaders()); + // no packet read in Hessian2Input + // startPacket(); + startCall(method, args.length); + for (int i = 0; i < length; i++) + writeObject(args[i]); + completeCall(); + // endPacket(); + completeEnvelope(); + } + + private void writeHeaders(Map headers) + { + try + { + writeInt(headers.size()); + for (Iterator it = headers.entrySet().iterator(); it.hasNext(); ) + { + Entry entry = (Entry) it.next(); + writeInt((Integer)entry.getKey()); + writeObject(entry.getValue()); + } + } + catch (Exception ex) + { + Constants.ahessianLogger.warn("", ex); + } + + } + + private void writeHeaders(HessianRPCReplyMessage message) + { + try + { + writeInt(message.getHeadersCount()); + if (message.getCallbackArgs() != null) + { + writeInt(CALLBACK_ARGS_HEADER_KEY); + writeObject(message.getCallbackArgs()); + } + if (message.getCallbackDone() != null) + { + writeInt(CALLBACK_DONE_HEADER_KEY); + writeObject(message.getCallbackDone()); + } + if (message.getCallbackId() != null) + { + writeInt(CALLBACK_ID_HEADER_KEY); + writeObject(message.getCallbackId()); + } + if (message.getCallbackMethod() != null) + { + writeInt(CALLBACK_METHOD_HEADER_KEY); + writeObject(message.getCallbackMethod()); + } + if (message.getCallId() != null) + { + writeInt(CALL_ID_HEADER_KEY); + writeObject(message.getCallId()); + } + if (message.getCompleted() != null) + { + writeInt(COMPLETED_HEADER_KEY); + writeObject(message.getCompleted()); + } + if (message.getGroup() != null) + { + writeInt(GROUP_HEADER_KEY); + writeObject(message.getGroup()); + } + } + catch (Exception ex) + { + Constants.ahessianLogger.warn("", ex); + } + } + +} diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/io/package-info.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/io/package-info.java new file mode 100644 index 0000000000..7616dd5f7a --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/io/package-info.java @@ -0,0 +1,5 @@ +/** + * Extends classes from the package com.caucho.hessian.io, so that + * they can be used in a non-servlet context. + */ +package org.rzo.netty.ahessian.rpc.io; \ No newline at end of file diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/message/FlushRequestMessage.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/message/FlushRequestMessage.java new file mode 100644 index 0000000000..561142ee0b --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/message/FlushRequestMessage.java @@ -0,0 +1,6 @@ +package org.rzo.netty.ahessian.rpc.message; + +public class FlushRequestMessage +{ + +} diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/message/GroupedMessage.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/message/GroupedMessage.java new file mode 100644 index 0000000000..f223c6bea6 --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/message/GroupedMessage.java @@ -0,0 +1,7 @@ +package org.rzo.netty.ahessian.rpc.message; + +public interface GroupedMessage +{ + public Integer getGroup(); + +} diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/message/HessianRPCCallDecoder.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/message/HessianRPCCallDecoder.java new file mode 100644 index 0000000000..6f7ac08ec6 --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/message/HessianRPCCallDecoder.java @@ -0,0 +1,141 @@ +package org.rzo.netty.ahessian.rpc.message; + +import java.io.InputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.jboss.netty.channel.ChannelHandlerContext; +import org.jboss.netty.channel.Channels; +import org.rzo.netty.ahessian.Constants; +import org.rzo.netty.ahessian.io.InputStreamConsumer; +import org.rzo.netty.ahessian.io.InputStreamDecoder; +import org.rzo.netty.ahessian.io.OutputStreamEncoder; +import org.rzo.netty.ahessian.rpc.io.Hessian2Input; +import org.rzo.netty.ahessian.rpc.server.HessianRPCServiceHandler; +import org.rzo.netty.ahessian.session.ServerSessionFilter; + +import com.caucho.hessian4.io.AbstractSerializerFactory; +import com.caucho.hessian4.io.SerializerFactory; + +/** + * Decodes a call request from an input stream + */ +public class HessianRPCCallDecoder implements InputStreamConsumer, Constants +{ + + /** The _factory. */ + HessianRPCServiceHandler _factory; + SerializerFactory sFactory = new SerializerFactory(); + volatile Hessian2Input in = null; + + + public HessianRPCCallDecoder() + { + super(); + } + + public HessianRPCCallDecoder(AbstractSerializerFactory serializerFactory) + { + super(); + if (serializerFactory != null) + sFactory.addFactory(serializerFactory); + } + + + public void consume(ChannelHandlerContext ctx, InputStream inx) + { + HessianRPCCallMessage result = null; + boolean getNextMessage = true; + if (in == null || in.isClosed() || in.getInputStream() != inx) + { + in = new Hessian2Input(inx); + in.setSerializerFactory(sFactory); + } + while (ctx.getChannel().isConnected() && getNextMessage) + { + try + { + + if (in.bufferEmpty()) + { + // we have nothing to parse + break; + } + + int ch; + if ((ch=in.read()) != 'H') + { + ahessianLogger.warn("H expected got " + "0x" + Integer.toHexString(ch & 0xff) + " (" + (char) + ch + ")"); + continue; + } + in.read(); + in.read(); + in.readEnvelope(); + String h = in.readString(); + if (!HEADER_STRING.equals(h)) + { + ahessianLogger.warn("missing header"); + continue; + } + + Map headers = new HashMap(); + String methodName = null; + List values = new ArrayList(); + + int l = in.readInt(); + for (int i=0; i _headers; + transient OutputStreamEncoder _outputStreamEncoder; + boolean _hasSessionFilter = false; + transient boolean _isServer = false; + transient Session _session; + + /** + * Gets the headers. + * + * @return the headers + */ + public Map getHeaders() + { + return _headers; + } + + + /** + * Instantiates a new hessian rpc call message. + * + * @param method the method + * @param args the args + * @param headers the headers + */ + public HessianRPCCallMessage(String method, Object[] args, Map headers, OutputStreamEncoder outputStreamEncoder) + { + _method = method; + _args = args; + _headers = headers; + _outputStreamEncoder = outputStreamEncoder; + } + + + /** + * Gets the method. + * + * @return the method + */ + public String getMethod() + { + return _method; + } + + /** + * Gets the args. + * + * @return the args + */ + public Object[] getArgs() + { + return _args; + } + + public String toString() + { + StringBuffer sb = new StringBuffer(); + sb.append("HessianRPCCallMessage"); + if (_headers != null && _headers.get(CALL_ID_HEADER_KEY) != null) + { + sb.append('#'); + sb.append(_headers.get(CALL_ID_HEADER_KEY)); + } + sb.append('['); + sb.append(_method); + sb.append('('); + if (_args != null) + for (int i=0; i<_args.length; i++) + { + sb.append(_args[i].toString()); + if (i != _args.length-1) + sb.append(','); + } + sb.append(")]"); + return sb.toString(); + } + + public boolean isValid() + { + boolean result = false; + if (!_hasSessionFilter || !_isServer) + result = (_outputStreamEncoder != null && _outputStreamEncoder.getBuffer() != null && _outputStreamEncoder.getBuffer().getContext() != null && _outputStreamEncoder.getBuffer().getContext().getChannel().isConnected()); + else if (_outputStreamEncoder != null && _outputStreamEncoder.getBuffer() != null && _outputStreamEncoder.getBuffer().getContext() != null) + { + ServerSessionFilter session = ServerSessionFilter.getServerSessionFilter(_outputStreamEncoder.getBuffer().getContext()); + result = session == null || session.isValid(); + } + return result; + } + + public Channel getChannel() + { + if (_outputStreamEncoder != null && _outputStreamEncoder.getBuffer() != null && _outputStreamEncoder.getBuffer().getContext() != null) + return _outputStreamEncoder.getBuffer().getContext().getChannel(); + return null; + } + + + public void setHasSessionFilter(boolean hasSessionFilter) + { + _hasSessionFilter = hasSessionFilter; + _headers.put(HAS_SESSION_FILTER_HEADER_KEY, _hasSessionFilter); + } + + + public void setServer(boolean isServer) + { + _isServer = isServer; + } + + + public Integer getGroup() + { + if (_headers == null || _headers.get(GROUP_HEADER_KEY) == null) + return 0; + return (Integer) _headers.get(GROUP_HEADER_KEY); + } + + public void setSession(Session session) + { + _session = session; + } + + public Session getSession() + { + return _session; + } + +} diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/message/HessianRPCReplyDecoder.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/message/HessianRPCReplyDecoder.java new file mode 100644 index 0000000000..47cee11292 --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/message/HessianRPCReplyDecoder.java @@ -0,0 +1,202 @@ +package org.rzo.netty.ahessian.rpc.message; + +import java.io.InputStream; + +import org.jboss.netty.channel.ChannelHandlerContext; +import org.jboss.netty.channel.Channels; +import org.rzo.netty.ahessian.Constants; +import org.rzo.netty.ahessian.io.InputStreamConsumer; +import org.rzo.netty.ahessian.io.InputStreamDecoder; +import org.rzo.netty.ahessian.rpc.callback.CallbackReplyMessage; +import org.rzo.netty.ahessian.rpc.client.HessianProxyFactory; +import org.rzo.netty.ahessian.rpc.io.Hessian2Input; + +import com.caucho.hessian4.io.AbstractSerializerFactory; +import com.caucho.hessian4.io.HessianProtocolException; + +/** + * reads a reply message from an input stream + */ +public class HessianRPCReplyDecoder implements InputStreamConsumer, Constants +{ + + /** The _factory. */ + HessianProxyFactory _factory; + volatile Hessian2Input in = null; + volatile AbstractSerializerFactory _serializerFactory; + + /** + * Instantiates a new hessian rpc reply decoder. + * + * @param factory + * the factory + */ + public HessianRPCReplyDecoder(HessianProxyFactory factory) + { + _factory = factory; + } + + public HessianRPCReplyDecoder(HessianProxyFactory factory, AbstractSerializerFactory serializerFactory) + { + _factory = factory; + _serializerFactory = serializerFactory; + } + + public void consume(ChannelHandlerContext ctx, InputStream inx) + { + while (ctx.getChannel().isConnected() && !isBufferEmpty()) + { + Channels.fireMessageReceived(ctx, parseReply(in)); + in.resetReferences(); + + if (isBufferEmpty()) + { + break; + } + } + } + + /** + * Parses the reply. + * + * @param is + * the is + * + * @return the hessian rpc reply message + */ + HessianRPCReplyMessage parseReply(Hessian2Input in) + { + Object value = null; + Object fault = null; + Long callbackId = null; + String callbackMethod = null; + Object[] callbackArgs = null; + Boolean callbackDone = null; + Boolean completed = null; + Integer group = null; + Long callId = null; + int code; + try + { + if ((code = in.read()) != 'H') + { + throw new HessianProtocolException("'" + (char) code + "' is an unknown code"); + } + in.read(); + in.read(); + in.readEnvelope(); + String h = in.readString(); + if (!HEADER_STRING.equals(h)) + { + throw new HessianProtocolException("Missing headers"); + } + int l = in.readInt(); + for (int i = 0; i < l; i++) + { + Integer key = in.readInt(); + Object hvalue = in.readObject(); + switch (key) + { + + case ICALLBACK_ID_HEADER_KEY: + callbackId = (Long)hvalue; + break; + case ICALLBACK_METHOD_HEADER_KEY: + callbackMethod = (String) hvalue; + break; + case ICALLBACK_ARGS_HEADER_KEY: + callbackArgs = (Object[]) hvalue; + break; + case ICALLBACK_DONE_HEADER_KEY: + callbackDone = (Boolean) hvalue; + break; + case ICOMPLETED_HEADER_KEY: + completed = (Boolean) hvalue; + break; + case IGROUP_HEADER_KEY: + group = (Integer) hvalue; + break; + case ICALL_ID_HEADER_KEY: + callId = (Long) hvalue; + break; + } + } + if ((code = in.read()) != 'H') + { + throw new HessianProtocolException("'" + (char) code + "' is an unknown code"); + } + in.read(); + in.read(); + + Object obj = null; + try + { + obj = in.readReply(null); + } + catch (Throwable e) + { + HessianRPCReplyMessage result = new HessianRPCReplyMessage(null, e, null); + result .setCallId(callId); + result.setGroup(group); + result.setCallbackId(callbackId); + return result; + } + finally + { + in.completeReply(); + in.completeEnvelope(); + in.resetReferences(); + } + + if (callbackId != null) + { + + CallbackReplyMessage result = new CallbackReplyMessage(callbackMethod, callbackArgs, null, null); + result.setCallbackDone(callbackDone); + result.setCallbackId(callbackId); + result.setCallId(callId); + result.setCompleted(completed); + result.setGroup(group); + return result; + } + else + { + HessianRPCReplyMessage result = new HessianRPCReplyMessage(obj, null, null); + result.setCallId(callId); + result.setCompleted(completed); + result.setGroup(group); + return result; + } + + } + catch (Throwable ex) + { + Constants.ahessianLogger.warn("", ex); + { + HessianRPCReplyMessage result = new HessianRPCReplyMessage(null, ex, null); + result.setCallId(callId); + result.setGroup(group); + result.setCallbackId(callbackId); + return result; + + } + } + + } + + public boolean isBufferEmpty() + { + return in != null && in.bufferEmpty(); + } + + public void setContext(ChannelHandlerContext ctx) + { + if (in == null) + { + in = new Hessian2Input(InputStreamDecoder.getInputStream(ctx)); + if (_serializerFactory != null) + in.getSerializerFactory().addFactory(_serializerFactory); + } + } + +} diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/message/HessianRPCReplyEncoder.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/message/HessianRPCReplyEncoder.java new file mode 100644 index 0000000000..aedbb7012a --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/message/HessianRPCReplyEncoder.java @@ -0,0 +1,85 @@ +package org.rzo.netty.ahessian.rpc.message; + +import java.io.OutputStream; + +import org.jboss.netty.channel.ChannelHandlerContext; +import org.jboss.netty.channel.ChannelPipelineCoverage; +import org.jboss.netty.channel.ChannelStateEvent; +import org.jboss.netty.channel.MessageEvent; +import org.jboss.netty.channel.SimpleChannelHandler; +import org.rzo.netty.ahessian.Constants; +import org.rzo.netty.ahessian.io.OutputStreamEncoder; +import org.rzo.netty.ahessian.rpc.io.Hessian2Output; + +import com.caucho.hessian4.io.AbstractSerializerFactory; + +/** + * writes an invocation reply message to an output stream + */ +@ChannelPipelineCoverage("one") +public class HessianRPCReplyEncoder extends SimpleChannelHandler + { + volatile Hessian2Output hOut = null; + volatile AbstractSerializerFactory _serializerFactory; + + public HessianRPCReplyEncoder() + { + this(null); + } + + public HessianRPCReplyEncoder(AbstractSerializerFactory serializerFactory) + { + _serializerFactory = serializerFactory; + } + + /* (non-Javadoc) + * @see org.jboss.netty.channel.SimpleChannelDownstreamHandler#writeRequested(org.jboss.netty.channel.ChannelHandlerContext, org.jboss.netty.channel.MessageEvent) + */ + synchronized public void writeRequested(ChannelHandlerContext ctx, MessageEvent e) throws Exception + { + Object msg = e.getMessage(); + if (msg instanceof FlushRequestMessage) + { + hOut.flush(e.getFuture()); + e.getFuture().await(5000); + return; + } + + try + { +// if (e.getMessage() instanceof Integer) +// { +// hOut.flush(); +// return; +// } + HessianRPCReplyMessage message = (HessianRPCReplyMessage) e.getMessage(); + //Constants.ahessianLogger.warn("encode reply for #"+message.getHeaders().get(Constants.CALL_ID_STRING)); + + hOut.resetReferences(); + hOut.writeReply(message); + //hOut.flush(); + //e.getFuture().setSuccess(); + } + catch (Exception ex) + { + Constants.ahessianLogger.warn("", ex); + e.getFuture().setFailure(ex); + } + } + + public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception + { + if (hOut == null) + { + OutputStream out = OutputStreamEncoder.getOutputStream(ctx); + hOut = new Hessian2Output(out); + if (_serializerFactory != null) + hOut.getSerializerFactory().addFactory(_serializerFactory); + } + else + hOut.reset(); + ctx.sendUpstream(e); + } + + +} diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/message/HessianRPCReplyMessage.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/message/HessianRPCReplyMessage.java new file mode 100644 index 0000000000..f8ad2e8eb6 --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/message/HessianRPCReplyMessage.java @@ -0,0 +1,206 @@ +package org.rzo.netty.ahessian.rpc.message; + +import org.jboss.netty.channel.Channel; +import org.rzo.netty.ahessian.Constants; + +/** + * reply message for a remote invocation + */ +public class HessianRPCReplyMessage implements Constants, GroupedMessage +{ + + /** The _value. */ + Object _value; + + /** The _fault. */ + Throwable _fault; + + Long _callId; + Object[] _callbackArgs; + Boolean _callbackDone; + Long _callbackId; + String _callbackMethod; + Integer _group; + Boolean _completed; + int _headersCount = 0; + + + + transient HessianRPCCallMessage _call; + + /** + * Instantiates a new hessian rpc reply message. + * + * @param value the value + * @param fault the fault + * @param headers the headers + * @param channel the channel + */ + public HessianRPCReplyMessage(Object value, Object fault, HessianRPCCallMessage call) + { + _value = value; + _fault = (Throwable) fault; + _call = call; + } + + public HessianRPCReplyMessage() + { + // TODO Auto-generated constructor stub + } + + /** + * Gets the value. + * + * @return the value + */ + public Object getValue() + { + return _value; + } + + /** + * Gets the fault. + * + * @return the fault + */ + public Throwable getFault() + { + return _fault; + } + + public String toString() + { + StringBuffer sb = new StringBuffer(); + sb.append("HessianRPCReplyMessage"); + sb.append('#'); + sb.append(_callId); + return sb.toString(); + } + + public Channel getChannel() + { + return _call.getChannel(); + } + + public boolean isValid() + { + return _call.isValid(); + } + + public Long getCallId() + { + return _callId; + } + + public Object[] getCallbackArgs() + { + return _callbackArgs; + } + + public Boolean getCallbackDone() + { + return _callbackDone; + } + + public Long getCallbackId() + { + return _callbackId; + } + + public String getCallbackMethod() + { + return _callbackMethod; + } + + public Integer getGroup() + { + return _group; + } + + public void setCallId(Long callId) + { + if (callId != null) + { + _callId = callId; + _headersCount++; + } + } + + public void setCallbackArgs(Object[] callbackArgs) + { + if (callbackArgs != null) + { + _callbackArgs = callbackArgs; + _headersCount++; + } + } + + public void setCallbackDone(Boolean callbackDone) + { + if (callbackDone != null) + { + _callbackDone = callbackDone; + _headersCount++; + } + } + + public void setCallbackId(Long callbackId) + { + if (callbackId != null) + { + _callbackId = callbackId; + _headersCount++; + } + } + + public void setCallbackMethod(String callbackMethod) + { + if (callbackMethod != null) + { + _callbackMethod = callbackMethod; + _headersCount++; + } + } + + public void setGroup(Integer group) + { + if (group != null) + { + _group = group; + _headersCount++; + } + } + + public void setValue(Object value) + { + _value = value; + } + + public void setFault(Throwable fault) + { + _fault = fault; + } + + public Boolean getCompleted() + { + return _completed; + } + + public void setCompleted(Boolean completed) + { + if (completed != null) + { + _completed = completed; + _headersCount++; + } + } + + public int getHeadersCount() + { + return _headersCount; + } + + + + +} diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/message/MappingSerializerFactory.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/message/MappingSerializerFactory.java new file mode 100644 index 0000000000..c2ddcf37ed --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/message/MappingSerializerFactory.java @@ -0,0 +1,60 @@ +package org.rzo.netty.ahessian.rpc.message; + +import java.util.Map; + +import com.caucho.hessian4.io.Deserializer; +import com.caucho.hessian4.io.HessianProtocolException; +import com.caucho.hessian4.io.Serializer; +import com.caucho.hessian4.io.SerializerFactory; + +public class MappingSerializerFactory extends SerializerFactory +{ + Map _serializers; + Map _deserializers; + + + public MappingSerializerFactory(Map serializers, Map deserializers) + { + _serializers = serializers; + _deserializers = deserializers; + } + + @Override + protected Deserializer loadDeserializer(Class cl) throws HessianProtocolException + { + String type = getType(cl, _deserializers); + return (Deserializer) instantiate(type); + + } + + protected Serializer loadSerializer(Class cl) throws HessianProtocolException + { + String type = getType(cl, _serializers); + return (Serializer) instantiate(type); + } + + private String getType(Class cl, Map mapping) + { + if (mapping == null || cl == null) + return null; + return mapping.get(cl.getName()); + } + + private Object instantiate(String type) + { + if (type == null) + return null; + try + { + Class clazz = Class.forName(type); + Object result = clazz.newInstance(); + return result; + } + catch (Exception ex) + { + ex.printStackTrace(); + return null; + } + + } +} diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/message/Message.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/message/Message.java new file mode 100644 index 0000000000..6ed508b1e3 --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/message/Message.java @@ -0,0 +1,6 @@ +package org.rzo.netty.ahessian.rpc.message; + +public class Message +{ + +} diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/message/OutputProducer.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/message/OutputProducer.java new file mode 100644 index 0000000000..86fa634161 --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/message/OutputProducer.java @@ -0,0 +1,182 @@ +package org.rzo.netty.ahessian.rpc.message; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Timer; +import java.util.concurrent.Executor; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +import org.jboss.netty.channel.ChannelHandlerContext; +import org.jboss.netty.channel.ChannelStateEvent; +import org.jboss.netty.channel.Channels; +import org.jboss.netty.channel.MessageEvent; +import org.jboss.netty.channel.SimpleChannelHandler; +import org.rzo.netty.ahessian.stopable.StopableHandler; +import org.rzo.netty.ahessian.utils.MyReentrantLock; +import org.rzo.netty.ahessian.utils.TimedBlockingPriorityQueue; + + +public class OutputProducer extends SimpleChannelHandler implements StopableHandler +{ + private TimedBlockingPriorityQueue _pendingCalls = new TimedBlockingPriorityQueue("OutputProducer"); + + + AtomicInteger _producerThreadsCount = new AtomicInteger(0); + Lock _lock = new MyReentrantLock(); + + Executor _executor; + Timer _timer; + List _pendingTermination = new ArrayList(); + + volatile boolean _stop = false; + + public OutputProducer(Executor executor) + { + _executor = executor; + } + + public void writeRequested(final ChannelHandlerContext ctx, MessageEvent e) throws Exception + { + //System.out.println(Thread.currentThread()+ " OutputProducer writeRequesed"); + GroupedMessage m = (GroupedMessage) e.getMessage(); + _pendingCalls.put(e, m.getGroup()); + if (_producerThreadsCount.get() < 2) + _executor.execute(new Runnable() + { + + public void run() + { + produce(ctx); + } + + }); + + } + @Override + public void channelConnected(final ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception + { + ctx.sendUpstream(e); + _executor.execute(new Runnable() + { + public void run() + { + for (Iterator it = _pendingTermination.iterator(); it.hasNext(); ) + { + _lock.lock(); + try + { + if (_stop) + return; + MessageEvent e = (MessageEvent) it.next(); + GroupedMessage m = (GroupedMessage) e.getMessage(); + _pendingCalls.put(e, m.getGroup()); + } + catch (Exception ex) + { + ex.printStackTrace(); + } + finally + { + _lock.unlock(); + } + } + produce(ctx); + } + }); + } + + private void produce(ChannelHandlerContext ctx) + { + if (_stop) + return; + + if (_producerThreadsCount.incrementAndGet() > 2) + { + // there is already a thread consuming and another at the gate to consume the last chunk + _producerThreadsCount.decrementAndGet(); + return; + } + //System.out.println(Thread.currentThread()+" produce"); + boolean produced = false; + _lock.lock(); + try + { + MessageEvent toSend = null; + while (ctx.getChannel().isConnected() && _pendingCalls.size() > 0) + { + if (_stop) + return; + + try + { + toSend = _pendingCalls.take(); + //System.out.println(Thread.currentThread()+ " OutputProducer sendMessage"); + ctx.sendDownstream(toSend); + _pendingTermination.add(toSend); + produced = true; + } + catch (Exception ex) + { + ex.printStackTrace(); + _pendingCalls.put(toSend, ((GroupedMessage) toSend.getMessage()).getGroup()); + } + } + if (produced && _pendingCalls.size() == 0) + { + //System.out.println(Thread.currentThread()+ " OutputProducer flush"); + Channels.write(ctx, Channels.future(ctx.getChannel()), new FlushRequestMessage()); + for (Iterator it = _pendingTermination.iterator(); it.hasNext(); ) + { + if (_stop) + return; + + try + { + MessageEvent e = (MessageEvent) it.next(); + GroupedMessage m = (GroupedMessage) e.getMessage(); + it.remove(); + e.getFuture().setSuccess(); + } + catch (Exception ex) + { + ex.printStackTrace(); + } + } + } + + } + catch (Exception ex) + { + ex.printStackTrace(); + } + finally + { + _producerThreadsCount.decrementAndGet(); + _lock.unlock(); + } + + } + + public boolean isStopEnabled() + { + return true; + } + + public void setStopEnabled(boolean stopEnabled) + { + } + + public void stop() + { + _stop = true; + for (MessageEvent event : _pendingCalls) + { + event.getFuture().cancel(); + } + } + + +} diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/message/package-info.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/message/package-info.java new file mode 100644 index 0000000000..54e0efb4ff --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/message/package-info.java @@ -0,0 +1,10 @@ +/** + * Provides handling of messages required for the implementation of hessian rpc + *
+ * Refer to + *
{@link handler.hessian.rpc.server.HessianRPCServiceHandler} + *
and + *
{@link handler.hessian.rpc.client.HessianProxyFactory} + * + */ +package org.rzo.netty.ahessian.rpc.message; \ No newline at end of file diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/server/Continuation.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/server/Continuation.java new file mode 100644 index 0000000000..803ba66c5c --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/server/Continuation.java @@ -0,0 +1,89 @@ +package org.rzo.netty.ahessian.rpc.server; + +import java.util.Date; + +import org.rzo.netty.ahessian.session.Session; + +/** + * Continuation offers similar functionality as Jetty Continuations + *
+ * A continuation is a mechanism by which an RPC request can be suspended and restarted after a timeout or an asynchronous event has occured. + *
+ * Typical usage within a service: + *
+ * The service interface exposed to the client: + *
+ *
+ * public TableData getTableData(Filter filter)
+ * 
+ *
+ * The service object implementation: + *
+ *
+ * public TableData getTableData(Continuation continuation, Filter filter)
+ * {
+ *      if (requestOk(filter))
+ *      	// add client to client list
+ * 			addClient(continuation, filter, context);
+ *		else // or send an error
+ *		    // continuation.fault(new Exception...());
+ * }
+ * 
+ * void onTableDataChange()
+ * {
+ *   // when table data changes send the new data to all attached clients
+ *   for (client : clients)
+ *   {
+ *     TableData newData = ...
+ *     client.getContinuation().send(newData)
+ *   }
+ * }
+ * 
+ * void onTableClosed()
+ * {
+ *   // on shutdown inform all clients that the invocation is completed and no further
+ *   // data will be sent.
+ *   for (client : clients)
+ *   {
+ *     client.getContinuation().completed(null);
+ *   }
+ *   clients.reset();
+ * }
+
+ * 
+ * + */ +public interface Continuation +{ + + /** + * Send an invocation reply to the client + * + * @param result the result + */ + public void send(Object result); + + /** + * Send the last reply to the client and inform that this is the last reply for the invocation request + * + * @param result the result + */ + public void complete(Object result); + + /** + * Send an error to the client and inform that this is the last reply for the invocation request + * + * @param result the result + */ + public void fault(Throwable result); + + /** + * If the client has given us a time out for the invocation calling send, complete or fault after the given + * point in time will result in an exception + * + * @return the tTL + */ + public Date getTTL(); + + public Session getSession(); +} diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/server/ContinuationService.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/server/ContinuationService.java new file mode 100644 index 0000000000..c1069aee55 --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/server/ContinuationService.java @@ -0,0 +1,163 @@ +package org.rzo.netty.ahessian.rpc.server; + +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.Executor; +import java.util.concurrent.LinkedBlockingQueue; + +import org.jboss.netty.channel.ChannelHandlerContext; +import org.rzo.netty.ahessian.Constants; +import org.rzo.netty.ahessian.rpc.message.HessianRPCCallMessage; +import org.rzo.netty.ahessian.session.ServerSessionFilter; + +/** + * Wraps an object as a {@link Service}. Call requests are added to a queue. + * Calls are removed from the queue and invoked within a thread from the thread pool. + * If no free threads are available in the pool, execution hangs until a thread is available. + *
+ * This type of service is used for long running invocations or for invocations + * which require {@link Continuation}. + * They therefore allow for multiple transmissions as result for a single call. + *
+ * A typical usage could be a request for table data. Each response will send just the rows which have + * changed. Only one call request is required to trigger the continuous table update. + *
+ * Typical code: + *
+ * // The object to be wrapped, "kind of" implements MyServiceInterface
+ * // This object implements for each method of the service-api a method with the same name and arguments
+ * // However an extra argument as to be added to the signature.
+ * // As first argument of the method an argument of type {@link Continuation} must be added.
+ * // The continuation object is used to send the result to the client.
+ * // The result of the method invocation is not sent to the client. 
+ * Object myContinuationServiceObject = ...;
+ * 
+ * // the netty rpc service handler
+ * HessianRPCServiceHandler handler = ...;
+ * 
+ * Service myService = new ContinuationService(myServiceObject, MyServiceInterface.class);
+ * 
+ * // Clients will access the service through the given name
+ * handler.addService("myServiceName", myService);
+ * 
+ * 
+ */ +public class ContinuationService extends HessianSkeleton +{ + private LinkedBlockingQueue _pendingCalls = new LinkedBlockingQueue(); + + /** Thread pool for executing invocations. */ + private Executor _executor; + + /** TODO indicates if execution is stopped */ + private boolean _stop = false; + + /** optimize search for service methods by name */ + private Map _methodMap = new HashMap(); + + volatile private ChannelHandlerContext _ctx = null; + /** + * Instantiates a new continuation service. + * + * @param service the service object, "kind of" implementing the apiClass + * @param apiClass the api of the object exposed to the clients + * @param factory the netty handler + * @param executor the thread pool for executing invocations + */ + public ContinuationService(Object service, Class apiClass, HessianRPCServiceHandler factory, Executor executor) + { + super(service, apiClass, factory); + + Method []methodList = service.getClass().getMethods(); + + for (int i = 0; i < methodList.length; i++) { + Method method = methodList[i]; + + if (method.getParameterTypes().length > 0 && method.getParameterTypes()[0].equals(Continuation.class)) + { + String mangeledName = method.getName()+"__"+(method.getParameterTypes().length-1); + if (super.getMethod(mangeledName) != null) + _methodMap.put(mangeledName, method); + } + + + } + + + _executor = executor; + _executor.execute(new Runnable() + { + public void run() + { + HessianRPCCallMessage message; + while (!_stop) + { + message = null; + try + { + message = _pendingCalls.take(); + } + catch (InterruptedException e1) + { + Constants.ahessianLogger.warn("", e1); + } + if (message != null) + { + invoke(_ctx, message); + } + } + + } + }); + + } + + /* (non-Javadoc) + * @see org.rzo.netty.ahessian.rpc.server.HessianSkeleton#messageReceived(org.rzo.netty.ahessian.rpc.HessianRPCCallMessage) + */ + + public void messageReceived(HessianRPCCallMessage message) + { + _pendingCalls.add(message); + } + + private void invoke(ChannelHandlerContext ctx, final HessianRPCCallMessage message) + { + _executor.execute(new Runnable() + { + + public void run() + { + Continuation continuation = new DefaultContinuation(message, ContinuationService.this, ServerSessionFilter.getSession(_ctx)); + try + { + Method method = getMethod(message); + int l = message.getArgs() == null ? 1 : message.getArgs().length + 1; + Object[] args = new Object[l]; + if (args.length > 1) + System.arraycopy(message.getArgs(), 0, args, 1, message.getArgs().length); + args[0] = continuation; + method.invoke(_service, args); + } + catch (Throwable ex) + { + Constants.ahessianLogger.warn("", ex); + continuation.fault(ex); + } + } + }); + + } + + /* (non-Javadoc) + * @see org.rzo.netty.ahessian.rpc.server.HessianSkeleton#getMethod(org.rzo.netty.ahessian.rpc.HessianRPCCallMessage) + */ + public Method getMethod(HessianRPCCallMessage message) + { + String mangeledName = message.getMethod()+"__"+(message.getArgs().length); + return _methodMap.get(mangeledName); + } + + +} diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/server/DefaultContinuation.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/server/DefaultContinuation.java new file mode 100644 index 0000000000..31746014ff --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/server/DefaultContinuation.java @@ -0,0 +1,127 @@ +package org.rzo.netty.ahessian.rpc.server; + +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +import org.rzo.netty.ahessian.Constants; +import org.rzo.netty.ahessian.rpc.message.HessianRPCCallMessage; +import org.rzo.netty.ahessian.rpc.message.HessianRPCReplyMessage; +import org.rzo.netty.ahessian.session.Session; + +/** + * Default implementation of a {@link Continuation}. + */ +class DefaultContinuation implements Continuation, Constants +{ + + /** The service. */ + private ContinuationService _service; + + /** The call request message. */ + private HessianRPCCallMessage _message; + + /** The headers for reply message. */ + private Map _headers; + + /** Indicates if the continuation has been completed. */ + private boolean _completed = false; + + /** Time to live. */ + private Date _ttl; + + private Session _session = null; + + + /** + * Instantiates a new default continuation. + * + * @param message the message + * @param service the service + */ + DefaultContinuation(HessianRPCCallMessage message, ContinuationService service, Session session) + { + _service = service; + _message = message; + _headers = _message.getHeaders(); + if (_headers == null) + _headers = new HashMap(); + Long ttl = (Long) _headers.get("TTL"); + if (ttl == null) + _ttl = new Date(Long.MAX_VALUE); + else + _ttl = new Date(System.currentTimeMillis() + ttl.longValue()); + _headers.put(COMPLETED_HEADER_KEY, Boolean.FALSE); + _session = session; + } + + /* (non-Javadoc) + * @see org.rzo.netty.ahessian.rpc.server.Continuation#complete(java.lang.Object) + */ + + public void complete(Object result) + { + checkCompleted(); + _completed = true; + sendReply(result, null); + } + + /* (non-Javadoc) + * @see org.rzo.netty.ahessian.rpc.server.Continuation#fault(java.lang.Throwable) + */ + + public void fault(Throwable result) + { + checkCompleted(); + _completed = true; + sendReply(null, result); + } + + /* (non-Javadoc) + * @see org.rzo.netty.ahessian.rpc.server.Continuation#getTTL() + */ + + public Date getTTL() + { + return _ttl; + } + + /* (non-Javadoc) + * @see org.rzo.netty.ahessian.rpc.server.Continuation#send(java.lang.Object) + */ + + public void send(Object result) + { + checkCompleted(); + sendReply(result, null); + + } + + private void checkCompleted() + { + if (_completed ) + throw new RuntimeException("Continuation already completed"); + if (System.currentTimeMillis() > _ttl.getTime()) + { + _completed = true; + throw new RuntimeException("Continuation already completed"); + } + } + + private void sendReply(Object result, Object fault) + { + if (_completed) + _headers.put(COMPLETED_HEADER_KEY, Boolean.TRUE); + //TODO set reply headers + _service.writeResult(new HessianRPCReplyMessage(result, fault, _message)); + } + + public Session getSession() + { + return _session; + } + + + + +} diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/server/ExecutorInvokeService.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/server/ExecutorInvokeService.java new file mode 100644 index 0000000000..7b7614bde0 --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/server/ExecutorInvokeService.java @@ -0,0 +1,130 @@ +package org.rzo.netty.ahessian.rpc.server; + +import java.io.InputStream; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.Executor; + +import org.rzo.netty.ahessian.Constants; +import org.rzo.netty.ahessian.rpc.callback.ClientCallback; +import org.rzo.netty.ahessian.rpc.callback.ServerCallbackProxy; +import org.rzo.netty.ahessian.rpc.message.HessianRPCCallMessage; +import org.rzo.netty.ahessian.rpc.message.HessianRPCReplyMessage; + +/** + * Wraps an object as a {@link Service}. Methods are invoked as soon as they are received. + * Invocation and return of result are executed within a thread of the given Executor.
+ * This type of service is used for invocations with different execution durations. + * Invokation hangs if all threads in the pool are exhausted + *
+ * Typical usage: + *
+ * 
+ * // the object to be wrapped, implements MyServiceInterface
+ * Object myServiceObject; 
+ * 
+ * // the netty rpc service handler
+ * HessianRPCServiceHandler handler;
+ * Executor executor = Executors.newFixedThreadPool(200); 
+ * Service myService = new ExecutorInvokeService(myServiceObject, MyServiceInterface.class, executor);
+ * 
+ * // Clients will access the service through the given name
+ * handler.addService("myServiceName", myService);
+ * 
+ * 
+ */ + + public class ExecutorInvokeService extends HessianSkeleton implements Constants + { + //public static ThreadLocal threadLocalSession = new ThreadLocal(); + Executor _executor; + + /** + * Instantiates a new immediate invoke service. + * + * @param service the service object implementing apiClass + * @param apiClass the api of the service exposed to the client + * @param factory the netty handler + */ + public ExecutorInvokeService(Object service, Class apiClass, HessianRPCServiceHandler factory, Executor executor) + { + super(service, apiClass, factory); + _executor = executor; + } + + /* (non-Javadoc) + * @see org.rzo.netty.ahessian.rpc.server.HessianSkeleton#messageReceived(org.rzo.netty.ahessian.rpc.HessianRPCCallMessage) + */ + @Override + public void messageReceived(HessianRPCCallMessage message) + { + //threadLocalSession.set(ServerSessionFilter.getSession(ctx)); + invoke(message); + } + + /** + * Invokes the RPC call and sends back the result + * + * @param message the message + */ + void invoke(final HessianRPCCallMessage message) + { + _executor.execute(new Runnable() + { + + public void run() + { + Object result = null; + Object fault = null; + try + { + Method method = getMethod(message); + Object[] args = message.getArgs(); + if (args != null) + { + for (int i=0; i clazzes = new ArrayList(); + while (clazz != null && (!clazz.equals(Object.class))) + { + clazzes.addAll(Arrays.asList(clazz.getInterfaces())); + clazz = clazz.getSuperclass(); + } + args[i] = Proxy.newProxyInstance(cl, (Class[])clazzes.toArray(new Class[clazzes.size()]), new ServerCallbackProxy(_factory, message, (ClientCallback) args[i])); + } + } + } + ServiceSessionProvider.set(message.getSession()); + result = method.invoke(_service, args); + ServiceSessionProvider.remove(); + } + catch (Throwable ex) + { + ServiceSessionProvider.remove(); + Constants.ahessianLogger.warn("", ex); + fault = ex; + } + if (fault == null && result instanceof InputStream) + { + handleInputStreamResult(fault, result, message); + } + else + { + handleDefaultResult(fault, result, message); + } + } + + }); + } + + + +} diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/server/HessianRPCServiceHandler.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/server/HessianRPCServiceHandler.java new file mode 100644 index 0000000000..2867f79f82 --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/server/HessianRPCServiceHandler.java @@ -0,0 +1,414 @@ +package org.rzo.netty.ahessian.rpc.server; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.Executor; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +import org.jboss.netty.channel.Channel; +import org.jboss.netty.channel.ChannelHandlerContext; +import org.jboss.netty.channel.ChannelPipeline; +import org.jboss.netty.channel.ChannelPipelineCoverage; +import org.jboss.netty.channel.ChannelStateEvent; +import org.jboss.netty.channel.ExceptionEvent; +import org.jboss.netty.channel.MessageEvent; +import org.jboss.netty.channel.SimpleChannelUpstreamHandler; +import org.jboss.netty.util.Timer; +import org.rzo.netty.ahessian.Constants; +import org.rzo.netty.ahessian.rpc.message.HessianRPCCallMessage; +import org.rzo.netty.ahessian.rpc.message.HessianRPCReplyMessage; +import org.rzo.netty.ahessian.utils.MyReentrantLock; +import org.rzo.netty.ahessian.utils.TimedBlockingPriorityQueue; + +/** + * Handles server side hessian rpc calls.
+ * A typical setup for a protocol in a TCP/IP socket would be: + * + *
+ * {@link ChannelPipeline} pipeline = ...;
+ *  pipeline.addLast("inputStream", new InputStreamDecoder(_executor));
+ *  pipeline.addLast("outputStream", new OutputStreamEncoder());
+ *  pipeline.addLast("callDecoder", new HessianRPCCallDecoder());
+ *  pipeline.addLast("replyEncoder", new HessianRPCReplyEncoder());
+ *  HessianRPCServiceHandler handler =  new HessianRPCServiceHandler(executor);
+ *  {@link Service} service = ...
+ *  {@link Executor} executor = ...
+ *  factory.addService("default", new ContinuationService(service, ServiceApi.class, handler, executor));
+ *  pipeline.addLast("hessianRPCServer", handler);
+ * 
+ */ +@ChannelPipelineCoverage("one") +public class HessianRPCServiceHandler extends SimpleChannelUpstreamHandler implements Constants +{ + + /** maps service names to services. */ + private Map _services = new HashMap(); + /** queue of pending replies. */ + private TimedBlockingPriorityQueue _pendingReplies; + private TimedBlockingPriorityQueue _pendingCalls; + private LinkedBlockingQueue _repliesRetry; + + /** thread pool to get a thread to send the replies */ + private Executor _executor; + + /** TODO indicates that execution should be stopped */ + private boolean _stop = false; + + final AtomicLong _openCounter = new AtomicLong(0); + final Lock _lock = new MyReentrantLock(); + final Condition _channelOpen = _lock.newCondition(); + + public HessianRPCServiceHandler(Executor executor) + { + this(executor, null, null); + } + + /** + * Instantiates a new hessian rpc service handler. + * + * @param executor + * the thread pool to get a thread to send replies + */ + public HessianRPCServiceHandler(Executor executor, Map options, Timer timer) + { + _executor = executor; + if (options == null || timer == null) + _pendingReplies = new TimedBlockingPriorityQueue("HessianRPCServiceHandler-PendingReplies"); + else + _pendingReplies = new TimedBlockingPriorityQueue(options, null, "HessianRPCServiceHandler-PendingReplies"); + + if (options == null || timer == null) + _pendingCalls = new TimedBlockingPriorityQueue("HessianRPCServiceHandler-PendingCalls"); + else + _pendingCalls = new TimedBlockingPriorityQueue(options, null, "HessianRPCServiceHandler-PendingCalls"); + + _repliesRetry = new LinkedBlockingQueue(); + + _executor.execute(new Runnable() + { + public void run() + { + Thread.currentThread().setName("HessianRPCServiceHandler-Call-Rx"); + HessianRPCCallMessage message = null; + while (!_stop) + try + { + message = _pendingCalls.take(); + HessianSkeleton service = getService(message); + service.messageReceived(message); + } + catch (Exception ex) + { + Constants.ahessianLogger.warn("", ex); + } + } + }); +/* + _executor.execute(new Runnable() + { + public void run() + { + Thread.currentThread().setName("HessianRPCServiceHandler-Reply-Tx"); + while (!_stop) + { + if (_openCounter.get() == 0) + { + //System.out.println("waiting for an open channel"); + _lock.lock(); + try + { + _channelOpen.await(); + } + catch (InterruptedException e2) + { + Constants.ahessianLogger.warn("", e2); + } + finally + { + _lock.unlock(); + //System.out.println("got an open channel"); + } + } + HessianRPCReplyMessage message = null; + try + { + message = _pendingReplies.take(); + } + catch (InterruptedException e1) + { + Constants.ahessianLogger.warn("", e1); + } + if (message == null) + continue; + sendMessage(message); + + } + } + }); + */ + +/* + _executor.execute(new Runnable() + { + public void run() + { + Thread.currentThread().setName("HessianRPCServiceHandler-ReplyRetry-Tx"); + int counter = 0; + while (!_stop) + { + if (_openCounter.get() == 0) + { + //System.out.println("waiting for an open channel"); + _lock.lock(); + try + { + _channelOpen.await(); + } + catch (InterruptedException e2) + { + Constants.ahessianLogger.warn("", e2); + } + finally + { + _lock.unlock(); + //System.out.println("got an open channel"); + } + } + HessianRPCReplyMessage message = null; + try + { + message = _repliesRetry.take(); + } + catch (InterruptedException e1) + { + Constants.ahessianLogger.warn("", e1); + } + if (message == null) + continue; + if (counter == 0) + counter = _repliesRetry.size()+1; + sendMessage(message); + if (counter-- == 0) + { + try + { + Thread.sleep(1000); + } + catch (InterruptedException e) + { + e.printStackTrace(); + } + } + + } + } + }); +*/ + } + + + + protected void sendMessage(HessianRPCReplyMessage message) + { + Channel ch = message.getChannel(); + if (ch != null) + ch.write(message); + else + ahessianLogger.warn("message channel null -> ignored: #"+message.getCallId()); + /* + //ahessianLogger.warn("send reply for #"+message.getHeaders().get(CALL_ID_STRING)); + if (message.isValid()) + { + final Channel ch = message.getChannel(); + final HessianRPCReplyMessage mess = message; + if (ch != null && ch.isConnected()) + { + _executor.execute(new Runnable() + { + + public void run() + { + //ahessianLogger.info("new thread for #"+mess.getHeaders().get(CALL_ID_STRING)); + String tName = Thread.currentThread().getName(); + //Thread.currentThread().setName("HessianRPCServiceHandler-Reply-Encod"); + try + { + ChannelFuture future = ch.write(mess); + future.await(); + +// if (_pendingCalls.size() == 0) +// { +// ch.write(new Integer(0)); +// } + } + catch (Exception ex) + { + Constants.ahessianLogger.warn("", ex); + try + { + ahessianLogger.warn("message write threw an exception, retry later: #"+mess.getCallId()); + _pendingReplies.put(mess); + } + catch (InterruptedException e) + { + Constants.ahessianLogger.warn("", e); + } + + } + //Thread.currentThread().setName(tName); + } + }); + } + else + { + try + { + ahessianLogger.warn("cannot send message, channel closed, retry later: #"+message.getCallId()); + _repliesRetry.put(message); + } + catch (InterruptedException e) + { + Constants.ahessianLogger.warn("", e); + } + } + } + else + { + ahessianLogger.warn("message invalid -> igonore: #"+message.getCallId()); + } + */ + } + + /** + * Adds a service to the handler. + * + * @param name + * the name of the service + * @param service + * the service wrapper + */ + public void addService(String name, HessianSkeleton service) + { + _services.put(name, service); + } + + /** + * Removes a service. + * + * @param name + * the name + */ + public void removeService(String name) + { + _services.remove(name); + } + + /* + * (non-Javadoc) + * + * @see + * org.jboss.netty.channel.SimpleChannelUpstreamHandler#messageReceived( + * org.jboss.netty.channel.ChannelHandlerContext, + * org.jboss.netty.channel.MessageEvent) + */ + @Override + public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception + { + HessianRPCCallMessage message = (HessianRPCCallMessage) e.getMessage(); + Integer group = (Integer) message.getHeaders().get(Constants.GROUP_HEADER_KEY); + _pendingCalls.put(message, group); + + } + + private HessianSkeleton getService(HessianRPCCallMessage message) + { + String id = (String) message.getHeaders().get(SERVICE_ID_HEADER_KEY); + if (id == null) + id = "default"; + return _services.get(id); + } + + public void writeResult(HessianRPCReplyMessage message) + { + /* + try + { + Integer group = message.getGroup(); + if (group == null) + _pendingReplies.put(message); + else + _pendingReplies.put(message, group.intValue()); + } + catch (InterruptedException e) + { + Constants.ahessianLogger.warn("", e); + } + */ + sendMessage(message); + } + + /* + * (non-Javadoc) + * + * @see + * org.jboss.netty.channel.SimpleChannelUpstreamHandler#channelConnected + * (org.jboss.netty.channel.ChannelHandlerContext, + * org.jboss.netty.channel.ChannelStateEvent) + */ + public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception + { + ahessianLogger.warn(ctx.getChannel()+" connected"); + if (_openCounter.incrementAndGet() == 1) + { + _lock.lock(); + try + { + _channelOpen.signal(); + } + catch (Exception ex) + { + + } + finally + { + _lock.unlock(); + } + } + + + super.channelOpen(ctx, e); + } + + /* + * (non-Javadoc) + * + * @see + * org.jboss.netty.channel.SimpleChannelUpstreamHandler#channelDisconnected + * (org.jboss.netty.channel.ChannelHandlerContext, + * org.jboss.netty.channel.ChannelStateEvent) + */ + public void channelDisconnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception + { + ahessianLogger.warn(ctx.getChannel()+" disconnected"); + + _openCounter.decrementAndGet(); + super.channelClosed(ctx, e); + } + + @Override + public void exceptionCaught( + ChannelHandlerContext ctx, ExceptionEvent e) throws Exception + { + ahessianLogger.warn(ctx.getChannel()+" exception "+ e.getCause()); + } + //TODO + public void stop() + { + _stop = true; + } + +} diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/server/HessianSkeleton.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/server/HessianSkeleton.java new file mode 100644 index 0000000000..0992ad88fb --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/server/HessianSkeleton.java @@ -0,0 +1,120 @@ +package org.rzo.netty.ahessian.rpc.server; + +import java.io.InputStream; +import java.lang.reflect.Method; + +import org.rzo.netty.ahessian.Constants; +import org.rzo.netty.ahessian.rpc.message.HessianRPCCallMessage; +import org.rzo.netty.ahessian.rpc.message.HessianRPCReplyMessage; +import org.rzo.netty.ahessian.rpc.stream.InputStreamReplyMessage; +import org.rzo.netty.ahessian.rpc.stream.ServerInputStream; +import org.rzo.netty.ahessian.rpc.stream.ServerInputStreamManager; + +/** + * The Class HessianSkeleton, extends the original HessianSkeleton, + * so that it can be used in a non-servlet environment + * + * TODO method name overloading. currently only overloading by number of arguments is supported + */ +public abstract class HessianSkeleton extends com.caucho.hessian4.server.HessianSkeleton implements Service, Constants +{ + + /** The _service. */ + Object _service; + + /** The _factory. */ + HessianRPCServiceHandler _factory; + + ServerInputStreamManager _serverInputStreamManager; + + /** + * Instantiates a new hessian skeleton. + * + * @param service the service + * @param apiClass the api class + * @param factory the factory + */ + public HessianSkeleton(Object service, Class apiClass, HessianRPCServiceHandler factory) + { + super(apiClass); + _service = service; + _factory = factory; + } + + + /** + * Gets the method. + * + * @param message the message + * + * @return the method + */ + public Method getMethod(HessianRPCCallMessage message) + { + Object[] args = message.getArgs(); + String methodName = message.getMethod(); + Method method = null; + if (args == null || args.length == 0) + method = getMethod(mangleName(methodName, args)); + if (method == null) + method = getMethod(message.getMethod()); + return method; + } + + public static String mangleName(String method, Object[] args) + { + if (args != null && args.length > 0) + { + StringBuffer sb = new StringBuffer(); + sb.append(method); + sb.append('_'); + sb.append(args.length); + return sb.toString(); + } + return method; + } + + /** + * Write result. + * + * @param message the message + */ + public void writeResult(HessianRPCReplyMessage message) + { + _factory.writeResult(message); + } + + public void handleDefaultResult(Object fault, Object result, HessianRPCCallMessage message) + { + HessianRPCReplyMessage reply = new HessianRPCReplyMessage(result, fault, message); + reply.setCompleted(true); + reply.setCallId((Long) message.getHeaders().get(CALL_ID_HEADER_KEY)); + reply.setGroup((Integer) message.getHeaders().get(GROUP_HEADER_KEY)); + writeResult(reply); + } + + public void handleInputStreamResult(Object fault, Object result, HessianRPCCallMessage message) + { + ServerInputStream serverInputStream = _serverInputStreamManager.createServerInputStream((InputStream) result, message.getChannel()); + InputStreamReplyMessage internalReply = new InputStreamReplyMessage(); + internalReply.setId(serverInputStream.getId()); + internalReply.setCreated(true); + HessianRPCReplyMessage reply = new HessianRPCReplyMessage(result, fault, message); + reply.setCompleted(true); + reply.setCallId((Long) message.getHeaders().get(CALL_ID_HEADER_KEY)); + reply.setGroup((Integer) message.getHeaders().get(GROUP_HEADER_KEY)); + writeResult(reply); + serverInputStream.start(); + } + + /* (non-Javadoc) + * @see org.rzo.netty.ahessian.rpc.server.Service#messageReceived(org.rzo.netty.ahessian.rpc.HessianRPCCallMessage) + */ + abstract public void messageReceived(HessianRPCCallMessage message); + + public void setServerInputStreamManager(ServerInputStreamManager serverInputStreamManager) + { + _serverInputStreamManager = serverInputStreamManager; + } + +} diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/server/ImmediateInvokeService.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/server/ImmediateInvokeService.java new file mode 100644 index 0000000000..c8b5c44bb8 --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/server/ImmediateInvokeService.java @@ -0,0 +1,114 @@ +package org.rzo.netty.ahessian.rpc.server; + +import java.io.InputStream; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.rzo.netty.ahessian.Constants; +import org.rzo.netty.ahessian.rpc.callback.ClientCallback; +import org.rzo.netty.ahessian.rpc.callback.ServerCallbackProxy; +import org.rzo.netty.ahessian.rpc.message.HessianRPCCallMessage; +import org.rzo.netty.ahessian.rpc.message.HessianRPCReplyMessage; +import org.rzo.netty.ahessian.rpc.stream.ServerInputStreamManager; + +/** + * Wraps an object as a {@link Service}. Methods are invoked as soon as they are received. + * Invocation and return of result are executed within the netty worker thread.
+ * This type of service is used for short running invocations. + *
+ * Typical usage: + *
+ * 
+ * // the object to be wrapped, implements MyServiceInterface
+ * Object myServiceObject; 
+ * 
+ * // the netty rpc service handler
+ * HessianRPCServiceHandler handler;
+ * 
+ * Service myService = new ImmediateInvokeService(myServiceObject, MyServiceInterface.class);
+ * 
+ * // Clients will access the service through the given name
+ * handler.addService("myServiceName", myService);
+ * 
+ * 
+ */ +public class ImmediateInvokeService extends HessianSkeleton implements Constants +{ + + /** + * Instantiates a new immediate invoke service. + * + * @param service the service object implementing apiClass + * @param apiClass the api of the service exposed to the client + * @param factory the netty handler + */ + public ImmediateInvokeService(Object service, Class apiClass, HessianRPCServiceHandler factory) + { + super(service, apiClass, factory); + } + + /* (non-Javadoc) + * @see org.rzo.netty.ahessian.rpc.server.HessianSkeleton#messageReceived(org.rzo.netty.ahessian.rpc.HessianRPCCallMessage) + */ + @Override + public void messageReceived(HessianRPCCallMessage message) + { + ServiceSessionProvider.set(message.getSession()); + invoke(message); + ServiceSessionProvider.remove(); + } + + /** + * Invokes the RPC call and sends back the result + * + * @param message the message + */ + void invoke(HessianRPCCallMessage message) + { + Object result = null; + Object fault = null; + try + { + Method method = getMethod(message); + Object[] args = message.getArgs(); + if (args != null) + { + for (int i=0; i clazzes = new ArrayList(); + while (clazz != null && (!clazz.equals(Object.class))) + { + clazzes.addAll(Arrays.asList(clazz.getInterfaces())); + clazz = clazz.getSuperclass(); + } + args[i] = Proxy.newProxyInstance(cl, (Class[])clazzes.toArray(new Class[clazzes.size()]), new ServerCallbackProxy(_factory, message, (ClientCallback) args[i])); + } + } + } + result = method.invoke(_service, args); + } + catch (Throwable ex) + { + Constants.ahessianLogger.warn("", ex); + fault = ex; + } + if (fault == null && result instanceof InputStream) + { + handleInputStreamResult(fault, result, message); + } + else + { + handleDefaultResult(fault, result, message); + } + } + + +} diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/server/Service.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/server/Service.java new file mode 100644 index 0000000000..f39aa7b184 --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/server/Service.java @@ -0,0 +1,25 @@ +package org.rzo.netty.ahessian.rpc.server; + +import org.rzo.netty.ahessian.rpc.message.HessianRPCCallMessage; + +/** + * A service wraps an object, so that it can be used as a service on the hessian RPC server.
+ * Currently 2 types of services are implemented: + * + *
    + *
  • {@link ImmediateInvokeService}
  • + *
  • {@link ContinuationService}
  • + *
+ * + */ +public interface Service +{ + + /** + * Handle a hessian RPC call request + * + * @param message + * the call request message + */ + public void messageReceived(HessianRPCCallMessage message); +} diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/server/ServiceSessionProvider.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/server/ServiceSessionProvider.java new file mode 100644 index 0000000000..1e626aec7e --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/server/ServiceSessionProvider.java @@ -0,0 +1,28 @@ +package org.rzo.netty.ahessian.rpc.server; + +import org.rzo.netty.ahessian.session.ServiceSession; +import org.rzo.netty.ahessian.session.Session; + +public class ServiceSessionProvider +{ + private static ThreadLocal threadLocalSession = new ThreadLocal(); + + public static ServiceSession getSession() + { + return threadLocalSession.get(); + } + + protected static void set(ServiceSession session) + { + threadLocalSession.set(session); + } + protected static void remove() + { + if (threadLocalSession.get() == null) + return; + if (threadLocalSession.get().isNew()) + ((Session)threadLocalSession.get()).setNew(false); + threadLocalSession.remove(); + } + +} diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/server/package-info.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/server/package-info.java new file mode 100644 index 0000000000..b24652148a --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/server/package-info.java @@ -0,0 +1,7 @@ +/** + * Provides hessian rpc server side handling + *
+ * {@link HessianRPCServiceHandler} is the netty handler which handles + * server side hessian RPC requests. + */ +package org.rzo.netty.ahessian.rpc.server; \ No newline at end of file diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/stream/ClientInputStream.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/stream/ClientInputStream.java new file mode 100644 index 0000000000..8b99263893 --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/stream/ClientInputStream.java @@ -0,0 +1,48 @@ +package org.rzo.netty.ahessian.rpc.stream; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.Buffer; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.jboss.netty.buffer.ChannelBuffers; +import org.rzo.netty.ahessian.io.InputStreamBuffer; + +public class ClientInputStream extends InputStreamBuffer +{ + private long _id; + + public ClientInputStream(long id) + { + _id = id; + } + + public void addMessage(InputStreamReplyMessage msg) + { + if (msg.isClosed()) + try + { + closeOnEmpty(); + } + catch (IOException e1) + { + e1.printStackTrace(); + } + else + try + { + this.write(ChannelBuffers.wrappedBuffer(msg.getData())); + } + catch (IOException e) + { + e.printStackTrace(); + } + } + + public long getId() + { + return _id; + } + + +} diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/stream/ClientStreamManager.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/stream/ClientStreamManager.java new file mode 100644 index 0000000000..dd9d8929a9 --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/stream/ClientStreamManager.java @@ -0,0 +1,36 @@ +package org.rzo.netty.ahessian.rpc.stream; + +import java.io.InputStream; +import java.util.HashMap; +import java.util.Map; + +public class ClientStreamManager +{ + Map _streams = new HashMap(); + + public synchronized InputStream newInputStream(long id) + { + ClientInputStream result = new ClientInputStream(id); + _streams.put(id, result); + return result; + } + + public synchronized void removeInputStream(ClientInputStream stream) + { + _streams.remove(stream.getId()); + } + + public synchronized void messageReceived(InputStreamReplyMessage msg) + { + ClientInputStream stream; + if (msg.isClosed()) + stream = _streams.remove(msg.getId()); + else + stream = _streams.get(msg.getId()); + if (stream != null) + stream.addMessage(msg); + else + System.out.println("message for non existing stream "+msg.getId()); + } + +} diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/stream/InputStreamReplyMessage.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/stream/InputStreamReplyMessage.java new file mode 100644 index 0000000000..a6c8227b63 --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/stream/InputStreamReplyMessage.java @@ -0,0 +1,38 @@ +package org.rzo.netty.ahessian.rpc.stream; + +public class InputStreamReplyMessage +{ + private long _id = -1; + private byte[] _data = null; + private boolean _closed = false; + private boolean _created = false; + + public void setId(long id) + { + _id = id; + } + public void setData(byte[] data) + { + _data = data; + } + public void setClosed(boolean b) + { + _closed = b; + } + public boolean isClosed() + { + return _closed; + } + public byte[] getData() + { + return _data; + } + public long getId() + { + return _id; + } + public void setCreated(boolean b) + { + _created = b; + } +} diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/stream/ServerInputStream.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/stream/ServerInputStream.java new file mode 100644 index 0000000000..5a6c915d36 --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/stream/ServerInputStream.java @@ -0,0 +1,162 @@ +package org.rzo.netty.ahessian.rpc.stream; + +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.Executor; +import java.util.concurrent.atomic.AtomicBoolean; + +import org.jboss.netty.channel.Channel; + +public class ServerInputStream +{ + static final int BATCH_SIZE = 1000; + private InputStream _in; + private Executor _executor; + private Channel _channel; + private long _id; + private static ServerInputStreamBoss _boss = new ServerInputStreamBoss(); + static {_boss.start();} + private AtomicBoolean _isDoingStream = new AtomicBoolean(false); + + + private static class ServerInputStreamBoss extends Thread implements Runnable + { + private List _streams = Collections.synchronizedList(new ArrayList()); + + public void add(ServerInputStream stream) + { + _streams.add(stream); + } + + public void remove(ServerInputStream stream) + { + _streams.remove(stream); + } + + public void run() + { + // TODO optimize, so we do not have a thread running for nothing + while (true) + { + synchronized(_streams) + { + for (ServerInputStream stream : _streams) + { + stream.doStream(); + } + } + try + { + Thread.sleep(500); + } + catch (Exception ex) + { + + } + } + } + + } + + public ServerInputStream(InputStream in, Executor executor, Channel channel, long id) + { + _in = in; + _executor = executor; + _channel = channel; + _id = id; + } + + public void start() + { + _boss.add(this); + } + + protected void doStream() + { + // only one executor at a time + if (_isDoingStream.get()) + return; + else + _isDoingStream.set(true); + _executor.execute(new Runnable() + { + + public void run() + { + try + { + while (_in.available() > 0 && _channel.isWritable()) + { + byte[] data = new byte[BATCH_SIZE]; + int l = -1; + try + { + _in.read(data); + } + catch (Exception ex) + { + ex.printStackTrace(); + } + if (l == -1) + { + doClose(); + return; + } + else + { + doSendData(l, data); + } + } + } + catch (Exception ex) + { + doClose(); + } + _isDoingStream.set(false); + } + + }); + } + + private void doSendData(int length, byte[] data) + { + if (length == 0) + return; + if (length < data.length) + { + byte[] newData = new byte[length]; + System.arraycopy(data, 0, newData, 0, length); + data = newData; + } + InputStreamReplyMessage msg = new InputStreamReplyMessage(); + msg.setId(_id); + msg.setData(data); + _channel.write(msg); + } + + private void doClose() + { + try + { + _in.close(); + } + catch (Exception ex) + { + ex.printStackTrace(); + } + _boss.remove(this); + InputStreamReplyMessage msg = new InputStreamReplyMessage(); + msg.setId(_id); + msg.setClosed(true); + _channel.write(msg); + + } + + public long getId() + { + return _id; + } + +} diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/stream/ServerInputStreamManager.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/stream/ServerInputStreamManager.java new file mode 100644 index 0000000000..ee6f1dba3b --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/rpc/stream/ServerInputStreamManager.java @@ -0,0 +1,33 @@ +package org.rzo.netty.ahessian.rpc.stream; + +import java.io.InputStream; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.Executor; + +import org.jboss.netty.channel.Channel; + +public class ServerInputStreamManager +{ + long _id = 0; + Map _streams = new HashMap(); + Executor _executor; + + public ServerInputStreamManager(Executor executor) + { + _executor = executor; + } + + synchronized public ServerInputStream createServerInputStream(InputStream stream, Channel channel) + { + ServerInputStream result = new ServerInputStream(stream, _executor, channel, _id); + synchronized (_streams) + { + _streams.put(_id, result); + } + _id++; + return result; + } + + +} diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/serialization/HessianDecoder.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/serialization/HessianDecoder.java new file mode 100644 index 0000000000..84dec9598b --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/serialization/HessianDecoder.java @@ -0,0 +1,70 @@ +package org.rzo.netty.ahessian.serialization; + +import java.io.InputStream; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.jboss.netty.channel.ChannelHandlerContext; +import org.jboss.netty.channel.ChannelPipeline; +import org.jboss.netty.channel.Channels; +import org.jboss.netty.channel.MessageEvent; +import org.jboss.netty.channel.SimpleChannelUpstreamHandler; +import org.rzo.netty.ahessian.Constants; + +import com.caucho.hessian4.io.HessianInput; + +/** + * Decodes a {@link ChannelBuffer} into a {@link java.lang.Object}. + * A typical setup for a serialization protocol in a TCP/IP socket would be: + *
+ * {@link ChannelPipeline} pipeline = ...;
+ * 
+ * // Encoder
+ * pipeline.addLast("outputStream", new {@link io.OutputStream}());
+ * pipeline.addLast("hessianEncoder", new {@link HessianEncoder}());
+ * 
+ * // Decoder
+ * pipeline.addLast("inputStream", new {@link io.InputStream}());
+ * pipeline.addLast("hessianDecoder", new {@link HessianDecoder}());
+ * pipeline.addLast("handler", new MyHandler());
+ * 
+ * and then, within the handler you can use a {@link java.lang.Object} instead of a {@link ChannelBuffer} + * as a message: + *
+ * void messageReceived(ChannelHandlerContext ctx, MessageEvent e) {
+ * // get the message
+ * Object msg = e.getMessage();
+ * // return the current time
+ * ch.write(new Date());
+ * }
+ * 
+ */ + +public class HessianDecoder extends SimpleChannelUpstreamHandler +{ + + /* (non-Javadoc) + * @see org.jboss.netty.channel.SimpleChannelUpstreamHandler#messageReceived(org.jboss.netty.channel.ChannelHandlerContext, org.jboss.netty.channel.MessageEvent) + */ + @Override + public void messageReceived( + ChannelHandlerContext ctx, MessageEvent e) throws Exception + { + InputStream in = (InputStream) e.getMessage(); + try + { + HessianInput hin = new HessianInput(in); + while (true) + { + Object obj = hin.readObject(null); + Channels.fireMessageReceived(ctx, obj); + } + } + catch (Exception ex) + { + Constants.ahessianLogger.debug("", ex); + } + } + + + +} diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/serialization/HessianEncoder.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/serialization/HessianEncoder.java new file mode 100644 index 0000000000..000a329fd9 --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/serialization/HessianEncoder.java @@ -0,0 +1,54 @@ +package org.rzo.netty.ahessian.serialization; + +import java.io.OutputStream; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.jboss.netty.channel.ChannelHandlerContext; +import org.jboss.netty.channel.ChannelPipeline; +import org.jboss.netty.channel.MessageEvent; +import org.jboss.netty.channel.SimpleChannelDownstreamHandler; +import org.rzo.netty.ahessian.io.OutputStreamEncoder; + +import com.caucho.hessian4.io.HessianOutput; + +/** + * Encodes the requested {@link java.lang.Object} into a {@link ChannelBuffer}. + * A typical setup for a serialization protocol in a TCP/IP socket would be: + *
+ * {@link ChannelPipeline} pipeline = ...;
+ * 
+ * // Encoder
+ * pipeline.addLast("outputStream", new {@link io.OutputStream}());
+ * pipeline.addLast("hessianEncoder", new {@link HessianEncoder}());
+ * 
+ * // Decoder
+ * pipeline.addLast("inputStream", new {@link io.InputStream}());
+ * pipeline.addLast("hessianDecoder", new {@link HessianDecoder}());
+ * pipeline.addLast("handler", new MyHandler());
+ * 
+ * and then, within the handler you can use a {@link java.lang.Object} instead of a {@link ChannelBuffer} + * as a message: + *
+ * void messageReceived(ChannelHandlerContext ctx, MessageEvent e) {
+ * // get the message
+ * Object msg = e.getMessage();
+ * // return the current time
+ * ch.write(new Date());
+ * }
+ * 
+ */ +public class HessianEncoder extends SimpleChannelDownstreamHandler +{ + + /* (non-Javadoc) + * @see org.jboss.netty.channel.SimpleChannelDownstreamHandler#writeRequested(org.jboss.netty.channel.ChannelHandlerContext, org.jboss.netty.channel.MessageEvent) + */ + public void writeRequested(ChannelHandlerContext ctx, MessageEvent e) throws Exception { + OutputStream out = OutputStreamEncoder.getOutputStream(ctx); + HessianOutput hout = new HessianOutput(out); + hout.writeObject(e.getMessage()); + hout.flush(); + } + + +} diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/serialization/package-info.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/serialization/package-info.java new file mode 100644 index 0000000000..0e2a10efcc --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/serialization/package-info.java @@ -0,0 +1,6 @@ +/** + * Encoder, decoder which + * transform a {@link java.lang.Object} into a byte buffer and + * vice versa using hessian serialization. + */ +package org.rzo.netty.ahessian.serialization; \ No newline at end of file diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/session/ClientSessionFilter.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/session/ClientSessionFilter.java new file mode 100644 index 0000000000..5fcb844090 --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/session/ClientSessionFilter.java @@ -0,0 +1,222 @@ +package org.rzo.netty.ahessian.session; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.jboss.netty.buffer.ChannelBuffers; +import org.jboss.netty.channel.ChannelFuture; +import org.jboss.netty.channel.ChannelHandlerContext; +import org.jboss.netty.channel.ChannelPipeline; +import org.jboss.netty.channel.ChannelPipelineCoverage; +import org.jboss.netty.channel.ChannelPipelineFactory; +import org.jboss.netty.channel.ChannelStateEvent; +import org.jboss.netty.channel.Channels; +import org.jboss.netty.channel.MessageEvent; +import org.jboss.netty.channel.SimpleChannelUpstreamHandler; +import org.rzo.netty.ahessian.Constants; +import org.rzo.netty.ahessian.stopable.StopablePipeline; + +/** + * Handles sessions on the client side. + * A typical setup for a protocol in a TCP/IP socket would be: + * + *
+ * // client session filter is an attribute of the ChannelPipelineFactory Class
+ * // it should not be created with each call to getPipeline()
+ * // _mixinFactory is a ChannelPipelineFactory which returns MixinPipeline
+ * _sessionFilter = new ClientSessionFilter(_mixinFactory);
+ * 
+ * {@link ChannelPipeline} pipeline = ...;
+ * 
+ * pipeline.addLast("sessionFilter", _sessionFilter);
+ * 
+ */ + +@ChannelPipelineCoverage("all") +public class ClientSessionFilter extends SimpleChannelUpstreamHandler +{ + + /** The current session. */ + private Session _session = null; + + /** Indicates if we have received a session. */ + private boolean _hasSession = false; + + /** String to read in the session id from the server */ + private String _sessionId = ""; + + /** Factory for creating session objects. */ + private SessionFactory _factory = new SessionFactory(); + + /** Connected events are intercepted and sent upstream once a session has been established. */ + private ChannelStateEvent _connectedEvent; + + /** The factory for getting a MixinPipeline for a new session */ + private ChannelPipelineFactory _mixinFactory; + + /** Assignment of session-id to pipelines created. //TODO destroy a pipeline if a session is timed out */ + private static Map _sessionPipelines = Collections.synchronizedMap(new HashMap()); + + private List _sessionClosedListeners = Collections.synchronizedList(new ArrayList()); + + private List _sessionNewListeners = Collections.synchronizedList(new ArrayList()); + + /** + * Instantiates a new client session filter. + * + * @param mixinFactory the mixin factory + */ + public ClientSessionFilter(ChannelPipelineFactory mixinFactory) + { + _mixinFactory = mixinFactory; + } + + /* (non-Javadoc) + * @see org.jboss.netty.channel.SimpleChannelUpstreamHandler#channelConnected(org.jboss.netty.channel.ChannelHandlerContext, org.jboss.netty.channel.ChannelStateEvent) + */ + @Override + public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception { + // remeber the event. it will be sent upstream when session has been created + _connectedEvent = e; + String id = _session == null ? "?" : _session.getId(); + // send the session id to client + ChannelFuture future = Channels.future(ctx.getChannel()); + Channels.write(ctx, future, ChannelBuffers.wrappedBuffer(id.getBytes())); + } + + /* (non-Javadoc) + * @see org.jboss.netty.channel.SimpleChannelUpstreamHandler#messageReceived(org.jboss.netty.channel.ChannelHandlerContext, org.jboss.netty.channel.MessageEvent) + */ + public void messageReceived( + ChannelHandlerContext ctx, MessageEvent e) throws Exception + { + // if session established forward all messages + if (_hasSession) + ctx.sendUpstream(e); + else + { + ChannelBuffer b = (ChannelBuffer) e.getMessage(); + _sessionId += b.toString("UTF-8"); + checkSession(ctx); + } + } + + private void checkSession(ChannelHandlerContext ctx) + { + if (_sessionId.length() == _factory.getSessionIdLength()*2) + { + if (_session == null) + newSession(ctx); + else if (_session.getId().equals(_sessionId)) + confirmSession(ctx); + else + changedSession(ctx); + } + + } + + private void changedSession(ChannelHandlerContext ctx) + { + closeSession(_session); + newSession(ctx); + } + + private void closeSession(Session session) + { + for (Runnable listener : _sessionClosedListeners) + { + try + { + listener.run(); + } + catch (Throwable e) + { + Constants.ahessianLogger.warn("", e); + } + } + ChannelPipeline p = _sessionPipelines.remove(session.getId()); + if (p instanceof StopablePipeline) + ((StopablePipeline)p).stop(); + + } + + private void confirmSession(ChannelHandlerContext ctx) + { + MixinPipeline pipeline = _sessionPipelines.get(_session.getId()); + handleSession(ctx, pipeline); + } + + private void newSession(ChannelHandlerContext ctx) + { + _session = _factory.createSession(_sessionId); + MixinPipeline pipeline = null; + try + { + pipeline = (MixinPipeline) _mixinFactory.getPipeline(); + _sessionPipelines.put(_session.getId(), pipeline); + } + catch (Exception e) + { + Constants.ahessianLogger.warn("", e); + } + handleSession(ctx, pipeline); + for (Runnable listener : _sessionNewListeners) + { + try + { + listener.run(); + } + catch (Throwable ex) + { + Constants.ahessianLogger.warn("", ex); + + } + } + } + + private void handleSession(ChannelHandlerContext ctx, MixinPipeline pipeline) + { + _hasSession = true; + // now that we have a session extend the pipeline + ChannelPipeline currentPipeline = ctx.getPipeline(); + pipeline.mixin(currentPipeline); + ctx.setAttachment(_session); + ctx.sendUpstream(_connectedEvent); + } + + @Override + public void channelClosed(ChannelHandlerContext ctx, ChannelStateEvent e) { + _hasSession = false; + _sessionId = ""; + ctx.sendUpstream(e); + } + + @Override + public void channelDisconnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception + { + // + } + + public void addSessionClosedListener(Runnable listener) + { + _sessionClosedListeners.add(listener); + } + + public void removeSessionClosedListener(Runnable listener) + { + _sessionClosedListeners.remove(listener); + } + + public void addSessionNewListener(Runnable listener) + { + _sessionNewListeners.add(listener); + } + + + + +} diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/session/MixinPipeline.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/session/MixinPipeline.java new file mode 100644 index 0000000000..331a9f403b --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/session/MixinPipeline.java @@ -0,0 +1,329 @@ +package org.rzo.netty.ahessian.session; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.NoSuchElementException; + +import org.jboss.netty.channel.Channel; +import org.jboss.netty.channel.ChannelEvent; +import org.jboss.netty.channel.ChannelFuture; +import org.jboss.netty.channel.ChannelHandler; +import org.jboss.netty.channel.ChannelHandlerContext; +import org.jboss.netty.channel.ChannelPipeline; +import org.jboss.netty.channel.ChannelSink; + +/** + * A channel pipeline which can be added to an active pipeline. + * NOTE: This class should not be used be used as a standard pipeline. + * It cannot be attached to a channel or context. It just manages a list + * of handlers which can be added to a standard pipeline. + */ +public class MixinPipeline implements ChannelPipeline +{ + + /** List of handlers added to the pipeline */ + private LinkedList _handlersList = new LinkedList(); + + /** Maps names to handlers */ + Map _handlersMap = new HashMap(); + + /** Names list, must be synchronous to handlersList */ + LinkedList _namesList = new LinkedList(); + + Channel _channel; + + + + /* (non-Javadoc) + * @see org.jboss.netty.channel.ChannelPipeline#addAfter(java.lang.String, java.lang.String, org.jboss.netty.channel.ChannelHandler) + */ + public void addAfter(String arg0, String arg1, ChannelHandler arg2) + { + ChannelHandler handler1 = getHandlerOrDie(arg0); + checkDuplicateName(arg1); + _handlersMap.put(arg1, arg2); + int i = _handlersList.indexOf(handler1); + _handlersList.add(i+1, arg2); + _namesList.add(i+1, arg1); + } + + private ChannelHandler getHandlerOrDie(String arg0) + { + ChannelHandler result = _handlersMap.get(arg0); + if (result == null) + throw new NoSuchElementException(arg0); + return result; + } + + private void checkDuplicateName(String name) { + if (_handlersMap.containsKey(name)) { + throw new IllegalArgumentException("Duplicate handler name."); + } + } + + + /* (non-Javadoc) + * @see org.jboss.netty.channel.ChannelPipeline#addBefore(java.lang.String, java.lang.String, org.jboss.netty.channel.ChannelHandler) + */ + + public void addBefore(String arg0, String arg1, ChannelHandler arg2) + { + ChannelHandler handler1 = getHandlerOrDie(arg0); + checkDuplicateName(arg1); + _handlersMap.put(arg1, arg2); + int i = _handlersList.indexOf(handler1); + _handlersList.add(i, arg2); + _namesList.add(i, arg1); + } + + /* (non-Javadoc) + * @see org.jboss.netty.channel.ChannelPipeline#addFirst(java.lang.String, org.jboss.netty.channel.ChannelHandler) + */ + + public void addFirst(String arg0, ChannelHandler arg1) + { + checkDuplicateName(arg0); + _handlersMap.put(arg0, arg1); + _handlersList.addFirst(arg1); + _namesList.addFirst(arg0); + } + + /* (non-Javadoc) + * @see org.jboss.netty.channel.ChannelPipeline#addLast(java.lang.String, org.jboss.netty.channel.ChannelHandler) + */ + + public void addLast(String arg0, ChannelHandler arg1) + { + checkDuplicateName(arg0); + _handlersMap.put(arg0, arg1); + _handlersList.addLast(arg1); + _namesList.addLast(arg0); + } + + /* (non-Javadoc) + * @see org.jboss.netty.channel.ChannelPipeline#attach(org.jboss.netty.channel.Channel, org.jboss.netty.channel.ChannelSink) + */ + + public void attach(Channel arg0, ChannelSink arg1) + { + } + + /* (non-Javadoc) + * @see org.jboss.netty.channel.ChannelPipeline#get(java.lang.String) + */ + + public ChannelHandler get(String arg0) + { + return _handlersMap.get(arg0); + } + + /* (non-Javadoc) + * @see org.jboss.netty.channel.ChannelPipeline#get(java.lang.Class) + */ + + public T get(Class arg0) + { + return null; + } + + /* (non-Javadoc) + * @see org.jboss.netty.channel.ChannelPipeline#getChannel() + */ + + public Channel getChannel() + { + return _channel; + } + + /* (non-Javadoc) + * @see org.jboss.netty.channel.ChannelPipeline#getContext(org.jboss.netty.channel.ChannelHandler) + */ + + public ChannelHandlerContext getContext(ChannelHandler arg0) + { + return null; + } + + /* (non-Javadoc) + * @see org.jboss.netty.channel.ChannelPipeline#getContext(java.lang.String) + */ + + public ChannelHandlerContext getContext(String arg0) + { + return null; + } + + /* (non-Javadoc) + * @see org.jboss.netty.channel.ChannelPipeline#getContext(java.lang.Class) + */ + + public ChannelHandlerContext getContext(Class arg0) + { + return null; + } + + /* (non-Javadoc) + * @see org.jboss.netty.channel.ChannelPipeline#getFirst() + */ + + public ChannelHandler getFirst() + { + return _handlersList.getFirst(); + } + + /* (non-Javadoc) + * @see org.jboss.netty.channel.ChannelPipeline#getLast() + */ + + public ChannelHandler getLast() + { + return _handlersList.getLast(); + } + + /* (non-Javadoc) + * @see org.jboss.netty.channel.ChannelPipeline#getSink() + */ + + public ChannelSink getSink() + { + return null; + } + + /* (non-Javadoc) + * @see org.jboss.netty.channel.ChannelPipeline#isAttached() + */ + + public boolean isAttached() + { + return false; + } + + /* (non-Javadoc) + * @see org.jboss.netty.channel.ChannelPipeline#remove(org.jboss.netty.channel.ChannelHandler) + */ + + public void remove(ChannelHandler arg0) + { + } + + /* (non-Javadoc) + * @see org.jboss.netty.channel.ChannelPipeline#remove(java.lang.String) + */ + + public ChannelHandler remove(String arg0) + { + return null; + } + + /* (non-Javadoc) + * @see org.jboss.netty.channel.ChannelPipeline#remove(java.lang.Class) + */ + + public T remove(Class arg0) + { + return null; + } + + /* (non-Javadoc) + * @see org.jboss.netty.channel.ChannelPipeline#removeFirst() + */ + + public ChannelHandler removeFirst() + { + return null; + } + + /* (non-Javadoc) + * @see org.jboss.netty.channel.ChannelPipeline#removeLast() + */ + + public ChannelHandler removeLast() + { + // TODO Auto-generated method stub + return null; + } + + /* (non-Javadoc) + * @see org.jboss.netty.channel.ChannelPipeline#replace(org.jboss.netty.channel.ChannelHandler, java.lang.String, org.jboss.netty.channel.ChannelHandler) + */ + + public void replace(ChannelHandler arg0, String arg1, ChannelHandler arg2) + { + // TODO Auto-generated method stub + + } + + /* (non-Javadoc) + * @see org.jboss.netty.channel.ChannelPipeline#replace(java.lang.String, java.lang.String, org.jboss.netty.channel.ChannelHandler) + */ + + public ChannelHandler replace(String arg0, String arg1, ChannelHandler arg2) + { + // TODO Auto-generated method stub + return null; + } + + /* (non-Javadoc) + * @see org.jboss.netty.channel.ChannelPipeline#replace(java.lang.Class, java.lang.String, org.jboss.netty.channel.ChannelHandler) + */ + + public T replace(Class arg0, String arg1, ChannelHandler arg2) + { + // TODO Auto-generated method stub + return null; + } + + /* (non-Javadoc) + * @see org.jboss.netty.channel.ChannelPipeline#sendDownstream(org.jboss.netty.channel.ChannelEvent) + */ + + public void sendDownstream(ChannelEvent arg0) + { + } + + /* (non-Javadoc) + * @see org.jboss.netty.channel.ChannelPipeline#sendUpstream(org.jboss.netty.channel.ChannelEvent) + */ + + public void sendUpstream(ChannelEvent arg0) + { + } + + public List getNames() { + return new ArrayList(_namesList); + } + + /* (non-Javadoc) + * @see org.jboss.netty.channel.ChannelPipeline#toMap() + */ + + public Map toMap() + { + return null; + } + + /** + * Adds this pipeline to the end of the given pipeline. + * + * @param pipeline a standard pipeline + */ + public void mixin(ChannelPipeline pipeline) + { + _channel = pipeline.getChannel(); + for (int i = 0; i<_namesList.size(); i++) + { + ChannelHandler handler = _handlersList.get(i); + String name = _namesList.get(i); + pipeline.addLast(name, handler); + } + } + + public ChannelFuture execute(Runnable task) { + return getSink().execute(this, task); + } + + +} diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/session/ServerSessionFilter.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/session/ServerSessionFilter.java new file mode 100644 index 0000000000..29ceeb911e --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/session/ServerSessionFilter.java @@ -0,0 +1,269 @@ +package org.rzo.netty.ahessian.session; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.jboss.netty.buffer.ChannelBuffers; +import org.jboss.netty.channel.Channel; +import org.jboss.netty.channel.ChannelFuture; +import org.jboss.netty.channel.ChannelHandlerContext; +import org.jboss.netty.channel.ChannelPipeline; +import org.jboss.netty.channel.ChannelPipelineFactory; +import org.jboss.netty.channel.ChannelStateEvent; +import org.jboss.netty.channel.Channels; +import org.jboss.netty.channel.MessageEvent; +import org.jboss.netty.channel.SimpleChannelUpstreamHandler; +import org.jboss.netty.util.Timeout; +import org.jboss.netty.util.Timer; +import org.jboss.netty.util.TimerTask; +import org.rzo.netty.ahessian.Constants; + +/** + * Handles sessions on the server side. A typical setup for a + * protocol in a TCP/IP socket would be: + * + *
+ * // _mixinFactory is a ChannelPipelineFactory which returns MixinPipeline
+ * {@link ChannelPipeline} pipeline = ...;
+ * 
+ * pipeline.addLast("sessionFilter", new ServerSessionFilter(_mixinFactory));
+ * 
+ */ +public class ServerSessionFilter extends SimpleChannelUpstreamHandler +{ + + /** Indicates if session has been assigned to the current channel */ + private boolean _hasSession = false; + + /** String for reading in a session id */ + private String _sessionId = ""; + + /** Factory for creating new session objects */ + private SessionFactory _factory = new SessionFactory(); + + /** Connected event is intercepted. It is sent upstream only after a session has been established*/ + private ChannelStateEvent _connectedEvent; + + /** A pipeline factory which returns a MixinPipeline */ + private ChannelPipelineFactory _mixinFactory; + + /** Assignment of session-id to the associated MixinPipeline */ + private static Map _sessionPipelines = Collections.synchronizedMap(new HashMap()); + + private long _sessionTimeout = -1; + + private Timer _timer = null; + + private volatile Channel _channel = null; + + private volatile boolean _valid = true; + + /** + * Instantiates a new server session filter. + * + * @param mixinFactory a pipeline factory which returns MixinPipeline + */ + public ServerSessionFilter(ChannelPipelineFactory mixinFactory, Timer timer, long sessionTimeout) + { + _mixinFactory = mixinFactory; + _timer = timer; + _sessionTimeout = sessionTimeout; + } + + public ServerSessionFilter(ChannelPipelineFactory mixinFactory) + { + this(mixinFactory, null, -1); + } + + /* (non-Javadoc) + * @see org.jboss.netty.channel.SimpleChannelUpstreamHandler#messageReceived(org.jboss.netty.channel.ChannelHandlerContext, org.jboss.netty.channel.MessageEvent) + */ + public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception + { + // if session established forward all messages + if (_hasSession) + { + Session session = ((Session)ctx.getAttachment()); + session.onMessage(); + ctx.sendUpstream(e); + } + else + { + ChannelBuffer b = (ChannelBuffer) e.getMessage(); + _sessionId += b.toString("UTF-8"); + if (_sessionId.equals("?")) + newSession(ctx); + else + checkSession(ctx); + } + } + + private void checkSession(ChannelHandlerContext ctx) + { + if (_sessionId.length() == _factory.getSessionIdLength() * 2) + { + Session session = _factory.getSession(_sessionId); + if (session == null) + newSession(ctx); + else + confirmSession(ctx); + } + + } + + private void newSession(ChannelHandlerContext ctx) + { + Session session = _factory.createSession(null); + Constants.ahessianLogger.info(ctx.getChannel()+" new session #"+session.getId()); + MixinPipeline pipeline = null; + try + { + pipeline = (MixinPipeline) _mixinFactory.getPipeline(); + _sessionPipelines.put(session.getId(), pipeline); + } + catch (Exception e) + { + Constants.ahessianLogger.warn("", e); + } + handleSession(ctx, session, pipeline); + } + + private void confirmSession(ChannelHandlerContext ctx) + { + Session session = _factory.getSession(_sessionId); + Constants.ahessianLogger.info(ctx.getChannel()+" reuse session #"+session.getId()); + MixinPipeline pipeline = _sessionPipelines.get(_sessionId); + handleSession(ctx, session, pipeline); + } + + private void handleSession(ChannelHandlerContext ctx, Session session, MixinPipeline pipeline) + { + _hasSession = true; + session.setClosed(false); + + // if we have a session timeout set, cancel it. + Timeout timeOut = session.removeTimeout(); + if (timeOut != null) + timeOut.cancel(); + + // check if the session is already connected to a channel + Channel c = pipeline.getChannel(); + if (c != null && c.isOpen()) + { + Constants.ahessianLogger.warn(ctx.getChannel()+" session already attached -> close connection"); + c.close(); + } + + // now that we have a session extend the pipeline + ChannelPipeline currentPipeline = ctx.getPipeline(); + pipeline.mixin(currentPipeline); + ctx.setAttachment(session); + _channel = ctx.getChannel(); + // first send session and wait until it has been transmitted + ChannelFuture future = Channels.future(ctx.getChannel()); + Channels.write(ctx, future, ChannelBuffers.wrappedBuffer(session.getId().getBytes())); + try + { + future.await(); + } + catch (InterruptedException e) + { + // TODO Auto-generated catch block + e.printStackTrace(); + } + // only then inform the mixin pipeline + ctx.sendUpstream(_connectedEvent); + } + + /** + * Helper Method: returns the session of associated with the pipeline of a given context + * + * @param ctx the context + * + * @return the session + */ + public static Session getSession(ChannelHandlerContext ctx) + { + ChannelHandlerContext handler = ctx.getPipeline().getContext(ServerSessionFilter.class); + if (handler == null) + return null; + return (Session) handler.getAttachment(); + } + + /* (non-Javadoc) + * @see org.jboss.netty.channel.SimpleChannelUpstreamHandler#channelConnected(org.jboss.netty.channel.ChannelHandlerContext, org.jboss.netty.channel.ChannelStateEvent) + */ + @Override + public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception + { + // remeber the event. it will be sent upstream when session has been + // created + _connectedEvent = e; + } + + /* (non-Javadoc) + * @see org.jboss.netty.channel.SimpleChannelUpstreamHandler#channelDisconnected(org.jboss.netty.channel.ChannelHandlerContext, org.jboss.netty.channel.ChannelStateEvent) + */ + @Override + public void channelDisconnected(final ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception + { + + _hasSession = false; + ((Session)ctx.getAttachment()).close(); + final String sessionId = ((Session)ctx.getAttachment()).getId(); + Constants.ahessianLogger.info("Session disconnected: "+ sessionId); + _sessionId = ""; + _connectedEvent = null; + _channel = null; + if (_sessionTimeout > 0) + { + Timeout timeOut = _timer.newTimeout(new TimerTask() + { + + public void run(Timeout arg0) throws Exception + { + ((Session)ctx.getAttachment()).invalidate(); + _factory.removeSession(sessionId); + _sessionPipelines.remove(sessionId); + _valid = false; + Constants.ahessianLogger.warn(ctx.getChannel()+" session timed out: "+sessionId); + } + + }, _sessionTimeout, TimeUnit.MILLISECONDS); + ((Session)ctx.getAttachment()).setTimeOut(timeOut); + } + ctx.sendUpstream(e); + } + + public long getSessionTimeout() + { + return _sessionTimeout; + } + + public void setSessionTimeout(long sessionTimeout) + { + _sessionTimeout = sessionTimeout; + } + + public boolean isValid() + { + return _valid; + } + + public Channel getChannel() + { + return _channel; + } + + public static ServerSessionFilter getServerSessionFilter(ChannelHandlerContext ctx) + { + return (ServerSessionFilter) ctx.getPipeline().getContext(ServerSessionFilter.class).getHandler(); + } + + + + +} diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/session/ServiceSession.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/session/ServiceSession.java new file mode 100644 index 0000000000..5b8e2e6f74 --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/session/ServiceSession.java @@ -0,0 +1,26 @@ +package org.rzo.netty.ahessian.session; + +import java.util.Collection; + +public interface ServiceSession +{ + public String getId(); + public void addClosedListener(Runnable listener); + public void addInvalidatedListener(Runnable listener); + + public Object getAttribute(String name); + public Collection getAttributeNames(); + public void removeAttribute(String name); + public void setAttribute(String name, Object value); + + public long getCreationTime(); + public long getLastConnectedTime(); + + public boolean isValid(); + public boolean isClosed(); + public boolean isNew(); + + public long getMessageCount(); + + +} diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/session/Session.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/session/Session.java new file mode 100644 index 0000000000..32def9dc7a --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/session/Session.java @@ -0,0 +1,31 @@ +package org.rzo.netty.ahessian.session; + +import org.jboss.netty.util.Timeout; + +/** + * A session object. + * TODO for now session objects just hold the id. Handling of session timeout is missing + */ +public interface Session extends ServiceSession +{ + + /** + * TODO Destroy the current session and associated pipeline. + */ + public void invalidate(); + + public void setNew(boolean newValue); + + public void setTimeOut(Timeout timeOut); + + public Timeout removeTimeout(); + + public void close(); + + public void onMessage(); + + public void setClosed(boolean b); + + + +} diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/session/SessionFactory.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/session/SessionFactory.java new file mode 100644 index 0000000000..638e739b97 --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/session/SessionFactory.java @@ -0,0 +1,198 @@ +package org.rzo.netty.ahessian.session; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Random; + +import org.rzo.netty.ahessian.Constants; + +/** + * A factory for creating Session objects. + * Session id generator: taken from apache tomcat. + */ +class SessionFactory +{ + + + /** + * The default message digest algorithm to use. + * TODO: enable user configured algorithms + */ + private static final String DEFAULT_ALGORITHM = "MD5"; + private Random random; + private String entropy; + private int sessionIdLength = 16; + + // number of duplicated session ids - anything >0 means we have problems + private int duplicates=0; + private MessageDigest digest; + + /** Assignment of id to sessions, to avoid duplicates */ + private static Map _sessions = Collections.synchronizedMap(new HashMap()); + + /** + * Creates a new Session object. + * If the given id is null a new id is generated + * TODO handle id duplicates + * + * @param id the given id + * + * @return the session object + */ + public Session createSession(String id) + { + if (id == null) + id = generateSessionId(); + Session session = new SessionImpl(id); + _sessions.put(id, session); + return session; + } + + + /** + * Generate and return a new session identifier. + * Taken from Tomcat + * TODO + */ + private synchronized String generateSessionId() { + + byte random[] = new byte[16]; + String jvmRoute = getJvmRoute(); + String result = null; + + // Render the result as a String of hexadecimal digits + StringBuffer buffer = new StringBuffer(); + do { + int resultLenBytes = 0; + if (result != null) { + buffer = new StringBuffer(); + duplicates++; + } + + while (resultLenBytes < this.sessionIdLength) { + getRandomBytes(random); + random = getDigest().digest(random); + for (int j = 0; + j < random.length && resultLenBytes < this.sessionIdLength; + j++) { + byte b1 = (byte) ((random[j] & 0xf0) >> 4); + byte b2 = (byte) (random[j] & 0x0f); + if (b1 < 10) + buffer.append((char) ('0' + b1)); + else + buffer.append((char) ('A' + (b1 - 10))); + if (b2 < 10) + buffer.append((char) ('0' + b2)); + else + buffer.append((char) ('A' + (b2 - 10))); + resultLenBytes++; + } + } + if (jvmRoute != null) { + buffer.append('.').append(jvmRoute); + } + result = buffer.toString(); + } while (_sessions.containsKey(result)); + return (result); + + } + + private MessageDigest getDigest() + { + if (this.digest == null) { + try + { + this.digest = MessageDigest.getInstance(DEFAULT_ALGORITHM); + } + catch (NoSuchAlgorithmException e) + { + Constants.ahessianLogger.warn("", e); + } + } + return digest; + + } + + private String getJvmRoute() + { + return System.getProperty("jvmRoute"); + } + + private void getRandomBytes(byte bytes[]) { + getRandom().nextBytes(bytes); + } + + /** + * Return the random number generator instance we should use for + * generating session identifiers. If there is no such generator + * currently defined, construct and seed a new one. + * + * @return the random + */ + private Random getRandom() { + if (this.random == null) { + // Calculate the new random number generator seed + long seed = System.currentTimeMillis(); + long t1 = seed; + char entropy[] = getEntropy().toCharArray(); + for (int i = 0; i < entropy.length; i++) { + long update = ((byte) entropy[i]) << ((i % 8) * 8); + seed ^= update; + } + this.random = new java.util.Random(); + this.random.setSeed(seed); + } + + return (this.random); + + } + + /** + * Return the entropy increaser value, or compute a semi-useful value + * if this String has not yet been set. + * + * @return the entropy + */ + private String getEntropy() { + + // Calculate a semi-useful value if this has not been set + if (entropy == null) { + entropy = this.toString(); + } + return entropy; + } + + /** + * Gets the session-id length. + * + * @return the session id length + */ + public int getSessionIdLength() + { + return sessionIdLength; + } + + /** + * Gets the session for a given id. + * + * @param id the id + * + * @return the session + */ + public Session getSession(String id) + { + return _sessions.get(id); + } + + public Session removeSession(String id) + { + return _sessions.remove(id); + } + + + + +} diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/session/SessionImpl.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/session/SessionImpl.java new file mode 100644 index 0000000000..c42e37ad60 --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/session/SessionImpl.java @@ -0,0 +1,195 @@ +package org.rzo.netty.ahessian.session; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.jboss.netty.util.Timeout; +import org.rzo.netty.ahessian.Constants; + +/** + * The Class SessionImpl. + */ +class SessionImpl implements Session +{ + + private String _id; + + private List _closedListeners = Collections.synchronizedList(new ArrayList()); + private List _invalidatedListeners = Collections.synchronizedList(new ArrayList()); + + private Map _attributes = new HashMap(); + + private long _created = System.currentTimeMillis(); + private volatile long _connected = System.currentTimeMillis(); + + private volatile boolean _new = true; + + private volatile Timeout _timeout = null; + + private volatile boolean _valid = true; + + private volatile boolean _closed = false; + + private volatile long _messageCount = 0; + + /** + * Instantiates a new session impl. + * + * @param id the id + */ + SessionImpl(String id) + { + _id = id; + _created = System.currentTimeMillis(); + + } + + /* (non-Javadoc) + * @see handler.session.Session#getId() + */ + + public String getId() + { + return _id; + } + + /* (non-Javadoc) + * @see handler.session.Session#addClosedListener(java.lang.Runnable) + */ + + public void addClosedListener(Runnable listener) + { + _closedListeners.add(listener); + } + + /* (non-Javadoc) + * @see handler.session.Session#close() + */ + + public void close() + { + _closed = true; + _connected = -1; + synchronized(_closedListeners) + { + for (Runnable listener : _closedListeners) + try + { + listener.run(); + } + catch (Throwable ex) + { + Constants.ahessianLogger.warn(" execption in closedListener in session: "+_id+ " "+ex); + } + } + } + + public void addInvalidatedListener(Runnable listener) + { + _invalidatedListeners.add(listener); + } + + public Object getAttribute(String name) + { + return _attributes.get(name); + } + + public Collection getAttributeNames() + { + return new ArrayList(_attributes.keySet()); + } + + public long getCreationTime() + { + return _created; + } + + public long getLastConnectedTime() + { + return _connected; + } + + public void invalidate() + { + _valid = false; + synchronized(_invalidatedListeners) + { + for (Runnable listener : _invalidatedListeners) + try + { + listener.run(); + } + catch (Throwable ex) + { + Constants.ahessianLogger.warn(" execption in invalidatedListener in session: "+_id+ " "+ex); + } + } + } + + public boolean isNew() + { + return _new; + } + + public void removeAttribute(String name) + { + _attributes.remove(name); + } + + public void setAttribute(String name, Object value) + { + _attributes.put(name, value); + } + + public void setNew(boolean newValue) + { + _new = newValue; + } + + public void setTimeOut(Timeout timeOut) + { + _timeout = timeOut; + } + + public Timeout removeTimeout() + { + Timeout result = _timeout; + _timeout = null; + return result; + } + + public boolean isValid() + { + return _valid; + } + + public void onMessage() + { + _messageCount++; + } + + public void onConnected() + { + _connected = System.currentTimeMillis(); + } + + public long getMessageCount() + { + return _messageCount; + } + + public boolean isClosed() + { + return _closed; + } + + public void setClosed(boolean b) + { + _closed = b; + } + +} \ No newline at end of file diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/session/package-info.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/session/package-info.java new file mode 100644 index 0000000000..cf92340027 --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/session/package-info.java @@ -0,0 +1,21 @@ +/** + * Provides Client and Server Session handling + *
+ * When using {@link ChannelPipelineFactory} netty creates a pipeline per connection + * With sessions we need a pipeline per session. Thus a pipeline may survive multiple connect/disconnect cycles. + * Sessions thus allows the server to maintain its state in-between disconnect/connect cycles of the client + * Currently only in-memory sessions are implemented. + * Therefore when the server is stopped all sessions are lost and reconnecting clients will have to + * adjust accordingly. + * + * To allow pipeline survival {@link MixinPipeline} is used. + * On creation of a new session a new {@link MixinPipeline} is created, added to the current pipeline and associated with the session + * On reconnect, once the session has been identified, the associated {@link MixinPipeline} is added to the + * current pipeline. + * + * TODO + * persistent sessions -> handler/pipeline api will have to be extended for persistence + * distributed sessions using jgroups + * session timeout -> handlers/pipeline api will have to be extended for cleanup + */ +package org.rzo.netty.ahessian.session; \ No newline at end of file diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/stopable/StopHandler.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/stopable/StopHandler.java new file mode 100644 index 0000000000..4c13608d62 --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/stopable/StopHandler.java @@ -0,0 +1,20 @@ +package org.rzo.netty.ahessian.stopable; + +import org.jboss.netty.channel.ChannelHandlerContext; +import org.jboss.netty.channel.ChannelPipeline; +import org.jboss.netty.channel.ChannelStateEvent; +import org.jboss.netty.channel.SimpleChannelUpstreamHandler; + +public class StopHandler extends SimpleChannelUpstreamHandler +{ + + @Override + public void channelClosed(final ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception + { + ChannelPipeline p = ctx.getPipeline(); + if (p instanceof StopablePipeline) + ((StopablePipeline)p).stop(); + } + + +} diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/stopable/StopableHandler.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/stopable/StopableHandler.java new file mode 100644 index 0000000000..62dc34c31e --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/stopable/StopableHandler.java @@ -0,0 +1,10 @@ +package org.rzo.netty.ahessian.stopable; + +import org.jboss.netty.channel.ChannelHandler; + +public interface StopableHandler extends ChannelHandler +{ + public void stop(); + public boolean isStopEnabled(); + public void setStopEnabled(boolean stopEnabled); +} diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/stopable/StopablePipeline.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/stopable/StopablePipeline.java new file mode 100644 index 0000000000..dfcf62b648 --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/stopable/StopablePipeline.java @@ -0,0 +1,36 @@ +package org.rzo.netty.ahessian.stopable; + +import org.jboss.netty.channel.ChannelHandler; +import org.jboss.netty.channel.DefaultChannelPipeline; + +public class StopablePipeline extends DefaultChannelPipeline +{ + public void stop() + { + ChannelHandler handler = this.getFirst(); + while (handler != null) + { + if (handler instanceof StopableHandler) + { + StopableHandler stopableHandler = (StopableHandler) handler; + if (stopableHandler.isStopEnabled()) + try + { + stopableHandler.stop(); + } + catch (Exception ex) + { + + } + } + this.removeFirst(); + handler = this.getFirst(); + } + } + + public static StopablePipeline pipeline() + { + return new StopablePipeline(); + } + +} diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/timeout/AbstractHeartBeatHandler.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/timeout/AbstractHeartBeatHandler.java new file mode 100644 index 0000000000..b11ab707f4 --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/timeout/AbstractHeartBeatHandler.java @@ -0,0 +1,82 @@ +package org.rzo.netty.ahessian.timeout; + +import org.jboss.netty.channel.ChannelHandlerContext; +import org.jboss.netty.channel.ChannelStateEvent; +import org.jboss.netty.channel.SimpleChannelHandler; +import org.jboss.netty.util.Timeout; +import org.jboss.netty.util.Timer; +import org.jboss.netty.util.TimerTask; +import org.rzo.netty.ahessian.Constants; + +abstract class AbstractHeartBeatHandler extends SimpleChannelHandler +{ + volatile long _lastCalled; + volatile ChannelHandlerContext _ctx; + final IntervalTimer _intervalTimer; + final String _name; + + public AbstractHeartBeatHandler(final String name, final Timer timer, final long timeout) + { + _name = name; + final TimerTask task = new TimerTask() + { + public void run(Timeout nTimeout) throws Exception + { + if (((getLastCalled() + timeout) <= System.currentTimeMillis()) && isConnected()) + try + { + timedOut(_ctx); + } + catch (Exception e) + { + Constants.ahessianLogger.warn("", e); + } + } + + }; + _intervalTimer = new IntervalTimer(timer, task, timeout); + } + + abstract void timedOut(ChannelHandlerContext ctx); + + long getLastCalled() + { + return _lastCalled; + } + + boolean isConnected() + { + return _ctx != null && _ctx.getChannel().isConnected(); + } + + public void channelDisconnected( + ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception + { + _ctx = null; + _intervalTimer.stop(); + ctx.sendUpstream(e); + } + + protected void ping() + { + _lastCalled = System.currentTimeMillis(); + } + + + public void channelConnected( + ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception { + ping(); + _ctx = ctx; + _intervalTimer.setName(_name+":"+_ctx.getChannel().getId()); + + Constants.ahessianLogger.info("AbstractHeartBeatHandler scheduler started: "+ _intervalTimer.getInterval()); + _intervalTimer.start(); + ctx.sendUpstream(e); + } + + + + + + +} diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/timeout/ClientHeartBeatHandler.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/timeout/ClientHeartBeatHandler.java new file mode 100644 index 0000000000..5d936d762f --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/timeout/ClientHeartBeatHandler.java @@ -0,0 +1,49 @@ +package org.rzo.netty.ahessian.timeout; + + +import java.util.Date; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.jboss.netty.buffer.ChannelBuffers; +import org.jboss.netty.channel.ChannelFuture; +import org.jboss.netty.channel.ChannelHandlerContext; +import org.jboss.netty.channel.Channels; +import org.jboss.netty.channel.DownstreamMessageEvent; +import org.jboss.netty.channel.MessageEvent; +import org.jboss.netty.util.Timer; +import org.rzo.netty.ahessian.Constants; + +public class ClientHeartBeatHandler extends AbstractHeartBeatHandler +{ + + public ClientHeartBeatHandler(String name, Timer timer, long timeout) + { + super(name, timer, timeout); + } + + @Override + void timedOut(ChannelHandlerContext ctx) + { + Constants.ahessianLogger.info("no writes since "+new Date(getLastCalled())+" -> send empty buffer heartbeat"); + ChannelFuture future = Channels.future(_ctx.getChannel()); + ChannelBuffer b = ChannelBuffers.buffer(1); + b.writeByte(0); + _ctx.sendDownstream(new DownstreamMessageEvent(_ctx.getChannel(), future, b, _ctx.getChannel().getRemoteAddress())); + try + { + future.await(); + } + catch (InterruptedException e) + { + e.printStackTrace(); + } + } + + public void writeRequested(ChannelHandlerContext ctx, MessageEvent e) throws Exception { + ping(); + ctx.sendDownstream(e); + } + + + +} diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/timeout/IntervalTimer.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/timeout/IntervalTimer.java new file mode 100644 index 0000000000..5b265bdfd7 --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/timeout/IntervalTimer.java @@ -0,0 +1,126 @@ +package org.rzo.netty.ahessian.timeout; + +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +import org.jboss.netty.util.Timeout; +import org.jboss.netty.util.Timer; +import org.jboss.netty.util.TimerTask; +import org.rzo.netty.ahessian.utils.MyReentrantLock; + +public class IntervalTimer +{ + private final Timer _timer; + private final TimerTask _task; + private volatile Timeout _timeout; + private final long _interval; + private volatile String _name = "?"; + private final Lock _lock = new MyReentrantLock(); + + public IntervalTimer(final Timer timer, final TimerTask task, final long interval) + { + _timer = timer; + _task = new TimerTask() + { + + public void run(Timeout timeout) throws Exception + { + _lock.lock(); + try + { + if (!timeout.equals(getTimeout())) + { + //System.out.println("other timeout -> ignore"); + return; + } + //System.out.println(new Date()+" timer called " + _name+"/"+_interval); + try + { + task.run(timeout); + } + catch (Throwable ex) + { + ex.printStackTrace(); + } + if (getTimeout() != null) + setTimeout(_timer.newTimeout(this, interval, TimeUnit.MILLISECONDS)); + } + finally + { + _lock.unlock(); + } + + } + + }; + _interval = interval; + } + + synchronized public void start() + { + _lock.lock(); + try + { + if (_timeout != null) + return; + //System.out.println("starting timer "+_name); + setTimeout(_timer.newTimeout(_task, _interval, TimeUnit.MILLISECONDS)); + } + catch (Exception ex) + { + ex.printStackTrace(); + } + finally + { + _lock.unlock(); + } + } + + synchronized public void stop() + { + _lock.lock(); + try + { + if (getTimeout() == null) + return; + //System.out.println("stopping timer "+_name); + getTimeout().cancel(); + setTimeout(null); + } + catch (Exception ex) + { + ex.printStackTrace(); + } + finally + { + _lock.unlock(); + } + } + + synchronized public boolean isActive() + { + return _timeout != null; + } + + public long getInterval() + { + return _interval; + } + + public void setName(String name) + { + _name = name; + } + + private Timeout getTimeout() + { + return _timeout; + } + + private void setTimeout(Timeout timeout) + { + _timeout = timeout; + } + +} diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/timeout/ServerHeartBeatHandler.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/timeout/ServerHeartBeatHandler.java new file mode 100644 index 0000000000..f7053c5e2a --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/timeout/ServerHeartBeatHandler.java @@ -0,0 +1,37 @@ +package org.rzo.netty.ahessian.timeout; + + +import java.util.Date; + +import org.jboss.netty.channel.ChannelHandlerContext; +import org.jboss.netty.channel.MessageEvent; +import org.jboss.netty.handler.timeout.ReadTimeoutException; +import org.jboss.netty.util.Timer; +import org.rzo.netty.ahessian.Constants; + +public class ServerHeartBeatHandler extends AbstractHeartBeatHandler +{ + static final ReadTimeoutException EXCEPTION = new ReadTimeoutException(); + + + public ServerHeartBeatHandler(String name, Timer timer, long timeout) + { + super(name, timer, timeout); + } + + @Override + void timedOut(ChannelHandlerContext ctx) + { + Constants.ahessianLogger.info("no reads since "+new Date(getLastCalled())+" -> close channel"); + ctx.getChannel().close(); + } + + public void messageReceived( + ChannelHandlerContext ctx, MessageEvent e) throws Exception { + ping(); + ctx.sendUpstream(e); + } + + + +} diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/utils/MyBlockingQueue.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/utils/MyBlockingQueue.java new file mode 100644 index 0000000000..7bd1ee862f --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/utils/MyBlockingQueue.java @@ -0,0 +1,14 @@ +package org.rzo.netty.ahessian.utils; + +import java.util.concurrent.BlockingQueue; + +public interface MyBlockingQueue extends BlockingQueue +{ + + long getTimeout(Integer group); + + boolean remove(T message, Integer group); + + void put(T message, Integer group) throws InterruptedException; + +} diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/utils/MyLinkedBlockingQueue.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/utils/MyLinkedBlockingQueue.java new file mode 100644 index 0000000000..752adb5fa5 --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/utils/MyLinkedBlockingQueue.java @@ -0,0 +1,23 @@ +package org.rzo.netty.ahessian.utils; + +import java.util.concurrent.LinkedBlockingQueue; + +public class MyLinkedBlockingQueue extends LinkedBlockingQueue implements MyBlockingQueue +{ + + public long getTimeout(Integer group) + { + return -1; + } + + public void put(T message, Integer group) throws InterruptedException + { + put(message); + } + + public boolean remove(T message, Integer group) + { + return remove(message); + } + +} diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/utils/MyReentrantLock.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/utils/MyReentrantLock.java new file mode 100644 index 0000000000..b730805063 --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/utils/MyReentrantLock.java @@ -0,0 +1,36 @@ +package org.rzo.netty.ahessian.utils; + +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.ReentrantLock; + +public class MyReentrantLock extends ReentrantLock +{ + + final static long timeout = 100; // millis + @Override + synchronized public void lock() { + // To avoid a hang that seems to be caused by a lost-wakeup + // we repeatedly use tryAcquire in a loop so that we can + // poll the lock state + + boolean locked = false; + boolean interrupted = false; + + while(!locked) { + try { + locked = tryLock(timeout, TimeUnit.MILLISECONDS); + } + catch (InterruptedException ex) { + interrupted = true; + } + } + + if (interrupted) { + // re-assert interrupt state that occurred while we + // were acquiring the lock + Thread.currentThread().interrupt(); + } + } + + +} diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/utils/TimedBlockingPriorityQueue.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/utils/TimedBlockingPriorityQueue.java new file mode 100644 index 0000000000..baa207d7f4 --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/ahessian/utils/TimedBlockingPriorityQueue.java @@ -0,0 +1,391 @@ +package org.rzo.netty.ahessian.utils; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +import org.jboss.netty.util.Timeout; +import org.jboss.netty.util.Timer; +import org.jboss.netty.util.TimerTask; +import org.rzo.netty.ahessian.Constants; + +public class TimedBlockingPriorityQueue implements MyBlockingQueue +{ + + int _defaultGroup = 0; + LinkedList[] _queues; + int[] _sizes; + long[] _timeouts; + Map _timers = new HashMap(); + Timer _timer; + Lock _lock = new MyReentrantLock(); + Condition _hasData = _lock.newCondition(); + volatile boolean waiting = false; + int _size = 0; + String _name = "?"; + T _last = null; + + public TimedBlockingPriorityQueue(Map options, Timer timer, String name) + { + _name = name; + List groups = new ArrayList(); + _timer = timer; + for (String key : options.keySet()) + { + if (key.startsWith("group.")) + + try { + String rest = key.substring(key.indexOf('.')+1); + int x = Integer.parseInt(rest.substring(0, rest.indexOf('.'))); + if (!groups.contains(x)) + groups.add(x); + + } + catch (Exception ex) + { + Constants.ahessianLogger.warn("", ex); + } + } + if (!groups.contains(0)) + groups.add(0); + Collections.sort(groups); + int size = groups.get(groups.size()-1)+1; + _queues = new LinkedList[size]; + _timeouts = new long[size]; + _sizes = new int[size]; + for (int i=0; i(); + _sizes[i] = queueSize; + Object timeout = options.get("group."+i+".timeout"); + long longTimeout = (long)-1; + if (timeout instanceof Number) + longTimeout = ((Number)timeout).longValue(); + _timeouts[i] = longTimeout; + } + } + + public TimedBlockingPriorityQueue(String name) + { + _name = name; + _queues = new LinkedList[1]; + _timeouts = new long[1]; + _sizes = new int[1]; + _queues[0] = new LinkedList(); + _timeouts[0] = -1; + _sizes[0] = Integer.MAX_VALUE; + } + + public boolean add(Object e) + { + throw new RuntimeException("Unimplemented"); + } + + public T element() + { + throw new RuntimeException("Unimplemented"); + } + + public boolean offer(T e) + { + return offer(e, _defaultGroup); + } + + public boolean offer(final T e, int group) + { + boolean result = false; + _lock.lock(); + try + { + //if (_size == 0) + //System.out.println("LRUQueue not empty: "+ _name); + _size++; + if (group >= _queues.length) + { + Constants.ahessianLogger.warn("group "+group+" not defined -> using group 0"); + group = 0; + } + final LinkedList q = _queues[group]; + result = q.offer((T) e); + if (q.size() >= _sizes[group]) + { + // if queue is full remove an element and undo its timer + T o = q.remove(); + Timeout timer = _timers.remove(o); + if (timer != null) + timer.cancel(); + Constants.ahessianLogger.warn("queue overflow -> removed "+e); + } + if (result) + _last = e; + + if (_timer != null && result && _timeouts[group] > 0) + { + Timeout timer = _timer.newTimeout(new TimerTask() + { + + public void run(Timeout arg0) throws Exception + { + _lock.lock(); + try + { + q.remove(e); + Constants.ahessianLogger.warn("message timed out -> removed from queue "+e); + } + finally + { + _lock.unlock(); + } + } + + }, _timeouts[group], TimeUnit.MILLISECONDS); + } + + if (result && waiting) + try + { + _hasData.signal(); + } + catch (Exception ex) + { + Constants.ahessianLogger.warn("", ex); + } + + } + finally + { + _lock.unlock(); + } + return result; + } + + public T poll() + { + T result = null; + for (int i=_queues.length-1; i>=0; i--) + { + result = poll(i); + if (result != null) + { + _size--; + return result; + } + } + return null; + } + + public T poll(int group) + { + LinkedList q = _queues[group]; + T result = null; + if (q != null) + { + result = q.poll(); + if (result != null) + { + Timeout timer = _timers.remove(result); + if (timer != null) + timer.cancel(); + } + } + return result; + } + + public T peek() + { + throw new RuntimeException("Unimplemented"); + } + + public T remove() + { + throw new RuntimeException("Unimplemented"); + } + + public boolean addAll(Collection c) + { + throw new RuntimeException("Unimplemented"); + } + + public void clear() + { + _lock.lock(); + try + { + for (int i=0; i<_queues.length; i++) + { + clear(i); + } + } + finally + { + _lock.unlock(); + } + } + + public void clear(int group) + { + _lock.lock(); + try + { + if (_queues[group] != null) + _queues[group].clear(); + } + finally + { + _lock.unlock(); + } + } + + public boolean contains(Object o) + { + throw new RuntimeException("Unimplemented"); + } + + public boolean containsAll(Collection c) + { + throw new RuntimeException("Unimplemented"); + } + + public boolean isEmpty() + { + throw new RuntimeException("Unimplemented"); + } + + public Iterator iterator() + { + throw new RuntimeException("Unimplemented"); + } + + public boolean remove(Object o) + { + throw new RuntimeException("Unimplemented"); + } + + public boolean remove(T o, Integer group) + { + if (_queues[group] != null) + return _queues[group].remove(o); + + return false; + } + + public boolean removeAll(Collection c) + { + throw new RuntimeException("Unimplemented"); + } + + public boolean retainAll(Collection c) + { + throw new RuntimeException("Unimplemented"); + } + + public int size() + { + return _size; + } + + public T[] toArray() + { + throw new RuntimeException("Unimplemented"); + } + + public T[] toArray(Object[] a) + { + throw new RuntimeException("Unimplemented"); + } + + public int drainTo(Collection c) + { + throw new RuntimeException("Unimplemented"); + } + + public int drainTo(Collection c, int maxElements) + { + throw new RuntimeException("Unimplemented"); + } + + public boolean offer(Object e, long timeout, TimeUnit unit) throws InterruptedException + { + throw new RuntimeException("Unimplemented"); + } + + public T poll(long timeout, TimeUnit unit) throws InterruptedException + { + throw new RuntimeException("Unimplemented"); + } + + public void put(T e) throws InterruptedException + { + put(e, _defaultGroup); + } + + public void put(T e, Integer group) + { + offer(e, group); + } + + public int remainingCapacity() + { + throw new RuntimeException("Unimplemented"); + } + + public T take() throws InterruptedException + { + T result = null; + _lock.lock(); + try + { + do + { + result = poll(); + if (result == null) + try + { + waiting = true; + _hasData.await(); + } + finally + { + waiting = false; + } + + } + while (result == null); + if (result == _last) + _last = null; + } + finally + { + _lock.unlock(); + } + //if (_size == 0) + // System.out.println("LRUQueue empty: "+_name); + + return result; + + } + + public long getTimeout(Integer group) + { + if (group < _timeouts.length) + return _timeouts[group]; + return -1; + } + + public T getLast() + { + return _last; + } + + +} diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/mcast/MulticastEndpoint.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/mcast/MulticastEndpoint.java new file mode 100644 index 0000000000..9cfea9f126 --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/mcast/MulticastEndpoint.java @@ -0,0 +1,128 @@ +package org.rzo.netty.mcast; + + +import java.net.*; +import java.nio.charset.Charset; +import java.util.Arrays; +import java.util.Enumeration; +import java.util.Random; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; + +import org.jboss.netty.bootstrap.ConnectionlessBootstrap; +import org.jboss.netty.buffer.ChannelBuffer; +import org.jboss.netty.buffer.ChannelBuffers; +import org.jboss.netty.channel.ChannelPipelineFactory; +import org.jboss.netty.channel.MessageEvent; +import org.jboss.netty.channel.socket.DatagramChannel; +import org.jboss.netty.channel.socket.DatagramChannelFactory; +import org.jboss.netty.channel.socket.oio.OioDatagramChannelFactory; + +public class MulticastEndpoint +{ + + private String mcastGroupIp = "228.10.10.10"; + private int mcastGroupPort = 12345; + private String bindAddress = "192.168.0.10"; + + private DatagramChannel datagramChannel; + private ConnectionlessBootstrap connectionlessBootstrap; + private InetSocketAddress multicastAddress; + private static Executor executor = Executors.newCachedThreadPool(); + byte[] id; + boolean init = false; + + public void init(ChannelPipelineFactory factory) throws Exception + { + id = String.format("%1$020d", Math.abs(new Random(System.currentTimeMillis()).nextLong())).getBytes(); + DatagramChannelFactory datagramChannelFactory = new + OioDatagramChannelFactory(executor); + + connectionlessBootstrap = new + ConnectionlessBootstrap(datagramChannelFactory); + connectionlessBootstrap.setOption("broadcast", true); + connectionlessBootstrap.setPipelineFactory(factory); + datagramChannel = (DatagramChannel) + connectionlessBootstrap.bind(new InetSocketAddress(mcastGroupPort)); + multicastAddress = new InetSocketAddress(mcastGroupIp, mcastGroupPort); + NetworkInterface networkInterface = + NetworkInterface.getByInetAddress(InetAddress.getByName(bindAddress)); + //for (Enumeration nifs = NetworkInterface.getNetworkInterfaces(); nifs.hasMoreElements(); ) + datagramChannel.joinGroup(multicastAddress, null);//(NetworkInterface) nifs.nextElement()); + init = true; + } + + public boolean isInit() + { + return init; + } + + public void send(ChannelBuffer msg) throws Exception + { + ChannelBuffer idbuf = ChannelBuffers.wrappedBuffer(id); + datagramChannel.write(ChannelBuffers.wrappedBuffer(idbuf, msg), multicastAddress); + } + + public String getMcastGroupIp() + { + return mcastGroupIp; + } + + public int getMcastGroupPort() + { + return mcastGroupPort; + } + + public String getBindAddress() + { + return bindAddress; + } + + public void setMcastGroupIp(String mcastGroupIp) + { + this.mcastGroupIp = mcastGroupIp; + } + + public void setMcastGroupPort(int mcastGroupPort) + { + this.mcastGroupPort = mcastGroupPort; + } + + public void setBindAddress(String bindAddress) + { + this.bindAddress = bindAddress; + } + + public void close() + { + datagramChannel.close(); + connectionlessBootstrap.releaseExternalResources(); + } + + public ChannelBuffer getMessage(MessageEvent e) + { + if (checkMessage(e)) + { + ChannelBuffer m = (ChannelBuffer) e.getMessage(); + return m.slice(id.length, m.readableBytes()-id.length); + } + return null; + } + + public String getStringMessage(MessageEvent e) + { + ChannelBuffer m = getMessage(e); + if (m == null) + return null; + return m.toString(Charset.defaultCharset()); + } + + public boolean checkMessage(MessageEvent e) + { + byte[] eId = new byte[id.length]; + ((ChannelBuffer) e.getMessage()).getBytes(0, eId, 0, eId.length); + return (! Arrays.equals(id, eId)); + } + + +} diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/mcast/bridge/MulticastAccessPoint.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/mcast/bridge/MulticastAccessPoint.java new file mode 100644 index 0000000000..99cc04f324 --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/mcast/bridge/MulticastAccessPoint.java @@ -0,0 +1,105 @@ +package org.rzo.netty.mcast.bridge; + +import java.net.InetSocketAddress; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.Executors; + +import org.jboss.netty.bootstrap.ServerBootstrap; +import org.jboss.netty.buffer.ChannelBuffer; +import org.jboss.netty.channel.Channel; +import org.jboss.netty.channel.ChannelFactory; +import org.jboss.netty.channel.ChannelHandlerContext; +import org.jboss.netty.channel.ChannelPipeline; +import org.jboss.netty.channel.ChannelPipelineFactory; +import org.jboss.netty.channel.Channels; +import org.jboss.netty.channel.ChildChannelStateEvent; +import org.jboss.netty.channel.ExceptionEvent; +import org.jboss.netty.channel.MessageEvent; +import org.jboss.netty.channel.SimpleChannelUpstreamHandler; +import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory; +import org.rzo.netty.ahessian.Constants; +import org.rzo.netty.mcast.MulticastEndpoint; + +public class MulticastAccessPoint +{ + + private static List remoteChannels = Collections.synchronizedList(new ArrayList()); + private static MulticastEndpoint mcast = new MulticastEndpoint(); + + public static void main(String[] args) + { + int port = Integer.parseInt(args[0]); + + ChannelFactory factory = + new NioServerSocketChannelFactory( + Executors.newCachedThreadPool(), + Executors.newCachedThreadPool()); + + ServerBootstrap bootstrap = new ServerBootstrap(factory); + + bootstrap.setPipelineFactory(new ChannelPipelineFactory() { + public ChannelPipeline getPipeline() { + return Channels.pipeline(new SimpleChannelUpstreamHandler() + { + @Override + public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception + { + if (mcast != null && mcast.isInit()) + mcast.send((ChannelBuffer) e.getMessage()); + } + + @Override + public void childChannelOpen(ChannelHandlerContext ctx, ChildChannelStateEvent e) + { + remoteChannels.add(ctx.getChannel()); + } + + @Override + public void childChannelClosed(ChannelHandlerContext ctx, ChildChannelStateEvent e) + { + remoteChannels.add(ctx.getChannel()); + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) { + Throwable cause = e.getCause(); + System.out.println(e); + } + }); + } + }); + bootstrap.bind(new InetSocketAddress(port)); + + try + { + mcast.init(new ChannelPipelineFactory() { + public ChannelPipeline getPipeline() { + return Channels.pipeline(new SimpleChannelUpstreamHandler() + { + @Override + public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception + { + ChannelBuffer b = mcast.getMessage(e); + if (b == null) + return; + for (Channel c : remoteChannels) + { + if (c.isConnected()) + c.write(b); + } + } + + }); + } + }); + } + catch (Exception e) + { + Constants.ahessianLogger.warn("", e); + } + + } + +} diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/mcast/bridge/MulticastAdapter.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/mcast/bridge/MulticastAdapter.java new file mode 100644 index 0000000000..c5ce3463f6 --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/mcast/bridge/MulticastAdapter.java @@ -0,0 +1,107 @@ +package org.rzo.netty.mcast.bridge; + +import java.net.ConnectException; +import java.net.InetSocketAddress; +import java.util.Timer; +import java.util.TimerTask; +import java.util.concurrent.Executors; + +import org.jboss.netty.bootstrap.ClientBootstrap; +import org.jboss.netty.buffer.ChannelBuffer; +import org.jboss.netty.channel.Channel; +import org.jboss.netty.channel.ChannelFactory; +import org.jboss.netty.channel.ChannelFuture; +import org.jboss.netty.channel.ChannelHandlerContext; +import org.jboss.netty.channel.ChannelPipeline; +import org.jboss.netty.channel.ChannelPipelineFactory; +import org.jboss.netty.channel.ChannelStateEvent; +import org.jboss.netty.channel.Channels; +import org.jboss.netty.channel.ExceptionEvent; +import org.jboss.netty.channel.MessageEvent; +import org.jboss.netty.channel.SimpleChannelUpstreamHandler; +import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory; +import org.rzo.netty.mcast.MulticastEndpoint; + +public class MulticastAdapter +{ + private static Channel channel; + private static MulticastEndpoint mcast = new MulticastEndpoint(); + private static long RECONNECT_DELAY = 5000; + private static Timer timer = new Timer(); + private static ClientBootstrap bootstrap; + + public static void main(String[] args) throws Exception + { + String host = args[0]; + int port = Integer.parseInt(args[1]); + + ChannelFactory factory = + new NioClientSocketChannelFactory( + Executors.newCachedThreadPool(), + Executors.newCachedThreadPool()); + + bootstrap = new ClientBootstrap(factory); + bootstrap.setOption( + "remoteAddress", new InetSocketAddress(host, port)); + + bootstrap.setPipelineFactory(new ChannelPipelineFactory() { + public ChannelPipeline getPipeline() { + return Channels.pipeline(new SimpleChannelUpstreamHandler() + { + @Override + public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception + { + if (mcast != null && mcast.isInit()) + mcast.send((ChannelBuffer) e.getMessage()); + } + + @Override + public void channelClosed(ChannelHandlerContext ctx, ChannelStateEvent e) { + timer.schedule(new TimerTask() { + public void run() { + bootstrap.connect(); + } + }, RECONNECT_DELAY); + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) { + Throwable cause = e.getCause(); + if (cause instanceof ConnectException) + { + System.out.println("conection lost: reconnecting..."); + } + ctx.getChannel().close(); + } + + + + }); + } + }); + + ChannelFuture f = bootstrap.connect(); + channel = f.getChannel(); + + mcast.init(new ChannelPipelineFactory() { + public ChannelPipeline getPipeline() { + return Channels.pipeline(new SimpleChannelUpstreamHandler() + { + @Override + public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception + { + ChannelBuffer b = mcast.getMessage(e); + if (b == null) + return; + if (channel != null && channel.isConnected()) + channel.write(b); + } + + }); + } + }); + + + } + +} diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/mcast/discovery/DiscoveryClient.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/mcast/discovery/DiscoveryClient.java new file mode 100644 index 0000000000..13feb4767f --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/mcast/discovery/DiscoveryClient.java @@ -0,0 +1,189 @@ +package org.rzo.netty.mcast.discovery; + +import static org.jboss.netty.channel.Channels.pipeline; + +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; + +import org.jboss.netty.buffer.ChannelBuffers; +import org.jboss.netty.channel.ChannelHandlerContext; +import org.jboss.netty.channel.ChannelPipeline; +import org.jboss.netty.channel.ChannelPipelineFactory; +import org.jboss.netty.channel.MessageEvent; +import org.jboss.netty.channel.SimpleChannelUpstreamHandler; +import org.jboss.netty.handler.ipfilter.IpFilterRule; +import org.jboss.netty.handler.ipfilter.IpFilterRuleList; +import org.rzo.netty.ahessian.Constants; +import org.rzo.netty.mcast.MulticastEndpoint; + +public class DiscoveryClient extends MulticastEndpoint +{ + +private String name; +private Set hosts = Collections.synchronizedSet(new HashSet()); +private volatile boolean stop = false; +private Set listeners = Collections.synchronizedSet(new HashSet()); + +private static Executor executor = Executors.newCachedThreadPool(); +private IpFilterRuleList firewall; + + + +public void init() throws Exception +{ + ChannelPipelineFactory factory = new ChannelPipelineFactory() + { + public ChannelPipeline getPipeline() throws Exception + { + ChannelPipeline pipeline = pipeline(); + pipeline.addLast("discoveryClient", new SimpleChannelUpstreamHandler() + { + + @Override + public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception + { + try + { + String response = getStringMessage(e); + if (response == null) + return; + String[] resp = response.split("&"); + if (resp.length == 3) + { + String remoteName = resp[0]; + if (!name.equals(remoteName)) + return; + if (!validate(e)) + return; + String host = resp[1]; + // check the name. if not valid will cause an exception + InetAddress.getByName(host); + // get the port. if not a number will cause an exception + int port = Integer.parseInt(resp[2]); + if (!hosts.contains(response)) + { + hosts.add(response); + for (DiscoveryListener listener : listeners) + { + listener.newHost(name, response); + } + } + + } + } + catch (Exception ex) + { + Constants.ahessianLogger.warn("", ex); + } + } + }); + return pipeline; + } + + }; + super.init(factory); + +} + +public void start() throws Exception +{ + stop = false; + discoverServices(); +} + + + +private void discoverServices() throws Exception +{ + executor.execute(new Runnable() + { + public void run() + { + while (!stop) + { + try + { + send(ChannelBuffers.wrappedBuffer((name).getBytes())); + } + catch (Exception e) + { + Constants.ahessianLogger.warn("", e); + } + try + { + Thread.sleep(1000); + } + catch (InterruptedException e) + { + Constants.ahessianLogger.warn("", e); + } + } + } + }); +} + + + +public String getName() +{ + return name; +} + + +public void setName(String name) +{ + this.name = name; +} + +public void stop() +{ + stop = true; +} + +public void addListener(DiscoveryListener listener) +{ + listeners.add(listener); +} + +public void removeHost(String host) +{ + hosts.remove(host); +} + +private boolean validate(MessageEvent e) +{ + if (firewall == null) + return true; + else + { + InetAddress inetAddress = ((InetSocketAddress)e.getRemoteAddress()).getAddress(); + Iterator iterator = firewall.iterator(); + IpFilterRule ipFilterRule = null; + while (iterator.hasNext()) + { + ipFilterRule = iterator.next(); + if (ipFilterRule.contains(inetAddress)) + { + // Match founds, is it a ALLOW or DENY rule + return ipFilterRule.isAllowRule(); + } + } + // No limitation founds and no allow either, but as it is like Firewall rules, it is therefore accepted + return true; + } +} + +public void setIpSet(IpFilterRuleList ipSet) +{ + this.firewall = ipSet; +} + + +} + diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/mcast/discovery/DiscoveryListener.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/mcast/discovery/DiscoveryListener.java new file mode 100644 index 0000000000..463f31a3e4 --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/mcast/discovery/DiscoveryListener.java @@ -0,0 +1,6 @@ +package org.rzo.netty.mcast.discovery; + +public interface DiscoveryListener +{ + public void newHost(String name, String host); +} diff --git a/javaUtilities/yajsw/src/ahessian/org/rzo/netty/mcast/discovery/DiscoveryServer.java b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/mcast/discovery/DiscoveryServer.java new file mode 100644 index 0000000000..c7f3c2f12b --- /dev/null +++ b/javaUtilities/yajsw/src/ahessian/org/rzo/netty/mcast/discovery/DiscoveryServer.java @@ -0,0 +1,153 @@ +package org.rzo.netty.mcast.discovery; + +import static org.jboss.netty.channel.Channels.pipeline; + +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.NetworkInterface; +import java.util.Enumeration; +import java.util.Iterator; + +import org.jboss.netty.buffer.ChannelBuffers; +import org.jboss.netty.channel.ChannelHandlerContext; +import org.jboss.netty.channel.ChannelPipeline; +import org.jboss.netty.channel.ChannelPipelineFactory; +import org.jboss.netty.channel.MessageEvent; +import org.jboss.netty.channel.SimpleChannelUpstreamHandler; +import org.jboss.netty.handler.ipfilter.IpFilterRule; +import org.jboss.netty.handler.ipfilter.IpFilterRuleList; +import org.rzo.netty.ahessian.Constants; +import org.rzo.netty.mcast.MulticastEndpoint; + +public class DiscoveryServer extends MulticastEndpoint +{ + private String name; + private String host; + private int port; + private IpFilterRuleList firewall; + + public void init() throws Exception + { + if (host == null) + host = whatIsMyIp(); + + ChannelPipelineFactory factory = new ChannelPipelineFactory() + { + public ChannelPipeline getPipeline() throws Exception + { + ChannelPipeline pipeline = pipeline(); + pipeline.addLast("discoveryServer", new SimpleChannelUpstreamHandler() + { + + @Override + public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception + { + String request = getStringMessage(e); + if (request == null) + return; + if (name != null && name.equals(request) && host != null && port > 0) + { + if (validate(e)) + send(ChannelBuffers.wrappedBuffer((name + "&" + host + "&" + port).getBytes())); + } + } + }); + return pipeline; + } + + }; + super.init(factory); + } + + public String getName() + { + return name; + } + + public String getHost() + { + return host; + } + + public int getPort() + { + return port; + } + + public void setName(String name) + { + this.name = name; + } + + public void setHost(String host) + { + this.host = host; + } + + public void setPort(int port) + { + this.port = port; + } + + public void setIpSet(IpFilterRuleList ipSet) + { + this.firewall = ipSet; + } + + private String whatIsMyIp() + { + String result = null; + try + { + Enumeration e = NetworkInterface.getNetworkInterfaces(); + + while (e.hasMoreElements()) + { + NetworkInterface ne = (NetworkInterface) e.nextElement(); + Enumeration e2 = ne.getInetAddresses(); + + while (e2.hasMoreElements()) + { + InetAddress ia = (InetAddress) e2.nextElement(); + + if (!ia.isAnyLocalAddress() && !ia.isLinkLocalAddress() + && !ia.isLoopbackAddress() && !ia.isMulticastAddress()) + if (result == null || !ia.isSiteLocalAddress()) + { + result = ia.getHostAddress(); + } + } + } + } catch (Exception ex) + { + Constants.ahessianLogger.warn("", ex); + } + return result; + + } + + private boolean validate(MessageEvent e) + { + if (firewall == null) + return true; + else + { + InetAddress inetAddress = ((InetSocketAddress)e.getRemoteAddress()).getAddress(); + Iterator iterator = firewall.iterator(); + IpFilterRule ipFilterRule = null; + while (iterator.hasNext()) + { + ipFilterRule = iterator.next(); + if (ipFilterRule.contains(inetAddress)) + { + // Match founds, is it a ALLOW or DENY rule + return ipFilterRule.isAllowRule(); + } + } + // No limitation founds and no allow either, but as it is like Firewall rules, it is therefore accepted + return true; + } + } + + +} diff --git a/javaUtilities/yajsw/src/app/java/org/rzo/yajsw/app/AbstractWrapperJVMMain.java b/javaUtilities/yajsw/src/app/java/org/rzo/yajsw/app/AbstractWrapperJVMMain.java new file mode 100644 index 0000000000..76a7228685 --- /dev/null +++ b/javaUtilities/yajsw/src/app/java/org/rzo/yajsw/app/AbstractWrapperJVMMain.java @@ -0,0 +1,51 @@ +package org.rzo.yajsw.app; + +import java.lang.reflect.Field; +import java.security.AccessController; +import java.security.PrivilegedAction; + +public abstract class AbstractWrapperJVMMain +{ + + /** The WRAPPE r_ manager. */ + public static WrapperManager WRAPPER_MANAGER; + public static Throwable exception = null; + // call java logger, so that it inits before groovy & co + //private static Logger dummy = Logger.getAnonymousLogger(); + + static class YajswUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler { + public void uncaughtException(Thread t, Throwable e) { + System.err.println ("Uncaught exception by " + t + ":"); + System.err.println(e.getClass().getName()+":"+e.getMessage()); + e.printStackTrace(); + } + } + + protected static void postExecute() + { + int exitCode; + if (exception == null) + exitCode = WRAPPER_MANAGER.getExitOnMainTerminate(); + else + exitCode = WRAPPER_MANAGER.getExitOnException(); + if (exitCode >= 0) + System.exit(exitCode); + } + + protected static void preExecute(String[] args) + { + final String[] finalArgs = args; + WRAPPER_MANAGER = (WrapperManager) AccessController.doPrivileged(new PrivilegedAction() + { + public Object run() + { + // set our own handler so that we may log out of memory errors + Thread.setDefaultUncaughtExceptionHandler(new YajswUncaughtExceptionHandler ()); + WrapperManager result = WrapperManagerProxy.getWrapperManager(finalArgs); + return result; + } + }); + } + + +} diff --git a/javaUtilities/yajsw/src/app/java/org/rzo/yajsw/app/WrapperGroovyMain.java b/javaUtilities/yajsw/src/app/java/org/rzo/yajsw/app/WrapperGroovyMain.java new file mode 100644 index 0000000000..e71a60a0c3 --- /dev/null +++ b/javaUtilities/yajsw/src/app/java/org/rzo/yajsw/app/WrapperGroovyMain.java @@ -0,0 +1,61 @@ +package org.rzo.yajsw.app; + +import groovy.lang.GroovyClassLoader; +import groovy.lang.GroovyObject; + +import java.io.File; +import java.io.IOException; + +public class WrapperGroovyMain extends AbstractWrapperJVMMain +{ + /** + * The main method. + * + * @param args + * the args + * @throws IOException + * + * @throws IllegalAccessException * + * @throws InstantiationException + */ + public static void main(String[] args) throws IOException + { + preExecute(args); + + executeMain(); + + postExecute(); + + } + + protected static void executeMain() + { + String scriptName = WRAPPER_MANAGER.getGroovyScript(); + if (scriptName == null) + { + System.out.println("script not found in configuration -> aborting"); + System.exit(999); + } + File scriptFile = new File(scriptName); + if (!scriptFile.exists()) + { + System.out.println("script not found -> aborting: " + scriptFile.getAbsolutePath()); + System.exit(999); + } + Object[] mainMethodArgs = WRAPPER_MANAGER.getMainMethodArgs(); + try + { + ClassLoader parent = WrapperGroovyMain.class.getClassLoader(); + GroovyClassLoader loader = new GroovyClassLoader(parent); + Class groovyClass = loader.parseClass(scriptFile); + GroovyObject script = (GroovyObject) groovyClass.newInstance(); + script.invokeMethod("main", mainMethodArgs); + } + catch (Throwable e) + { + e.printStackTrace(); + exception = e; + } + } + +} diff --git a/javaUtilities/yajsw/src/app/java/org/rzo/yajsw/app/WrapperJVMMain.java b/javaUtilities/yajsw/src/app/java/org/rzo/yajsw/app/WrapperJVMMain.java new file mode 100644 index 0000000000..51d3a09566 --- /dev/null +++ b/javaUtilities/yajsw/src/app/java/org/rzo/yajsw/app/WrapperJVMMain.java @@ -0,0 +1,64 @@ +/* This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + *

+ * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ +package org.rzo.yajsw.app; + +import java.io.*; +import java.io.IOException; +import java.lang.reflect.Method; + +// TODO: Auto-generated Javadoc +/** + * The Class WrapperMain. + */ +public class WrapperJVMMain extends AbstractWrapperJVMMain +{ + /** + * The main method. + * + * @param args + * the args + * @throws IOException + * + * @throws IllegalAccessException * + * @throws InstantiationException + */ + public static void main(String[] args) throws IOException + { + preExecute(args); + + executeMain(); + + postExecute(); + + } + + protected static void executeMain() + { + final Method mainMethod = WRAPPER_MANAGER.getMainMethod(); + if (mainMethod == null) + { + System.out.println("no java main method found -> aborting"); + System.exit(999); + } + Object[] mainMethodArgs = WRAPPER_MANAGER.getMainMethodArgs(); + try + { + mainMethod.invoke(null, new Object[] + { mainMethodArgs }); + } + catch (Throwable e) + { + e.printStackTrace(); + exception = e; + } + } + +} diff --git a/javaUtilities/yajsw/src/app/java/org/rzo/yajsw/app/WrapperMainServiceUnix.java b/javaUtilities/yajsw/src/app/java/org/rzo/yajsw/app/WrapperMainServiceUnix.java new file mode 100644 index 0000000000..b8e90bd451 --- /dev/null +++ b/javaUtilities/yajsw/src/app/java/org/rzo/yajsw/app/WrapperMainServiceUnix.java @@ -0,0 +1,137 @@ +/* This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + *

+ * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ +package org.rzo.yajsw.app; + +import java.io.File; +import java.util.HashMap; +import java.util.List; + +import org.rzo.yajsw.boot.WrapperLoader; +import org.rzo.yajsw.config.YajswConfigurationImpl; +import org.rzo.yajsw.os.OperatingSystem; +import org.rzo.yajsw.os.StopableService; +import org.rzo.yajsw.wrapper.WrappedProcess; +import org.rzo.yajsw.wrapper.WrappedProcessFactory; +import org.rzo.yajsw.wrapper.WrappedProcessList; + +// TODO: Auto-generated Javadoc +/** + * The Class WrapperMainService. + */ +public class WrapperMainServiceUnix implements StopableService +{ + + /** The w. */ + static WrappedProcess w; + static volatile WrappedProcessList wList = new WrappedProcessList(); + + + /** + * Instantiates a new wrapper main service. + */ + public WrapperMainServiceUnix() + { + } + + // this is the wrapper for services + /** + * The main method. + * + * @param args + * the arguments + */ + public static void main(String[] args) + { + String wrapperJar = WrapperLoader.getWrapperJar(); + String homeDir = new File(wrapperJar).getParent(); + if (!OperatingSystem.instance().setWorkingDir(homeDir)) + { + System.out.println("could not set working dir. pls check configuration or user rights :"+homeDir); + } + + StopableService service = new WrapperMainServiceUnix(); + YajswConfigurationImpl _config = new YajswConfigurationImpl(false); + //w = WrappedProcessFactory.createProcess(_config); + // start the application + //w.setDebug(true); + //w.init(); + //w.setService(service); + + if (_config.containsKey("wrapperx.config")) + { + List configs = _config.getList("wrapperx.config"); + wList = WrappedProcessFactory.createProcessList(new HashMap(), configs, true); + for (WrappedProcess p : wList) + { + p.setService(service); + } + } + else + { + WrappedProcess w = WrappedProcessFactory.createProcess(_config); + // set service in wrapper so that we may stop the service in case the application terminates and we need to shutdown the wrapper + w.setService(service); + w.init(); + wList.add(w); + } + + w = wList.get(0); + + + /* use wrapper.control + Runtime.getRuntime().addShutdownHook(new Thread() + { + public void run() + { + w.stop(); + w.shutdown(); + // give scripts time to terminate + try + { + Thread.sleep(5000); + } + catch (InterruptedException e) + { + e.printStackTrace(); + } + } + }); + */ + wList.startAll(); + } + + public void onStop() + { + // give any running scripts time to terminate + try + { + Thread.sleep(5000); + } + catch (InterruptedException e) + { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + public void waitOnStop() + { + // TODO Auto-generated method stub + + } + + public void signalStopping(long waitHint) + { + // TODO Auto-generated method stub + + } + +} diff --git a/javaUtilities/yajsw/src/app/java/org/rzo/yajsw/app/WrapperMainServiceWin.java b/javaUtilities/yajsw/src/app/java/org/rzo/yajsw/app/WrapperMainServiceWin.java new file mode 100644 index 0000000000..4330d8d660 --- /dev/null +++ b/javaUtilities/yajsw/src/app/java/org/rzo/yajsw/app/WrapperMainServiceWin.java @@ -0,0 +1,253 @@ +/* This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + *

+ * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ +package org.rzo.yajsw.app; + +import java.io.File; +import java.util.HashMap; +import java.util.List; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; + +import jnacontrib.win32.Win32Service; + +import org.rzo.yajsw.Constants; +import org.rzo.yajsw.boot.WrapperLoader; +import org.rzo.yajsw.config.YajswConfigurationImpl; +import org.rzo.yajsw.os.OperatingSystem; +import org.rzo.yajsw.os.StopableService; +import org.rzo.yajsw.wrapper.WrappedProcess; +import org.rzo.yajsw.wrapper.WrappedProcessFactory; +import org.rzo.yajsw.wrapper.WrappedProcessList; + +// TODO: Auto-generated Javadoc +/** + * The Class WrapperMainService. + */ +public class WrapperMainServiceWin extends Win32Service implements StopableService +{ + + /** The w. */ + static volatile WrappedProcessList wList = new WrappedProcessList(); + static volatile WrappedProcess w; + static volatile boolean _waitOnStop = false; + + /** The service. */ + static WrapperMainServiceWin service; + static ExecutorService pool = Executors.newFixedThreadPool(5); + + /** + * Instantiates a new wrapper main service. + */ + public WrapperMainServiceWin() + { + } + + // this is the wrapper for services + /** + * The main method. + * + * @param args + * the arguments + */ + public static void main(String[] args) + { + String wrapperJar = WrapperLoader.getWrapperJar(); + // set home dir of the service to the wrapper jar parent, so that we may find required libs + String homeDir = new File(wrapperJar).getParent(); + if (!OperatingSystem.instance().setWorkingDir(homeDir)) + System.out.println("could not set working dir. pls check configuration or user rights :"+homeDir); + YajswConfigurationImpl _config = new YajswConfigurationImpl(false); + boolean debug = _config.getBoolean("wrapper.debug", false); + service = new WrapperMainServiceWin(); + // set service shutdown timeout + service.setServiceName(_config.getString("wrapper.ntservice.name")); + long timeout = _config.getInt("wrapper.shutdown.timeout", Constants.DEFAULT_SHUTDOWN_TIMEOUT) * 1000; + timeout += _config.getInt("wrapper.script.STOP.timeout", 0) * 1000; + timeout += _config.getInt("wrapper.script.SHUTDOWN.timeout", 0) * 1000; + timeout += _config.getInt("wrapper.script.IDLE.timeout", 0) * 1000; + timeout += _config.getInt("wrapper.script.ABORT.timeout", 0) * 1000; + if (timeout > Integer.MAX_VALUE) + timeout = Integer.MAX_VALUE; + service.setStopTimeout((int) timeout); + + timeout = _config.getInt("wrapper.startup.timeout", Constants.DEFAULT_STARTUP_TIMEOUT) * 1000; + if (timeout > Integer.MAX_VALUE) + timeout = Integer.MAX_VALUE; + service.setStartupTimeout((int) timeout); + + service.setAutoReportStartup(_config.getBoolean("wrapper.ntservice.autoreport.startup", true)); + + if (_config.containsKey("wrapperx.config")) + { + List configs = _config.getList("wrapperx.config"); + wList = WrappedProcessFactory.createProcessList(new HashMap(), configs, true); + for (WrappedProcess p : wList) + { + p.setService(service); + } + } + else + { + WrappedProcess w = WrappedProcessFactory.createProcess(_config); + // set service in wrapper so that we may stop the service in case the application terminates and we need to shutdown the wrapper + w.setService(service); + w.init(); + wList.add(w); + } + + w = wList.get(0); + + + + // start the applications + // the wrapper may have to wait longer for the application to come up -> + // start the application + // in a separate thread and then check that the wrapper is up after a + // max timeout + // but return as soon as possible to the windows service controller + final long maxStartTime = w.getMaxStartTime(); + final Future future = pool.submit(new Runnable() + { + public void run() + { + try + { + Thread.yield(); + wList.startAll(); + } + catch (Throwable ex) + { + ex.printStackTrace(); + w.getWrapperLogger().info("Win Service: error starting wrapper " + ex.getMessage()); + Runtime.getRuntime().halt(999); + } + } + }); + pool.execute(new Runnable() + { + public void run() + { + try + { + future.get(maxStartTime, TimeUnit.MILLISECONDS); + } + catch (Exception ex) + { + ex.printStackTrace(); + w.getWrapperLogger().info("Win Service: wrapper did not start within " + maxStartTime + " ms " + ex.getMessage()); + Runtime.getRuntime().halt(999); + } + } + }); + if (debug) + w.getWrapperLogger().info("Win service: before service init"); + + service.setDebug(debug); + + // init the service for signaling with services.exe. app will hang + // here until service is stopped + service.init(); + // service has terminated -> halt the wrapper jvm + if (debug) + w.getWrapperLogger().info("Win service: terminated correctly"); + try + { + if (_config.getBoolean("wrapper.update.auto", false)) + { + w.update(null, false); + } + } + catch (Exception ex) + { + + } + Runtime.getRuntime().halt(0); + } + + /* + * (non-Javadoc) + * + * @see jnacontrib.win32.Win32Service#onStart() + */ + @Override + public void onStart() + { + log("onstart"); + } + + /* + * (non-Javadoc) + * + * @see jnacontrib.win32.Win32Service#onStop() + */ + @Override + public void onStop() + { + // execute in a separate thread so that the controller callback can return + pool.execute(new Runnable() + { + public void run() + { + + try + { + w.getWrapperLogger().info("Win service stop - timeout: "+service.getStopTimeout()); + if (w.isHaltAppOnWrapper()) + { + w.getWrapperLogger().info("Win service wrapper.control -> stopping application"); + // remove the listener, so it does not call System.exit + wList.removeStateChangeListener(WrappedProcess.STATE_IDLE); + wList.stopAll(_stopReason); + } + wList.shutdown(); + w.getWrapperLogger().info("Win service stop - after shutdown"); + synchronized (waitObject) + { + w.getWrapperLogger().info("Win service stop - before notify"); + waitObject.notifyAll(); + } + w.getWrapperLogger().info("Win service terminated"); + } + catch (Exception e) + { + e.printStackTrace(); + w.getWrapperLogger().throwing(this.getClass().getName(), "error in win service doStop", e); + } + } + }); + + + } + + public void log(String txt) + { + if (_debug && w != null && w.getWrapperLogger() != null) + w.getWrapperLogger().info(txt); + } + + public void waitOnStop() + { + int i = 0; + try + { + while (!_waitOnStop && i++ < 20) + Thread.sleep(500); + } + catch (InterruptedException e) + { + e.printStackTrace(); + } + + } + +} diff --git a/javaUtilities/yajsw/src/app/java/org/rzo/yajsw/app/WrapperManager.java b/javaUtilities/yajsw/src/app/java/org/rzo/yajsw/app/WrapperManager.java new file mode 100644 index 0000000000..da0965a888 --- /dev/null +++ b/javaUtilities/yajsw/src/app/java/org/rzo/yajsw/app/WrapperManager.java @@ -0,0 +1,91 @@ +/* This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + *

+ * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ +package org.rzo.yajsw.app; + +import java.lang.reflect.Method; +import java.util.Properties; + +// TODO: Auto-generated Javadoc +/** + * The Interface WrapperManager. + */ +public interface WrapperManager +{ + + /** + * Gets the main method. + * + * @return the main method + */ + Method getMainMethod(); + + /** + * Gets the main method args. + * + * @return the main method args + */ + Object[] getMainMethodArgs(); + + /** + * Checks if is exit on main terminate. + * + * @return true, if is exit on main terminate + */ + int getExitOnMainTerminate(); + + /** + * Inits the. + * + * @param args + * the args + * @param wrapperClassLoader + * the wrapper class loader + */ + void init(String[] args, ClassLoader wrapperClassLoader); + + /** + * Start. + */ + void start(); + + /** + * Thread dump. + */ + public void threadDump(); + + /** + * Gets the pid. + * + * @return the pid + */ + public int getPid(); + + /** + * Stop. + */ + public void stop(); + + public void restart(); + + String getGroovyScript(); + + int getExitOnException(); + + public void reportServiceStartup(); + + void executeScript(String scriptFileName, ClassLoader wrapperClassLoader); + + public void signalStopping(int timeoutHint); + + public Properties getProperties(); + + public String getStopReason(); +} diff --git a/javaUtilities/yajsw/src/app/java/org/rzo/yajsw/app/WrapperManagerClassLoader.java b/javaUtilities/yajsw/src/app/java/org/rzo/yajsw/app/WrapperManagerClassLoader.java new file mode 100644 index 0000000000..22d74cd882 --- /dev/null +++ b/javaUtilities/yajsw/src/app/java/org/rzo/yajsw/app/WrapperManagerClassLoader.java @@ -0,0 +1,72 @@ +/* This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + *

+ * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ +package org.rzo.yajsw.app; + +import java.net.URL; +import java.net.URLClassLoader; + +// TODO: Auto-generated Javadoc +/** + * The Class WrapperManagerClassLoader. + */ +public class WrapperManagerClassLoader extends URLClassLoader +{ + + /** The _parent. */ + ClassLoader _parent; + + /** + * Instantiates a new wrapper manager class loader. + * + * @param urls + * the urls + * @param parent + * the parent + */ + public WrapperManagerClassLoader(URL[] urls, ClassLoader parent) + { + super(urls, null); + _parent = parent; + } + + /* + * (non-Javadoc) + * + * @see java.lang.ClassLoader#loadClass(java.lang.String, boolean) + */ + @Override + public synchronized Class findClass(String name) throws ClassNotFoundException + { + // First, check if the class has already been loaded + Class c = findLoadedClass(name); + if (c == null) + { + if (!"org.rzo.yajsw.app.WrapperManager".equals(name) && !"org.rzo.yajsw.app.WrapperManagerProxy".equals(name)) + try + { + c = super.findClass(name); + + } + catch (ClassNotFoundException e) + { + // If still not found, then invoke findClass in order + // to find the class. + } + } + if (c == null) + if (_parent != null) + c = _parent.loadClass(name); + + return c; + + } + +} diff --git a/javaUtilities/yajsw/src/app/java/org/rzo/yajsw/app/WrapperManagerProxy.java b/javaUtilities/yajsw/src/app/java/org/rzo/yajsw/app/WrapperManagerProxy.java new file mode 100644 index 0000000000..bc3c5b14d5 --- /dev/null +++ b/javaUtilities/yajsw/src/app/java/org/rzo/yajsw/app/WrapperManagerProxy.java @@ -0,0 +1,99 @@ +/* This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + *

+ * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ +package org.rzo.yajsw.app; + +import java.net.URL; + +import org.rzo.yajsw.boot.WrapperClassLoader; +import org.rzo.yajsw.boot.WrapperLoader; + +// TODO: Auto-generated Javadoc +/** + * The Class WrapperMangerProxy. + */ +public class WrapperManagerProxy +{ + + /** The wrapper class loader. */ + static ClassLoader wrapperClassLoader; + + /** + * Gets the wrapper manager. + * + * @param args + * the args + * + * @return the wrapper manager + */ + public static WrapperManager getWrapperManager(final String[] args) + { + wrapperClassLoader = getWrapperClassLoader(); + Class wrapperManagerClass; + final WrapperManager wm; + WrapperManager result = null; + try + { + wrapperManagerClass = wrapperClassLoader.loadClass("org.rzo.yajsw.app.WrapperManagerImpl"); + wm = (WrapperManager) wrapperManagerClass.newInstance(); + wm.init(args, wrapperClassLoader); + // start the wrapper manager in a separate thread + // so application may start even if communication to controller + // takes time + + new Thread(new Runnable() + { + + public void run() + { + try + { + wm.start(); + } + catch (Exception ex) + { + ex.printStackTrace(); + } + } + }, "yajsw.app.manager.start").start(); + // give the manager.start a chance. + Thread.yield(); + result = wm; + String preScript = System.getProperty("wrapper.app.pre_main.script"); + if (preScript != null & !"".equals(preScript)) + { + //Logger logger = new MyLogger(); + //logger.addHandler(new ConsoleHandler()); + wm.executeScript(preScript, wrapperClassLoader); + } + + } + catch (Exception e1) + { + e1.printStackTrace(); + } + + return result; + } + + /** + * Gets the wrapper class loader. + * + * @return the wrapper class loader + */ + private static ClassLoader getWrapperClassLoader() + { + URL[] urlsArr = WrapperLoader.getWrapperClasspath("App", true); + if (urlsArr == null) + return Thread.currentThread().getContextClassLoader(); + return new WrapperClassLoader(urlsArr, ClassLoader.getSystemClassLoader()); + } + +} diff --git a/javaUtilities/yajsw/src/app/java/org/rzo/yajsw/boot/WrapperClassLoader.java b/javaUtilities/yajsw/src/app/java/org/rzo/yajsw/boot/WrapperClassLoader.java new file mode 100644 index 0000000000..796aba3cc6 --- /dev/null +++ b/javaUtilities/yajsw/src/app/java/org/rzo/yajsw/boot/WrapperClassLoader.java @@ -0,0 +1,83 @@ +/* This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + *

+ * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ +package org.rzo.yajsw.boot; + +import java.net.URL; +import java.net.URLClassLoader; + +/** + * Class loader for handling YAJSW related libraries. The classloader will first + * search the YAJSW related libraries before searching the parent (system + * classloader). The libraries required by YAJSW will thus not be visible to the + * application which is started within the system classloader. Exception is for + * the interface WrapperManager and for the class WrapperManagerProxy. An + * singleton instance of WrapperManager is visible to the application for + * shutdown or for restart. WrapperManagerProxy is required for on application + * startup. + */ +public class WrapperClassLoader extends URLClassLoader +{ + + /** The _parent. */ + ClassLoader _parent; + + /** + * Instantiates a new wrapper manager class loader. + * + * @param urls + * the urls + * @param parent + * the parent + */ + public WrapperClassLoader(URL[] urls, ClassLoader parent) + { + super(urls, null); + _parent = parent; + } + + /* + * (non-Javadoc) + * + * @see java.lang.ClassLoader#loadClass(java.lang.String, boolean) + */ + @Override + public synchronized Class findClass(String name) throws ClassNotFoundException + { + // First, check if the class has already been loaded + Class c = findLoadedClass(name); + if (c == null) + { + if (!"org.rzo.yajsw.app.WrapperManager".equals(name) && !"org.rzo.yajsw.app.WrapperManagerProxy".equals(name)) + try + { + c = super.findClass(name); + //System.out.println("got wrapper class "+name); + + } + catch (ClassNotFoundException e) + { + // If still not found, then invoke findClass in order + // to find the class. + } + } + if (c == null) + if (_parent != null) + { + c = _parent.loadClass(name); + //System.out.println("got main class "+name); + + } + + return c; + + } + +} diff --git a/javaUtilities/yajsw/src/app/java/org/rzo/yajsw/boot/WrapperExeBooter.java b/javaUtilities/yajsw/src/app/java/org/rzo/yajsw/boot/WrapperExeBooter.java new file mode 100644 index 0000000000..a727c35c53 --- /dev/null +++ b/javaUtilities/yajsw/src/app/java/org/rzo/yajsw/boot/WrapperExeBooter.java @@ -0,0 +1,52 @@ +/* This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + *

+ * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ +package org.rzo.yajsw.boot; + +import java.io.File; +import java.lang.reflect.Method; +import java.net.URLClassLoader; + +public class WrapperExeBooter +{ + + /** + * The main method. Loads the libs required by YAJSW and starts + * WrapperExe.main + * + * @param args + * the arguments + */ + public static void main(String[] args) + { + if (System.getProperty("java.io.tmpdir") != null) + { + File tmp = new File(System.getProperty("java.io.tmpdir")); + if (!tmp.exists()) + tmp.mkdirs(); + } + + URLClassLoader cl = WrapperLoader.getWrapperClassLoader(); + Thread.currentThread().setContextClassLoader(cl); + try + { + Class cls = Class.forName("org.rzo.yajsw.WrapperExe", true, cl); + Method mainMethod = cls.getDeclaredMethod("main", new Class[] + { String[].class }); + mainMethod.invoke(null, new Object[] + { args }); + } + catch (Exception e) + { + e.printStackTrace(); + } + } + +} diff --git a/javaUtilities/yajsw/src/app/java/org/rzo/yajsw/boot/WrapperLoader.java b/javaUtilities/yajsw/src/app/java/org/rzo/yajsw/boot/WrapperLoader.java new file mode 100644 index 0000000000..329e34a1eb --- /dev/null +++ b/javaUtilities/yajsw/src/app/java/org/rzo/yajsw/boot/WrapperLoader.java @@ -0,0 +1,262 @@ +package org.rzo.yajsw.boot; + +import java.io.File; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.jar.Attributes; +import java.util.jar.JarFile; +import java.util.jar.Manifest; + +import org.rzo.yajsw.Constants; +import org.rzo.yajsw.app.WrapperJVMMain; + +public class WrapperLoader +{ + + private static boolean checkPath(String path) + { + int ix = path.indexOf("!"); + if (ix == -1) + { + System.out.println("/wrapper.jar not found, please check classpath. aborting wrapper !"); + Runtime.getRuntime().halt(999);// -> groovy eclipse plugin crashes + return false; + } + return true; + + } + + + /** + * Gets the wrapper jar. + * + * @return the wrapper jar + */ + public static String getWrapperJar() + { + String cn = Constants.class.getCanonicalName(); + String rn = cn.replace('.', '/') + ".class"; + String path = "."; + try + { + path = Constants.class.getClassLoader().getResource(rn).getPath(); + if (!checkPath(path)) + return null; + path = path.substring(0, path.indexOf("!")); + path = new URI(path).getPath(); + path.replaceAll("%20", " "); + //System.out.println("wrapper jar "+path); + // if (path.startsWith("/")) + // path = path.substring(1); + return path; + } + catch (Exception e1) + { + e1.printStackTrace(); + } + return null; + } + + public static String getWrapperAppJar() + { + String cn = WrapperJVMMain.class.getCanonicalName(); + String rn = cn.replace('.', '/') + ".class"; + String path = "."; + try + { + path = WrapperJVMMain.class.getClassLoader().getResource(rn).getPath(); + if (!checkPath(path)) + return null; + path = path.substring(0, path.indexOf("!")); + path = new URI(path).getPath(); + path.replaceAll("%20", " "); + //System.out.println("wrapper jar "+path); + // if (path.startsWith("/")) + // path = path.substring(1); + return path; + } + catch (Exception e1) + { + e1.printStackTrace(); + } + return null; + } + + public static ArrayList getGroovyClasspath() + { + ArrayList result = new ArrayList(); + String wrapperHome = getWrapperHome(); + File groovyLib = new File(wrapperHome, "lib"); + if (! groovyLib.exists()) + { + System.out.println("/lib folder not found. Please check that relative the lib folder is in the same folder as /wrapper.jar"); + return result; + } + File[] groovyLibs = groovyLib.listFiles(); + for (File file : groovyLibs) + { + if (file.isDirectory()) + result.addAll(getFiles(file)); + else + try + { + result.add(file.toURI().toURL()); + } + catch (MalformedURLException e) + { + System.out.println("Error in getGroovyClasspath: "+e.getMessage()); + } + } + return result; + } + + private static Collection getFiles(File parent) + { + ArrayList result = new ArrayList(); + File[] files = parent.listFiles(); + for (File file : files) + { + if (file.isDirectory()) + result.addAll(getFiles(file)); + else + try + { + result.add(file.toURI().toURL()); + } + catch (MalformedURLException e) + { + System.out.println("Error in getGroovyClasspath: "+e.getMessage()); + } + } + return result; + } + + public static URL[] getWrapperClasspath(String type, boolean logErrors) + { + String wrapperJar; + if ("App".equals(type)) + wrapperJar = getWrapperAppJar(); + else + wrapperJar = getWrapperJar(); + Manifest manifest; + try + { + manifest = new JarFile(wrapperJar).getManifest(); + } + catch (IOException e1) + { + // TODO Auto-generated catch block + e1.printStackTrace(); + return null; + } + Attributes attr = manifest.getMainAttributes(); + + String cl = attr.getValue("Class-Path-" + type); + if (cl == null) + return null; + + ArrayList classpath = new ArrayList(); + classpath.add(new File(wrapperJar)); + String[] clArr = cl.split(" "); + File parent = new File(wrapperJar).getParentFile(); + for (int i = 0; i < clArr.length; i++) + { + String file = clArr[i]; + File myFile; + try + { + myFile = new File(parent, file); + if (!myFile.exists() && logErrors) + System.out.println("WARNING: lib not found: " + myFile.getCanonicalPath()); + classpath.add(myFile); + } + catch (Exception e) + { + e.printStackTrace(); + } + } + + // add rt.jar + if ("App".equals(type)) + try + { + String rt = getRTJar(); + File rtf = new File(rt); + if (!rtf.exists()) + System.out.println("could not find rt.jar"); + else + classpath.add(rtf); + } + catch (Exception ex) + { + ex.printStackTrace(); + } + + + URL[] urlsArr = new URL[classpath.size()]; + int i = 0; + for (Iterator it = classpath.iterator(); it.hasNext(); i++) + try + { + urlsArr[i] = ((File) it.next()).toURI().toURL(); + // System.out.println("classpath: "+urlsArr[i]); + } + catch (Exception e) + { + // log.throwing(WrapperMain.class.getName(), "main", e); + e.printStackTrace(); + } + + return urlsArr; + + } + + public static URLClassLoader getWrapperClassLoader() + { + URL[] core = getWrapperClasspath("Wrapper-Core", true); + URL[] extended = getWrapperClasspath("Wrapper-Extended", false); + URL[] urls = new URL[core.length+ extended.length]; + System.arraycopy(core, 0, urls, 0, core.length); + System.arraycopy(extended, 0, urls, core.length, extended.length); + return new WrapperClassLoader(urls, Thread.currentThread().getContextClassLoader()); + } + + public static String getWrapperHome() + { + return new File(getWrapperJar()).getParent(); + } + + public static String getRTJar() + { + String cn = Object.class.getCanonicalName(); + String rn = cn.replace('.', '/') + ".class"; + String path = "."; + try + { + path = WrapperJVMMain.class.getClassLoader().getResource(rn).getPath(); + if (!checkPath(path)) + return null; + path = path.substring(0, path.indexOf("!")); + path = new URI(path).getPath(); + path.replaceAll("%20", " "); + //System.out.println("wrapper jar "+path); + // if (path.startsWith("/")) + // path = path.substring(1); + return path; + } + catch (Exception e1) + { + e1.printStackTrace(); + } + return null; + } + + + +} diff --git a/javaUtilities/yajsw/src/app/java/org/rzo/yajsw/boot/WrapperServiceBooter.java b/javaUtilities/yajsw/src/app/java/org/rzo/yajsw/boot/WrapperServiceBooter.java new file mode 100644 index 0000000000..589cc586a6 --- /dev/null +++ b/javaUtilities/yajsw/src/app/java/org/rzo/yajsw/boot/WrapperServiceBooter.java @@ -0,0 +1,40 @@ +package org.rzo.yajsw.boot; + +import java.io.File; +import java.lang.reflect.Method; +import java.net.URLClassLoader; + +public class WrapperServiceBooter +{ + public static void main(String[] args) + { + if (System.getProperty("java.io.tmpdir") != null) + { + File tmp = new File(System.getProperty("java.io.tmpdir")); + if (!tmp.exists()) + tmp.mkdirs(); + } + + URLClassLoader cl = WrapperLoader.getWrapperClassLoader(); + Thread.currentThread().setContextClassLoader(cl); + String osName = System.getProperty("os.name"); + String clazz = null; + if (osName.toLowerCase().startsWith("windows")) + clazz = "org.rzo.yajsw.app.WrapperMainServiceWin"; + else + clazz = "org.rzo.yajsw.app.WrapperMainServiceUnix"; + try + { + Class cls = Class.forName(clazz, true, cl); + Method mainMethod = cls.getDeclaredMethod("main", new Class[] + { String[].class }); + mainMethod.invoke(null, new Object[] + { args }); + } + catch (Exception e) + { + e.printStackTrace(); + } + } + +} diff --git a/javaUtilities/yajsw/src/app/java/org/rzo/yajsw/log/DateFileHandler.java b/javaUtilities/yajsw/src/app/java/org/rzo/yajsw/log/DateFileHandler.java new file mode 100644 index 0000000000..2512d0db14 --- /dev/null +++ b/javaUtilities/yajsw/src/app/java/org/rzo/yajsw/log/DateFileHandler.java @@ -0,0 +1,397 @@ +/* This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + *

+ * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ +package org.rzo.yajsw.log; + +import java.io.File; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Date; +import java.util.LinkedList; +import java.util.List; +import java.util.logging.FileHandler; +import java.util.logging.Formatter; +import java.util.logging.Handler; +import java.util.logging.Level; +import java.util.logging.LogRecord; + +import org.rzo.yajsw.log.MyFileHandler.FileChangeListner; + +// TODO: Auto-generated Javadoc +/** + * The Class DateFileHandler. + */ +public class DateFileHandler extends Handler +{ + + /** The _handler. */ + volatile MyFileHandler _handler; + + /** The _end date. */ + volatile long _endDate; + + /** The _pattern. */ + volatile String _pattern; + + /** The _limit. */ + volatile int _limit; + + /** The _count. */ + volatile int _count; + + /** The _append. */ + volatile boolean _append; + + /** The format. */ + final SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd"); + + /** The _init. */ + volatile boolean _init = false; + + volatile boolean _rollDate = false; + + volatile long _startDate = System.currentTimeMillis(); + + //volatile LinkedList _previousFiles = new LinkedList(); + //volatile LinkedList _currentFiles = new LinkedList(); + volatile LinkedList _previousDates = new LinkedList(); + volatile String _currentDate = null; + volatile int _maxDays = -1; + + /** + * Instantiates a new date file handler. + * + * @param pattern + * the pattern + * @param limit + * the limit + * @param count + * the count + * @param append + * the append + * + * @throws IOException + * Signals that an I/O exception has occurred. + * @throws SecurityException + * the security exception + */ + public DateFileHandler(String pattern, int limit, int count, boolean append, boolean rollDate, PatternFormatter fileFormatter, Level logLevel, String encoding, int maxDays) + { + _pattern = pattern; + _limit = limit; + _count = count; + _append = append; + _rollDate = rollDate; + _maxDays = maxDays; + _init = true; + if (encoding != null) + try + { + setEncoding(encoding); + } + catch (Exception e) + { + e.printStackTrace(); + } + setFormatter(fileFormatter); + setLevel(logLevel); + findPreviousDates(); + rotateDate(); + //checkFileCount(); + } + + /* + * (non-Javadoc) + * + * @see java.util.logging.Handler#close() + */ + @Override + public void close() throws SecurityException + { + _handler.close(); + + } + + /* + * (non-Javadoc) + * + * @see java.util.logging.Handler#flush() + */ + @Override + public void flush() + { + _handler.flush(); + } + + /* + * (non-Javadoc) + * + * @see java.util.logging.Handler#publish(java.util.logging.LogRecord) + */ + @Override + public void publish(LogRecord record) + { + if (_rollDate) + { + if (_endDate < record.getMillis()) + rotateDate(); + if (System.currentTimeMillis() - _startDate > 25*60*60*1000) + { + String msg = record.getMessage(); + record.setMessage("missed file rolling at: "+new Date(_endDate)+"\n"+msg); + } + } + _handler.publish(record); + + } + + /* + * (non-Javadoc) + * + * @see java.util.logging.Handler#setFormatter(java.util.logging.Formatter) + */ + @Override + public void setFormatter(Formatter newFormatter) + { + super.setFormatter(newFormatter); + if (_handler != null) + _handler.setFormatter(newFormatter); + } + + /** + * Rotate date. + */ + private void rotateDate() + { + _startDate = System.currentTimeMillis(); + if (_handler != null) + _handler.close(); + _previousDates.addLast(_currentDate); + cleanupDates(); + _currentDate = format.format(new Date()); + String pattern = _pattern.replace("%d", _currentDate); + try + { + File dd = MyFileHandler.generate(pattern, 0, 0, _count); + if (!dd.getParentFile().exists()) + dd.getParentFile().mkdirs(); + } + catch (IOException e1) + { + } + Calendar next = Calendar.getInstance(); // current date + // begin of next date + next.set(Calendar.HOUR_OF_DAY, 0); + next.set(Calendar.MINUTE, 0); + next.set(Calendar.SECOND, 0); + next.set(Calendar.MILLISECOND, 0); + next.add(Calendar.DATE, 1); + _endDate = next.getTimeInMillis(); + + try + { + _handler = new MyFileHandler(pattern, _limit, _count, _append); + if (_init) + { + _handler.setEncoding(this.getEncoding()); + _handler.setErrorManager(this.getErrorManager()); + _handler.setFilter(this.getFilter()); + _handler.setFormatter(this.getFormatter()); + _handler.setLevel(this.getLevel()); + //findFiles(); + //_currentFiles.clear(); + //addFiles(_handler.getCurrentFiles()); + /* + _handler.setNewFileListner(new FileChangeListner() + { + + public void fileChange(File file, boolean added) + { + System.out.println("file change: "+added+ " "+file.getName()); + if (added) + addFile(file); + } + + }); + */ + + } + } + catch (SecurityException e) + { + // TODO Auto-generated catch block + e.printStackTrace(); + } + catch (IOException e) + { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + private void cleanupDates() + { + if (_maxDays >= 0) + while (_previousDates.size() > _maxDays) + { + String toCleanup = _previousDates.removeFirst(); + cleanupDate(toCleanup); + } + } + + private void cleanupDate(String date) + { + String pattern = _pattern.replace("%d", date); + File f; + for (int unique=0; unique<_count; unique++) + { + try + { + f = MyFileHandler.generate(pattern, 0, unique, _count); + if (!f.exists()) + break; + } + catch (IOException e1) + { + // should not happen, but keep silent for now + } + for (int generation=0; generation<_count; generation++) + { + try + { + f = MyFileHandler.generate(pattern, generation, unique, _count); + if (f.exists()) + { + f.delete(); + } + else + break; + } + catch (IOException e) + { + // should not happen, but keep silent for now + } + } + } + // cleanup parent folder if necessary + try + { + // remove old lock files + f = MyFileHandler.generate(pattern+".lck", 0, 0, _count); + f.delete(); + f = MyFileHandler.generate(pattern, 0, 0, _count); + while (!f.getName().contains(date)) + f = f.getParentFile(); + if (f.isDirectory()) + f.delete(); + } + catch (IOException e) + { + // should not happen, but keep silent for now + } + } + + private void findPreviousDates() + { + if (_maxDays < 0) + return; + Calendar date = Calendar.getInstance(); + // service may not run daily + // for performance: do not scan all files. + // service may not run every day, but we assume it will run at least once a year + int scanDays = Math.max(365, _maxDays); + for (int i = 0; i 0 && _previousFiles.size()+_currentFiles.size()>_count) + { + File f = _previousFiles.removeLast(); + if (f.exists()) + f.delete(); + } + } + + private void addFile(File f) + { + if (!_currentFiles.contains(f)) + _currentFiles.addFirst(f); + checkFileCount(); + } + + private void addFiles(LinkedList files) + { + while (!files.isEmpty()) + addFile(files.removeLast()); + } + + private void findFiles() throws IOException + { + Calendar date = Calendar.getInstance(); + while (_previousFiles.size() < _count) + { + date.add(Calendar.DAY_OF_MONTH, -1); + String pattern = _pattern.replace("%d", format.format(date.getTime())); + for (int unique=0; unique<_count; unique++) + { + for (int generation=0; generation<_count; generation++) + { + File f = MyFileHandler.generate(pattern, generation, unique, _count); + if (f.exists() && !_previousFiles.contains(f)) + { + _previousFiles.addLast(f); + } + else + { + break; + } + } + File f = MyFileHandler.generate(pattern, 0, unique, _count); + if (!f.exists() || _previousFiles.contains(f)) + break; + } + File f = MyFileHandler.generate(pattern, 0, 0, _count); + if (!f.exists()) + break; + } + + } + */ + +} diff --git a/javaUtilities/yajsw/src/app/java/org/rzo/yajsw/log/MyFileHandler.java b/javaUtilities/yajsw/src/app/java/org/rzo/yajsw/log/MyFileHandler.java new file mode 100644 index 0000000000..daa12fe5c1 --- /dev/null +++ b/javaUtilities/yajsw/src/app/java/org/rzo/yajsw/log/MyFileHandler.java @@ -0,0 +1,661 @@ +package org.rzo.yajsw.log; + +/* + * @(#)FileHandler.java 1.37 10/03/23 + * + * Copyright (c) 2006, Oracle and/or its affiliates. All rights reserved. + * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. + */ + +import java.io.*; +import java.nio.channels.FileChannel; +import java.nio.channels.FileLock; +import java.security.*; +import java.util.LinkedList; +import java.util.logging.ErrorManager; +import java.util.logging.Level; +import java.util.logging.LogManager; +import java.util.logging.LogRecord; +import java.util.logging.StreamHandler; + +/** + * Simple file logging Handler. + *

+ * The FileHandler can either write to a specified file, + * or it can write to a rotating set of files. + *

+ * For a rotating set of files, as each file reaches a given size + * limit, it is closed, rotated out, and a new file opened. + * Successively older files are named by adding "0", "1", "2", + * etc into the base filename. + *

+ * By default buffering is enabled in the IO libraries but each log + * record is flushed out when it is complete. + *

+ * By default the XMLFormatter class is used for formatting. + *

+ * Configuration: + * By default each FileHandler is initialized using the following + * LogManager configuration properties. If properties are not defined + * (or have invalid values) then the specified default values are used. + *

    + *
  • java.util.logging.FileHandler.level + * specifies the default level for the Handler + * (defaults to Level.ALL). + *
  • java.util.logging.FileHandler.filter + * specifies the name of a Filter class to use + * (defaults to no Filter). + *
  • java.util.logging.FileHandler.formatter + * specifies the name of a Formatter class to use + * (defaults to java.util.logging.XMLFormatter) + *
  • java.util.logging.FileHandler.encoding + * the name of the character set encoding to use (defaults to + * the default platform encoding). + *
  • java.util.logging.FileHandler.limit + * specifies an approximate maximum amount to write (in bytes) + * to any one file. If this is zero, then there is no limit. + * (Defaults to no limit). + *
  • java.util.logging.FileHandler.count + * specifies how many output files to cycle through (defaults to 1). + *
  • java.util.logging.FileHandler.pattern + * specifies a pattern for generating the output file name. See + * below for details. (Defaults to "%h/java%u.log"). + *
  • java.util.logging.FileHandler.append + * specifies whether the FileHandler should append onto + * any existing files (defaults to false). + *
+ *

+ *

+ * A pattern consists of a string that includes the following special + * components that will be replaced at runtime: + *

    + *
  • "/" the local pathname separator + *
  • "%t" the system temporary directory + *
  • "%h" the value of the "user.home" system property + *
  • "%g" the generation number to distinguish rotated logs + *
  • "%u" a unique number to resolve conflicts + *
  • "%%" translates to a single percent sign "%" + *
+ * If no "%g" field has been specified and the file count is greater + * than one, then the generation number will be added to the end of + * the generated filename, after a dot. + *

+ * Thus for example a pattern of "%t/java%g.log" with a count of 2 + * would typically cause log files to be written on Solaris to + * /var/tmp/java0.log and /var/tmp/java1.log whereas on Windows 95 they + * would be typically written to C:\TEMP\java0.log and C:\TEMP\java1.log + *

+ * Generation numbers follow the sequence 0, 1, 2, etc. + *

+ * Normally the "%u" unique field is set to 0. However, if the FileHandler + * tries to open the filename and finds the file is currently in use by + * another process it will increment the unique number field and try + * again. This will be repeated until FileHandler finds a file name that + * is not currently in use. If there is a conflict and no "%u" field has + * been specified, it will be added at the end of the filename after a dot. + * (This will be after any automatically added generation number.) + *

+ * Thus if three processes were all trying to log to fred%u.%g.txt then + * they might end up using fred0.0.txt, fred1.0.txt, fred2.0.txt as + * the first file in their rotating sequences. + *

+ * Note that the use of unique ids to avoid conflicts is only guaranteed + * to work reliably when using a local disk file system. + * + * @version 1.37, 03/23/10 + * @since 1.4 + */ + +public class MyFileHandler extends StreamHandler { + private MeteredStream meter; + private boolean append; + private int limit; // zero => no limit. + private int count; + private String pattern; + private String lockFileName; + private FileOutputStream lockStream; + private File files[]; + private static final int MAX_LOCKS = 100; + private static java.util.HashMap locks = new java.util.HashMap(); + + private FileChangeListner _listener = null; + + interface FileChangeListner + { + void fileChange(File file, boolean added); + } + + // A metered stream is a subclass of OutputStream that + // (a) forwards all its output to a target stream + // (b) keeps track of how many bytes have been written + private class MeteredStream extends OutputStream { + OutputStream out; + int written; + + MeteredStream(OutputStream out, int written) { + this.out = out; + this.written = written; + } + + public void write(int b) throws IOException { + out.write(b); + written++; + } + + public void write(byte buff[]) throws IOException { + out.write(buff); + written += buff.length; + } + + public void write(byte buff[], int off, int len) throws IOException { + out.write(buff,off,len); + written += len; + } + + public void flush() throws IOException { + out.flush(); + } + + public void close() throws IOException { + out.close(); + } + } + + private void open(File fname, boolean append) throws IOException { + int len = 0; + if (append) { + len = (int)fname.length(); + } + if (!fname.getParentFile().exists()) + fname.getParentFile().mkdirs(); + FileOutputStream fout = new FileOutputStream(fname.toString(), append); + BufferedOutputStream bout = new BufferedOutputStream(fout); + meter = new MeteredStream(bout, len); + setOutputStream(meter); + } + + // Private method to configure a FileHandler from LogManager + // properties and/or default values as specified in the class + // javadoc. + private void configure() { + /* + LogManager manager = LogManager.getLogManager(); + + String cname = getClass().getName(); + + pattern = manager.getStringProperty(cname + ".pattern", "%h/java%u.log"); + limit = manager.getIntProperty(cname + ".limit", 0); + if (limit < 0) { + limit = 0; + } + count = manager.getIntProperty(cname + ".count", 1); + if (count <= 0) { + count = 1; + } + append = manager.getBooleanProperty(cname + ".append", false); + setLevel(manager.getLevelProperty(cname + ".level", Level.ALL)); + setFilter(manager.getFilterProperty(cname + ".filter", null)); + setFormatter(manager.getFormatterProperty(cname + ".formatter", new XMLFormatter())); + try { + setEncoding(manager.getStringProperty(cname +".encoding", null)); + } catch (Exception ex) { + try { + setEncoding(null); + } catch (Exception ex2) { + // doing a setEncoding with null should always work. + // assert false; + } + } + */ + } + + + /** + * Construct a default FileHandler. This will be configured + * entirely from LogManager properties (or their default values). + *

+ * @exception IOException if there are IO problems opening the files. + * @exception SecurityException if a security manager exists and if + * the caller does not have LoggingPermission("control")). + * @exception NullPointerException if pattern property is an empty String. + */ + public MyFileHandler() throws IOException, SecurityException { + //checkAccess(); + configure(); + openFiles(); + } + + /** + * Initialize a FileHandler to write to the given filename. + *

+ * The FileHandler is configured based on LogManager + * properties (or their default values) except that the given pattern + * argument is used as the filename pattern, the file limit is + * set to no limit, and the file count is set to one. + *

+ * There is no limit on the amount of data that may be written, + * so use this with care. + * + * @param pattern the name of the output file + * @exception IOException if there are IO problems opening the files. + * @exception SecurityException if a security manager exists and if + * the caller does not have LoggingPermission("control"). + * @exception IllegalArgumentException if pattern is an empty string + */ + public MyFileHandler(String pattern) throws IOException, SecurityException { + if (pattern.length() < 1 ) { + throw new IllegalArgumentException(); + } + //checkAccess(); + configure(); + this.pattern = pattern; + this.limit = 0; + this.count = 1; + openFiles(); + } + + /** + * Initialize a FileHandler to write to the given filename, + * with optional append. + *

+ * The FileHandler is configured based on LogManager + * properties (or their default values) except that the given pattern + * argument is used as the filename pattern, the file limit is + * set to no limit, the file count is set to one, and the append + * mode is set to the given append argument. + *

+ * There is no limit on the amount of data that may be written, + * so use this with care. + * + * @param pattern the name of the output file + * @param append specifies append mode + * @exception IOException if there are IO problems opening the files. + * @exception SecurityException if a security manager exists and if + * the caller does not have LoggingPermission("control"). + * @exception IllegalArgumentException if pattern is an empty string + */ + public MyFileHandler(String pattern, boolean append) throws IOException, SecurityException { + if (pattern.length() < 1 ) { + throw new IllegalArgumentException(); + } + //checkAccess(); + configure(); + this.pattern = pattern; + this.limit = 0; + this.count = 1; + this.append = append; + openFiles(); + } + + /** + * Initialize a FileHandler to write to a set of files. When + * (approximately) the given limit has been written to one file, + * another file will be opened. The output will cycle through a set + * of count files. + *

+ * The FileHandler is configured based on LogManager + * properties (or their default values) except that the given pattern + * argument is used as the filename pattern, the file limit is + * set to the limit argument, and the file count is set to the + * given count argument. + *

+ * The count must be at least 1. + * + * @param pattern the pattern for naming the output file + * @param limit the maximum number of bytes to write to any one file + * @param count the number of files to use + * @exception IOException if there are IO problems opening the files. + * @exception SecurityException if a security manager exists and if + * the caller does not have LoggingPermission("control"). + * @exception IllegalArgumentException if limit < 0, or count < 1. + * @exception IllegalArgumentException if pattern is an empty string + */ + public MyFileHandler(String pattern, int limit, int count) + throws IOException, SecurityException { + if (limit < 0 || count < 1 || pattern.length() < 1) { + throw new IllegalArgumentException(); + } + //checkAccess(); + configure(); + this.pattern = pattern; + this.limit = limit; + this.count = count; + openFiles(); + } + + /** + * Initialize a FileHandler to write to a set of files + * with optional append. When (approximately) the given limit has + * been written to one file, another file will be opened. The + * output will cycle through a set of count files. + *

+ * The FileHandler is configured based on LogManager + * properties (or their default values) except that the given pattern + * argument is used as the filename pattern, the file limit is + * set to the limit argument, and the file count is set to the + * given count argument, and the append mode is set to the given + * append argument. + *

+ * The count must be at least 1. + * + * @param pattern the pattern for naming the output file + * @param limit the maximum number of bytes to write to any one file + * @param count the number of files to use + * @param append specifies append mode + * @exception IOException if there are IO problems opening the files. + * @exception SecurityException if a security manager exists and if + * the caller does not have LoggingPermission("control"). + * @exception IllegalArgumentException if limit < 0, or count < 1. + * @exception IllegalArgumentException if pattern is an empty string + * + */ + public MyFileHandler(String pattern, int limit, int count, boolean append) + throws IOException, SecurityException { + if (limit < 0 || count < 1 || pattern.length() < 1) { + throw new IllegalArgumentException(); + } + //checkAccess(); + configure(); + this.pattern = pattern; + this.limit = limit; + this.count = count; + this.append = append; + openFiles(); + } + + public MyFileHandler(String pattern, int limit, int count, boolean append, PatternFormatter fileFormatter, Level logLevel, String encoding) throws SecurityException, IOException + { + this(pattern, limit, count, append); + if (encoding != null) + setEncoding(encoding); + setFormatter(fileFormatter); + setLevel(logLevel); + } + + // Private method to open the set of output files, based on the + // configured instance variables. + private void openFiles() throws IOException { + LogManager manager = LogManager.getLogManager(); + manager.checkAccess(); + if (count < 1) { + throw new IllegalArgumentException("file count = " + count); + } + if (limit < 0) { + limit = 0; + } + + // We register our own ErrorManager during initialization + // so we can record exceptions. + InitializationErrorManager em = new InitializationErrorManager(); + setErrorManager(em); + + // Create a lock file. This grants us exclusive access + // to our set of output files, as long as we are alive. + int unique = -1; + for (;;) { + unique++; + if (unique > MAX_LOCKS) { + throw new IOException("Couldn't get lock for " + pattern); + } + // Generate a lock file name from the "unique" int. + lockFileName = generate(pattern, 0, unique, count).toString() + ".lck"; + // Now try to lock that filename. + // Because some systems (e.g. Solaris) can only do file locks + // between processes (and not within a process), we first check + // if we ourself already have the file locked. + synchronized(locks) { + if (locks.get(lockFileName) != null) { + // We already own this lock, for a different FileHandler + // object. Try again. + continue; + } + FileChannel fc; + try { + lockStream = new FileOutputStream(lockFileName); + fc = lockStream.getChannel(); + } catch (IOException ix) { + // We got an IOException while trying to open the file. + // Try the next file. + continue; + } + try { + FileLock fl = fc.tryLock(); + if (fl == null) { + // We failed to get the lock. Try next file. + continue; + } + // We got the lock OK. + } catch (IOException ix) { + // We got an IOException while trying to get the lock. + // This normally indicates that locking is not supported + // on the target directory. We have to proceed without + // getting a lock. Drop through. + } + // We got the lock. Remember it. + locks.put(lockFileName, lockFileName); + break; + } + } + + files = new File[count]; + for (int i = 0; i < count; i++) { + files[i] = generate(pattern, i, unique, count); + } + + // Create the initial log file. + if (append) { + open(files[0], true); + } else { + rotate(); + } + + // Did we detect any exceptions during initialization? + Exception ex = em.lastException; + if (ex != null) { + if (ex instanceof IOException) { + throw (IOException) ex; + } else if (ex instanceof SecurityException) { + throw (SecurityException) ex; + } else { + throw new IOException("Exception: " + ex); + } + } + + // Install the normal default ErrorManager. + setErrorManager(new ErrorManager()); + } + + // Generate a filename from a pattern. + static File generate(String pattern, int generation, int unique, int count) throws IOException { + File file = null; + String word = ""; + int ix = 0; + boolean sawg = false; + boolean sawu = false; + while (ix < pattern.length()) { + char ch = pattern.charAt(ix); + ix++; + char ch2 = 0; + if (ix < pattern.length()) { + ch2 = Character.toLowerCase(pattern.charAt(ix)); + } + if (ch == '/') { + if (file == null) { + file = new File(word); + } else { + file = new File(file, word); + } + word = ""; + continue; + } else if (ch == '%') { + if (ch2 == 't') { + String tmpDir = System.getProperty("java.io.tmpdir"); + if (tmpDir == null) { + tmpDir = System.getProperty("user.home"); + } + file = new File(tmpDir); + ix++; + word = ""; + continue; + } else if (ch2 == 'h') { + file = new File(System.getProperty("user.home")); + if (isSetUID()) { + // Ok, we are in a set UID program. For safety's sake + // we disallow attempts to open files relative to %h. + throw new IOException("can't use %h in set UID program"); + } + ix++; + word = ""; + continue; + } else if (ch2 == 'g') { + word = word + generation; + sawg = true; + ix++; + continue; + } else if (ch2 == 'u') { + word = word + unique; + sawu = true; + ix++; + continue; + } else if (ch2 == '%') { + word = word + "%"; + ix++; + continue; + } + } + word = word + ch; + } + if (count > 1 && !sawg && generation != 0) { + word = word + "." + generation; + } + if (unique > 0 && !sawu && unique != 0) { + word = word + "." + unique; + } + if (word.length() > 0) { + if (file == null) { + file = new File(word); + } else { + file = new File(file, word); + } + } + return file; + } + + // Rotate the set of output files + private synchronized void rotate() { + Level oldLevel = getLevel(); + setLevel(Level.OFF); + + super.close(); + for (int i = count-2; i >= 0; i--) { + File f1 = files[i]; + File f2 = files[i+1]; + if (f1.exists()) { + if (f2.exists()) { + f2.delete(); + } + else if (_listener != null) + _listener.fileChange(f2, true); + f1.renameTo(f2); + } + } + try { + open(files[0], false); + } catch (IOException ix) { + // We don't want to throw an exception here, but we + // report the exception to any registered ErrorManager. + reportError(null, ix, ErrorManager.OPEN_FAILURE); + + } + setLevel(oldLevel); + } + + /** + * Format and publish a LogRecord. + * + * @param record description of the log event. A null record is + * silently ignored and is not published + */ + public synchronized void publish(LogRecord record) { + if (!isLoggable(record)) { + return; + } + super.publish(record); + flush(); + if (limit > 0 && meter.written >= limit) { + // We performed access checks in the "init" method to make sure + // we are only initialized from trusted code. So we assume + // it is OK to write the target files, even if we are + // currently being called from untrusted code. + // So it is safe to raise privilege here. + AccessController.doPrivileged(new PrivilegedAction() { + public Object run() { + rotate(); + return null; + } + }); + } + } + + public File currentFile() + { + return files[0]; + } + + public LinkedList getCurrentFiles() + { + LinkedList result = new LinkedList(); + for (File f : files) + { + if (f.exists()) + result.addLast(f); + else + break; + } + return result; + } + + public void setNewFileListner(FileChangeListner listener) + { + _listener = listener; + } + + /** + * Close all the files. + * + * @exception SecurityException if a security manager exists and if + * the caller does not have LoggingPermission("control"). + */ + public synchronized void close() throws SecurityException { + super.close(); + // Unlock any lock file. + if (lockFileName == null) { + return; + } + try { + // Closing the lock file's FileOutputStream will close + // the underlying channel and free any locks. + lockStream.close(); + } catch (Exception ex) { + // Problems closing the stream. Punt. + } + synchronized(locks) { + locks.remove(lockFileName); + } + new File(lockFileName).delete(); + lockFileName = null; + lockStream = null; + } + + private static class InitializationErrorManager extends ErrorManager { + Exception lastException; + public void error(String msg, Exception ex, int code) { + lastException = ex; + } + } + + // Private native method to check if we are in a set UID program. + private static native boolean isSetUID(); +} + + diff --git a/javaUtilities/yajsw/src/app/java/org/rzo/yajsw/log/MyLogger.java b/javaUtilities/yajsw/src/app/java/org/rzo/yajsw/log/MyLogger.java new file mode 100644 index 0000000000..46b87f0d01 --- /dev/null +++ b/javaUtilities/yajsw/src/app/java/org/rzo/yajsw/log/MyLogger.java @@ -0,0 +1,70 @@ +/* This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + *

+ * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ +package org.rzo.yajsw.log; + +import java.util.Arrays; +import java.util.logging.LogRecord; +import java.util.logging.Logger; + +// TODO: Auto-generated Javadoc +/** + * The Class MyLogger. + */ +public class MyLogger extends Logger +{ + + /** The _name. */ + String _pid; + String _name; + + /** + * Instantiates a new my logger. + */ + public MyLogger() + { + super(null, null); + } + + /** + * Sets the name. + * + * @param name + * the new name + */ + public void setPID(String pid) + { + _pid = pid; + } + + public void setName(String name) + { + _name = name; + } + + @Override + public void log(LogRecord record) + { + Object[] newParams = null; + Object[] params = record.getParameters(); + if (params == null || params.length == 0) + newParams = new String[] { _pid, _name }; + else + { + int newSize = params.length + 2; + newParams = Arrays.copyOf(params, newSize); + newParams[newSize - 2] = _pid; + newParams[newSize - 1] = _name; + } + + record.setParameters(newParams); + super.log(record); + } +} diff --git a/javaUtilities/yajsw/src/app/java/org/rzo/yajsw/log/PatternFormatter.java b/javaUtilities/yajsw/src/app/java/org/rzo/yajsw/log/PatternFormatter.java new file mode 100644 index 0000000000..8342d9e769 --- /dev/null +++ b/javaUtilities/yajsw/src/app/java/org/rzo/yajsw/log/PatternFormatter.java @@ -0,0 +1,253 @@ +/** + * Copyright [2007] [Rohit B. Rai] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.rzo.yajsw.log; + +import java.text.DateFormat; +import java.text.MessageFormat; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.logging.Formatter; +import java.util.logging.LogManager; +import java.util.logging.LogRecord; + +// TODO: Auto-generated Javadoc +/** + * The Class PatternFormatter. + */ +public class PatternFormatter extends Formatter +{ + + /** The sh. */ + java.util.logging.SimpleFormatter sh; + + /** + *

+	 * The Log Formatter will use the following formatting tokens  LoggerName %LOGGER% Level %LEVEL% Time %TIME% Message %MESSAGE% SourceClassName %SOURCECLASS% SourceMethodName %SOURCEMETHOD% Exception Message %EXCEPTION% ExceptionStackTrace %STACKTRACE% Parameter %PARAM%
+	 * 
+ * + * The default log format is "[%LOGGER% - %LEVEL%] %TIME%: %MESSAGE%" And + * exception format is [%LOGGER% - %LEVEL%] %TIME% %MESSAGE% \n Exception: + * %EXCEPTION% \n %STACKTRACE% Apart from this the time format may be + * specified in satand java time format in the timeFormat variable The + * default time format is "dd-MMM-yyy; HH:mm:ss". + */ + + private String logPattern; + + /** The exception pattern. */ + private String exceptionPattern; + + /** The time format. */ + private String timeFormat; + + /** The log message format. */ + private MessageFormat logMessageFormat; + + /** The exception message format. */ + private MessageFormat exceptionMessageFormat; + + /** The date format. */ + private DateFormat dateFormat; + + /** + * Instantiates a new pattern formatter. + */ + public PatternFormatter() + { + LogManager manager = LogManager.getLogManager(); + String cname = getClass().getName(); + + timeFormat = manager.getProperty(cname + ".timeFormat"); + + if (timeFormat == null) + { + timeFormat = "dd-MMM-yyy; HH:mm:ss"; + } + setTimeFormat(timeFormat); + + logPattern = manager.getProperty(cname + ".logPattern"); + if (logPattern == null) + { + logPattern = "[{0} - {1}] {2}: {3} \n"; + } + setLogPattern(logPattern); + + exceptionPattern = manager.getProperty(cname + ".exceptionPattern"); + if (exceptionPattern == null) + { + exceptionPattern = "[{0} - {1}] {2} {3} \nException in {4}: {6} \n{7} "; + } + setExceptionPattern(exceptionPattern); + + logMessageFormat = new MessageFormat(logPattern); + exceptionMessageFormat = new MessageFormat(exceptionPattern); + + dateFormat = new SimpleDateFormat(timeFormat); + } + + /** + * Sets the time format. + * + * @param timeFormat + * the new time format + */ + public void setTimeFormat(String timeFormat) + { + this.timeFormat = timeFormat; + dateFormat = new SimpleDateFormat(timeFormat); + } + + /** + * Sets the log pattern. + * + * @param logFormat + * the new log pattern + */ + public void setLogPattern(String logFormat) + { + logFormat = logFormat.replace("%LOGGER%", "{0}"); + logFormat = logFormat.replace("%LEVEL%", "{1}"); + logFormat = logFormat.replace("%TIME%", "{2}"); + logFormat = logFormat.replace("%MESSAGE%", "{3}"); + logFormat = logFormat.replace("%SOURCECLASS%", "{4}"); + logFormat = logFormat.replace("%SOURCEMETHOD%", "{5}"); + logFormat = logFormat.replace("%PARAM0%", "{6}"); + logFormat = logFormat.replace("%PARAM1%", "{7}"); + + this.logPattern = logFormat; + + logMessageFormat = new MessageFormat(logPattern); + } + + /** + * Sets the exception pattern. + * + * @param exceptionFormat + * the new exception pattern + */ + public void setExceptionPattern(String exceptionFormat) + { + exceptionFormat = exceptionFormat.replace("%LOGGER%", "{0}"); + exceptionFormat = exceptionFormat.replace("%LEVEL%", "{1}"); + exceptionFormat = exceptionFormat.replace("%TIME%", "{2}"); + exceptionFormat = exceptionFormat.replace("%MESSAGE%", "{3}"); + exceptionFormat = exceptionFormat.replace("%SOURCECLASS%", "{4}"); + exceptionFormat = exceptionFormat.replace("%SOURCEMETHOD%", "{5}"); + exceptionFormat = exceptionFormat.replace("%EXCEPTION%", "{6}"); + exceptionFormat = exceptionFormat.replace("%STACKTRACE%", "{7}"); + + this.exceptionPattern = exceptionFormat; + + exceptionMessageFormat = new MessageFormat(logPattern); + } + + /* + * (non-Javadoc) + * + * @see java.util.logging.Formatter#format(java.util.logging.LogRecord) + */ + @Override + public String format(LogRecord record) + { + Date time = new Date(record.getMillis()); + String formattedTime = dateFormat.format(time); + + String logMessage = ""; + + if (record.getThrown() == null) + { + Object[] log = + { record.getLoggerName(), record.getLevel(), formattedTime, record.getMessage(), record.getSourceClassName(), + record.getSourceMethodName(), record.getParameters() == null ? "" : record.getParameters()[0], record.getParameters() == null ? "" : record.getParameters()[1] }; + + logMessage = logMessageFormat.format(log); + } + else + { + String stack = getStackLayout(record.getThrown(), ""); + + Object[] log = + { record.getLoggerName(), record.getLevel(), formattedTime, record.getMessage(), record.getSourceClassName(), + record.getSourceMethodName(), record.getThrown().getMessage(), stack }; + + logMessage = exceptionMessageFormat.format(log); + } + return logMessage; + } + + /** + * Gets the stack layout. + * + * @param t + * the t + * @param indenter + * the indenter + * + * @return the stack layout + */ + private String getStackLayout(Throwable t, String indenter) + { + indenter = indenter + " "; + + StackTraceElement[] ste = t.getStackTrace(); + String stack = indenter + ste[0].toString(); + for (int i = 1; i < ste.length; i++) + { + stack = stack + "\n" + indenter + ste[i]; + } + + String innerStack = ""; + if (t.getCause() != null) + { + innerStack = indenter + "Caused by: " + t.getCause().getMessage() + "\n"; + innerStack = innerStack + getStackLayout(t.getCause(), indenter); + } + stack = stack + "\n" + innerStack; + + return stack; + } + + /** + * Gets the exception pattern. + * + * @return the exception pattern + */ + public String getExceptionPattern() + { + return exceptionPattern; + } + + /** + * Gets the log pattern. + * + * @return the log pattern + */ + public String getLogPattern() + { + return logPattern; + } + + /** + * Gets the time format. + * + * @return the time format + */ + public String getTimeFormat() + { + return timeFormat; + } + +} diff --git a/javaUtilities/yajsw/src/build/java/org/rzo/builder/JarBuilder.java b/javaUtilities/yajsw/src/build/java/org/rzo/builder/JarBuilder.java new file mode 100644 index 0000000000..5d40107852 --- /dev/null +++ b/javaUtilities/yajsw/src/build/java/org/rzo/builder/JarBuilder.java @@ -0,0 +1,140 @@ +package org.rzo.builder; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; + +import org.apache.commons.vfs2.FileObject; +import org.apache.commons.vfs2.FileSystemManager; +import org.apache.commons.vfs2.VFS; +import org.rzo.yajsw.util.VFSUtils; + +/** + * + * compress multiple jar files into a single jar file + * + * 1. run a program with -verbose:class, so that all required classes are loaded and logged + * 2. write log to a file + * 3. call java JarBuilder log-file newjar + * + * JarBuilder will build a jar with the files used by the application + * + */ + +public class JarBuilder +{ + // multimap: assignment of jar files to class files used in the application + Map> _jar2class = new HashMap>(); + + // populate jar2class + private void parseLog(String file) throws IOException + { + InputStream in = new FileInputStream(file); + // -verbose:class logs have the pattern [Loaded from file:/] + Pattern p = Pattern.compile("\\[Loaded\\s(.*)\\s+from file\\:/(.*)\\]"); + BufferedReader reader = new BufferedReader(new InputStreamReader(in)); + String line = null; + try + { + while ((line = reader.readLine()) != null) + { + Matcher m = p.matcher(line); + if (m.find()) + { + String clazz = m.group(1); + String jar = m.group(2); + // TODO do not add java classes + if (!jar.endsWith("/rt.jar")) + addClazz(jar, clazz); + } + } + } + catch (IOException e) + { + e.printStackTrace(); + } + } + + // add an entry to the multimap + private void addClazz(String jar, String clazz) + { + Set clazzes = _jar2class.get(jar); + System.out.println("add "+jar+"!"+clazz); + if (clazzes == null) + { + clazzes = new HashSet(); + _jar2class.put(jar, clazzes); + } + clazzes.add(clazz); + } + + // add a file to the destination jar + private void addFile(FileObject f, String name, ZipOutputStream out) throws IOException + { + InputStream in = f.getContent().getInputStream(); + out.putNextEntry(new ZipEntry(name)); + + byte[] buf = new byte[1024]; + int len; + while ((len = in.read(buf)) > 0) { + out.write(buf, 0, len); + } + // Complete the entry + out.closeEntry(); + in.close(); + } + + // build the jar + // VFS currently does not support building zip files -> use java's ZipOutputStream + private void buildJar(String newJar) throws IOException + { + ZipOutputStream out = new ZipOutputStream(new FileOutputStream(newJar )); + FileSystemManager fsManager = VFS.getManager(); + for (String jar : _jar2class.keySet()) + { + FileObject jarFile; + if (jar.endsWith(".jar")) + jarFile = fsManager.resolveFile( "jar:"+jar ); + else + jarFile = fsManager.resolveFile( jar ); + + for (String file : _jar2class.get(jar)) + { + file = file.replaceAll("\\.", "/"); + file += ".class"; + FileObject f = fsManager.resolveFile(jarFile, file); + if (f.exists()) + addFile(f, file, out); + else + System.out.println("file not found "+f); + } + + } + out.close(); + } + + + public static void main(String[] args) throws Exception + { + String logFile = args[0]; + String newJar = args[1]; + new File(newJar).delete(); + JarBuilder b = new JarBuilder(); + b.parseLog(logFile); + b.buildJar(newJar); + + } + +} diff --git a/javaUtilities/yajsw/src/build/java/org/rzo/builder/PolicyFileBuilder.java b/javaUtilities/yajsw/src/build/java/org/rzo/builder/PolicyFileBuilder.java new file mode 100644 index 0000000000..3fd36ead76 --- /dev/null +++ b/javaUtilities/yajsw/src/build/java/org/rzo/builder/PolicyFileBuilder.java @@ -0,0 +1,6 @@ +package org.rzo.builder; + +public class PolicyFileBuilder +{ + +} diff --git a/javaUtilities/yajsw/src/build/java/secmgr/manager/ProfilingSecurityManager.java b/javaUtilities/yajsw/src/build/java/secmgr/manager/ProfilingSecurityManager.java new file mode 100644 index 0000000000..e722417c7b --- /dev/null +++ b/javaUtilities/yajsw/src/build/java/secmgr/manager/ProfilingSecurityManager.java @@ -0,0 +1,241 @@ +/* + + * Copyright (c) 2006 Mark Petrovic + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Original Author: Mark Petrovic + * +*/ + +package secmgr.manager; + +import static java.lang.System.err; +import static java.lang.System.out; + +import java.lang.reflect.Field; + +import java.net.URL; + +import java.security.AccessController; +import java.security.AccessControlContext; +import java.security.CodeSource; +import java.security.Permission; +import java.security.ProtectionDomain; + +import java.util.ArrayList; + +/** + * ProfilingSecurityManager is a Java security manager that profiles + * what resources an application accesses, and in what manner --- e.g., read, write, etc. It does not enforce a + * security policy, but rather produces a starting point for crafting one. + *

+ * It extends java.lang.SecurityManager and overrides the two forms of the checkPermission() method. + * For each call to checkPermission(), ProfilingSecurityManager first guards against the + * condition that it itself induced the call to checkPermission(), which would result in + * unterminated recursion. If a call to checkPermission() resulted from a call outside + * ProfilingSecurityManager, the current context is examined and each class found therein is + * profiled as needing access to the java.security.Permission in question. + * + * Profiling is manifested as a writing to System.out a "grant" rule for each java.security.Permission requested + * on a per CodeBase basis. + * + * The implementation here does some very simple rule caching. If a rule has been seen previously, it is not output to System.out. + * The caching cannot prevent a security check, but it can reduce I/O during profiling. + * + * @author Mark S. Petrovic + */ +public class ProfilingSecurityManager extends SecurityManager { + + /* Variables of pure convenience */ + final private String thisClassName; + final private String thisCodeSourceURLString; + final private String psmMsg = "ProfilingSecurityManager"; + final private ArrayList cacheList = new ArrayList(); + + // --------------------------------- + + public ProfilingSecurityManager() { + thisClassName=this.getClass().getName(); + CodeSource thisCodeSource = this.getClass().getProtectionDomain().getCodeSource(); + thisCodeSourceURLString = thisCodeSource.getLocation().toString(); + } + + // ----------------- + + @Override + public void checkPermission(final Permission permission) { + final Throwable t = new Throwable("Profiler stack probe"); + final StackTraceElement[] stack = t.getStackTrace(); + // Avoid recursion owing to actions in this class itself inducing callbacks + if( !isRecur(stack) ) { + buildRules(permission, AccessController.getContext()); + } + } + + // ----------------- + + @Override + public void checkPermission(final Permission permission, final Object context) { + buildRules(permission, (AccessControlContext)context); + } + + // ----------------- + + // With a Permission and an AccessControlContext, we can build and print rules + private void buildRules(final Permission permission, final AccessControlContext ctx) { + try { + final ProtectionDomain[] protectionDomain = getProtectionDomains(ctx); + if( null != protectionDomain ) { + for(int i=0;i=1;--i) { + final boolean c = st[i].getClassName().equals(thisClassName); + final boolean m = st[i].getMethodName().equals("buildRules"); + if (c && m) { + v = true; + break; + } + } + return v; + } + + // ----------------- + + /* Get the protection domains by Java reflection. There is no public API for this info, + * making this code Sun Java 1.5 JVM implementation dependent. + */ + private ProtectionDomain[] getProtectionDomains(final AccessControlContext context) throws + IllegalStateException { + ProtectionDomain[] pda = null; + try { + final Field[] fields = AccessControlContext.class.getDeclaredFields(); + if( null == fields ) { + throw new IllegalStateException("No fields"); + } + for(int i=0; i + *

  • java.api.class - the Java interface for the object interface. + *
  • java.ejb.home.class - the EJB home interface + *
  • java.ejb.remote.class - the EJB remote interface + *
  • java.primary.key.class - the EJB primary key class + * + */ + public Object _hessian_getAttribute(String name); +} diff --git a/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/client/HessianProxy.java b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/client/HessianProxy.java new file mode 100644 index 0000000000..bdfe9dafa8 --- /dev/null +++ b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/client/HessianProxy.java @@ -0,0 +1,472 @@ +/* + * The Apache Software License, Version 1.1 + * + * Copyright (c) 2001-2004 Caucho Technology, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Caucho Technology (http://www.caucho.com/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Hessian", "Resin", and "Caucho" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * info@caucho.com. + * + * 5. Products derived from this software may not be called "Resin" + * nor may "Resin" appear in their names without prior written + * permission of Caucho Technology. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Scott Ferguson + */ + +package com.caucho.hessian4.client; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.PrintWriter; +import java.io.Serializable; +import java.io.Writer; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.net.URL; +import java.net.URLConnection; +import java.util.WeakHashMap; +import java.util.logging.Level; +import java.util.logging.Logger; + +import com.caucho.hessian4.io.AbstractHessianInput; +import com.caucho.hessian4.io.AbstractHessianOutput; +import com.caucho.hessian4.io.HessianDebugInputStream; +import com.caucho.hessian4.io.HessianDebugOutputStream; +import com.caucho.hessian4.io.HessianProtocolException; +import com.caucho.hessian4.io.HessianRemote; +import com.caucho.hessian4.services.server.AbstractSkeleton; + +/** + * Proxy implementation for Hessian clients. Applications will generally + * use HessianProxyFactory to create proxy clients. + */ +public class HessianProxy implements InvocationHandler, Serializable { + private static final Logger log + = Logger.getLogger(HessianProxy.class.getName()); + + protected HessianProxyFactory _factory; + + private WeakHashMap _mangleMap + = new WeakHashMap(); + + private Class _type; + private URL _url; + + /** + * Protected constructor for subclassing + */ + protected HessianProxy(URL url, HessianProxyFactory factory) + { + this(url, factory, null); + } + + /** + * Protected constructor for subclassing + */ + protected HessianProxy(URL url, + HessianProxyFactory factory, + Class type) + { + _factory = factory; + _url = url; + _type = type; + } + + /** + * Returns the proxy's URL. + */ + public URL getURL() + { + return _url; + } + + /** + * Handles the object invocation. + * + * @param proxy the proxy object to invoke + * @param method the method to call + * @param args the arguments to the proxy object + */ + public Object invoke(Object proxy, Method method, Object []args) + throws Throwable + { + String mangleName; + + synchronized (_mangleMap) { + mangleName = _mangleMap.get(method); + } + + if (mangleName == null) { + String methodName = method.getName(); + Class []params = method.getParameterTypes(); + + // equals and hashCode are special cased + if (methodName.equals("equals") + && params.length == 1 && params[0].equals(Object.class)) { + Object value = args[0]; + if (value == null || ! Proxy.isProxyClass(value.getClass())) + return Boolean.FALSE; + + Object proxyHandler = Proxy.getInvocationHandler(value); + + if (! (proxyHandler instanceof HessianProxy)) + return Boolean.FALSE; + + HessianProxy handler = (HessianProxy) proxyHandler; + + return new Boolean(_url.equals(handler.getURL())); + } + else if (methodName.equals("hashCode") && params.length == 0) + return new Integer(_url.hashCode()); + else if (methodName.equals("getHessianType")) + return proxy.getClass().getInterfaces()[0].getName(); + else if (methodName.equals("getHessianURL")) + return _url.toString(); + else if (methodName.equals("toString") && params.length == 0) + return "HessianProxy[" + _url + "]"; + + if (! _factory.isOverloadEnabled()) + mangleName = method.getName(); + else + mangleName = mangleName(method); + + synchronized (_mangleMap) { + _mangleMap.put(method, mangleName); + } + } + + InputStream is = null; + HessianConnection conn = null; + + try { + if (log.isLoggable(Level.FINER)) + log.finer("Hessian[" + _url + "] calling " + mangleName); + + conn = sendRequest(mangleName, args); + + is = conn.getInputStream(); + + if (log.isLoggable(Level.FINEST)) { + PrintWriter dbg = new PrintWriter(new LogWriter(log)); + HessianDebugInputStream dIs + = new HessianDebugInputStream(is, dbg); + + dIs.startTop2(); + + is = dIs; + } + + AbstractHessianInput in; + + int code = is.read(); + + if (code == 'H') { + int major = is.read(); + int minor = is.read(); + + in = _factory.getHessian2Input(is); + + Object value = in.readReply(method.getReturnType()); + + return value; + } + else if (code == 'r') { + int major = is.read(); + int minor = is.read(); + + in = _factory.getHessianInput(is); + + in.startReplyBody(); + + Object value = in.readObject(method.getReturnType()); + + if (value instanceof InputStream) { + value = new ResultInputStream(conn, is, in, (InputStream) value); + is = null; + conn = null; + } + else + in.completeReply(); + + return value; + } + else + throw new HessianProtocolException("'" + (char) code + "' is an unknown code"); + } catch (HessianProtocolException e) { + throw new HessianRuntimeException(e); + } finally { + try { + if (is != null) + is.close(); + } catch (Exception e) { + log.log(Level.FINE, e.toString(), e); + } + + try { + if (conn != null) + conn.destroy(); + } catch (Exception e) { + log.log(Level.FINE, e.toString(), e); + } + } + } + + protected String mangleName(Method method) + { + Class []param = method.getParameterTypes(); + + if (param == null || param.length == 0) + return method.getName(); + else + return AbstractSkeleton.mangleName(method, false); + } + + /** + * Sends the HTTP request to the Hessian connection. + */ + protected HessianConnection sendRequest(String methodName, Object []args) + throws IOException + { + HessianConnection conn = null; + + conn = _factory.getConnectionFactory().open(_url); + boolean isValid = false; + + try { + addRequestHeaders(conn); + + OutputStream os = null; + + try { + os = conn.getOutputStream(); + } catch (Exception e) { + throw new HessianRuntimeException(e); + } + + if (log.isLoggable(Level.FINEST)) { + PrintWriter dbg = new PrintWriter(new LogWriter(log)); + HessianDebugOutputStream dOs = new HessianDebugOutputStream(os, dbg); + dOs.startTop2(); + os = dOs; + } + + AbstractHessianOutput out = _factory.getHessianOutput(os); + + out.call(methodName, args); + out.flush(); + + conn.sendRequest(); + + isValid = true; + + return conn; + } finally { + if (! isValid && conn != null) + conn.destroy(); + } + } + + /** + * Method that allows subclasses to add request headers such as cookies. + * Default implementation is empty. + */ + protected void addRequestHeaders(HessianConnection conn) + { + conn.addHeader("Content-Type", "x-application/hessian"); + + String basicAuth = _factory.getBasicAuth(); + + if (basicAuth != null) + conn.addHeader("Authorization", basicAuth); + } + + /** + * Method that allows subclasses to parse response headers such as cookies. + * Default implementation is empty. + * @param conn + */ + protected void parseResponseHeaders(URLConnection conn) + { + } + + public Object writeReplace() + { + return new HessianRemote(_type.getName(), _url.toString()); + } + + static class ResultInputStream extends InputStream { + private HessianConnection _conn; + private InputStream _connIs; + private AbstractHessianInput _in; + private InputStream _hessianIs; + + ResultInputStream(HessianConnection conn, + InputStream is, + AbstractHessianInput in, + InputStream hessianIs) + { + _conn = conn; + _connIs = is; + _in = in; + _hessianIs = hessianIs; + } + + public int read() + throws IOException + { + if (_hessianIs != null) { + int value = _hessianIs.read(); + + if (value < 0) + close(); + + return value; + } + else + return -1; + } + + public int read(byte []buffer, int offset, int length) + throws IOException + { + if (_hessianIs != null) { + int value = _hessianIs.read(buffer, offset, length); + + if (value < 0) + close(); + + return value; + } + else + return -1; + } + + public void close() + throws IOException + { + HessianConnection conn = _conn; + _conn = null; + + InputStream connIs = _connIs; + _connIs = null; + + AbstractHessianInput in = _in; + _in = null; + + InputStream hessianIs = _hessianIs; + _hessianIs = null; + + try { + if (hessianIs != null) + hessianIs.close(); + } catch (Exception e) { + log.log(Level.FINE, e.toString(), e); + } + + try { + if (in != null) { + in.completeReply(); + in.close(); + } + } catch (Exception e) { + log.log(Level.FINE, e.toString(), e); + } + + try { + if (connIs != null) { + connIs.close(); + } + } catch (Exception e) { + log.log(Level.FINE, e.toString(), e); + } + + try { + if (conn != null) { + conn.close(); + } + } catch (Exception e) { + log.log(Level.FINE, e.toString(), e); + } + } + } + + static class LogWriter extends Writer { + private Logger _log; + private Level _level = Level.FINEST; + private StringBuilder _sb = new StringBuilder(); + + LogWriter(Logger log) + { + _log = log; + } + + public void write(char ch) + { + if (ch == '\n' && _sb.length() > 0) { + _log.fine(_sb.toString()); + _sb.setLength(0); + } + else + _sb.append((char) ch); + } + + public void write(char []buffer, int offset, int length) + { + for (int i = 0; i < length; i++) { + char ch = buffer[offset + i]; + + if (ch == '\n' && _sb.length() > 0) { + _log.log(_level, _sb.toString()); + _sb.setLength(0); + } + else + _sb.append((char) ch); + } + } + + public void flush() + { + } + + public void close() + { + if (_sb.length() > 0) + _log.log(_level, _sb.toString()); + } + } +} diff --git a/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/client/HessianProxyFactory.java b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/client/HessianProxyFactory.java new file mode 100644 index 0000000000..e125d4e1f2 --- /dev/null +++ b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/client/HessianProxyFactory.java @@ -0,0 +1,607 @@ +/* + * The Apache Software License, Version 1.1 + * + * Copyright (c) 2001-2004 Caucho Technology, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Caucho Technology (http://www.caucho.com/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Hessian", "Resin", and "Caucho" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * info@caucho.com. + * + * 5. Products derived from this software may not be called "Resin" + * nor may "Resin" appear in their names without prior written + * permission of Caucho Technology. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Scott Ferguson + */ + +package com.caucho.hessian4.client; + +import java.io.InputStream; +import java.io.OutputStream; +import java.io.PrintWriter; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Proxy; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.logging.Logger; + +import com.caucho.hessian4.io.AbstractHessianInput; +import com.caucho.hessian4.io.AbstractHessianOutput; +import com.caucho.hessian4.io.Hessian2Input; +import com.caucho.hessian4.io.Hessian2Output; +import com.caucho.hessian4.io.HessianDebugInputStream; +import com.caucho.hessian4.io.HessianInput; +import com.caucho.hessian4.io.HessianOutput; +import com.caucho.hessian4.io.HessianRemoteObject; +import com.caucho.hessian4.io.HessianRemoteResolver; +import com.caucho.hessian4.io.SerializerFactory; +import com.caucho.hessian4.services.client.ServiceProxyFactory; + +/** + * Factory for creating Hessian client stubs. The returned stub will + * call the remote object for all methods. + * + *
    + * String url = "http://localhost:8080/ejb/hello";
    + * HelloHome hello = (HelloHome) factory.create(HelloHome.class, url);
    + * 
    + * + * After creation, the stub can be like a regular Java class. Because + * it makes remote calls, it can throw more exceptions than a Java class. + * In particular, it may throw protocol exceptions. + * + * The factory can also be configured as a JNDI resource. The factory + * expects to parameters: "type" and "url", corresponding to the two + * arguments to create + * + * In Resin 3.0, the above example would be configured as: + *
    + * <reference>
    + *   <jndi-name>hessian/hello</jndi-name>
    + *   <factory>com.caucho.hessian.client.HessianProxyFactory</factory>
    + *   <init-param url="http://localhost:8080/ejb/hello"/>
    + *   <init-param type="test.HelloHome"/>
    + * </reference>
    + * 
    + * + * To get the above resource, use JNDI as follows: + *
    + * Context ic = new InitialContext();
    + * HelloHome hello = (HelloHome) ic.lookup("java:comp/env/hessian/hello");
    + *
    + * System.out.println("Hello: " + hello.helloWorld());
    + * 
    + * + *

    Authentication

    + * + *

    The proxy can use HTTP basic authentication if the user and the + * password are set. + */ +public class HessianProxyFactory implements ServiceProxyFactory /*, ObjectFactory */{ + protected static Logger log + = Logger.getLogger(HessianProxyFactory.class.getName()); + + private final ClassLoader _loader; + + private SerializerFactory _serializerFactory; + + private HessianConnectionFactory _connFactory; + + private HessianRemoteResolver _resolver; + + private String _user; + private String _password; + private String _basicAuth; + + private boolean _isOverloadEnabled = false; + + private boolean _isHessian2Reply = true; + private boolean _isHessian2Request = false; + + private boolean _isChunkedPost = true; + private boolean _isDebug = false; + + private long _readTimeout = -1; + private long _connectTimeout = -1; + + /** + * Creates the new proxy factory. + */ + public HessianProxyFactory() + { + this(Thread.currentThread().getContextClassLoader()); + } + + /** + * Creates the new proxy factory. + */ + public HessianProxyFactory(ClassLoader loader) + { + _loader = loader; + _resolver = new HessianProxyResolver(this); + } + + /** + * Sets the user. + */ + public void setUser(String user) + { + _user = user; + _basicAuth = null; + } + + /** + * Sets the password. + */ + public void setPassword(String password) + { + _password = password; + _basicAuth = null; + } + + public String getBasicAuth() + { + if (_basicAuth != null) + return _basicAuth; + + else if (_user != null && _password != null) + return "Basic " + base64(_user + ":" + _password); + + else + return null; + } + + /** + * Sets the connection factory to use when connecting + * to the Hessian service. + */ + public void setConnectionFactory(HessianConnectionFactory factory) + { + _connFactory = factory; + } + + /** + * Returns the connection factory to be used for the HTTP request. + */ + public HessianConnectionFactory getConnectionFactory() + { + if (_connFactory == null) { + _connFactory = createHessianConnectionFactory(); + _connFactory.setHessianProxyFactory(this); + } + + return _connFactory; + } + + /** + * Sets the debug + */ + public void setDebug(boolean isDebug) + { + _isDebug = isDebug; + } + + /** + * Gets the debug + */ + public boolean isDebug() + { + return _isDebug; + } + + /** + * Returns true if overloaded methods are allowed (using mangling) + */ + public boolean isOverloadEnabled() + { + return _isOverloadEnabled; + } + + /** + * set true if overloaded methods are allowed (using mangling) + */ + public void setOverloadEnabled(boolean isOverloadEnabled) + { + _isOverloadEnabled = isOverloadEnabled; + } + + /** + * Set true if should use chunked encoding on the request. + */ + public void setChunkedPost(boolean isChunked) + { + _isChunkedPost = isChunked; + } + + /** + * Set true if should use chunked encoding on the request. + */ + public boolean isChunkedPost() + { + return _isChunkedPost; + } + + /** + * The socket timeout on requests in milliseconds. + */ + public long getReadTimeout() + { + return _readTimeout; + } + + /** + * The socket timeout on requests in milliseconds. + */ + public void setReadTimeout(long timeout) + { + _readTimeout = timeout; + } + + /** + * The socket connection timeout in milliseconds. + */ + public long getConnectTimeout() + { + return _connectTimeout; + } + + /** + * The socket connect timeout in milliseconds. + */ + public void setConnectTimeout(long timeout) + { + _connectTimeout = timeout; + } + + /** + * True if the proxy can read Hessian 2 responses. + */ + public void setHessian2Reply(boolean isHessian2) + { + _isHessian2Reply = isHessian2; + } + + /** + * True if the proxy should send Hessian 2 requests. + */ + public void setHessian2Request(boolean isHessian2) + { + _isHessian2Request = isHessian2; + + if (isHessian2) + _isHessian2Reply = true; + } + + /** + * Returns the remote resolver. + */ + public HessianRemoteResolver getRemoteResolver() + { + return _resolver; + } + + /** + * Sets the serializer factory. + */ + public void setSerializerFactory(SerializerFactory factory) + { + _serializerFactory = factory; + } + + /** + * Gets the serializer factory. + */ + public SerializerFactory getSerializerFactory() + { + if (_serializerFactory == null) + _serializerFactory = new SerializerFactory(_loader); + + return _serializerFactory; + } + + protected HessianConnectionFactory createHessianConnectionFactory() + { + String className + = System.getProperty(HessianConnectionFactory.class.getName()); + + HessianConnectionFactory factory = null; + + try { + if (className != null) { + ClassLoader loader = Thread.currentThread().getContextClassLoader(); + + Class cl = Class.forName(className, false, loader); + + factory = (HessianConnectionFactory) cl.newInstance(); + + return factory; + } + } catch (Exception e) { + throw new RuntimeException(e); + } + + return new HessianURLConnectionFactory(); + } + /** + * Creates a new proxy with the specified URL. The API class uses + * the java.api.class value from _hessian_ + * + * @param url the URL where the client object is located. + * + * @return a proxy to the object with the specified interface. + */ + public Object create(String url) + throws MalformedURLException, ClassNotFoundException + { + HessianMetaInfoAPI metaInfo; + + metaInfo = (HessianMetaInfoAPI) create(HessianMetaInfoAPI.class, url); + + String apiClassName = + (String) metaInfo._hessian_getAttribute("java.api.class"); + + if (apiClassName == null) + throw new HessianRuntimeException(url + " has an unknown api."); + + Class apiClass = Class.forName(apiClassName, false, _loader); + + return create(apiClass, url); + } + + /** + * Creates a new proxy with the specified URL. The returned object + * is a proxy with the interface specified by api. + * + *

    +   * String url = "http://localhost:8080/ejb/hello");
    +   * HelloHome hello = (HelloHome) factory.create(HelloHome.class, url);
    +   * 
    + * + * @param api the interface the proxy class needs to implement + * @param url the URL where the client object is located. + * + * @return a proxy to the object with the specified interface. + */ + public Object create(Class api, String urlName) + throws MalformedURLException + { + return create(api, urlName, _loader); + } + + /** + * Creates a new proxy with the specified URL. The returned object + * is a proxy with the interface specified by api. + * + *
    +   * String url = "http://localhost:8080/ejb/hello");
    +   * HelloHome hello = (HelloHome) factory.create(HelloHome.class, url);
    +   * 
    + * + * @param api the interface the proxy class needs to implement + * @param url the URL where the client object is located. + * + * @return a proxy to the object with the specified interface. + */ + public Object create(Class api, String urlName, ClassLoader loader) + throws MalformedURLException + { + URL url = new URL(urlName); + + return create(api, url, loader); + } + + /** + * Creates a new proxy with the specified URL. The returned object + * is a proxy with the interface specified by api. + * + *
    +   * String url = "http://localhost:8080/ejb/hello");
    +   * HelloHome hello = (HelloHome) factory.create(HelloHome.class, url);
    +   * 
    + * + * @param api the interface the proxy class needs to implement + * @param url the URL where the client object is located. + * + * @return a proxy to the object with the specified interface. + */ + public Object create(Class api, URL url, ClassLoader loader) + { + if (api == null) + throw new NullPointerException("api must not be null for HessianProxyFactory.create()"); + InvocationHandler handler = null; + + handler = new HessianProxy(url, this, api); + + return Proxy.newProxyInstance(loader, + new Class[] { api, + HessianRemoteObject.class }, + handler); + } + + public AbstractHessianInput getHessianInput(InputStream is) + { + return getHessian2Input(is); + } + + public AbstractHessianInput getHessian1Input(InputStream is) + { + AbstractHessianInput in; + + if (_isDebug) + is = new HessianDebugInputStream(is, new PrintWriter(System.out)); + + in = new HessianInput(is); + + in.setRemoteResolver(getRemoteResolver()); + + in.setSerializerFactory(getSerializerFactory()); + + return in; + } + + public AbstractHessianInput getHessian2Input(InputStream is) + { + AbstractHessianInput in; + + if (_isDebug) + is = new HessianDebugInputStream(is, new PrintWriter(System.out)); + + in = new Hessian2Input(is); + + in.setRemoteResolver(getRemoteResolver()); + + in.setSerializerFactory(getSerializerFactory()); + + return in; + } + + public AbstractHessianOutput getHessianOutput(OutputStream os) + { + AbstractHessianOutput out; + + if (_isHessian2Request) + out = new Hessian2Output(os); + else { + HessianOutput out1 = new HessianOutput(os); + out = out1; + + if (_isHessian2Reply) + out1.setVersion(2); + } + + out.setSerializerFactory(getSerializerFactory()); + + return out; + } + +// /** +// * JNDI object factory so the proxy can be used as a resource. +// */ +// public Object getObjectInstance(Object obj, Name name, +// Context nameCtx, Hashtable environment) +// throws Exception +// { +// Reference ref = (Reference) obj; +// +// String api = null; +// String url = null; +// String user = null; +// String password = null; +// +// for (int i = 0; i < ref.size(); i++) { +// RefAddr addr = ref.get(i); +// +// String type = addr.getType(); +// String value = (String) addr.getContent(); +// +// if (type.equals("type")) +// api = value; +// else if (type.equals("url")) +// url = value; +// else if (type.equals("user")) +// setUser(value); +// else if (type.equals("password")) +// setPassword(value); +// } +// +// if (url == null) +// throw new NamingException("`url' must be configured for HessianProxyFactory."); +// // XXX: could use meta protocol to grab this +// if (api == null) +// throw new NamingException("`type' must be configured for HessianProxyFactory."); +// +// Class apiClass = Class.forName(api, false, _loader); +// +// return create(apiClass, url); +// } + + /** + * Creates the Base64 value. + */ + private String base64(String value) + { + StringBuffer cb = new StringBuffer(); + + int i = 0; + for (i = 0; i + 2 < value.length(); i += 3) { + long chunk = (int) value.charAt(i); + chunk = (chunk << 8) + (int) value.charAt(i + 1); + chunk = (chunk << 8) + (int) value.charAt(i + 2); + + cb.append(encode(chunk >> 18)); + cb.append(encode(chunk >> 12)); + cb.append(encode(chunk >> 6)); + cb.append(encode(chunk)); + } + + if (i + 1 < value.length()) { + long chunk = (int) value.charAt(i); + chunk = (chunk << 8) + (int) value.charAt(i + 1); + chunk <<= 8; + + cb.append(encode(chunk >> 18)); + cb.append(encode(chunk >> 12)); + cb.append(encode(chunk >> 6)); + cb.append('='); + } + else if (i < value.length()) { + long chunk = (int) value.charAt(i); + chunk <<= 16; + + cb.append(encode(chunk >> 18)); + cb.append(encode(chunk >> 12)); + cb.append('='); + cb.append('='); + } + + return cb.toString(); + } + + public static char encode(long d) + { + d &= 0x3f; + if (d < 26) + return (char) (d + 'A'); + else if (d < 52) + return (char) (d + 'a' - 26); + else if (d < 62) + return (char) (d + '0' - 52); + else if (d == 62) + return '+'; + else + return '/'; + } +} + diff --git a/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/client/HessianProxyResolver.java b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/client/HessianProxyResolver.java new file mode 100644 index 0000000000..047fde6fde --- /dev/null +++ b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/client/HessianProxyResolver.java @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2001-2004 Caucho Technology, Inc. All rights reserved. + * + * The Apache Software License, Version 1.1 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Caucho Technology (http://www.caucho.com/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Hessian", "Resin", and "Caucho" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * info@caucho.com. + * + * 5. Products derived from this software may not be called "Resin" + * nor may "Resin" appear in their names without prior written + * permission of Caucho Technology. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Scott Ferguson + */ + +package com.caucho.hessian4.client; + +import java.io.IOException; + +import com.caucho.hessian4.io.HessianRemoteResolver; + +/** + * Looks up remote objects in the proxy. + */ +public class HessianProxyResolver implements HessianRemoteResolver { + private HessianProxyFactory _factory; + + /** + * Creates an uninitialized Hessian remote resolver. + */ + public HessianProxyResolver(HessianProxyFactory factory) + { + _factory = factory; + } + + /** + * Looks up a proxy object. + */ + public Object lookup(String type, String url) + throws IOException + { + ClassLoader loader = Thread.currentThread().getContextClassLoader(); + + try { + Class api = Class.forName(type, false, loader); + + return _factory.create(api, url); + } catch (Exception e) { + throw new IOException(String.valueOf(e)); + } + } +} diff --git a/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/client/HessianRuntimeException.java b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/client/HessianRuntimeException.java new file mode 100644 index 0000000000..a8f27a99ff --- /dev/null +++ b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/client/HessianRuntimeException.java @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2001-2004 Caucho Technology, Inc. All rights reserved. + * + * The Apache Software License, Version 1.1 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Caucho Technology (http://www.caucho.com/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Hessian", "Resin", and "Caucho" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * info@caucho.com. + * + * 5. Products derived from this software may not be called "Resin" + * nor may "Resin" appear in their names without prior written + * permission of Caucho Technology. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Scott Ferguson + */ + +package com.caucho.hessian4.client; + +/** + * Wrapper for protocol exceptions thrown in the proxy. + */ +public class HessianRuntimeException extends RuntimeException { + private Throwable rootCause; + + /** + * Zero-arg constructor. + */ + public HessianRuntimeException() + { + } + + /** + * Create the exception. + */ + public HessianRuntimeException(String message) + { + super(message); + } + + /** + * Create the exception. + */ + public HessianRuntimeException(String message, Throwable rootCause) + { + super(message); + + this.rootCause = rootCause; + } + + /** + * Create the exception. + */ + public HessianRuntimeException(Throwable rootCause) + { + super(String.valueOf(rootCause)); + + this.rootCause = rootCause; + } + + /** + * Returns the underlying cause. + */ + public Throwable getRootCause() + { + return this.rootCause; + } + + /** + * Returns the underlying cause. + */ + public Throwable getCause() + { + return getRootCause(); + } +} diff --git a/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/client/HessianURLConnection.java b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/client/HessianURLConnection.java new file mode 100644 index 0000000000..2ad61eba9c --- /dev/null +++ b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/client/HessianURLConnection.java @@ -0,0 +1,203 @@ +/* + * Copyright (c) 2001-2004 Caucho Technology, Inc. All rights reserved. + * + * The Apache Software License, Version 1.1 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Caucho Technology (http://www.caucho.com/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Burlap", "Resin", and "Caucho" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * info@caucho.com. + * + * 5. Products derived from this software may not be called "Resin" + * nor may "Resin" appear in their names without prior written + * permission of Caucho Technology. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Scott Ferguson + */ + +package com.caucho.hessian4.client; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.HttpURLConnection; +import java.net.URL; +import java.net.URLConnection; + +/** + * Internal connection to a server. The default connection is based on + * java.net + */ +public class HessianURLConnection extends AbstractHessianConnection { + private URL _url; + private URLConnection _conn; + + private int _statusCode; + private String _statusMessage; + + private InputStream _inputStream; + private InputStream _errorStream; + + HessianURLConnection(URL url, URLConnection conn) + { + _url = url; + _conn = conn; + } + + /** + * Adds a HTTP header. + */ + public void addHeader(String key, String value) + { + _conn.setRequestProperty(key, value); + } + + /** + * Returns the output stream for the request. + */ + public OutputStream getOutputStream() + throws IOException + { + return _conn.getOutputStream(); + } + + /** + * Sends the request + */ + public void sendRequest() + throws IOException + { + if (_conn instanceof HttpURLConnection) { + HttpURLConnection httpConn = (HttpURLConnection) _conn; + + _statusCode = 500; + + try { + _statusCode = httpConn.getResponseCode(); + } catch (Exception e) { + } + + parseResponseHeaders(httpConn); + + InputStream is = null; + + if (_statusCode != 200) { + StringBuffer sb = new StringBuffer(); + int ch; + + try { + is = httpConn.getInputStream(); + + if (is != null) { + while ((ch = is.read()) >= 0) + sb.append((char) ch); + + is.close(); + } + + is = httpConn.getErrorStream(); + if (is != null) { + while ((ch = is.read()) >= 0) + sb.append((char) ch); + } + + _statusMessage = sb.toString(); + } catch (FileNotFoundException e) { + throw new HessianConnectionException("HessianProxy cannot connect to '" + _url, e); + } catch (IOException e) { + if (is == null) + throw new HessianConnectionException(_statusCode + ": " + e, e); + else + throw new HessianConnectionException(_statusCode + ": " + sb, e); + } + + if (is != null) + is.close(); + + throw new HessianConnectionException(_statusCode + ": " + sb.toString()); + } + } + } + + protected void parseResponseHeaders(HttpURLConnection conn) + throws IOException + { + } + + /** + * Returns the status code. + */ + public int getStatusCode() + { + return _statusCode; + } + + /** + * Returns the status string. + */ + public String getStatusMessage() + { + return _statusMessage; + } + + /** + * Returns the InputStream to the result + */ + public InputStream getInputStream() + throws IOException + { + return _conn.getInputStream(); + } + + /** + * Close/free the connection + */ + public void close() + { + } + + /** + * Disconnect the connection + */ + public void destroy() + { + URLConnection conn = _conn; + _conn = null; + + if (conn instanceof HttpURLConnection) + ((HttpURLConnection) conn).disconnect(); + } +} + diff --git a/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/client/HessianURLConnectionFactory.java b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/client/HessianURLConnectionFactory.java new file mode 100644 index 0000000000..5665a9e3ba --- /dev/null +++ b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/client/HessianURLConnectionFactory.java @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2001-2004 Caucho Technology, Inc. All rights reserved. + * + * The Apache Software License, Version 1.1 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Caucho Technology (http://www.caucho.com/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Burlap", "Resin", and "Caucho" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * info@caucho.com. + * + * 5. Products derived from this software may not be called "Resin" + * nor may "Resin" appear in their names without prior written + * permission of Caucho Technology. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Scott Ferguson + */ + +package com.caucho.hessian4.client; + +import java.io.IOException; +import java.net.URL; +import java.net.URLConnection; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Internal factory for creating connections to the server. The default + * factory is java.net + */ +public class HessianURLConnectionFactory implements HessianConnectionFactory { + private static final Logger log + = Logger.getLogger(HessianURLConnectionFactory.class.getName()); + + private HessianProxyFactory _proxyFactory; + + public void setHessianProxyFactory(HessianProxyFactory factory) + { + _proxyFactory = factory; + } + + /** + * Opens a new or recycled connection to the HTTP server. + */ + public HessianConnection open(URL url) + throws IOException + { + if (log.isLoggable(Level.FINER)) + log.finer(this + " open(" + url + ")"); + + URLConnection conn = url.openConnection(); + + // HttpURLConnection httpConn = (HttpURLConnection) conn; + // httpConn.setRequestMethod("POST"); + // conn.setDoInput(true); + + long connectTimeout = _proxyFactory.getConnectTimeout(); + + if (connectTimeout >= 0) + conn.setConnectTimeout((int) connectTimeout); + + conn.setDoOutput(true); + + long readTimeout = _proxyFactory.getReadTimeout(); + + if (readTimeout > 0) { + try { + conn.setReadTimeout((int) readTimeout); + } catch (Throwable e) { + } + } + + /* + // Used chunked mode when available, i.e. JDK 1.5. + if (_proxyFactory.isChunkedPost() && conn instanceof HttpURLConnection) { + try { + HttpURLConnection httpConn = (HttpURLConnection) conn; + + httpConn.setChunkedStreamingMode(8 * 1024); + } catch (Throwable e) { + } + } + */ + + return new HessianURLConnection(url, conn); + } +} diff --git a/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/client/package.html b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/client/package.html new file mode 100644 index 0000000000..0b15710b98 --- /dev/null +++ b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/client/package.html @@ -0,0 +1,59 @@ + + +Portable client code for using Hessian services. Since this package is +independent of all Resin code, its classes can be copied to a +non-Resin client jar. + +

    RPC Proxy Clients - HessianProxyFactory

    + +Most application clients will use HessianProxyFactory to +create stub objects. The stub objects can be called with normal +Java calls. Because the objects are remote, the client application needs +to be able to deal with IOException caused by an unavailable server or +a protocol error. + +
    +import com.caucho.hessian.client.HessianProxyFactory;
    +
    +...
    +
    +URL url = new URL("http://localhost:8080/ejb/hello");
    +HelloHome hello = (HelloHome) factory.create(HelloHome.class, url);
    +
    +System.out.println("hello: " + hello.hello());
    +
    + +

    Serialization

    + +Since the Hessian protocol serializes Java objects to XML, the +HessianSerializerOutput and HessianSerializerInput +can be used for serialization. + +

    Serialization

    +
    +OutputStream os = new FileOutputStream("test.xml");
    +HessianOutput out = new HessianSerializerOutput(os);
    +
    +out.writeObject(obj);
    +os.close();
    +
    + +

    Deserialization

    +
    +InputStream is = new FileInputStream("test.xml");
    +HessianInput in = new HessianSerializerInput(in);
    +
    +Object obj = in.readObject();
    +
    +is.close();
    +
    + +

    MicroHessianInput and MicroHessianOutput

    + +These two classes only require classes from Java MicroEdition. So they +can be extracted separately into a hessian-micro.jar. Because of this +restriction and their small size, these two classes +are ideal for limited size devices like mobile phones and PDAs. + + + \ No newline at end of file diff --git a/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/AbstractDeserializer.java b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/AbstractDeserializer.java new file mode 100644 index 0000000000..fd34993915 --- /dev/null +++ b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/AbstractDeserializer.java @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2001-2004 Caucho Technology, Inc. All rights reserved. + * + * The Apache Software License, Version 1.1 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Caucho Technology (http://www.caucho.com/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Burlap", "Resin", and "Caucho" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * info@caucho.com. + * + * 5. Products derived from this software may not be called "Resin" + * nor may "Resin" appear in their names without prior written + * permission of Caucho Technology. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Scott Ferguson + */ + +package com.caucho.hessian4.io; + +import java.io.IOException; + +/** + * Deserializing an object. + */ +public class AbstractDeserializer implements Deserializer { + public static final NullDeserializer NULL = new NullDeserializer(); + + public Class getType() + { + return Object.class; + } + + public boolean isReadResolve() + { + return false; + } + + public Object readObject(AbstractHessianInput in) + throws IOException + { + Object obj = in.readObject(); + + String className = getClass().getName(); + + if (obj != null) + throw error(className + ": unexpected object " + obj.getClass().getName() + " (" + obj + ")"); + else + throw error(className + ": unexpected null value"); + } + + public Object readList(AbstractHessianInput in, int length) + throws IOException + { + throw new UnsupportedOperationException(String.valueOf(this)); + } + + public Object readLengthList(AbstractHessianInput in, int length) + throws IOException + { + throw new UnsupportedOperationException(String.valueOf(this)); + } + + public Object readMap(AbstractHessianInput in) + throws IOException + { + Object obj = in.readObject(); + + String className = getClass().getName(); + + if (obj != null) + throw error(className + ": unexpected object " + obj.getClass().getName() + " (" + obj + ")"); + else + throw error(className + ": unexpected null value"); + } + + /** + * Creates the field array for a class. The default + * implementation returns a String[] array. + * + * @param len number of items in the array + * @return the new empty array + */ + public Object []createFields(int len) + { + return new String[len]; + } + + /** + * Creates a field value class. The default + * implementation returns the String. + * + * @param len number of items in the array + * @return the new empty array + */ + public Object createField(String name) + { + return name; + } + + public Object readObject(AbstractHessianInput in, + String []fieldNames) + throws IOException + { + return readObject(in, (Object []) fieldNames); + } + + /** + * Reads an object instance from the input stream + */ + public Object readObject(AbstractHessianInput in, + Object []fields) + throws IOException + { + throw new UnsupportedOperationException(toString()); + } + + protected HessianProtocolException error(String msg) + { + return new HessianProtocolException(msg); + } + + protected String codeName(int ch) + { + if (ch < 0) + return "end of file"; + else + return "0x" + Integer.toHexString(ch & 0xff); + } + + /** + * The NullDeserializer exists as a marker for the factory classes so + * they save a null result. + */ + static final class NullDeserializer extends AbstractDeserializer { + } +} diff --git a/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/AbstractHessianInput.java b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/AbstractHessianInput.java new file mode 100644 index 0000000000..e28f3c1eeb --- /dev/null +++ b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/AbstractHessianInput.java @@ -0,0 +1,487 @@ +/* + * Copyright (c) 2001-2004 Caucho Technology, Inc. All rights reserved. + * + * The Apache Software License, Version 1.1 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Caucho Technology (http://www.caucho.com/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Burlap", "Resin", and "Caucho" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * info@caucho.com. + * + * 5. Products derived from this software may not be called "Resin" + * nor may "Resin" appear in their names without prior written + * permission of Caucho Technology. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Scott Ferguson + */ + +package com.caucho.hessian4.io; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.Reader; + +/** + * Abstract base class for Hessian requests. Hessian users should only + * need to use the methods in this class. + * + *
    + * AbstractHessianInput in = ...; // get input
    + * String value;
    + *
    + * in.startReply();         // read reply header
    + * value = in.readString(); // read string value
    + * in.completeReply();      // read reply footer
    + * 
    + */ +abstract public class AbstractHessianInput { + private HessianRemoteResolver resolver; + private byte []_buffer; + + /** + * Initialize the Hessian stream with the underlying input stream. + */ + public void init(InputStream is) + { + } + + /** + * Returns the call's method + */ + abstract public String getMethod(); + + /** + * Sets the resolver used to lookup remote objects. + */ + public void setRemoteResolver(HessianRemoteResolver resolver) + { + this.resolver = resolver; + } + + /** + * Sets the resolver used to lookup remote objects. + */ + public HessianRemoteResolver getRemoteResolver() + { + return resolver; + } + + /** + * Sets the serializer factory. + */ + public void setSerializerFactory(SerializerFactory ser) + { + } + + /** + * Reads the call + * + *
    +   * c major minor
    +   * 
    + */ + abstract public int readCall() + throws IOException; + + /** + * For backward compatibility with HessianSkeleton + */ + public void skipOptionalCall() + throws IOException + { + } + + /** + * Reads a header, returning null if there are no headers. + * + *
    +   * H b16 b8 value
    +   * 
    + */ + abstract public String readHeader() + throws IOException; + + /** + * Starts reading the call + * + *

    A successful completion will have a single value: + * + *

    +   * m b16 b8 method
    +   * 
    + */ + abstract public String readMethod() + throws IOException; + + /** + * Reads the number of method arguments + * + * @return -1 for a variable length (hessian 1.0) + */ + public int readMethodArgLength() + throws IOException + { + return -1; + } + + /** + * Starts reading the call, including the headers. + * + *

    The call expects the following protocol data + * + *

    +   * c major minor
    +   * m b16 b8 method
    +   * 
    + */ + abstract public void startCall() + throws IOException; + + /** + * Completes reading the call + * + *

    The call expects the following protocol data + * + *

    +   * Z
    +   * 
    + */ + abstract public void completeCall() + throws IOException; + + /** + * Reads a reply as an object. + * If the reply has a fault, throws the exception. + */ + abstract public Object readReply(Class expectedClass) + throws Throwable; + + /** + * Starts reading the reply + * + *

    A successful completion will have a single value: + * + *

    +   * r
    +   * v
    +   * 
    + */ + abstract public void startReply() + throws Throwable; + + /** + * Starts reading the body of the reply, i.e. after the 'r' has been + * parsed. + */ + public void startReplyBody() + throws Throwable + { + } + + /** + * Completes reading the call + * + *

    A successful completion will have a single value: + * + *

    +   * z
    +   * 
    + */ + abstract public void completeReply() + throws IOException; + + /** + * Reads a boolean + * + *
    +   * T
    +   * F
    +   * 
    + */ + abstract public boolean readBoolean() + throws IOException; + + /** + * Reads a null + * + *
    +   * N
    +   * 
    + */ + abstract public void readNull() + throws IOException; + + /** + * Reads an integer + * + *
    +   * I b32 b24 b16 b8
    +   * 
    + */ + abstract public int readInt() + throws IOException; + + /** + * Reads a long + * + *
    +   * L b64 b56 b48 b40 b32 b24 b16 b8
    +   * 
    + */ + abstract public long readLong() + throws IOException; + + /** + * Reads a double. + * + *
    +   * D b64 b56 b48 b40 b32 b24 b16 b8
    +   * 
    + */ + abstract public double readDouble() + throws IOException; + + /** + * Reads a date. + * + *
    +   * T b64 b56 b48 b40 b32 b24 b16 b8
    +   * 
    + */ + abstract public long readUTCDate() + throws IOException; + + /** + * Reads a string encoded in UTF-8 + * + *
    +   * s b16 b8 non-final string chunk
    +   * S b16 b8 final string chunk
    +   * 
    + */ + abstract public String readString() + throws IOException; + + /** + * Reads an XML node encoded in UTF-8 + * + *
    +   * x b16 b8 non-final xml chunk
    +   * X b16 b8 final xml chunk
    +   * 
    + */ + public org.w3c.dom.Node readNode() + throws IOException + { + throw new UnsupportedOperationException(getClass().getSimpleName()); + } + + /** + * Starts reading a string. All the characters must be read before + * calling the next method. The actual characters will be read with + * the reader's read() or read(char [], int, int). + * + *
    +   * s b16 b8 non-final string chunk
    +   * S b16 b8 final string chunk
    +   * 
    + */ + abstract public Reader getReader() + throws IOException; + + /** + * Starts reading a byte array using an input stream. All the bytes + * must be read before calling the following method. + * + *
    +   * b b16 b8 non-final binary chunk
    +   * B b16 b8 final binary chunk
    +   * 
    + */ + abstract public InputStream readInputStream() + throws IOException; + + /** + * Reads data to an output stream. + * + *
    +   * b b16 b8 non-final binary chunk
    +   * B b16 b8 final binary chunk
    +   * 
    + */ + public boolean readToOutputStream(OutputStream os) + throws IOException + { + InputStream is = readInputStream(); + + if (is == null) + return false; + + if (_buffer == null) + _buffer = new byte[256]; + + try { + int len; + + while ((len = is.read(_buffer, 0, _buffer.length)) > 0) { + os.write(_buffer, 0, len); + } + + return true; + } finally { + is.close(); + } + } + + + + /** + * Reads a byte array. + * + *
    +   * b b16 b8 non-final binary chunk
    +   * B b16 b8 final binary chunk
    +   * 
    + */ + abstract public byte []readBytes() + throws IOException; + + /** + * Reads an arbitrary object from the input stream. + * + * @param expectedClass the expected class if the protocol doesn't supply it. + */ + abstract public Object readObject(Class expectedClass) + throws IOException; + + /** + * Reads an arbitrary object from the input stream. + */ + abstract public Object readObject() + throws IOException; + + /** + * Reads a remote object reference to the stream. The type is the + * type of the remote interface. + * + *
    +   * 'r' 't' b16 b8 type url
    +   * 
    + */ + abstract public Object readRemote() + throws IOException; + + /** + * Reads a reference + * + *
    +   * R b32 b24 b16 b8
    +   * 
    + */ + abstract public Object readRef() + throws IOException; + + /** + * Adds an object reference. + */ + abstract public int addRef(Object obj) + throws IOException; + + /** + * Sets an object reference. + */ + abstract public void setRef(int i, Object obj) + throws IOException; + + /** + * Resets the references for streaming. + */ + public void resetReferences() + { + } + + /** + * Reads the start of a list + */ + abstract public int readListStart() + throws IOException; + + /** + * Reads the length of a list. + */ + abstract public int readLength() + throws IOException; + + /** + * Reads the start of a map + */ + abstract public int readMapStart() + throws IOException; + + /** + * Reads an object type. + */ + abstract public String readType() + throws IOException; + + /** + * Returns true if the data has ended. + */ + abstract public boolean isEnd() + throws IOException; + + /** + * Read the end byte + */ + abstract public void readEnd() + throws IOException; + + /** + * Read the end byte + */ + abstract public void readMapEnd() + throws IOException; + + /** + * Read the end byte + */ + abstract public void readListEnd() + throws IOException; + + public void close() + throws IOException + { + } +} diff --git a/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/AbstractHessianOutput.java b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/AbstractHessianOutput.java new file mode 100644 index 0000000000..0e1bcc3ffe --- /dev/null +++ b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/AbstractHessianOutput.java @@ -0,0 +1,582 @@ +/* + * Copyright (c) 2001-2008 Caucho Technology, Inc. All rights reserved. + * + * The Apache Software License, Version 1.1 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Caucho Technology (http://www.caucho.com/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Hessian", "Resin", and "Caucho" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * info@caucho.com. + * + * 5. Products derived from this software may not be called "Resin" + * nor may "Resin" appear in their names without prior written + * permission of Caucho Technology. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Scott Ferguson + */ + +package com.caucho.hessian4.io; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +/** + * Abstract output stream for Hessian requests. + * + *
    + * OutputStream os = ...; // from http connection
    + * AbstractOutput out = new HessianSerializerOutput(os);
    + * String value;
    + *
    + * out.startCall("hello");  // start hello call
    + * out.writeString("arg1"); // write a string argument
    + * out.completeCall();      // complete the call
    + * 
    + */ +abstract public class AbstractHessianOutput { + // serializer factory + private SerializerFactory _defaultSerializerFactory; + + // serializer factory + protected SerializerFactory _serializerFactory; + + private byte []_byteBuffer; + + /** + * Sets the serializer factory. + */ + public void setSerializerFactory(SerializerFactory factory) + { + _serializerFactory = factory; + } + + /** + * Gets the serializer factory. + */ + public SerializerFactory getSerializerFactory() + { + // the default serializer factory cannot be modified by external + // callers + if (_serializerFactory == _defaultSerializerFactory) { + _serializerFactory = new SerializerFactory(); + } + + return _serializerFactory; + } + + /** + * Gets the serializer factory. + */ + protected final SerializerFactory findSerializerFactory() + { + SerializerFactory factory = _serializerFactory; + + if (factory == null) { + factory = SerializerFactory.createDefault(); + _defaultSerializerFactory = factory; + _serializerFactory = factory; + } + + return factory; + } + + /** + * Initialize the output with a new underlying stream. + */ + public void init(OutputStream os) + { + } + + /** + * Writes a complete method call. + */ + public void call(String method, Object []args) + throws IOException + { + int length = args != null ? args.length : 0; + + startCall(method, length); + + for (int i = 0; i < length; i++) + writeObject(args[i]); + + completeCall(); + } + + /** + * Starts the method call: + * + *
    +   * C
    +   * 
    + * + * @param method the method name to call. + */ + abstract public void startCall() + throws IOException; + + /** + * Starts the method call: + * + *
    +   * C string int
    +   * 
    + * + * @param method the method name to call. + */ + abstract public void startCall(String method, int length) + throws IOException; + + /** + * For Hessian 2.0, use the Header envelope instead + * + * @deprecated + */ + public void writeHeader(String name) + throws IOException + { + throw new UnsupportedOperationException(getClass().getSimpleName()); + } + + /** + * Writes the method tag. + * + *
    +   * string
    +   * 
    + * + * @param method the method name to call. + */ + abstract public void writeMethod(String method) + throws IOException; + + /** + * Completes the method call: + * + *
    +   * 
    + */ + abstract public void completeCall() + throws IOException; + + /** + * Writes a boolean value to the stream. The boolean will be written + * with the following syntax: + * + *
    +   * T
    +   * F
    +   * 
    + * + * @param value the boolean value to write. + */ + abstract public void writeBoolean(boolean value) + throws IOException; + + /** + * Writes an integer value to the stream. The integer will be written + * with the following syntax: + * + *
    +   * I b32 b24 b16 b8
    +   * 
    + * + * @param value the integer value to write. + */ + abstract public void writeInt(int value) + throws IOException; + + /** + * Writes a long value to the stream. The long will be written + * with the following syntax: + * + *
    +   * L b64 b56 b48 b40 b32 b24 b16 b8
    +   * 
    + * + * @param value the long value to write. + */ + abstract public void writeLong(long value) + throws IOException; + + /** + * Writes a double value to the stream. The double will be written + * with the following syntax: + * + *
    +   * D b64 b56 b48 b40 b32 b24 b16 b8
    +   * 
    + * + * @param value the double value to write. + */ + abstract public void writeDouble(double value) + throws IOException; + + /** + * Writes a date to the stream. + * + *
    +   * T  b64 b56 b48 b40 b32 b24 b16 b8
    +   * 
    + * + * @param time the date in milliseconds from the epoch in UTC + */ + abstract public void writeUTCDate(long time) + throws IOException; + + /** + * Writes a null value to the stream. + * The null will be written with the following syntax + * + *
    +   * N
    +   * 
    + * + * @param value the string value to write. + */ + abstract public void writeNull() + throws IOException; + + /** + * Writes a string value to the stream using UTF-8 encoding. + * The string will be written with the following syntax: + * + *
    +   * S b16 b8 string-value
    +   * 
    + * + * If the value is null, it will be written as + * + *
    +   * N
    +   * 
    + * + * @param value the string value to write. + */ + abstract public void writeString(String value) + throws IOException; + + /** + * Writes a string value to the stream using UTF-8 encoding. + * The string will be written with the following syntax: + * + *
    +   * S b16 b8 string-value
    +   * 
    + * + * If the value is null, it will be written as + * + *
    +   * N
    +   * 
    + * + * @param value the string value to write. + */ + abstract public void writeString(char []buffer, int offset, int length) + throws IOException; + + /** + * Writes a byte array to the stream. + * The array will be written with the following syntax: + * + *
    +   * B b16 b18 bytes
    +   * 
    + * + * If the value is null, it will be written as + * + *
    +   * N
    +   * 
    + * + * @param value the string value to write. + */ + abstract public void writeBytes(byte []buffer) + throws IOException; + /** + * Writes a byte array to the stream. + * The array will be written with the following syntax: + * + *
    +   * B b16 b18 bytes
    +   * 
    + * + * If the value is null, it will be written as + * + *
    +   * N
    +   * 
    + * + * @param value the string value to write. + */ + abstract public void writeBytes(byte []buffer, int offset, int length) + throws IOException; + + /** + * Writes a byte buffer to the stream. + */ + abstract public void writeByteBufferStart() + throws IOException; + + /** + * Writes a byte buffer to the stream. + * + *
    +   * b b16 b18 bytes
    +   * 
    + * + * @param value the string value to write. + */ + abstract public void writeByteBufferPart(byte []buffer, + int offset, + int length) + throws IOException; + + /** + * Writes the last chunk of a byte buffer to the stream. + * + *
    +   * b b16 b18 bytes
    +   * 
    + * + * @param value the string value to write. + */ + abstract public void writeByteBufferEnd(byte []buffer, + int offset, + int length) + throws IOException; + + /** + * Writes a full output stream. + */ + public void writeByteStream(InputStream is) + throws IOException + { + writeByteBufferStart(); + + if (_byteBuffer == null) + _byteBuffer = new byte[1024]; + + byte []buffer = _byteBuffer; + + int len; + while ((len = is.read(buffer, 0, buffer.length)) > 0) { + if (len < buffer.length) { + int len2 = is.read(buffer, len, buffer.length - len); + + if (len2 < 0) { + writeByteBufferEnd(buffer, 0, len); + return; + } + + len += len2; + } + + writeByteBufferPart(buffer, 0, len); + } + + writeByteBufferEnd(buffer, 0, 0); + } + + /** + * Writes a reference. + * + *
    +   * Q int
    +   * 
    + * + * @param value the integer value to write. + */ + abstract protected void writeRef(int value) + throws IOException; + + /** + * Removes a reference. + */ + @Deprecated + public boolean removeRef(Object obj) + throws IOException + { + return false; + } + + /** + * Replaces a reference from one object to another. + */ + abstract public boolean replaceRef(Object oldRef, Object newRef) + throws IOException; + + /** + * Adds an object to the reference list. If the object already exists, + * writes the reference, otherwise, the caller is responsible for + * the serialization. + * + *
    +   * R b32 b24 b16 b8
    +   * 
    + * + * @param object the object to add as a reference. + * + * @return true if the object has already been written. + */ + abstract public boolean addRef(Object object) + throws IOException; + + /** + * Resets the references for streaming. + */ + public void resetReferences() + { + } + + /** + * Writes a generic object to the output stream. + */ + abstract public void writeObject(Object object) + throws IOException; + + /** + * Writes the list header to the stream. List writers will call + * writeListBegin followed by the list contents and then + * call writeListEnd. + * + *
    +   * V
    +   *   x13 java.util.ArrayList   # type
    +   *   x93                       # length=3
    +   *   x91                       # 1
    +   *   x92                       # 2
    +   *   x93                       # 3
    +   * </list>
    +   * 
    + */ + abstract public boolean writeListBegin(int length, String type) + throws IOException; + + /** + * Writes the tail of the list to the stream. + */ + abstract public void writeListEnd() + throws IOException; + + /** + * Writes the map header to the stream. Map writers will call + * writeMapBegin followed by the map contents and then + * call writeMapEnd. + * + *
    +   * M type ( )* Z
    +   * 
    + */ + abstract public void writeMapBegin(String type) + throws IOException; + + /** + * Writes the tail of the map to the stream. + */ + abstract public void writeMapEnd() + throws IOException; + + /** + * Writes the object header to the stream (for Hessian 2.0), or a + * Map for Hessian 1.0. Object writers will call + * writeObjectBegin followed by the map contents and then + * call writeObjectEnd. + * + *
    +   * C type int *
    +   * C int *
    +   * 
    + * + * @return true if the object has already been defined. + */ + public int writeObjectBegin(String type) + throws IOException + { + writeMapBegin(type); + + return -2; + } + + /** + * Writes the end of the class. + */ + public void writeClassFieldLength(int len) + throws IOException + { + } + + /** + * Writes the tail of the object to the stream. + */ + public void writeObjectEnd() + throws IOException + { + } + + public void writeReply(Object o) + throws IOException + { + startReply(); + writeObject(o); + completeReply(); + } + + + public void startReply() + throws IOException + { + } + + public void completeReply() + throws IOException + { + } + + public void writeFault(String code, String message, Object detail) + throws IOException + { + } + + public void flush() + throws IOException + { + } + + public void close() + throws IOException + { + } +} diff --git a/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/AbstractHessianResolver.java b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/AbstractHessianResolver.java new file mode 100644 index 0000000000..9872559d02 --- /dev/null +++ b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/AbstractHessianResolver.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2001-2004 Caucho Technology, Inc. All rights reserved. + * + * The Apache Software License, Version 1.1 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Caucho Technology (http://www.caucho.com/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Hessian", "Resin", and "Caucho" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * info@caucho.com. + * + * 5. Products derived from this software may not be called "Resin" + * nor may "Resin" appear in their names without prior written + * permission of Caucho Technology. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Scott Ferguson + */ + +package com.caucho.hessian4.io; + +import java.io.IOException; + +/** + * Looks up remote objects. The default just returns a HessianRemote object. + */ +public class AbstractHessianResolver implements HessianRemoteResolver { + /** + * Looks up a proxy object. + */ + public Object lookup(String type, String url) + throws IOException + { + return new HessianRemote(type, url); + } +} diff --git a/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/AbstractListDeserializer.java b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/AbstractListDeserializer.java new file mode 100644 index 0000000000..73a636e78d --- /dev/null +++ b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/AbstractListDeserializer.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2001-2004 Caucho Technology, Inc. All rights reserved. + * + * The Apache Software License, Version 1.1 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Caucho Technology (http://www.caucho.com/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Hessian", "Resin", and "Caucho" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * info@caucho.com. + * + * 5. Products derived from this software may not be called "Resin" + * nor may "Resin" appear in their names without prior written + * permission of Caucho Technology. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Scott Ferguson + */ + +package com.caucho.hessian4.io; + +import java.io.IOException; + +/** + * Deserializing a JDK 1.2 Collection. + */ +public class AbstractListDeserializer extends AbstractDeserializer { + public Object readObject(AbstractHessianInput in) + throws IOException + { + Object obj = in.readObject(); + + if (obj != null) + throw error("expected list at " + obj.getClass().getName() + " (" + obj + ")"); + else + throw error("expected list at null"); + } +} diff --git a/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/AbstractMapDeserializer.java b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/AbstractMapDeserializer.java new file mode 100644 index 0000000000..2d2a2a8ed7 --- /dev/null +++ b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/AbstractMapDeserializer.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2001-2004 Caucho Technology, Inc. All rights reserved. + * + * The Apache Software License, Version 1.1 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Caucho Technology (http://www.caucho.com/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Burlap", "Resin", and "Caucho" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * info@caucho.com. + * + * 5. Products derived from this software may not be called "Resin" + * nor may "Resin" appear in their names without prior written + * permission of Caucho Technology. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Scott Ferguson + */ + +package com.caucho.hessian4.io; + +import java.io.IOException; +import java.util.HashMap; + +/** + * Serializing an object for known object types. + */ +public class AbstractMapDeserializer extends AbstractDeserializer { + + public Class getType() + { + return HashMap.class; + } + + public Object readObject(AbstractHessianInput in) + throws IOException + { + Object obj = in.readObject(); + + if (obj != null) + throw error("expected map/object at " + obj.getClass().getName() + " (" + obj + ")"); + else + throw error("expected map/object at null"); + } +} diff --git a/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/AbstractSerializer.java b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/AbstractSerializer.java new file mode 100644 index 0000000000..92fe417f73 --- /dev/null +++ b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/AbstractSerializer.java @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2001-2004 Caucho Technology, Inc. All rights reserved. + * + * The Apache Software License, Version 1.1 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Caucho Technology (http://www.caucho.com/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Burlap", "Resin", and "Caucho" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * info@caucho.com. + * + * 5. Products derived from this software may not be called "Resin" + * nor may "Resin" appear in their names without prior written + * permission of Caucho Technology. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Scott Ferguson + */ + +package com.caucho.hessian4.io; + +import java.io.IOException; +import java.util.logging.Logger; + +import com.caucho.hessian4.HessianException; + +/** + * Serializing an object. + */ +abstract public class AbstractSerializer implements Serializer { + public static final NullSerializer NULL = new NullSerializer(); + + protected static final Logger log + = Logger.getLogger(AbstractSerializer.class.getName()); + + public void writeObject(Object obj, AbstractHessianOutput out) + throws IOException + { + if (out.addRef(obj)) { + return; + } + + try { + Object replace = writeReplace(obj); + + if (replace != null) { + // out.removeRef(obj); + + out.writeObject(replace); + + out.replaceRef(replace, obj); + + return; + } + } catch (RuntimeException e) { + throw e; + } catch (Exception e) { + // log.log(Level.FINE, e.toString(), e); + throw new HessianException(e); + } + + Class cl = getClass(obj); + + int ref = out.writeObjectBegin(cl.getName()); + + if (ref < -1) { + writeObject10(obj, out); + } + else { + if (ref == -1) { + writeDefinition20(cl, out); + + out.writeObjectBegin(cl.getName()); + } + + writeInstance(obj, out); + } + } + + protected Object writeReplace(Object obj) + { + return null; + } + + protected Class getClass(Object obj) + { + return obj.getClass(); + } + + protected void writeObject10(Object obj, + AbstractHessianOutput out) + throws IOException + { + throw new UnsupportedOperationException(getClass().getName()); + } + + protected void writeDefinition20(Class cl, + AbstractHessianOutput out) + throws IOException + { + throw new UnsupportedOperationException(getClass().getName()); + } + + protected void writeInstance(Object obj, + AbstractHessianOutput out) + throws IOException + { + throw new UnsupportedOperationException(getClass().getName()); + } + + /** + * The NullSerializer exists as a marker for the factory classes so + * they save a null result. + */ + static final class NullSerializer extends AbstractSerializer { + public void writeObject(Object obj, AbstractHessianOutput out) + throws IOException + { + throw new IllegalStateException(getClass().getName()); + } + } +} diff --git a/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/AbstractSerializerFactory.java b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/AbstractSerializerFactory.java new file mode 100644 index 0000000000..71a64c5576 --- /dev/null +++ b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/AbstractSerializerFactory.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2001-2004 Caucho Technology, Inc. All rights reserved. + * + * The Apache Software License, Version 1.1 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Caucho Technology (http://www.caucho.com/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Burlap", "Resin", and "Caucho" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * info@caucho.com. + * + * 5. Products derived from this software may not be called "Resin" + * nor may "Resin" appear in their names without prior written + * permission of Caucho Technology. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Scott Ferguson + */ + +package com.caucho.hessian4.io; + +/** + * Factory for returning serialization methods. + */ +abstract public class AbstractSerializerFactory { + /** + * Returns the serializer for a class. + * + * @param cl the class of the object that needs to be serialized. + * + * @return a serializer object for the serialization. + */ + abstract public Serializer getSerializer(Class cl) + throws HessianProtocolException; + + /** + * Returns the deserializer for a class. + * + * @param cl the class of the object that needs to be deserialized. + * + * @return a deserializer object for the serialization. + */ + abstract public Deserializer getDeserializer(Class cl) + throws HessianProtocolException; +} diff --git a/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/AbstractStreamDeserializer.java b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/AbstractStreamDeserializer.java new file mode 100644 index 0000000000..9cd5faf070 --- /dev/null +++ b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/AbstractStreamDeserializer.java @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2001-2004 Caucho Technology, Inc. All rights reserved. + * + * The Apache Software License, Version 1.1 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Caucho Technology (http://www.caucho.com/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Hessian", "Resin", and "Caucho" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * info@caucho.com. + * + * 5. Products derived from this software may not be called "Resin" + * nor may "Resin" appear in their names without prior written + * permission of Caucho Technology. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Scott Ferguson + */ + +package com.caucho.hessian4.io; + +import java.io.IOException; + +/** + * Deserializing a byte stream + */ +abstract public class AbstractStreamDeserializer extends AbstractDeserializer { + abstract public Class getType(); + + /** + * Reads the Hessian 1.0 style map. + */ + public Object readMap(AbstractHessianInput in) + throws IOException + { + Object value = null; + + while (! in.isEnd()) { + String key = in.readString(); + + if (key.equals("value")) + value = readStreamValue(in); + else + in.readObject(); + } + + in.readMapEnd(); + + return value; + } + + public Object readObject(AbstractHessianInput in, Object []fields) + throws IOException + { + String []fieldNames = (String []) fields; + + Object value = null; + + for (int i = 0; i < fieldNames.length; i++) { + if ("value".equals(fieldNames[i])) + value = readStreamValue(in); + else + in.readObject(); + } + + return value; + } + + abstract protected Object readStreamValue(AbstractHessianInput in) + throws IOException; +} diff --git a/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/AbstractStreamSerializer.java b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/AbstractStreamSerializer.java new file mode 100644 index 0000000000..262a1734ba --- /dev/null +++ b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/AbstractStreamSerializer.java @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2001-2008 Caucho Technology, Inc. All rights reserved. + * + * The Apache Software License, Version 1.1 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Caucho Technology (http://www.caucho.com/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Burlap", "Resin", and "Caucho" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * info@caucho.com. + * + * 5. Products derived from this software may not be called "Resin" + * nor may "Resin" appear in their names without prior written + * permission of Caucho Technology. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Scott Ferguson + */ + +package com.caucho.hessian4.io; + +import java.io.IOException; +import java.io.InputStream; + +/** + * Serializing an object containing a byte stream. + */ +abstract public class AbstractStreamSerializer extends AbstractSerializer +{ + /** + * Writes the object to the output stream. + */ + public void writeObject(Object obj, AbstractHessianOutput out) + throws IOException + { + if (out.addRef(obj)) { + return; + } + + int ref = out.writeObjectBegin(getClassName(obj)); + + if (ref < -1) { + out.writeString("value"); + + InputStream is = getInputStream(obj); + try { + out.writeByteStream(is); + } finally { + is.close(); + } + + out.writeMapEnd(); + } + else { + if (ref == -1) { + out.writeClassFieldLength(1); + out.writeString("value"); + + out.writeObjectBegin(getClassName(obj)); + } + + InputStream is = getInputStream(obj); + + try { + if (is != null) + out.writeByteStream(is); + else + out.writeNull(); + } finally { + if (is != null) + is.close(); + } + } + } + + protected String getClassName(Object obj) + { + return obj.getClass().getName(); + } + + abstract protected InputStream getInputStream(Object obj) + throws IOException; +} diff --git a/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/AbstractStringValueDeserializer.java b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/AbstractStringValueDeserializer.java new file mode 100644 index 0000000000..dbd079dd66 --- /dev/null +++ b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/AbstractStringValueDeserializer.java @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2001-2004 Caucho Technology, Inc. All rights reserved. + * + * The Apache Software License, Version 1.1 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Caucho Technology (http://www.caucho.com/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Hessian", "Resin", and "Caucho" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * info@caucho.com. + * + * 5. Products derived from this software may not be called "Resin" + * nor may "Resin" appear in their names without prior written + * permission of Caucho Technology. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Scott Ferguson + */ + +package com.caucho.hessian4.io; + +import java.io.IOException; + +/** + * Deserializes a string-valued object like BigDecimal. + */ +abstract public class AbstractStringValueDeserializer + extends AbstractDeserializer +{ + abstract protected Object create(String value) + throws IOException; + + @Override + public Object readMap(AbstractHessianInput in) + throws IOException + { + String value = null; + + while (! in.isEnd()) { + String key = in.readString(); + + if (key.equals("value")) + value = in.readString(); + else + in.readObject(); + } + + in.readMapEnd(); + + Object object = create(value); + + in.addRef(object); + + return object; + } + + @Override + public Object readObject(AbstractHessianInput in, Object []fields) + throws IOException + { + String []fieldNames = (String []) fields; + + String value = null; + + for (int i = 0; i < fieldNames.length; i++) { + if ("value".equals(fieldNames[i])) + value = in.readString(); + else + in.readObject(); + } + + Object object = create(value); + + in.addRef(object); + + return object; + } +} diff --git a/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/AnnotationDeserializer.java b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/AnnotationDeserializer.java new file mode 100644 index 0000000000..b9b961e42e --- /dev/null +++ b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/AnnotationDeserializer.java @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2001-2008 Caucho Technology, Inc. All rights reserved. + * + * The Apache Software License, Version 1.1 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Caucho Technology (http://www.caucho.com/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Burlap", "Resin", and "Caucho" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * info@caucho.com. + * + * 5. Products derived from this software may not be called "Resin" + * nor may "Resin" appear in their names without prior written + * permission of Caucho Technology. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Scott Ferguson + */ + +package com.caucho.hessian4.io; + +import java.io.IOException; +import java.lang.reflect.Proxy; +import java.util.HashMap; +import java.util.logging.Logger; + +import com.caucho.hessian4.HessianException; + +/** + * Deserializing a java annotation for known object types. + */ +public class AnnotationDeserializer extends AbstractMapDeserializer { + private static final Logger log + = Logger.getLogger(AnnotationDeserializer.class.getName()); + + private Class _annType; + + public AnnotationDeserializer(Class annType) + { + _annType = annType; + } + + public Class getType() + { + return _annType; + } + + public Object readMap(AbstractHessianInput in) + throws IOException + { + try { + int ref = in.addRef(null); + + HashMap valueMap = new HashMap(8); + + while (! in.isEnd()) { + String key = in.readString(); + Object value = in.readObject(); + + valueMap.put(key, value); + } + + in.readMapEnd(); + + return Proxy.newProxyInstance(_annType.getClassLoader(), + new Class[] { _annType }, + new AnnotationInvocationHandler(_annType, valueMap)); + + } catch (IOException e) { + throw e; + } catch (Exception e) { + throw new IOExceptionWrapper(e); + } + } + + public Object readObject(AbstractHessianInput in, + Object []fields) + throws IOException + { + String []fieldNames = (String []) fields; + + try { + int ref = in.addRef(null); + + HashMap valueMap = new HashMap(8); + + for (int i = 0; i < fieldNames.length; i++) { + String name = fieldNames[i]; + + valueMap.put(name, in.readObject()); + } + + return Proxy.newProxyInstance(_annType.getClassLoader(), + new Class[] { _annType }, + new AnnotationInvocationHandler(_annType, valueMap)); + + } catch (IOException e) { + throw e; + } catch (Exception e) { + throw new HessianException(_annType.getName() + ":" + e, e); + } + } +} diff --git a/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/AnnotationInvocationHandler.java b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/AnnotationInvocationHandler.java new file mode 100644 index 0000000000..93e5979262 --- /dev/null +++ b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/AnnotationInvocationHandler.java @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2001-2008 Caucho Technology, Inc. All rights reserved. + * + * The Apache Software License, Version 1.1 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Caucho Technology (http://www.caucho.com/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Burlap", "Resin", and "Caucho" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * info@caucho.com. + * + * 5. Products derived from this software may not be called "Resin" + * nor may "Resin" appear in their names without prior written + * permission of Caucho Technology. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Scott Ferguson + */ + +package com.caucho.hessian4.io; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Map; + +/** + * Proxy for a java annotation for known object types. + */ +public class AnnotationInvocationHandler implements InvocationHandler { + private Class _annType; + private HashMap _valueMap; + + public AnnotationInvocationHandler(Class annType, + HashMap valueMap) + { + _annType = annType; + _valueMap = valueMap; + } + + public Object invoke(Object proxy, Method method, Object []args) + throws Throwable + { + String name = method.getName(); + + if (args != null && args.length != 0) + return null; + + if (name.equals("annotationType")) + return _annType; + else if (name.equals("toString")) + return toString(); + + return _valueMap.get(method.getName()); + } + + public String toString() + { + StringBuilder sb = new StringBuilder(); + + sb.append("@"); + sb.append(_annType.getName()); + sb.append("["); + + boolean isFirst = true; + for (Map.Entry entry : _valueMap.entrySet()) { + if (! isFirst) + sb.append(", "); + isFirst = false; + + sb.append(entry.getKey()); + sb.append("="); + + if (entry.getValue() instanceof String) + sb.append('"').append(entry.getValue()).append('"'); + else + sb.append(entry.getValue()); + } + sb.append("]"); + + return sb.toString(); + } +} diff --git a/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/AnnotationSerializer.java b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/AnnotationSerializer.java new file mode 100644 index 0000000000..63d5db589e --- /dev/null +++ b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/AnnotationSerializer.java @@ -0,0 +1,392 @@ +/* + * Copyright (c) 2001-2008 Caucho Technology, Inc. All rights reserved. + * + * The Apache Software License, Version 1.1 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Caucho Technology (http://www.caucho.com/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Burlap", "Resin", and "Caucho" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * info@caucho.com. + * + * 5. Products derived from this software may not be called "Resin" + * nor may "Resin" appear in their names without prior written + * permission of Caucho Technology. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Scott Ferguson + */ + +package com.caucho.hessian4.io; + +import java.io.IOException; +import java.lang.annotation.Annotation; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.logging.Level; +import java.util.logging.Logger; + +import com.caucho.hessian4.HessianException; + +/** + * Serializing a Java annotation + */ +public class AnnotationSerializer extends AbstractSerializer +{ + private static final Logger log + = Logger.getLogger(AnnotationSerializer.class.getName()); + + private static Object []NULL_ARGS = new Object[0]; + + private Class _annType; + private Method []_methods; + private MethodSerializer []_methodSerializers; + + public AnnotationSerializer(Class annType) + { + if (! Annotation.class.isAssignableFrom(annType)) { + throw new IllegalStateException(annType.getName() + " is invalid because it is not a java.lang.annotation.Annotation"); + } + } + + public void writeObject(Object obj, AbstractHessianOutput out) + throws IOException + { + if (out.addRef(obj)) { + return; + } + + init(((Annotation) obj).annotationType()); + + int ref = out.writeObjectBegin(_annType.getName()); + + if (ref < -1) { + writeObject10(obj, out); + } + else { + if (ref == -1) { + writeDefinition20(out); + out.writeObjectBegin(_annType.getName()); + } + + writeInstance(obj, out); + } + } + + protected void writeObject10(Object obj, AbstractHessianOutput out) + throws IOException + { + for (int i = 0; i < _methods.length; i++) { + Method method = _methods[i]; + + out.writeString(method.getName()); + + _methodSerializers[i].serialize(out, obj, method); + } + + out.writeMapEnd(); + } + + private void writeDefinition20(AbstractHessianOutput out) + throws IOException + { + out.writeClassFieldLength(_methods.length); + + for (int i = 0; i < _methods.length; i++) { + Method method = _methods[i]; + + out.writeString(method.getName()); + } + } + + public void writeInstance(Object obj, AbstractHessianOutput out) + throws IOException + { + for (int i = 0; i < _methods.length; i++) { + Method method = _methods[i]; + + _methodSerializers[i].serialize(out, obj, method); + } + } + + + private void init(Class cl) + { + synchronized (this) { + if (_annType != null) + return; + + _annType = cl; + + ArrayList methods = new ArrayList(); + + for (Method method : _annType.getDeclaredMethods()) { + if (method.getName().equals("hashCode") + || method.getName().equals("toString") + || method.getName().equals("annotationType")) { + continue; + } + + if (method.getParameterTypes().length != 0) + continue; + + methods.add(method); + + method.setAccessible(true); + } + + if (_annType == null) + throw new IllegalStateException(cl.getName() + " is invalid because it does not have a valid annotationType()"); + + _methods = new Method[methods.size()]; + methods.toArray(_methods); + + _methodSerializers = new MethodSerializer[_methods.length]; + + for (int i = 0; i < _methods.length; i++) { + _methodSerializers[i] = getMethodSerializer(_methods[i].getReturnType()); + } + } + } + + private Class getAnnotationType(Class cl) + { + if (cl == null) + return null; + + if (Annotation.class.equals(cl.getSuperclass())) + return cl; + + Class ifaces[] = cl.getInterfaces(); + + if (ifaces != null) { + for (Class iface : ifaces) { + if (iface.equals(Annotation.class)) + return cl; + + Class annType = getAnnotationType(iface); + + if (annType != null) + return annType; + } + } + + return getAnnotationType(cl.getSuperclass()); + } + + private static MethodSerializer getMethodSerializer(Class type) + { + if (int.class.equals(type) + || byte.class.equals(type) + || short.class.equals(type) + || int.class.equals(type)) { + return IntMethodSerializer.SER; + } + else if (long.class.equals(type)) { + return LongMethodSerializer.SER; + } + else if (double.class.equals(type) || + float.class.equals(type)) { + return DoubleMethodSerializer.SER; + } + else if (boolean.class.equals(type)) { + return BooleanMethodSerializer.SER; + } + else if (String.class.equals(type)) { + return StringMethodSerializer.SER; + } + else if (java.util.Date.class.equals(type) + || java.sql.Date.class.equals(type) + || java.sql.Timestamp.class.equals(type) + || java.sql.Time.class.equals(type)) { + return DateMethodSerializer.SER; + } + else + return MethodSerializer.SER; + } + + static HessianException error(Method method, Throwable cause) + { + String msg = (method.getDeclaringClass().getSimpleName() + + "." + method.getName() + "(): " + cause); + + throw new HessianMethodSerializationException(msg, cause); + } + + static class MethodSerializer { + static final MethodSerializer SER = new MethodSerializer(); + + void serialize(AbstractHessianOutput out, Object obj, Method method) + throws IOException + { + Object value = null; + + try { + value = method.invoke(obj); + } catch (InvocationTargetException e) { + throw error(method, e.getCause()); + } catch (IllegalAccessException e) { + log.log(Level.FINE, e.toString(), e); + } + + try { + out.writeObject(value); + } catch (Exception e) { + throw error(method, e); + } + } + } + + static class BooleanMethodSerializer extends MethodSerializer { + static final MethodSerializer SER = new BooleanMethodSerializer(); + + void serialize(AbstractHessianOutput out, Object obj, Method method) + throws IOException + { + boolean value = false; + + try { + value = (Boolean) method.invoke(obj); + } catch (InvocationTargetException e) { + throw error(method, e.getCause()); + } catch (IllegalAccessException e) { + log.log(Level.FINE, e.toString(), e); + } + + out.writeBoolean(value); + } + } + + static class IntMethodSerializer extends MethodSerializer { + static final MethodSerializer SER = new IntMethodSerializer(); + + void serialize(AbstractHessianOutput out, Object obj, Method method) + throws IOException + { + int value = 0; + + try { + value = (Integer) method.invoke(obj); + } catch (InvocationTargetException e) { + throw error(method, e.getCause()); + } catch (IllegalAccessException e) { + log.log(Level.FINE, e.toString(), e); + } + + out.writeInt(value); + } + } + + static class LongMethodSerializer extends MethodSerializer { + static final MethodSerializer SER = new LongMethodSerializer(); + + void serialize(AbstractHessianOutput out, Object obj, Method method) + throws IOException + { + long value = 0; + + try { + value = (Long) method.invoke(obj); + } catch (InvocationTargetException e) { + throw error(method, e.getCause()); + } catch (IllegalAccessException e) { + log.log(Level.FINE, e.toString(), e); + } + + out.writeLong(value); + } + } + + static class DoubleMethodSerializer extends MethodSerializer { + static final MethodSerializer SER = new DoubleMethodSerializer(); + + void serialize(AbstractHessianOutput out, Object obj, Method method) + throws IOException + { + double value = 0; + + try { + value = (Double) method.invoke(obj); + } catch (InvocationTargetException e) { + throw error(method, e.getCause()); + } catch (IllegalAccessException e) { + log.log(Level.FINE, e.toString(), e); + } + + out.writeDouble(value); + } + } + + static class StringMethodSerializer extends MethodSerializer { + static final MethodSerializer SER = new StringMethodSerializer(); + + void serialize(AbstractHessianOutput out, Object obj, Method method) + throws IOException + { + String value = null; + + try { + value = (String) method.invoke(obj); + } catch (InvocationTargetException e) { + throw error(method, e.getCause()); + } catch (IllegalAccessException e) { + log.log(Level.FINE, e.toString(), e); + } + + out.writeString(value); + } + } + + static class DateMethodSerializer extends MethodSerializer { + static final MethodSerializer SER = new DateMethodSerializer(); + + void serialize(AbstractHessianOutput out, Object obj, Method method) + throws IOException + { + java.util.Date value = null; + + try { + value = (java.util.Date) method.invoke(obj); + } catch (InvocationTargetException e) { + throw error(method, e.getCause()); + } catch (IllegalAccessException e) { + log.log(Level.FINE, e.toString(), e); + } + + if (value == null) + out.writeNull(); + else + out.writeUTCDate(value.getTime()); + } + } +} diff --git a/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/ArrayDeserializer.java b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/ArrayDeserializer.java new file mode 100644 index 0000000000..70f3791063 --- /dev/null +++ b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/ArrayDeserializer.java @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2001-2004 Caucho Technology, Inc. All rights reserved. + * + * The Apache Software License, Version 1.1 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Caucho Technology (http://www.caucho.com/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Hessian", "Resin", and "Caucho" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * info@caucho.com. + * + * 5. Products derived from this software may not be called "Resin" + * nor may "Resin" appear in their names without prior written + * permission of Caucho Technology. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Scott Ferguson + */ + +package com.caucho.hessian4.io; + +import java.io.IOException; +import java.lang.reflect.Array; +import java.util.ArrayList; + +/** + * Deserializing a Java array + */ +public class ArrayDeserializer extends AbstractListDeserializer { + private Class _componentType; + private Class _type; + + public ArrayDeserializer(Class componentType) + { + _componentType = componentType; + + if (_componentType != null) { + try { + _type = Array.newInstance(_componentType, 0).getClass(); + } catch (Exception e) { + } + } + + if (_type == null) + _type = Object[].class; + } + + public Class getType() + { + return _type; + } + + /** + * Reads the array. + */ + public Object readList(AbstractHessianInput in, int length) + throws IOException + { + if (length >= 0) { + Object []data = createArray(length); + + in.addRef(data); + + if (_componentType != null) { + for (int i = 0; i < data.length; i++) + data[i] = in.readObject(_componentType); + } + else { + for (int i = 0; i < data.length; i++) + data[i] = in.readObject(); + } + + in.readListEnd(); + + return data; + } + else { + ArrayList list = new ArrayList(); + + in.addRef(list); + + if (_componentType != null) { + while (! in.isEnd()) + list.add(in.readObject(_componentType)); + } + else { + while (! in.isEnd()) + list.add(in.readObject()); + } + + in.readListEnd(); + + Object []data = createArray(list.size()); + for (int i = 0; i < data.length; i++) + data[i] = list.get(i); + + return data; + } + } + + /** + * Reads the array. + */ + public Object readLengthList(AbstractHessianInput in, int length) + throws IOException + { + Object []data = createArray(length); + + in.addRef(data); + + if (_componentType != null) { + for (int i = 0; i < data.length; i++) + data[i] = in.readObject(_componentType); + } + else { + for (int i = 0; i < data.length; i++) + data[i] = in.readObject(); + } + + return data; + } + + protected Object []createArray(int length) + { + if (_componentType != null) + return (Object []) Array.newInstance(_componentType, length); + else + return new Object[length]; + } + + public String toString() + { + return "ArrayDeserializer[" + _componentType + "]"; + } +} diff --git a/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/ArraySerializer.java b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/ArraySerializer.java new file mode 100644 index 0000000000..8afc611eb0 --- /dev/null +++ b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/ArraySerializer.java @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2001-2004 Caucho Technology, Inc. All rights reserved. + * + * The Apache Software License, Version 1.1 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Caucho Technology (http://www.caucho.com/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Hessian", "Resin", and "Caucho" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * info@caucho.com. + * + * 5. Products derived from this software may not be called "Resin" + * nor may "Resin" appear in their names without prior written + * permission of Caucho Technology. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Scott Ferguson + */ + +package com.caucho.hessian4.io; + +import java.io.IOException; + +/** + * Serializing a Java array. + */ +public class ArraySerializer extends AbstractSerializer { + public void writeObject(Object obj, AbstractHessianOutput out) + throws IOException + { + if (out.addRef(obj)) + return; + + Object []array = (Object []) obj; + + boolean hasEnd = out.writeListBegin(array.length, + getArrayType(obj.getClass())); + + for (int i = 0; i < array.length; i++) + out.writeObject(array[i]); + + if (hasEnd) + out.writeListEnd(); + } + + /** + * Returns the <type> name for a <list>. + */ + private String getArrayType(Class cl) + { + if (cl.isArray()) + return '[' + getArrayType(cl.getComponentType()); + + String name = cl.getName(); + + if (name.equals("java.lang.String")) + return "string"; + else if (name.equals("java.lang.Object")) + return "object"; + else if (name.equals("java.util.Date")) + return "date"; + else + return name; + } +} diff --git a/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/BasicDeserializer.java b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/BasicDeserializer.java new file mode 100644 index 0000000000..9faef4a343 --- /dev/null +++ b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/BasicDeserializer.java @@ -0,0 +1,608 @@ +/* + * Copyright (c) 2001-2008 Caucho Technology, Inc. All rights reserved. + * + * The Apache Software License, Version 1.1 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Caucho Technology (http://www.caucho.com/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Burlap", "Resin", and "Caucho" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * info@caucho.com. + * + * 5. Products derived from this software may not be called "Resin" + * nor may "Resin" appear in their names without prior written + * permission of Caucho Technology. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Scott Ferguson + */ + +package com.caucho.hessian4.io; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Date; + +/** + * Serializing an object for known object types. + */ +public class BasicDeserializer extends AbstractDeserializer { + public static final int NULL = BasicSerializer.NULL; + public static final int BOOLEAN = BasicSerializer.BOOLEAN; + public static final int BYTE = BasicSerializer.BYTE; + public static final int SHORT = BasicSerializer.SHORT; + public static final int INTEGER = BasicSerializer.INTEGER; + public static final int LONG = BasicSerializer.LONG; + public static final int FLOAT = BasicSerializer.FLOAT; + public static final int DOUBLE = BasicSerializer.DOUBLE; + public static final int CHARACTER = BasicSerializer.CHARACTER; + public static final int CHARACTER_OBJECT = BasicSerializer.CHARACTER_OBJECT; + public static final int STRING = BasicSerializer.STRING; + public static final int DATE = BasicSerializer.DATE; + public static final int NUMBER = BasicSerializer.NUMBER; + public static final int OBJECT = BasicSerializer.OBJECT; + + public static final int BOOLEAN_ARRAY = BasicSerializer.BOOLEAN_ARRAY; + public static final int BYTE_ARRAY = BasicSerializer.BYTE_ARRAY; + public static final int SHORT_ARRAY = BasicSerializer.SHORT_ARRAY; + public static final int INTEGER_ARRAY = BasicSerializer.INTEGER_ARRAY; + public static final int LONG_ARRAY = BasicSerializer.LONG_ARRAY; + public static final int FLOAT_ARRAY = BasicSerializer.FLOAT_ARRAY; + public static final int DOUBLE_ARRAY = BasicSerializer.DOUBLE_ARRAY; + public static final int CHARACTER_ARRAY = BasicSerializer.CHARACTER_ARRAY; + public static final int STRING_ARRAY = BasicSerializer.STRING_ARRAY; + public static final int OBJECT_ARRAY = BasicSerializer.OBJECT_ARRAY; + + private int _code; + + public BasicDeserializer(int code) + { + _code = code; + } + + public Class getType() + { + switch (_code) { + case NULL: + return void.class; + case BOOLEAN: + return Boolean.class; + case BYTE: + return Byte.class; + case SHORT: + return Short.class; + case INTEGER: + return Integer.class; + case LONG: + return Long.class; + case FLOAT: + return Float.class; + case DOUBLE: + return Double.class; + case CHARACTER: + return Character.class; + case CHARACTER_OBJECT: + return Character.class; + case STRING: + return String.class; + case DATE: + return Date.class; + case NUMBER: + return Number.class; + case OBJECT: + return Object.class; + + case BOOLEAN_ARRAY: + return boolean[].class; + case BYTE_ARRAY: + return byte[].class; + case SHORT_ARRAY: + return short[].class; + case INTEGER_ARRAY: + return int[].class; + case LONG_ARRAY: + return long[].class; + case FLOAT_ARRAY: + return float[].class; + case DOUBLE_ARRAY: + return double[].class; + case CHARACTER_ARRAY: + return char[].class; + case STRING_ARRAY: + return String[].class; + case OBJECT_ARRAY: + return Object[].class; + default: + throw new UnsupportedOperationException(); + } + } + + public Object readObject(AbstractHessianInput in) + throws IOException + { + switch (_code) { + case NULL: + // hessian/3490 + in.readObject(); + + return null; + + case BOOLEAN: + return Boolean.valueOf(in.readBoolean()); + + case BYTE: + return Byte.valueOf((byte) in.readInt()); + + case SHORT: + return Short.valueOf((short) in.readInt()); + + case INTEGER: + return Integer.valueOf(in.readInt()); + + case LONG: + return Long.valueOf(in.readLong()); + + case FLOAT: + return Float.valueOf((float) in.readDouble()); + + case DOUBLE: + return Double.valueOf(in.readDouble()); + + case STRING: + return in.readString(); + + case OBJECT: + return in.readObject(); + + case CHARACTER: + { + String s = in.readString(); + if (s == null || s.equals("")) + return Character.valueOf((char) 0); + else + return Character.valueOf(s.charAt(0)); + } + + case CHARACTER_OBJECT: + { + String s = in.readString(); + if (s == null || s.equals("")) + return null; + else + return Character.valueOf(s.charAt(0)); + } + + case DATE: + return new Date(in.readUTCDate()); + + case NUMBER: + return in.readObject(); + + case BYTE_ARRAY: + return in.readBytes(); + + case CHARACTER_ARRAY: + { + String s = in.readString(); + + if (s == null) + return null; + else { + int len = s.length(); + char []chars = new char[len]; + s.getChars(0, len, chars, 0); + return chars; + } + } + + case BOOLEAN_ARRAY: + case SHORT_ARRAY: + case INTEGER_ARRAY: + case LONG_ARRAY: + case FLOAT_ARRAY: + case DOUBLE_ARRAY: + case STRING_ARRAY: + { + int code = in.readListStart(); + + switch (code) { + case 'N': + return null; + + case 0x10: case 0x11: case 0x12: case 0x13: + case 0x14: case 0x15: case 0x16: case 0x17: + case 0x18: case 0x19: case 0x1a: case 0x1b: + case 0x1c: case 0x1d: case 0x1e: case 0x1f: + int length = code - 0x10; + in.readInt(); + + return readLengthList(in, length); + + default: + String type = in.readType(); + length = in.readLength(); + + return readList(in, length); + } + } + + default: + throw new UnsupportedOperationException(); + } + } + + public Object readList(AbstractHessianInput in, int length) + throws IOException + { + switch (_code) { + case BOOLEAN_ARRAY: { + if (length >= 0) { + boolean []data = new boolean[length]; + + in.addRef(data); + + for (int i = 0; i < data.length; i++) + data[i] = in.readBoolean(); + + in.readEnd(); + + return data; + } + else { + ArrayList list = new ArrayList(); + + while (! in.isEnd()) + list.add(Boolean.valueOf(in.readBoolean())); + + in.readEnd(); + + boolean []data = new boolean[list.size()]; + + in.addRef(data); + + for (int i = 0; i < data.length; i++) + data[i] = ((Boolean) list.get(i)).booleanValue(); + + return data; + } + } + + case SHORT_ARRAY: { + if (length >= 0) { + short []data = new short[length]; + + in.addRef(data); + + for (int i = 0; i < data.length; i++) + data[i] = (short) in.readInt(); + + in.readEnd(); + + return data; + } + else { + ArrayList list = new ArrayList(); + + while (! in.isEnd()) + list.add(Short.valueOf((short) in.readInt())); + + in.readEnd(); + + short []data = new short[list.size()]; + for (int i = 0; i < data.length; i++) + data[i] = ((Short) list.get(i)).shortValue(); + + in.addRef(data); + + return data; + } + } + + case INTEGER_ARRAY: { + if (length >= 0) { + int []data = new int[length]; + + in.addRef(data); + + for (int i = 0; i < data.length; i++) + data[i] = in.readInt(); + + in.readEnd(); + + return data; + } + else { + ArrayList list = new ArrayList(); + + while (! in.isEnd()) + list.add(Integer.valueOf(in.readInt())); + + + in.readEnd(); + + int []data = new int[list.size()]; + for (int i = 0; i < data.length; i++) + data[i] = ((Integer) list.get(i)).intValue(); + + in.addRef(data); + + return data; + } + } + + case LONG_ARRAY: { + if (length >= 0) { + long []data = new long[length]; + + in.addRef(data); + + for (int i = 0; i < data.length; i++) + data[i] = in.readLong(); + + in.readEnd(); + + return data; + } + else { + ArrayList list = new ArrayList(); + + while (! in.isEnd()) + list.add(Long.valueOf(in.readLong())); + + in.readEnd(); + + long []data = new long[list.size()]; + for (int i = 0; i < data.length; i++) + data[i] = ((Long) list.get(i)).longValue(); + + in.addRef(data); + + return data; + } + } + + case FLOAT_ARRAY: { + if (length >= 0) { + float []data = new float[length]; + in.addRef(data); + + for (int i = 0; i < data.length; i++) + data[i] = (float) in.readDouble(); + + in.readEnd(); + + return data; + } + else { + ArrayList list = new ArrayList(); + + while (! in.isEnd()) + list.add(new Float(in.readDouble())); + + in.readEnd(); + + float []data = new float[list.size()]; + for (int i = 0; i < data.length; i++) + data[i] = ((Float) list.get(i)).floatValue(); + + in.addRef(data); + + return data; + } + } + + case DOUBLE_ARRAY: { + if (length >= 0) { + double []data = new double[length]; + in.addRef(data); + + for (int i = 0; i < data.length; i++) + data[i] = in.readDouble(); + + in.readEnd(); + + return data; + } + else { + ArrayList list = new ArrayList(); + + while (! in.isEnd()) + list.add(new Double(in.readDouble())); + + in.readEnd(); + + double []data = new double[list.size()]; + in.addRef(data); + for (int i = 0; i < data.length; i++) + data[i] = ((Double) list.get(i)).doubleValue(); + + return data; + } + } + + case STRING_ARRAY: { + if (length >= 0) { + String []data = new String[length]; + in.addRef(data); + + for (int i = 0; i < data.length; i++) + data[i] = in.readString(); + + in.readEnd(); + + return data; + } + else { + ArrayList list = new ArrayList(); + + while (! in.isEnd()) + list.add(in.readString()); + + in.readEnd(); + + String []data = new String[list.size()]; + in.addRef(data); + for (int i = 0; i < data.length; i++) + data[i] = (String) list.get(i); + + return data; + } + } + + case OBJECT_ARRAY: { + if (length >= 0) { + Object []data = new Object[length]; + in.addRef(data); + + for (int i = 0; i < data.length; i++) + data[i] = in.readObject(); + + in.readEnd(); + + return data; + } + else { + ArrayList list = new ArrayList(); + + in.addRef(list); // XXX: potential issues here + + while (! in.isEnd()) + list.add(in.readObject()); + + in.readEnd(); + + Object []data = new Object[list.size()]; + for (int i = 0; i < data.length; i++) + data[i] = (Object) list.get(i); + + return data; + } + } + + default: + throw new UnsupportedOperationException(String.valueOf(this)); + } + } + + public Object readLengthList(AbstractHessianInput in, int length) + throws IOException + { + switch (_code) { + case BOOLEAN_ARRAY: { + boolean []data = new boolean[length]; + + in.addRef(data); + + for (int i = 0; i < data.length; i++) + data[i] = in.readBoolean(); + + return data; + } + + case SHORT_ARRAY: { + short []data = new short[length]; + + in.addRef(data); + + for (int i = 0; i < data.length; i++) + data[i] = (short) in.readInt(); + + return data; + } + + case INTEGER_ARRAY: { + int []data = new int[length]; + + in.addRef(data); + + for (int i = 0; i < data.length; i++) + data[i] = in.readInt(); + + return data; + } + + case LONG_ARRAY: { + long []data = new long[length]; + + in.addRef(data); + + for (int i = 0; i < data.length; i++) + data[i] = in.readLong(); + + return data; + } + + case FLOAT_ARRAY: { + float []data = new float[length]; + in.addRef(data); + + for (int i = 0; i < data.length; i++) + data[i] = (float) in.readDouble(); + + return data; + } + + case DOUBLE_ARRAY: { + double []data = new double[length]; + in.addRef(data); + + for (int i = 0; i < data.length; i++) + data[i] = in.readDouble(); + + return data; + } + + case STRING_ARRAY: { + String []data = new String[length]; + in.addRef(data); + + for (int i = 0; i < data.length; i++) + data[i] = in.readString(); + + return data; + } + + case OBJECT_ARRAY: { + Object []data = new Object[length]; + in.addRef(data); + + for (int i = 0; i < data.length; i++) + data[i] = in.readObject(); + + return data; + } + + default: + throw new UnsupportedOperationException(String.valueOf(this)); + } + } +} diff --git a/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/BasicSerializer.java b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/BasicSerializer.java new file mode 100644 index 0000000000..c9ed7febfc --- /dev/null +++ b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/BasicSerializer.java @@ -0,0 +1,330 @@ +/* + * Copyright (c) 2001-2004 Caucho Technology, Inc. All rights reserved. + * + * The Apache Software License, Version 1.1 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Caucho Technology (http://www.caucho.com/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Burlap", "Resin", and "Caucho" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * info@caucho.com. + * + * 5. Products derived from this software may not be called "Resin" + * nor may "Resin" appear in their names without prior written + * permission of Caucho Technology. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Scott Ferguson + */ + +package com.caucho.hessian4.io; + +import java.io.IOException; +import java.util.Date; + +/** + * Serializing an object for known object types. + */ +public class BasicSerializer extends AbstractSerializer + implements ObjectSerializer +{ + public static final int NULL = 0; + public static final int BOOLEAN = NULL + 1; + public static final int BYTE = BOOLEAN + 1; + public static final int SHORT = BYTE + 1; + public static final int INTEGER = SHORT + 1; + public static final int LONG = INTEGER + 1; + public static final int FLOAT = LONG + 1; + public static final int DOUBLE = FLOAT + 1; + public static final int CHARACTER = DOUBLE + 1; + public static final int CHARACTER_OBJECT = CHARACTER + 1; + public static final int STRING = CHARACTER_OBJECT + 1; + public static final int DATE = STRING + 1; + public static final int NUMBER = DATE + 1; + public static final int OBJECT = NUMBER + 1; + + public static final int BOOLEAN_ARRAY = OBJECT + 1; + public static final int BYTE_ARRAY = BOOLEAN_ARRAY + 1; + public static final int SHORT_ARRAY = BYTE_ARRAY + 1; + public static final int INTEGER_ARRAY = SHORT_ARRAY + 1; + public static final int LONG_ARRAY = INTEGER_ARRAY + 1; + public static final int FLOAT_ARRAY = LONG_ARRAY + 1; + public static final int DOUBLE_ARRAY = FLOAT_ARRAY + 1; + public static final int CHARACTER_ARRAY = DOUBLE_ARRAY + 1; + public static final int STRING_ARRAY = CHARACTER_ARRAY + 1; + public static final int OBJECT_ARRAY = STRING_ARRAY + 1; + + public static final int BYTE_HANDLE = OBJECT_ARRAY + 1; + public static final int SHORT_HANDLE = BYTE_HANDLE + 1; + public static final int FLOAT_HANDLE = SHORT_HANDLE + 1; + + private static final BasicSerializer BYTE_HANDLE_SERIALIZER + = new BasicSerializer(BYTE_HANDLE); + + private static final BasicSerializer SHORT_HANDLE_SERIALIZER + = new BasicSerializer(SHORT_HANDLE); + + private static final BasicSerializer FLOAT_HANDLE_SERIALIZER + = new BasicSerializer(FLOAT_HANDLE); + + private int _code; + + public BasicSerializer(int code) + { + _code = code; + } + + public Serializer getObjectSerializer() + { + switch (_code) { + case BYTE: + return BYTE_HANDLE_SERIALIZER; + case SHORT: + return SHORT_HANDLE_SERIALIZER; + case FLOAT: + return FLOAT_HANDLE_SERIALIZER; + default: + return this; + } + } + + public void writeObject(Object obj, AbstractHessianOutput out) + throws IOException + { + switch (_code) { + case BOOLEAN: + out.writeBoolean(((Boolean) obj).booleanValue()); + break; + + case BYTE: + case SHORT: + case INTEGER: + out.writeInt(((Number) obj).intValue()); + break; + + case LONG: + out.writeLong(((Number) obj).longValue()); + break; + + case FLOAT: + case DOUBLE: + out.writeDouble(((Number) obj).doubleValue()); + break; + + case CHARACTER: + case CHARACTER_OBJECT: + out.writeString(String.valueOf(obj)); + break; + + case STRING: + out.writeString((String) obj); + break; + + case DATE: + out.writeUTCDate(((Date) obj).getTime()); + break; + + case BOOLEAN_ARRAY: + { + if (out.addRef(obj)) + return; + + boolean []data = (boolean []) obj; + boolean hasEnd = out.writeListBegin(data.length, "[boolean"); + for (int i = 0; i < data.length; i++) + out.writeBoolean(data[i]); + + if (hasEnd) + out.writeListEnd(); + + break; + } + + case BYTE_ARRAY: + { + byte []data = (byte []) obj; + out.writeBytes(data, 0, data.length); + break; + } + + case SHORT_ARRAY: + { + if (out.addRef(obj)) + return; + + short []data = (short []) obj; + boolean hasEnd = out.writeListBegin(data.length, "[short"); + + for (int i = 0; i < data.length; i++) + out.writeInt(data[i]); + + if (hasEnd) + out.writeListEnd(); + break; + } + + case INTEGER_ARRAY: + { + if (out.addRef(obj)) + return; + + int []data = (int []) obj; + + boolean hasEnd = out.writeListBegin(data.length, "[int"); + + for (int i = 0; i < data.length; i++) + out.writeInt(data[i]); + + if (hasEnd) + out.writeListEnd(); + + break; + } + + case LONG_ARRAY: + { + if (out.addRef(obj)) + return; + + long []data = (long []) obj; + + boolean hasEnd = out.writeListBegin(data.length, "[long"); + + for (int i = 0; i < data.length; i++) + out.writeLong(data[i]); + + if (hasEnd) + out.writeListEnd(); + break; + } + + case FLOAT_ARRAY: + { + if (out.addRef(obj)) + return; + + float []data = (float []) obj; + + boolean hasEnd = out.writeListBegin(data.length, "[float"); + + for (int i = 0; i < data.length; i++) + out.writeDouble(data[i]); + + if (hasEnd) + out.writeListEnd(); + break; + } + + case DOUBLE_ARRAY: + { + if (out.addRef(obj)) + return; + + double []data = (double []) obj; + boolean hasEnd = out.writeListBegin(data.length, "[double"); + + for (int i = 0; i < data.length; i++) + out.writeDouble(data[i]); + + if (hasEnd) + out.writeListEnd(); + break; + } + + case STRING_ARRAY: + { + if (out.addRef(obj)) + return; + + String []data = (String []) obj; + + boolean hasEnd = out.writeListBegin(data.length, "[string"); + + for (int i = 0; i < data.length; i++) { + out.writeString(data[i]); + } + + if (hasEnd) + out.writeListEnd(); + break; + } + + case CHARACTER_ARRAY: + { + char []data = (char []) obj; + out.writeString(data, 0, data.length); + break; + } + + case OBJECT_ARRAY: + { + if (out.addRef(obj)) + return; + + Object []data = (Object []) obj; + + boolean hasEnd = out.writeListBegin(data.length, "[object"); + + for (int i = 0; i < data.length; i++) { + out.writeObject(data[i]); + } + + if (hasEnd) + out.writeListEnd(); + break; + } + + case NULL: + out.writeNull(); + break; + + case OBJECT: + ObjectHandleSerializer.SER.writeObject(obj, out); + break; + + case BYTE_HANDLE: + out.writeObject(new ByteHandle((Byte) obj)); + break; + + case SHORT_HANDLE: + out.writeObject(new ShortHandle((Short) obj)); + break; + + case FLOAT_HANDLE: + out.writeObject(new FloatHandle((Float) obj)); + break; + + default: + throw new RuntimeException(_code + " unknown code for " + obj.getClass()); + } + } +} diff --git a/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/BeanDeserializer.java b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/BeanDeserializer.java new file mode 100644 index 0000000000..bccc1e5dea --- /dev/null +++ b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/BeanDeserializer.java @@ -0,0 +1,295 @@ +/* + * Copyright (c) 2001-2008 Caucho Technology, Inc. All rights reserved. + * + * The Apache Software License, Version 1.1 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Caucho Technology (http://www.caucho.com/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Burlap", "Resin", and "Caucho" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * info@caucho.com. + * + * 5. Products derived from this software may not be called "Resin" + * nor may "Resin" appear in their names without prior written + * permission of Caucho Technology. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Scott Ferguson + */ + +package com.caucho.hessian4.io; + +import java.io.IOException; +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.HashMap; + +/** + * Serializing an object for known object types. + */ +public class BeanDeserializer extends AbstractMapDeserializer { + private Class _type; + private HashMap _methodMap; + private Method _readResolve; + private Constructor _constructor; + private Object []_constructorArgs; + + public BeanDeserializer(Class cl) + { + _type = cl; + _methodMap = getMethodMap(cl); + + _readResolve = getReadResolve(cl); + + Constructor []constructors = cl.getConstructors(); + int bestLength = Integer.MAX_VALUE; + + for (int i = 0; i < constructors.length; i++) { + if (constructors[i].getParameterTypes().length < bestLength) { + _constructor = constructors[i]; + bestLength = _constructor.getParameterTypes().length; + } + } + + if (_constructor != null) { + _constructor.setAccessible(true); + Class []params = _constructor.getParameterTypes(); + _constructorArgs = new Object[params.length]; + for (int i = 0; i < params.length; i++) { + _constructorArgs[i] = getParamArg(params[i]); + } + } + } + + public Class getType() + { + return _type; + } + + public Object readMap(AbstractHessianInput in) + throws IOException + { + try { + Object obj = instantiate(); + + return readMap(in, obj); + } catch (IOException e) { + throw e; + } catch (Exception e) { + throw new IOExceptionWrapper(e); + } + } + + public Object readMap(AbstractHessianInput in, Object obj) + throws IOException + { + try { + int ref = in.addRef(obj); + + while (! in.isEnd()) { + Object key = in.readObject(); + + Method method = (Method) _methodMap.get(key); + + if (method != null) { + Object value = in.readObject(method.getParameterTypes()[0]); + + method.invoke(obj, new Object[] {value }); + } + else { + Object value = in.readObject(); + } + } + + in.readMapEnd(); + + Object resolve = resolve(obj); + + if (obj != resolve) + in.setRef(ref, resolve); + + return resolve; + } catch (IOException e) { + throw e; + } catch (Exception e) { + throw new IOExceptionWrapper(e); + } + } + + private Object resolve(Object obj) + { + // if there's a readResolve method, call it + try { + if (_readResolve != null) + return _readResolve.invoke(obj, new Object[0]); + } catch (Exception e) { + } + + return obj; + } + + protected Object instantiate() + throws Exception + { + return _constructor.newInstance(_constructorArgs); + } + + /** + * Returns the readResolve method + */ + protected Method getReadResolve(Class cl) + { + for (; cl != null; cl = cl.getSuperclass()) { + Method []methods = cl.getDeclaredMethods(); + + for (int i = 0; i < methods.length; i++) { + Method method = methods[i]; + + if (method.getName().equals("readResolve") && + method.getParameterTypes().length == 0) + return method; + } + } + + return null; + } + + /** + * Creates a map of the classes fields. + */ + protected HashMap getMethodMap(Class cl) + { + HashMap methodMap = new HashMap(); + + for (; cl != null; cl = cl.getSuperclass()) { + Method []methods = cl.getDeclaredMethods(); + + for (int i = 0; i < methods.length; i++) { + Method method = methods[i]; + + if (Modifier.isStatic(method.getModifiers())) + continue; + + String name = method.getName(); + + if (! name.startsWith("set")) + continue; + + Class []paramTypes = method.getParameterTypes(); + if (paramTypes.length != 1) + continue; + + if (! method.getReturnType().equals(void.class)) + continue; + + if (findGetter(methods, name, paramTypes[0]) == null) + continue; + + // XXX: could parameterize the handler to only deal with public + try { + method.setAccessible(true); + } catch (Throwable e) { + e.printStackTrace(); + } + + name = name.substring(3); + + int j = 0; + for (; j < name.length() && Character.isUpperCase(name.charAt(j)); j++) { + } + + if (j == 1) + name = name.substring(0, j).toLowerCase() + name.substring(j); + else if (j > 1) + name = name.substring(0, j - 1).toLowerCase() + name.substring(j - 1); + + + methodMap.put(name, method); + } + } + + return methodMap; + } + + /** + * Finds any matching setter. + */ + private Method findGetter(Method []methods, String setterName, Class arg) + { + String getterName = "get" + setterName.substring(3); + + for (int i = 0; i < methods.length; i++) { + Method method = methods[i]; + + if (! method.getName().equals(getterName)) + continue; + + if (! method.getReturnType().equals(arg)) + continue; + + Class []params = method.getParameterTypes(); + + if (params.length == 0) + return method; + } + + return null; + } + + /** + * Creates a map of the classes fields. + */ + protected static Object getParamArg(Class cl) + { + if (! cl.isPrimitive()) + return null; + else if (boolean.class.equals(cl)) + return Boolean.FALSE; + else if (byte.class.equals(cl)) + return Byte.valueOf((byte) 0); + else if (short.class.equals(cl)) + return Short.valueOf((short) 0); + else if (char.class.equals(cl)) + return Character.valueOf((char) 0); + else if (int.class.equals(cl)) + return Integer.valueOf(0); + else if (long.class.equals(cl)) + return Long.valueOf(0); + else if (float.class.equals(cl)) + return Double.valueOf(0); + else if (double.class.equals(cl)) + return Double.valueOf(0); + else + throw new UnsupportedOperationException(); + } +} diff --git a/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/BeanSerializer.java b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/BeanSerializer.java new file mode 100644 index 0000000000..77af6aa2e3 --- /dev/null +++ b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/BeanSerializer.java @@ -0,0 +1,316 @@ +/* + * Copyright (c) 2001-2004 Caucho Technology, Inc. All rights reserved. + * + * The Apache Software License, Version 1.1 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Caucho Technology (http://www.caucho.com/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Burlap", "Resin", and "Caucho" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * info@caucho.com. + * + * 5. Products derived from this software may not be called "Resin" + * nor may "Resin" appear in their names without prior written + * permission of Caucho Technology. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Scott Ferguson + */ + +package com.caucho.hessian4.io; + +import java.io.IOException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Serializing an object for known object types. + */ +public class BeanSerializer extends AbstractSerializer { + private static final Logger log + = Logger.getLogger(BeanSerializer.class.getName()); + + private static final Object []NULL_ARGS = new Object[0]; + private Method []_methods; + private String []_names; + + private Object _writeReplaceFactory; + private Method _writeReplace; + + public BeanSerializer(Class cl, ClassLoader loader) + { + introspectWriteReplace(cl, loader); + + ArrayList primitiveMethods = new ArrayList(); + ArrayList compoundMethods = new ArrayList(); + + for (; cl != null; cl = cl.getSuperclass()) { + Method []methods = cl.getDeclaredMethods(); + + for (int i = 0; i < methods.length; i++) { + Method method = methods[i]; + + if (Modifier.isStatic(method.getModifiers())) + continue; + + if (method.getParameterTypes().length != 0) + continue; + + String name = method.getName(); + + if (! name.startsWith("get")) + continue; + + Class type = method.getReturnType(); + + if (type.equals(void.class)) + continue; + + if (findSetter(methods, name, type) == null) + continue; + + // XXX: could parameterize the handler to only deal with public + method.setAccessible(true); + + if (type.isPrimitive() + || type.getName().startsWith("java.lang.") + && ! type.equals(Object.class)) + primitiveMethods.add(method); + else + compoundMethods.add(method); + } + } + + ArrayList methodList = new ArrayList(); + methodList.addAll(primitiveMethods); + methodList.addAll(compoundMethods); + + Collections.sort(methodList, new MethodNameCmp()); + + _methods = new Method[methodList.size()]; + methodList.toArray(_methods); + + _names = new String[_methods.length]; + + for (int i = 0; i < _methods.length; i++) { + String name = _methods[i].getName(); + + name = name.substring(3); + + int j = 0; + for (; j < name.length() && Character.isUpperCase(name.charAt(j)); j++) { + } + + if (j == 1) + name = name.substring(0, j).toLowerCase() + name.substring(j); + else if (j > 1) + name = name.substring(0, j - 1).toLowerCase() + name.substring(j - 1); + + _names[i] = name; + } + } + + private void introspectWriteReplace(Class cl, ClassLoader loader) + { + try { + String className = cl.getName() + "HessianSerializer"; + + Class serializerClass = Class.forName(className, false, loader); + + Object serializerObject = serializerClass.newInstance(); + + Method writeReplace = getWriteReplace(serializerClass, cl); + + if (writeReplace != null) { + _writeReplaceFactory = serializerObject; + _writeReplace = writeReplace; + + return; + } + } catch (ClassNotFoundException e) { + } catch (Exception e) { + log.log(Level.FINER, e.toString(), e); + } + + _writeReplace = getWriteReplace(cl); + } + + /** + * Returns the writeReplace method + */ + protected Method getWriteReplace(Class cl) + { + for (; cl != null; cl = cl.getSuperclass()) { + Method []methods = cl.getDeclaredMethods(); + + for (int i = 0; i < methods.length; i++) { + Method method = methods[i]; + + if (method.getName().equals("writeReplace") && + method.getParameterTypes().length == 0) + return method; + } + } + + return null; + } + + /** + * Returns the writeReplace method + */ + protected Method getWriteReplace(Class cl, Class param) + { + for (; cl != null; cl = cl.getSuperclass()) { + for (Method method : cl.getDeclaredMethods()) { + if (method.getName().equals("writeReplace") + && method.getParameterTypes().length == 1 + && param.equals(method.getParameterTypes()[0])) + return method; + } + } + + return null; + } + + public void writeObject(Object obj, AbstractHessianOutput out) + throws IOException + { + if (out.addRef(obj)) + return; + + Class cl = obj.getClass(); + + try { + if (_writeReplace != null) { + Object repl; + + if (_writeReplaceFactory != null) + repl = _writeReplace.invoke(_writeReplaceFactory, obj); + else + repl = _writeReplace.invoke(obj); + + // out.removeRef(obj); + + out.writeObject(repl); + + out.replaceRef(repl, obj); + + return; + } + } catch (Exception e) { + log.log(Level.FINER, e.toString(), e); + } + + int ref = out.writeObjectBegin(cl.getName()); + + if (ref < -1) { + // Hessian 1.1 uses a map + + for (int i = 0; i < _methods.length; i++) { + Method method = _methods[i]; + Object value = null; + + try { + value = _methods[i].invoke(obj, (Object []) null); + } catch (Exception e) { + log.log(Level.FINE, e.toString(), e); + } + + out.writeString(_names[i]); + + out.writeObject(value); + } + + out.writeMapEnd(); + } + else { + if (ref == -1) { + out.writeInt(_names.length); + + for (int i = 0; i < _names.length; i++) + out.writeString(_names[i]); + + out.writeObjectBegin(cl.getName()); + } + + for (int i = 0; i < _methods.length; i++) { + Method method = _methods[i]; + Object value = null; + + try { + value = _methods[i].invoke(obj, (Object []) null); + } catch (Exception e) { + log.log(Level.FINER, e.toString(), e); + } + + out.writeObject(value); + } + } + } + + /** + * Finds any matching setter. + */ + private Method findSetter(Method []methods, String getterName, Class arg) + { + String setterName = "set" + getterName.substring(3); + + for (int i = 0; i < methods.length; i++) { + Method method = methods[i]; + + if (! method.getName().equals(setterName)) + continue; + + if (! method.getReturnType().equals(void.class)) + continue; + + Class []params = method.getParameterTypes(); + + if (params.length == 1 && params[0].equals(arg)) + return method; + } + + return null; + } + + static class MethodNameCmp implements Comparator { + public int compare(Method a, Method b) + { + return a.getName().compareTo(b.getName()); + } + } +} diff --git a/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/BeanSerializerFactory.java b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/BeanSerializerFactory.java new file mode 100644 index 0000000000..6231628a14 --- /dev/null +++ b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/BeanSerializerFactory.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2001-2004 Caucho Technology, Inc. All rights reserved. + * + * The Apache Software License, Version 1.1 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Caucho Technology (http://www.caucho.com/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Burlap", "Resin", and "Caucho" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * info@caucho.com. + * + * 5. Products derived from this software may not be called "Resin" + * nor may "Resin" appear in their names without prior written + * permission of Caucho Technology. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Scott Ferguson + */ + +package com.caucho.hessian4.io; + +/** + * Factory for returning serialization methods. + */ +public class BeanSerializerFactory extends SerializerFactory { + /** + * Returns the default serializer for a class that isn't matched + * directly. Application can override this method to produce + * bean-style serialization instead of field serialization. + * + * @param cl the class of the object that needs to be serialized. + * + * @return a serializer object for the serialization. + */ + protected Serializer getDefaultSerializer(Class cl) + { + return new BeanSerializer(cl, getClassLoader()); + } + + /** + * Returns the default deserializer for a class that isn't matched + * directly. Application can override this method to produce + * bean-style serialization instead of field serialization. + * + * @param cl the class of the object that needs to be serialized. + * + * @return a serializer object for the serialization. + */ + protected Deserializer getDefaultDeserializer(Class cl) + { + return new BeanDeserializer(cl); + } +} diff --git a/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/BigDecimalDeserializer.java b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/BigDecimalDeserializer.java new file mode 100644 index 0000000000..34f2bed301 --- /dev/null +++ b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/BigDecimalDeserializer.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2001-2004 Caucho Technology, Inc. All rights reserved. + * + * The Apache Software License, Version 1.1 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Caucho Technology (http://www.caucho.com/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Hessian", "Resin", and "Caucho" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * info@caucho.com. + * + * 5. Products derived from this software may not be called "Resin" + * nor may "Resin" appear in their names without prior written + * permission of Caucho Technology. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Scott Ferguson + */ + +package com.caucho.hessian4.io; + +import java.math.BigDecimal; + +/** + * Deserializing a BigDecimal + */ +public class BigDecimalDeserializer extends AbstractStringValueDeserializer { + @Override + public Class getType() + { + return BigDecimal.class; + } + + @Override + protected Object create(String value) + { + return new BigDecimal(value); + } +} diff --git a/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/ByteHandle.java b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/ByteHandle.java new file mode 100644 index 0000000000..aa63cc7967 --- /dev/null +++ b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/ByteHandle.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2001-2004 Caucho Technology, Inc. All rights reserved. + * + * The Apache Software License, Version 1.1 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Caucho Technology (http://www.caucho.com/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Burlap", "Resin", and "Caucho" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * info@caucho.com. + * + * 5. Products derived from this software may not be called "Resin" + * nor may "Resin" appear in their names without prior written + * permission of Caucho Technology. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Scott Ferguson + */ + +package com.caucho.hessian4.io; + +import java.io.Serializable; + +/** + * Handle for Java Byte objects. + */ +public class ByteHandle implements Serializable { + private byte _value; + + private ByteHandle() + { + } + + public ByteHandle(byte value) + { + _value = value; + } + + public byte getValue() + { + return _value; + } + + public Object readResolve() + { + return new Byte(_value); + } + + public String toString() + { + return getClass().getSimpleName() + "[" + _value + "]"; + } +} diff --git a/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/CalendarHandle.java b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/CalendarHandle.java new file mode 100644 index 0000000000..37480e0926 --- /dev/null +++ b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/CalendarHandle.java @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2001-2004 Caucho Technology, Inc. All rights reserved. + * + * The Apache Software License, Version 1.1 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Caucho Technology (http://www.caucho.com/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Hessian", "Resin", and "Caucho" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * info@caucho.com. + * + * 5. Products derived from this software may not be called "Resin" + * nor may "Resin" appear in their names without prior written + * permission of Caucho Technology. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Scott Ferguson + */ + +package com.caucho.hessian4.io; + +import java.util.Calendar; +import java.util.Date; +import java.util.GregorianCalendar; + +/** + * Handle for a calendar object. + */ +public class CalendarHandle implements java.io.Serializable, HessianHandle { + private Class type; + private Date date; + + public CalendarHandle() + { + } + + public CalendarHandle(Class type, long time) + { + if (! GregorianCalendar.class.equals(type)) + this.type = type; + + this.date = new Date(time); + } + + private Object readResolve() + { + try { + Calendar cal; + + if (this.type != null) + cal = (Calendar) this.type.newInstance(); + else + cal = new GregorianCalendar(); + + cal.setTimeInMillis(this.date.getTime()); + + return cal; + } catch (RuntimeException e) { + throw e; + } catch (Exception e) { + throw new RuntimeException(e); + } + } +} diff --git a/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/CalendarSerializer.java b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/CalendarSerializer.java new file mode 100644 index 0000000000..37117f4fad --- /dev/null +++ b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/CalendarSerializer.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2001-2004 Caucho Technology, Inc. All rights reserved. + * + * The Apache Software License, Version 1.1 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Caucho Technology (http://www.caucho.com/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Hessian", "Resin", and "Caucho" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * info@caucho.com. + * + * 5. Products derived from this software may not be called "Resin" + * nor may "Resin" appear in their names without prior written + * permission of Caucho Technology. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Scott Ferguson + */ + +package com.caucho.hessian4.io; + +import java.util.Calendar; + +/** + * Serializing a calendar. + */ +public class CalendarSerializer extends AbstractSerializer { + public static final Serializer SER = new CalendarSerializer(); + + /** + * java.util.Calendar serializes to com.caucho.hessian.io.CalendarHandle + */ + @Override + public Object writeReplace(Object obj) + { + Calendar cal = (Calendar) obj; + + return new CalendarHandle(cal.getClass(), cal.getTimeInMillis()); + } +} diff --git a/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/ClassDeserializer.java b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/ClassDeserializer.java new file mode 100644 index 0000000000..743dfa0f75 --- /dev/null +++ b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/ClassDeserializer.java @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2001-2004 Caucho Technology, Inc. All rights reserved. + * + * The Apache Software License, Version 1.1 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Caucho Technology (http://www.caucho.com/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Hessian", "Resin", and "Caucho" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * info@caucho.com. + * + * 5. Products derived from this software may not be called "Resin" + * nor may "Resin" appear in their names without prior written + * permission of Caucho Technology. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Scott Ferguson + */ + +package com.caucho.hessian4.io; + +import java.io.IOException; +import java.util.HashMap; + +/** + * Deserializing a JDK 1.2 Class. + */ +public class ClassDeserializer extends AbstractMapDeserializer { + private static final HashMap _primClasses + = new HashMap(); + + private ClassLoader _loader; + + public ClassDeserializer(ClassLoader loader) + { + _loader = loader; + } + + public Class getType() + { + return Class.class; + } + + public Object readMap(AbstractHessianInput in) + throws IOException + { + int ref = in.addRef(null); + + String name = null; + + while (! in.isEnd()) { + String key = in.readString(); + + if (key.equals("name")) + name = in.readString(); + else + in.readObject(); + } + + in.readMapEnd(); + + Object value = create(name); + + in.setRef(ref, value); + + return value; + } + + public Object readObject(AbstractHessianInput in, Object []fields) + throws IOException + { + String []fieldNames = (String []) fields; + + int ref = in.addRef(null); + + String name = null; + + for (int i = 0; i < fieldNames.length; i++) { + if ("name".equals(fieldNames[i])) + name = in.readString(); + else + in.readObject(); + } + + Object value = create(name); + + in.setRef(ref, value); + + return value; + } + + Object create(String name) + throws IOException + { + if (name == null) + throw new IOException("Serialized Class expects name."); + + Class cl = _primClasses.get(name); + + if (cl != null) + return cl; + + try { + if (_loader != null) + return Class.forName(name, false, _loader); + else + return Class.forName(name); + } catch (Exception e) { + throw new IOExceptionWrapper(e); + } + } + + static { + _primClasses.put("void", void.class); + _primClasses.put("boolean", boolean.class); + _primClasses.put("java.lang.Boolean", Boolean.class); + _primClasses.put("byte", byte.class); + _primClasses.put("java.lang.Byte", Byte.class); + _primClasses.put("char", char.class); + _primClasses.put("java.lang.Character", Character.class); + _primClasses.put("short", short.class); + _primClasses.put("java.lang.Short", Short.class); + _primClasses.put("int", int.class); + _primClasses.put("java.lang.Integer", Integer.class); + _primClasses.put("long", long.class); + _primClasses.put("java.lang.Long", Long.class); + _primClasses.put("float", float.class); + _primClasses.put("java.lang.Float", Float.class); + _primClasses.put("double", double.class); + _primClasses.put("java.lang.Double", Double.class); + _primClasses.put("java.lang.String", String.class); + } +} diff --git a/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/ClassSerializer.java b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/ClassSerializer.java new file mode 100644 index 0000000000..f8794c7efb --- /dev/null +++ b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/ClassSerializer.java @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2001-2004 Caucho Technology, Inc. All rights reserved. + * + * The Apache Software License, Version 1.1 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Caucho Technology (http://www.caucho.com/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Hessian", "Resin", and "Caucho" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * info@caucho.com. + * + * 5. Products derived from this software may not be called "Resin" + * nor may "Resin" appear in their names without prior written + * permission of Caucho Technology. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Scott Ferguson + */ + +package com.caucho.hessian4.io; + +import java.io.IOException; + +/** + * Serializing a remote object. + */ +public class ClassSerializer extends AbstractSerializer { + public void writeObject(Object obj, AbstractHessianOutput out) + throws IOException + { + Class cl = (Class) obj; + + if (cl == null) { + out.writeNull(); + } + else if (out.addRef(obj)) { + return; + } + else { + int ref = out.writeObjectBegin("java.lang.Class"); + + if (ref < -1) { + out.writeString("name"); + out.writeString(cl.getName()); + out.writeMapEnd(); + } + else { + if (ref == -1) { + out.writeInt(1); + out.writeString("name"); + out.writeObjectBegin("java.lang.Class"); + } + + out.writeString(cl.getName()); + } + } + } +} diff --git a/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/CollectionDeserializer.java b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/CollectionDeserializer.java new file mode 100644 index 0000000000..5fe0e531ed --- /dev/null +++ b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/CollectionDeserializer.java @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2001-2004 Caucho Technology, Inc. All rights reserved. + * + * The Apache Software License, Version 1.1 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Caucho Technology (http://www.caucho.com/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Hessian", "Resin", and "Caucho" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * info@caucho.com. + * + * 5. Products derived from this software may not be called "Resin" + * nor may "Resin" appear in their names without prior written + * permission of Caucho Technology. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Scott Ferguson + */ + +package com.caucho.hessian4.io; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.SortedSet; +import java.util.TreeSet; + +/** + * Deserializing a JDK 1.2 Collection. + */ +public class CollectionDeserializer extends AbstractListDeserializer { + private Class _type; + + public CollectionDeserializer(Class type) + { + _type = type; + } + + public Class getType() + { + return _type; + } + + public Object readList(AbstractHessianInput in, int length) + throws IOException + { + Collection list = createList(); + + in.addRef(list); + + while (! in.isEnd()) + list.add(in.readObject()); + + in.readEnd(); + + return list; + } + + public Object readLengthList(AbstractHessianInput in, int length) + throws IOException + { + Collection list = createList(); + + in.addRef(list); + + for (; length > 0; length--) + list.add(in.readObject()); + + return list; + } + + private Collection createList() + throws IOException + { + Collection list = null; + + if (_type == null) + list = new ArrayList(); + else if (! _type.isInterface()) { + try { + list = (Collection) _type.newInstance(); + } catch (Exception e) { + } + } + + if (list != null) { + } + else if (SortedSet.class.isAssignableFrom(_type)) + list = new TreeSet(); + else if (Set.class.isAssignableFrom(_type)) + list = new HashSet(); + else if (List.class.isAssignableFrom(_type)) + list = new ArrayList(); + else if (Collection.class.isAssignableFrom(_type)) + list = new ArrayList(); + else { + try { + list = (Collection) _type.newInstance(); + } catch (Exception e) { + throw new IOExceptionWrapper(e); + } + } + + return list; + } +} + + diff --git a/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/CollectionSerializer.java b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/CollectionSerializer.java new file mode 100644 index 0000000000..482db61ecb --- /dev/null +++ b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/CollectionSerializer.java @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2001-2004 Caucho Technology, Inc. All rights reserved. + * + * The Apache Software License, Version 1.1 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Caucho Technology (http://www.caucho.com/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Hessian", "Resin", and "Caucho" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * info@caucho.com. + * + * 5. Products derived from this software may not be called "Resin" + * nor may "Resin" appear in their names without prior written + * permission of Caucho Technology. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Scott Ferguson + */ + +package com.caucho.hessian4.io; + +import java.io.IOException; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; + +/** + * Serializing a JDK 1.2 Collection. + */ +public class CollectionSerializer extends AbstractSerializer +{ + private boolean _sendJavaType = true; + + /** + * Set true if the java type of the collection should be sent. + */ + public void setSendJavaType(boolean sendJavaType) + { + _sendJavaType = sendJavaType; + } + + /** + * Return true if the java type of the collection should be sent. + */ + public boolean getSendJavaType() + { + return _sendJavaType; + } + + public void writeObject(Object obj, AbstractHessianOutput out) + throws IOException + { + if (out.addRef(obj)) + return; + + Collection list = (Collection) obj; + + Class cl = obj.getClass(); + boolean hasEnd; + + if (cl.equals(ArrayList.class) + || ! _sendJavaType + || ! Serializable.class.isAssignableFrom(cl)) + hasEnd = out.writeListBegin(list.size(), null); + else + hasEnd = out.writeListBegin(list.size(), obj.getClass().getName()); + + Iterator iter = list.iterator(); + while (iter.hasNext()) { + Object value = iter.next(); + + out.writeObject(value); + } + + if (hasEnd) + out.writeListEnd(); + } +} diff --git a/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/ContextSerializerFactory.java b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/ContextSerializerFactory.java new file mode 100644 index 0000000000..62e2d072e3 --- /dev/null +++ b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/ContextSerializerFactory.java @@ -0,0 +1,480 @@ +/* + * Copyright (c) 2001-2008 Caucho Technology, Inc. All rights reserved. + * + * The Apache Software License, Version 1.1 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Caucho Technology (http://www.caucho.com/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Burlap", "Resin", and "Caucho" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * info@caucho.com. + * + * 5. Products derived from this software may not be called "Resin" + * nor may "Resin" appear in their names without prior written + * permission of Caucho Technology. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Scott Ferguson + */ + +package com.caucho.hessian4.io; + +import java.io.InputStream; +import java.lang.ref.SoftReference; +import java.net.URL; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Properties; +import java.util.WeakHashMap; +import java.util.concurrent.ConcurrentHashMap; +import java.util.logging.Level; +import java.util.logging.Logger; + +import com.caucho.hessian4.HessianException; + +/** + * The classloader-specific Factory for returning serialization + */ +public class ContextSerializerFactory +{ + private static final Logger log + = Logger.getLogger(ContextSerializerFactory.class.getName()); + + private static Deserializer OBJECT_DESERIALIZER + = new BasicDeserializer(BasicDeserializer.OBJECT); + + private static final WeakHashMap> + _contextRefMap + = new WeakHashMap>(); + + private static final ClassLoader _systemClassLoader; + + private static HashMap _staticSerializerMap; + private static HashMap _staticDeserializerMap; + private static HashMap _staticClassNameMap; + + private ContextSerializerFactory _parent; + private ClassLoader _loader; + + private final HashSet _serializerFiles = new HashSet(); + private final HashSet _deserializerFiles = new HashSet(); + + private final HashMap _serializerClassMap + = new HashMap(); + + private final ConcurrentHashMap _customSerializerMap + = new ConcurrentHashMap(); + + private final HashMap _serializerInterfaceMap + = new HashMap(); + + private final HashMap _deserializerClassMap + = new HashMap(); + + private final HashMap _deserializerClassNameMap + = new HashMap(); + + private final ConcurrentHashMap _customDeserializerMap + = new ConcurrentHashMap(); + + private final HashMap _deserializerInterfaceMap + = new HashMap(); + + public ContextSerializerFactory(ContextSerializerFactory parent, + ClassLoader loader) + { + if (loader == null) + loader = _systemClassLoader; + + _loader = loader; + + init(); + } + + public static ContextSerializerFactory create() + { + return create(Thread.currentThread().getContextClassLoader()); + } + + public static ContextSerializerFactory create(ClassLoader loader) + { + synchronized (_contextRefMap) { + SoftReference factoryRef + = _contextRefMap.get(loader); + + ContextSerializerFactory factory = null; + + if (factoryRef != null) + factory = factoryRef.get(); + + if (factory == null) { + ContextSerializerFactory parent = null; + + if (loader != null) + parent = create(loader.getParent()); + + factory = new ContextSerializerFactory(parent, loader); + factoryRef = new SoftReference(factory); + + _contextRefMap.put(loader, factoryRef); + } + + return factory; + } + } + + public ClassLoader getClassLoader() + { + return _loader; + } + + /** + * Returns the serializer for a given class. + */ + public Serializer getSerializer(String className) + { + Serializer serializer = _serializerClassMap.get(className); + + if (serializer == AbstractSerializer.NULL) + return null; + else + return serializer; + } + + /** + * Returns a custom serializer the class + * + * @param cl the class of the object that needs to be serialized. + * + * @return a serializer object for the serialization. + */ + public Serializer getCustomSerializer(Class cl) + { + Serializer serializer = _customSerializerMap.get(cl.getName()); + + if (serializer == AbstractSerializer.NULL) + return null; + else if (serializer != null) + return serializer; + + try { + Class serClass = Class.forName(cl.getName() + "HessianSerializer", + false, cl.getClassLoader()); + + Serializer ser = (Serializer) serClass.newInstance(); + + _customSerializerMap.put(cl.getName(), ser); + + return ser; + } catch (ClassNotFoundException e) { + log.log(Level.ALL, e.toString(), e); + } catch (Exception e) { + throw new HessianException(e); + } + + _customSerializerMap.put(cl.getName(), AbstractSerializer.NULL); + + return null; + } + + /** + * Returns the deserializer for a given class. + */ + public Deserializer getDeserializer(String className) + { + Deserializer deserializer = _deserializerClassMap.get(className); + + if (deserializer == AbstractDeserializer.NULL) + return null; + else + return deserializer; + } + + /** + * Returns a custom deserializer the class + * + * @param cl the class of the object that needs to be deserialized. + * + * @return a deserializer object for the deserialization. + */ + public Deserializer getCustomDeserializer(Class cl) + { + Deserializer deserializer = _customDeserializerMap.get(cl.getName()); + + if (deserializer == AbstractDeserializer.NULL) + return null; + else if (deserializer != null) + return deserializer; + + try { + Class serClass = Class.forName(cl.getName() + "HessianDeserializer", + false, cl.getClassLoader()); + + Deserializer ser = (Deserializer) serClass.newInstance(); + + _customDeserializerMap.put(cl.getName(), ser); + + return ser; + } catch (ClassNotFoundException e) { + log.log(Level.ALL, e.toString(), e); + } catch (Exception e) { + throw new HessianException(e); + } + + _customDeserializerMap.put(cl.getName(), AbstractDeserializer.NULL); + + return null; + } + + /** + * Initialize the factory + */ + private void init() + { + if (_parent != null) { + _serializerFiles.addAll(_parent._serializerFiles); + _deserializerFiles.addAll(_parent._deserializerFiles); + + _serializerClassMap.putAll(_parent._serializerClassMap); + _deserializerClassMap.putAll(_parent._deserializerClassMap); + } + + if (_parent == null) { + _serializerClassMap.putAll(_staticSerializerMap); + _deserializerClassMap.putAll(_staticDeserializerMap); + _deserializerClassNameMap.putAll(_staticClassNameMap); + } + + HashMap classMap; + + classMap = new HashMap(); + initSerializerFiles("META-INF/hessian/serializers", + _serializerFiles, + classMap, + Serializer.class); + + for (Map.Entry entry : classMap.entrySet()) { + try { + Serializer ser = (Serializer) entry.getValue().newInstance(); + + if (entry.getKey().isInterface()) + _serializerInterfaceMap.put(entry.getKey(), ser); + else + _serializerClassMap.put(entry.getKey().getName(), ser); + } catch (Exception e) { + throw new HessianException(e); + } + } + + classMap = new HashMap(); + initSerializerFiles("META-INF/hessian/deserializers", + _deserializerFiles, + classMap, + Deserializer.class); + + for (Map.Entry entry : classMap.entrySet()) { + try { + Deserializer ser = (Deserializer) entry.getValue().newInstance(); + + if (entry.getKey().isInterface()) + _deserializerInterfaceMap.put(entry.getKey(), ser); + else { + _deserializerClassMap.put(entry.getKey().getName(), ser); + } + } catch (Exception e) { + throw new HessianException(e); + } + } + } + + private void initSerializerFiles(String fileName, + HashSet fileList, + HashMap classMap, + Class type) + { + try { + Enumeration iter; + + iter = getClassLoader().getResources(fileName); + while (iter.hasMoreElements()) { + URL url = (URL) iter.nextElement(); + + if (fileList.contains(url.toString())) + continue; + + fileList.add(url.toString()); + + InputStream is = null; + try { + is = url.openStream(); + + Properties props = new Properties(); + props.load(is); + + for (Map.Entry entry : props.entrySet()) { + String apiName = (String) entry.getKey(); + String serializerName = (String) entry.getValue(); + + Class apiClass = null; + Class serializerClass = null; + + try { + apiClass = Class.forName(apiName, false, getClassLoader()); + } catch (ClassNotFoundException e) { + log.fine(url + ": " + apiName + " is not available in this context: " + getClassLoader()); + continue; + } + + try { + serializerClass = Class.forName(serializerName, false, getClassLoader()); + } catch (ClassNotFoundException e) { + log.fine(url + ": " + serializerName + " is not available in this context: " + getClassLoader()); + continue; + } + + if (! type.isAssignableFrom(serializerClass)) + throw new HessianException(url + ": " + serializerClass.getName() + " is invalid because it does not implement " + type.getName()); + + classMap.put(apiClass, serializerClass); + } + } finally { + if (is != null) + is.close(); + } + } + } catch (RuntimeException e) { + throw e; + } catch (Exception e) { + throw new HessianException(e); + } + } + + private static void addBasic(Class cl, String typeName, int type) + { + _staticSerializerMap.put(cl.getName(), new BasicSerializer(type)); + + Deserializer deserializer = new BasicDeserializer(type); + _staticDeserializerMap.put(cl.getName(), deserializer); + _staticClassNameMap.put(typeName, deserializer); + } + + static { + _staticSerializerMap = new HashMap(); + _staticDeserializerMap = new HashMap(); + _staticClassNameMap = new HashMap(); + + addBasic(void.class, "void", BasicSerializer.NULL); + + addBasic(Boolean.class, "boolean", BasicSerializer.BOOLEAN); + addBasic(Byte.class, "byte", BasicSerializer.BYTE); + addBasic(Short.class, "short", BasicSerializer.SHORT); + addBasic(Integer.class, "int", BasicSerializer.INTEGER); + addBasic(Long.class, "long", BasicSerializer.LONG); + addBasic(Float.class, "float", BasicSerializer.FLOAT); + addBasic(Double.class, "double", BasicSerializer.DOUBLE); + addBasic(Character.class, "char", BasicSerializer.CHARACTER_OBJECT); + addBasic(String.class, "string", BasicSerializer.STRING); + addBasic(Object.class, "object", BasicSerializer.OBJECT); + addBasic(java.util.Date.class, "date", BasicSerializer.DATE); + + addBasic(boolean.class, "boolean", BasicSerializer.BOOLEAN); + addBasic(byte.class, "byte", BasicSerializer.BYTE); + addBasic(short.class, "short", BasicSerializer.SHORT); + addBasic(int.class, "int", BasicSerializer.INTEGER); + addBasic(long.class, "long", BasicSerializer.LONG); + addBasic(float.class, "float", BasicSerializer.FLOAT); + addBasic(double.class, "double", BasicSerializer.DOUBLE); + addBasic(char.class, "char", BasicSerializer.CHARACTER); + + addBasic(boolean[].class, "[boolean", BasicSerializer.BOOLEAN_ARRAY); + addBasic(byte[].class, "[byte", BasicSerializer.BYTE_ARRAY); + addBasic(short[].class, "[short", BasicSerializer.SHORT_ARRAY); + addBasic(int[].class, "[int", BasicSerializer.INTEGER_ARRAY); + addBasic(long[].class, "[long", BasicSerializer.LONG_ARRAY); + addBasic(float[].class, "[float", BasicSerializer.FLOAT_ARRAY); + addBasic(double[].class, "[double", BasicSerializer.DOUBLE_ARRAY); + addBasic(char[].class, "[char", BasicSerializer.CHARACTER_ARRAY); + addBasic(String[].class, "[string", BasicSerializer.STRING_ARRAY); + addBasic(Object[].class, "[object", BasicSerializer.OBJECT_ARRAY); + + Deserializer objectDeserializer = new JavaDeserializer(Object.class); + _staticDeserializerMap.put("object", objectDeserializer); + _staticClassNameMap.put("object", objectDeserializer); + + _staticSerializerMap.put(Class.class.getName(), new ClassSerializer()); + + _staticDeserializerMap.put(Number.class.getName(), new BasicDeserializer(BasicSerializer.NUMBER)); + + /* + for (Class cl : new Class[] { BigDecimal.class, File.class, ObjectName.class }) { + _staticSerializerMap.put(cl, StringValueSerializer.SER); + _staticDeserializerMap.put(cl, new StringValueDeserializer(cl)); + } + + _staticSerializerMap.put(ObjectName.class, StringValueSerializer.SER); + try { + _staticDeserializerMap.put(ObjectName.class, + new StringValueDeserializer(ObjectName.class)); + } catch (Throwable e) { + } + */ + + _staticSerializerMap.put(java.sql.Date.class.getName(), + new SqlDateSerializer()); + _staticSerializerMap.put(java.sql.Time.class.getName(), + new SqlDateSerializer()); + _staticSerializerMap.put(java.sql.Timestamp.class.getName(), + new SqlDateSerializer()); + + _staticDeserializerMap.put(java.sql.Date.class.getName(), + new SqlDateDeserializer(java.sql.Date.class)); + _staticDeserializerMap.put(java.sql.Time.class.getName(), + new SqlDateDeserializer(java.sql.Time.class)); + _staticDeserializerMap.put(java.sql.Timestamp.class.getName(), + new SqlDateDeserializer(java.sql.Timestamp.class)); + + // hessian/3bb5 + _staticDeserializerMap.put(StackTraceElement.class.getName(), + new StackTraceElementDeserializer()); + + ClassLoader systemClassLoader = null; + try { + systemClassLoader = ClassLoader.getSystemClassLoader(); + } catch (Exception e) { + } + + _systemClassLoader = systemClassLoader; + } +} + diff --git a/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/Deflation.java b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/Deflation.java new file mode 100644 index 0000000000..ccfd5e6a6f --- /dev/null +++ b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/Deflation.java @@ -0,0 +1,204 @@ +/* + * Copyright (c) 2001-2004 Caucho Technology, Inc. All rights reserved. + * + * The Apache Software License, Version 1.1 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Caucho Technology (http://www.caucho.com/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Hessian", "Resin", and "Caucho" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * info@caucho.com. + * + * 5. Products derived from this software may not be called "Resin" + * nor may "Resin" appear in their names without prior written + * permission of Caucho Technology. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Scott Ferguson + */ + +package com.caucho.hessian4.io; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.zip.DeflaterOutputStream; +import java.util.zip.InflaterInputStream; + +public class Deflation extends HessianEnvelope { + public Deflation() + { + } + + public Hessian2Output wrap(Hessian2Output out) + throws IOException + { + OutputStream os = new DeflateOutputStream(out); + + Hessian2Output filterOut = new Hessian2Output(os); + + filterOut.setCloseStreamOnClose(true); + + return filterOut; + } + + public Hessian2Input unwrap(Hessian2Input in) + throws IOException + { + int version = in.readEnvelope(); + + String method = in.readMethod(); + + if (! method.equals(getClass().getName())) + throw new IOException("expected hessian Envelope method '" + + getClass().getName() + "' at '" + method + "'"); + + return unwrapHeaders(in); + } + + public Hessian2Input unwrapHeaders(Hessian2Input in) + throws IOException + { + InputStream is = new DeflateInputStream(in); + + Hessian2Input filter = new Hessian2Input(is); + + filter.setCloseStreamOnClose(true); + + return filter; + } + + static class DeflateOutputStream extends OutputStream { + private Hessian2Output _out; + private OutputStream _bodyOut; + private DeflaterOutputStream _deflateOut; + + DeflateOutputStream(Hessian2Output out) + throws IOException + { + _out = out; + + _out.startEnvelope(Deflation.class.getName()); + + _out.writeInt(0); + + _bodyOut = _out.getBytesOutputStream(); + + _deflateOut = new DeflaterOutputStream(_bodyOut); + } + + public void write(int ch) + throws IOException + { + _deflateOut.write(ch); + } + + public void write(byte []buffer, int offset, int length) + throws IOException + { + _deflateOut.write(buffer, offset, length); + } + + public void close() + throws IOException + { + Hessian2Output out = _out; + _out = null; + + if (out != null) { + _deflateOut.close(); + _bodyOut.close(); + + out.writeInt(0); + + out.completeEnvelope(); + + out.close(); + } + } + } + + static class DeflateInputStream extends InputStream { + private Hessian2Input _in; + + private InputStream _bodyIn; + private InflaterInputStream _inflateIn; + + DeflateInputStream(Hessian2Input in) + throws IOException + { + _in = in; + + int len = in.readInt(); + + if (len != 0) + throw new IOException("expected no headers"); + + _bodyIn = _in.readInputStream(); + + _inflateIn = new InflaterInputStream(_bodyIn); + } + + public int read() + throws IOException + { + return _inflateIn.read(); + } + + public int read(byte []buffer, int offset, int length) + throws IOException + { + return _inflateIn.read(buffer, offset, length); + } + + public void close() + throws IOException + { + Hessian2Input in = _in; + _in = null; + + if (in != null) { + _inflateIn.close(); + _bodyIn.close(); + + int len = in.readInt(); + + if (len != 0) + throw new IOException("Unexpected footer"); + + in.completeEnvelope(); + + in.close(); + } + } + } +} diff --git a/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/Deserializer.java b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/Deserializer.java new file mode 100644 index 0000000000..06d81d6255 --- /dev/null +++ b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/Deserializer.java @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2001-2004 Caucho Technology, Inc. All rights reserved. + * + * The Apache Software License, Version 1.1 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Caucho Technology (http://www.caucho.com/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Burlap", "Resin", and "Caucho" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * info@caucho.com. + * + * 5. Products derived from this software may not be called "Resin" + * nor may "Resin" appear in their names without prior written + * permission of Caucho Technology. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Scott Ferguson + */ + +package com.caucho.hessian4.io; + +import java.io.IOException; + +/** + * Deserializing an object. Custom deserializers should extend + * from AbstractDeserializer to avoid issues with signature + * changes. + */ +public interface Deserializer { + public Class getType(); + + public boolean isReadResolve(); + + public Object readObject(AbstractHessianInput in) + throws IOException; + + public Object readList(AbstractHessianInput in, int length) + throws IOException; + + public Object readLengthList(AbstractHessianInput in, int length) + throws IOException; + + public Object readMap(AbstractHessianInput in) + throws IOException; + + /** + * Creates an empty array for the deserializers field + * entries. + * + * @param len number of fields to be read + * @return empty array of the proper field type. + */ + public Object []createFields(int len); + + /** + * Returns the deserializer's field reader for the given name. + * + * @param name the field name + * @return the deserializer's internal field reader + */ + public Object createField(String name); + + /** + * Reads the object from the input stream, given the field + * definition. + * + * @param in the input stream + * @param fields the deserializer's own field marshal + * @return the new object + * @throws IOException + */ + public Object readObject(AbstractHessianInput in, + Object []fields) + throws IOException; + + public Object readObject(AbstractHessianInput in, + String []fieldNames) + throws IOException; +} diff --git a/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/EnumDeserializer.java b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/EnumDeserializer.java new file mode 100644 index 0000000000..732c56d528 --- /dev/null +++ b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/EnumDeserializer.java @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2001-2004 Caucho Technology, Inc. All rights reserved. + * + * The Apache Software License, Version 1.1 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Caucho Technology (http://www.caucho.com/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Hessian", "Resin", and "Caucho" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * info@caucho.com. + * + * 5. Products derived from this software may not be called "Resin" + * nor may "Resin" appear in their names without prior written + * permission of Caucho Technology. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Scott Ferguson + */ + +package com.caucho.hessian4.io; + +import java.io.IOException; +import java.lang.reflect.Method; + +/** + * Deserializing an enum valued object + */ +public class EnumDeserializer extends AbstractDeserializer { + private Class _enumType; + private Method _valueOf; + + public EnumDeserializer(Class cl) + { + // hessian/33b[34], hessian/3bb[78] + if (cl.isEnum()) + _enumType = cl; + else if (cl.getSuperclass().isEnum()) + _enumType = cl.getSuperclass(); + else + throw new RuntimeException("Class " + cl.getName() + " is not an enum"); + + try { + _valueOf = _enumType.getMethod("valueOf", + new Class[] { Class.class, String.class }); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public Class getType() + { + return _enumType; + } + + public Object readMap(AbstractHessianInput in) + throws IOException + { + String name = null; + + while (! in.isEnd()) { + String key = in.readString(); + + if (key.equals("name")) + name = in.readString(); + else + in.readObject(); + } + + in.readMapEnd(); + + Object obj = create(name); + + in.addRef(obj); + + return obj; + } + + @Override + public Object readObject(AbstractHessianInput in, Object []fields) + throws IOException + { + String []fieldNames = (String []) fields; + String name = null; + + for (int i = 0; i < fieldNames.length; i++) { + if ("name".equals(fieldNames[i])) + name = in.readString(); + else + in.readObject(); + } + + Object obj = create(name); + + in.addRef(obj); + + return obj; + } + + private Object create(String name) + throws IOException + { + if (name == null) + throw new IOException(_enumType.getName() + " expects name."); + + try { + return _valueOf.invoke(null, _enumType, name); + } catch (Exception e) { + throw new IOExceptionWrapper(e); + } + } +} diff --git a/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/EnumSerializer.java b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/EnumSerializer.java new file mode 100644 index 0000000000..770a0230b3 --- /dev/null +++ b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/EnumSerializer.java @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2001-2004 Caucho Technology, Inc. All rights reserved. + * + * The Apache Software License, Version 1.1 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Caucho Technology (http://www.caucho.com/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Burlap", "Resin", and "Caucho" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * info@caucho.com. + * + * 5. Products derived from this software may not be called "Resin" + * nor may "Resin" appear in their names without prior written + * permission of Caucho Technology. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Scott Ferguson + */ + +package com.caucho.hessian4.io; + +import java.io.IOException; +import java.lang.reflect.Method; + +/** + * Serializing an object for known object types. + */ +public class EnumSerializer extends AbstractSerializer { + private Method _name; + + public EnumSerializer(Class cl) + { + // hessian/32b[12], hessian/3ab[23] + if (! cl.isEnum() && cl.getSuperclass().isEnum()) + cl = cl.getSuperclass(); + + try { + _name = cl.getMethod("name", new Class[0]); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public void writeObject(Object obj, AbstractHessianOutput out) + throws IOException + { + if (out.addRef(obj)) + return; + + Class cl = obj.getClass(); + + if (! cl.isEnum() && cl.getSuperclass().isEnum()) + cl = cl.getSuperclass(); + + String name = null; + try { + name = (String) _name.invoke(obj, (Object[]) null); + } catch (Exception e) { + throw new RuntimeException(e); + } + + int ref = out.writeObjectBegin(cl.getName()); + + if (ref < -1) { + out.writeString("name"); + out.writeString(name); + out.writeMapEnd(); + } + else { + if (ref == -1) { + out.writeClassFieldLength(1); + out.writeString("name"); + out.writeObjectBegin(cl.getName()); + } + + out.writeString(name); + } + } +} diff --git a/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/EnumerationDeserializer.java b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/EnumerationDeserializer.java new file mode 100644 index 0000000000..ecaf8e4151 --- /dev/null +++ b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/EnumerationDeserializer.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2001-2004 Caucho Technology, Inc. All rights reserved. + * + * The Apache Software License, Version 1.1 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Caucho Technology (http://www.caucho.com/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Hessian", "Resin", and "Caucho" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * info@caucho.com. + * + * 5. Products derived from this software may not be called "Resin" + * nor may "Resin" appear in their names without prior written + * permission of Caucho Technology. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Scott Ferguson + */ + +package com.caucho.hessian4.io; + +import java.io.IOException; +import java.util.Vector; + +/** + * Deserializing a JDK 1.2 Collection. + */ +public class EnumerationDeserializer extends AbstractListDeserializer { + private static EnumerationDeserializer _deserializer; + + public static EnumerationDeserializer create() + { + if (_deserializer == null) + _deserializer = new EnumerationDeserializer(); + + return _deserializer; + } + + public Object readList(AbstractHessianInput in, int length) + throws IOException + { + Vector list = new Vector(); + + in.addRef(list); + + while (! in.isEnd()) + list.add(in.readObject()); + + in.readEnd(); + + return list.elements(); + } +} + + diff --git a/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/EnumerationSerializer.java b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/EnumerationSerializer.java new file mode 100644 index 0000000000..56f0993e8b --- /dev/null +++ b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/EnumerationSerializer.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2001-2004 Caucho Technology, Inc. All rights reserved. + * + * The Apache Software License, Version 1.1 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Caucho Technology (http://www.caucho.com/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Hessian", "Resin", and "Caucho" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * info@caucho.com. + * + * 5. Products derived from this software may not be called "Resin" + * nor may "Resin" appear in their names without prior written + * permission of Caucho Technology. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Scott Ferguson + */ + +package com.caucho.hessian4.io; + +import java.io.IOException; +import java.util.Enumeration; + +/** + * Serializing a JDK 1.2 Enumeration. + */ +public class EnumerationSerializer extends AbstractSerializer { + private static EnumerationSerializer _serializer; + + public static EnumerationSerializer create() + { + if (_serializer == null) + _serializer = new EnumerationSerializer(); + + return _serializer; + } + + public void writeObject(Object obj, AbstractHessianOutput out) + throws IOException + { + Enumeration iter = (Enumeration) obj; + + boolean hasEnd = out.writeListBegin(-1, null); + + while (iter.hasMoreElements()) { + Object value = iter.nextElement(); + + out.writeObject(value); + } + + if (hasEnd) + out.writeListEnd(); + } +} diff --git a/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/EnvelopeFactory.java b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/EnvelopeFactory.java new file mode 100644 index 0000000000..39a9c9f3df --- /dev/null +++ b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/EnvelopeFactory.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2001-2004 Caucho Technology, Inc. All rights reserved. + * + * The Apache Software License, Version 1.1 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Caucho Technology (http://www.caucho.com/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Hessian", "Resin", and "Caucho" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * info@caucho.com. + * + * 5. Products derived from this software may not be called "Resin" + * nor may "Resin" appear in their names without prior written + * permission of Caucho Technology. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Scott Ferguson + */ + +package com.caucho.hessian4.io; + +import java.util.logging.Logger; + +public class EnvelopeFactory +{ + private static final Logger log + = Logger.getLogger(EnvelopeFactory.class.getName()); +} diff --git a/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/ExtSerializerFactory.java b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/ExtSerializerFactory.java new file mode 100644 index 0000000000..95fc0813d2 --- /dev/null +++ b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/ExtSerializerFactory.java @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2001-2004 Caucho Technology, Inc. All rights reserved. + * + * The Apache Software License, Version 1.1 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Caucho Technology (http://www.caucho.com/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Burlap", "Resin", and "Caucho" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * info@caucho.com. + * + * 5. Products derived from this software may not be called "Resin" + * nor may "Resin" appear in their names without prior written + * permission of Caucho Technology. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Scott Ferguson + */ + +package com.caucho.hessian4.io; + +import java.util.HashMap; + +/** + * Factory for returning serialization methods. + */ +public class ExtSerializerFactory extends AbstractSerializerFactory { + private HashMap _serializerMap = new HashMap(); + private HashMap _deserializerMap = new HashMap(); + + /** + * Adds a serializer. + * + * @param cl the class of the serializer + * @param serializer the serializer + */ + public void addSerializer(Class cl, Serializer serializer) + { + _serializerMap.put(cl, serializer); + } + + /** + * Adds a deserializer. + * + * @param cl the class of the deserializer + * @param deserializer the deserializer + */ + public void addDeserializer(Class cl, Deserializer deserializer) + { + _deserializerMap.put(cl, deserializer); + } + + /** + * Returns the serializer for a class. + * + * @param cl the class of the object that needs to be serialized. + * + * @return a serializer object for the serialization. + */ + public Serializer getSerializer(Class cl) + throws HessianProtocolException + { + return (Serializer) _serializerMap.get(cl); + } + + /** + * Returns the deserializer for a class. + * + * @param cl the class of the object that needs to be deserialized. + * + * @return a deserializer object for the serialization. + */ + public Deserializer getDeserializer(Class cl) + throws HessianProtocolException + { + return (Deserializer) _deserializerMap.get(cl); + } +} diff --git a/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/FileDeserializer.java b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/FileDeserializer.java new file mode 100644 index 0000000000..0948750a61 --- /dev/null +++ b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/FileDeserializer.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2001-2004 Caucho Technology, Inc. All rights reserved. + * + * The Apache Software License, Version 1.1 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Caucho Technology (http://www.caucho.com/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Hessian", "Resin", and "Caucho" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * info@caucho.com. + * + * 5. Products derived from this software may not be called "Resin" + * nor may "Resin" appear in their names without prior written + * permission of Caucho Technology. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Scott Ferguson + */ + +package com.caucho.hessian4.io; + +import java.io.File; + +/** + * Deserializing a File + */ +public class FileDeserializer extends AbstractStringValueDeserializer { + @Override + public Class getType() + { + return File.class; + } + + @Override + protected Object create(String value) + { + return new File(value); + } +} diff --git a/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/FloatHandle.java b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/FloatHandle.java new file mode 100644 index 0000000000..f79821c6b9 --- /dev/null +++ b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/FloatHandle.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2001-2004 Caucho Technology, Inc. All rights reserved. + * + * The Apache Software License, Version 1.1 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Caucho Technology (http://www.caucho.com/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Burlap", "Resin", and "Caucho" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * info@caucho.com. + * + * 5. Products derived from this software may not be called "Resin" + * nor may "Resin" appear in their names without prior written + * permission of Caucho Technology. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Scott Ferguson + */ + +package com.caucho.hessian4.io; + +import java.io.Serializable; + +/** + * Handle for Java Float objects. + */ +public class FloatHandle implements Serializable { + private float _value; + + private FloatHandle() + { + } + + public FloatHandle(float value) + { + _value = value; + } + + public float getValue() + { + return _value; + } + + public Object readResolve() + { + return new Float(_value); + } + + public String toString() + { + return getClass().getSimpleName() + "[" + _value + "]"; + } +} diff --git a/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/FlushableOutput.java b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/FlushableOutput.java new file mode 100644 index 0000000000..6622542603 --- /dev/null +++ b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/FlushableOutput.java @@ -0,0 +1,12 @@ +package com.caucho.hessian4.io; + +import java.io.IOException; + +import org.jboss.netty.channel.ChannelFuture; + +public interface FlushableOutput +{ + public void flush(ChannelFuture future) throws IOException; + public void reset(); + +} diff --git a/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/Hessian2Constants.java b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/Hessian2Constants.java new file mode 100644 index 0000000000..6e7ee88ec7 --- /dev/null +++ b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/Hessian2Constants.java @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2001-2008 Caucho Technology, Inc. All rights reserved. + * + * The Apache Software License, Version 1.1 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Caucho Technology (http://www.caucho.com/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Burlap", "Resin", and "Caucho" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * info@caucho.com. + * + * 5. Products derived from this software may not be called "Resin" + * nor may "Resin" appear in their names without prior written + * permission of Caucho Technology. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Scott Ferguson + */ + +package com.caucho.hessian4.io; + +public interface Hessian2Constants +{ + public static final int BC_BINARY = 'B'; // final chunk + public static final int BC_BINARY_CHUNK = 'A'; // non-final chunk + public static final int BC_BINARY_DIRECT = 0x20; // 1-byte length binary + public static final int BINARY_DIRECT_MAX = 0x0f; + public static final int BC_BINARY_SHORT = 0x34; // 2-byte length binary + public static final int BINARY_SHORT_MAX = 0x3ff; // 0-1023 binary + + public static final int BC_CLASS_DEF = 'C'; // object/class definition + + public static final int BC_DATE = 0x4a; // 64-bit millisecond UTC date + public static final int BC_DATE_MINUTE = 0x4b; // 32-bit minute UTC date + + public static final int BC_DOUBLE = 'D'; // IEEE 64-bit double + + public static final int BC_DOUBLE_ZERO = 0x5b; + public static final int BC_DOUBLE_ONE = 0x5c; + public static final int BC_DOUBLE_BYTE = 0x5d; + public static final int BC_DOUBLE_SHORT = 0x5e; + public static final int BC_DOUBLE_MILL = 0x5f; + + public static final int BC_FALSE = 'F'; // boolean false + + public static final int BC_INT = 'I'; // 32-bit int + + public static final int INT_DIRECT_MIN = -0x10; + public static final int INT_DIRECT_MAX = 0x2f; + public static final int BC_INT_ZERO = 0x90; + + public static final int INT_BYTE_MIN = -0x800; + public static final int INT_BYTE_MAX = 0x7ff; + public static final int BC_INT_BYTE_ZERO = 0xc8; + + public static final int BC_END = 'Z'; + + public static final int INT_SHORT_MIN = -0x40000; + public static final int INT_SHORT_MAX = 0x3ffff; + public static final int BC_INT_SHORT_ZERO = 0xd4; + + public static final int BC_LIST_VARIABLE =0x55; + public static final int BC_LIST_FIXED = 'V'; + public static final int BC_LIST_VARIABLE_UNTYPED = 0x57; + public static final int BC_LIST_FIXED_UNTYPED =0x58; + + public static final int BC_LIST_DIRECT = 0x70; + public static final int BC_LIST_DIRECT_UNTYPED = 0x78; + public static final int LIST_DIRECT_MAX = 0x7; + + public static final int BC_LONG = 'L'; // 64-bit signed integer + public static final long LONG_DIRECT_MIN = -0x08; + public static final long LONG_DIRECT_MAX = 0x0f; + public static final int BC_LONG_ZERO = 0xe0; + + public static final long LONG_BYTE_MIN = -0x800; + public static final long LONG_BYTE_MAX = 0x7ff; + public static final int BC_LONG_BYTE_ZERO = 0xf8; + + public static final int LONG_SHORT_MIN = -0x40000; + public static final int LONG_SHORT_MAX = 0x3ffff; + public static final int BC_LONG_SHORT_ZERO = 0x3c; + + public static final int BC_LONG_INT = 0x59; + + public static final int BC_MAP = 'M'; + public static final int BC_MAP_UNTYPED = 'H'; + + public static final int BC_NULL = 'N'; + + public static final int BC_OBJECT = 'O'; + public static final int BC_OBJECT_DEF = 'C'; + + public static final int BC_OBJECT_DIRECT = 0x60; + public static final int OBJECT_DIRECT_MAX = 0x0f; + + public static final int BC_REF = 0x51; + + public static final int BC_STRING = 'S'; // final string + public static final int BC_STRING_CHUNK = 'R'; // non-final string + + public static final int BC_STRING_DIRECT = 0x00; + public static final int STRING_DIRECT_MAX = 0x1f; + public static final int BC_STRING_SHORT = 0x30; + public static final int STRING_SHORT_MAX = 0x3ff; + + public static final int BC_TRUE = 'T'; + + public static final int P_PACKET_CHUNK = 0x4f; + public static final int P_PACKET = 'P'; + + public static final int P_PACKET_DIRECT = 0x80; + public static final int PACKET_DIRECT_MAX = 0x7f; + + public static final int P_PACKET_SHORT = 0x70; + public static final int PACKET_SHORT_MAX = 0xfff; +} diff --git a/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/Hessian2Input.java b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/Hessian2Input.java new file mode 100644 index 0000000000..45a5d037f7 --- /dev/null +++ b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/Hessian2Input.java @@ -0,0 +1,2984 @@ +/* + * Copyright (c) 2001-2008 Caucho Technology, Inc. All rights reserved. + * + * The Apache Software License, Version 1.1 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Caucho Technology (http://www.caucho.com/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Hessian", "Resin", and "Caucho" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * info@caucho.com. + * + * 5. Products derived from this software may not be called "Resin" + * nor may "Resin" appear in their names without prior written + * permission of Caucho Technology. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Scott Ferguson + */ + +package com.caucho.hessian4.io; + +import java.io.ByteArrayOutputStream; +import java.io.EOFException; +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Input stream for Hessian requests. + * + *

    HessianInput is unbuffered, so any client needs to provide + * its own buffering. + * + *

    + * InputStream is = ...; // from http connection
    + * HessianInput in = new HessianInput(is);
    + * String value;
    + *
    + * in.startReply();         // read reply header
    + * value = in.readString(); // read string value
    + * in.completeReply();      // read reply footer
    + * 
    + */ +public class Hessian2Input + extends AbstractHessianInput + implements Hessian2Constants +{ + private static final Logger log + = Logger.getLogger(Hessian2Input.class.getName()); + + private static final int END_OF_DATA = -2; + + private static Field _detailMessageField; + + private static final int SIZE = 1024*1024; + private static final int GAP = 16; + + // standard, unmodified factory for deserializing objects + protected SerializerFactory _defaultSerializerFactory; + // factory for deserializing objects in the input stream + protected SerializerFactory _serializerFactory; + + private static boolean _isCloseStreamOnClose; + + protected ArrayList _refs + = new ArrayList(); + protected ArrayList _classDefs + = new ArrayList(); + protected ArrayList _types + = new ArrayList(); + + // the underlying input stream + volatile protected InputStream _is; + private final byte []_buffer = new byte[SIZE]; + + // a peek character + protected int _offset; + protected int _length; + + // the method for a call + private String _method; + private Throwable _replyFault; + + private StringBuffer _sbuf = new StringBuffer(); + + // true if this is the last chunk + private boolean _isLastChunk; + // the chunk length + private int _chunkLength; + + /** + * Creates a new Hessian input stream, initialized with an + * underlying input stream. + * + * @param is the underlying input stream. + */ + public Hessian2Input(InputStream is) + { + _is = is; + } + + /** + * Sets the serializer factory. + */ + public void setSerializerFactory(SerializerFactory factory) + { + _serializerFactory = factory; + } + + /** + * Gets the serializer factory. + */ + public SerializerFactory getSerializerFactory() + { + // the default serializer factory cannot be modified by external + // callers + if (_serializerFactory == _defaultSerializerFactory) { + _serializerFactory = new SerializerFactory(); + } + + return _serializerFactory; + } + + /** + * Gets the serializer factory. + */ + protected final SerializerFactory findSerializerFactory() + { + SerializerFactory factory = _serializerFactory; + + if (factory == null) { + factory = SerializerFactory.createDefault(); + _defaultSerializerFactory = factory; + _serializerFactory = factory; + } + + return factory; + } + + public void setCloseStreamOnClose(boolean isClose) + { + _isCloseStreamOnClose = isClose; + } + + public boolean isCloseStreamOnClose() + { + return _isCloseStreamOnClose; + } + + /** + * Returns the calls method + */ + public String getMethod() + { + return _method; + } + + /** + * Returns any reply fault. + */ + public Throwable getReplyFault() + { + return _replyFault; + } + + /** + * Starts reading the call + * + *
    +   * c major minor
    +   * 
    + */ + public int readCall() + throws IOException + { + int tag = read(); + + if (tag != 'C') + throw error("expected hessian call ('C') at " + codeName(tag)); + + return 0; + } + + /** + * Starts reading the envelope + * + *
    +   * E major minor
    +   * 
    + */ + public int readEnvelope() + throws IOException + { + int tag = read(); + int version = 0; + + if (tag == 'H') { + int major = read(); + int minor = read(); + + version = (major << 16) + minor; + + tag = read(); + } + + if (tag != 'E') + throw error("expected hessian Envelope ('E') at " + codeName(tag)); + + return version; + } + + /** + * Completes reading the envelope + * + *

    A successful completion will have a single value: + * + *

    +   * Z
    +   * 
    + */ + public void completeEnvelope() + throws IOException + { + int tag = read(); + + if (tag != 'Z') + error("expected end of envelope at " + codeName(tag)); + } + + /** + * Starts reading the call + * + *

    A successful completion will have a single value: + * + *

    +   * string
    +   * 
    + */ + public String readMethod() + throws IOException + { + _method = readString(); + + return _method; + } + + /** + * Returns the number of method arguments + * + *
    +   * int
    +   * 
    + */ + @Override + public int readMethodArgLength() + throws IOException + { + return readInt(); + } + + /** + * Starts reading the call, including the headers. + * + *

    The call expects the following protocol data + * + *

    +   * c major minor
    +   * m b16 b8 method
    +   * 
    + */ + public void startCall() + throws IOException + { + readCall(); + + readMethod(); + } + + public Object []readArguments() + throws IOException + { + int len = readInt(); + + Object []args = new Object[len]; + + for (int i = 0; i < len; i++) + args[i] = readObject(); + + return args; + } + + /** + * Completes reading the call + * + *

    A successful completion will have a single value: + * + *

    +   * 
    + */ + public void completeCall() + throws IOException + { + } + + /** + * Reads a reply as an object. + * If the reply has a fault, throws the exception. + */ + @Override + public Object readReply(Class expectedClass) + throws Throwable + { + int tag = read(); + + if (tag == 'R') + return readObject(expectedClass); + else if (tag == 'F') { + HashMap map = (HashMap) readObject(HashMap.class); + + throw prepareFault(map); + } + else { + StringBuilder sb = new StringBuilder(); + sb.append((char) tag); + + try { + int ch; + + while ((ch = read()) >= 0) { + sb.append((char) ch); + } + } catch (IOException e) { + log.log(Level.FINE, e.toString(), e); + } + + throw error("expected hessian reply at " + codeName(tag) + "\n" + + sb); + } + } + + /** + * Starts reading the reply + * + *

    A successful completion will have a single value: + * + *

    +   * r
    +   * 
    + */ + public void startReply() + throws Throwable + { + // XXX: for variable length (?) + + readReply(Object.class); + } + + /** + * Prepares the fault. + */ + private Throwable prepareFault(HashMap fault) + throws IOException + { + Object detail = fault.get("detail"); + String message = (String) fault.get("message"); + + if (detail instanceof Throwable) { + _replyFault = (Throwable) detail; + + if (message != null && _detailMessageField != null) { + try { + _detailMessageField.set(_replyFault, message); + } catch (Throwable e) { + } + } + + return _replyFault; + } + + else { + String code = (String) fault.get("code"); + + _replyFault = new HessianServiceException(message, code, detail); + + return _replyFault; + } + } + + /** + * Completes reading the call + * + *

    A successful completion will have a single value: + * + *

    +   * z
    +   * 
    + */ + public void completeReply() + throws IOException + { + } + + /** + * Completes reading the call + * + *

    A successful completion will have a single value: + * + *

    +   * z
    +   * 
    + */ + public void completeValueReply() + throws IOException + { + int tag = read(); + + if (tag != 'Z') + error("expected end of reply at " + codeName(tag)); + } + + /** + * Reads a header, returning null if there are no headers. + * + *
    +   * H b16 b8 value
    +   * 
    + */ + public String readHeader() + throws IOException + { + return null; + } + + /** + * Starts reading a packet + * + *
    +   * p major minor
    +   * 
    + */ + public int startMessage() + throws IOException + { + int tag = read(); + + if (tag == 'p') { + } else if (tag == 'P') { + } else + throw error("expected Hessian message ('p') at " + codeName(tag)); + + int major = read(); + int minor = read(); + + return (major << 16) + minor; + } + + /** + * Completes reading the message + * + *

    A successful completion will have a single value: + * + *

    +   * z
    +   * 
    + */ + public void completeMessage() + throws IOException + { + int tag = read(); + + if (tag != 'Z') + error("expected end of message at " + codeName(tag)); + } + + /** + * Reads a null + * + *
    +   * N
    +   * 
    + */ + public void readNull() + throws IOException + { + int tag = read(); + + switch (tag) { + case 'N': return; + + default: + throw expect("null", tag); + } + } + + /** + * Reads a boolean + * + *
    +   * T
    +   * F
    +   * 
    + */ + public boolean readBoolean() + throws IOException + { + int tag = _offset < _length ? (_buffer[_offset++] & 0xff) : read(); + + switch (tag) { + case 'T': return true; + case 'F': return false; + + // direct integer + case 0x80: case 0x81: case 0x82: case 0x83: + case 0x84: case 0x85: case 0x86: case 0x87: + case 0x88: case 0x89: case 0x8a: case 0x8b: + case 0x8c: case 0x8d: case 0x8e: case 0x8f: + + case 0x90: case 0x91: case 0x92: case 0x93: + case 0x94: case 0x95: case 0x96: case 0x97: + case 0x98: case 0x99: case 0x9a: case 0x9b: + case 0x9c: case 0x9d: case 0x9e: case 0x9f: + + case 0xa0: case 0xa1: case 0xa2: case 0xa3: + case 0xa4: case 0xa5: case 0xa6: case 0xa7: + case 0xa8: case 0xa9: case 0xaa: case 0xab: + case 0xac: case 0xad: case 0xae: case 0xaf: + + case 0xb0: case 0xb1: case 0xb2: case 0xb3: + case 0xb4: case 0xb5: case 0xb6: case 0xb7: + case 0xb8: case 0xb9: case 0xba: case 0xbb: + case 0xbc: case 0xbd: case 0xbe: case 0xbf: + return tag != BC_INT_ZERO; + + // INT_BYTE = 0 + case 0xc8: + return read() != 0; + + // INT_BYTE != 0 + case 0xc0: case 0xc1: case 0xc2: case 0xc3: + case 0xc4: case 0xc5: case 0xc6: case 0xc7: + case 0xc9: case 0xca: case 0xcb: + case 0xcc: case 0xcd: case 0xce: case 0xcf: + read(); + return true; + + // INT_SHORT = 0 + case 0xd4: + return (256 * read() + read()) != 0; + + // INT_SHORT != 0 + case 0xd0: case 0xd1: case 0xd2: case 0xd3: + case 0xd5: case 0xd6: case 0xd7: + read(); + read(); + return true; + + case 'I': return + parseInt() != 0; + + case 0xd8: case 0xd9: case 0xda: case 0xdb: + case 0xdc: case 0xdd: case 0xde: case 0xdf: + + case 0xe0: case 0xe1: case 0xe2: case 0xe3: + case 0xe4: case 0xe5: case 0xe6: case 0xe7: + case 0xe8: case 0xe9: case 0xea: case 0xeb: + case 0xec: case 0xed: case 0xee: case 0xef: + return tag != BC_LONG_ZERO; + + // LONG_BYTE = 0 + case 0xf8: + return read() != 0; + + // LONG_BYTE != 0 + case 0xf0: case 0xf1: case 0xf2: case 0xf3: + case 0xf4: case 0xf5: case 0xf6: case 0xf7: + case 0xf9: case 0xfa: case 0xfb: + case 0xfc: case 0xfd: case 0xfe: case 0xff: + read(); + return true; + + // INT_SHORT = 0 + case 0x3c: + return (256 * read() + read()) != 0; + + // INT_SHORT != 0 + case 0x38: case 0x39: case 0x3a: case 0x3b: + case 0x3d: case 0x3e: case 0x3f: + read(); + read(); + return true; + + case BC_LONG_INT: + return (0x1000000L * read() + + 0x10000L * read() + + 0x100 * read() + + read()) != 0; + + case 'L': + return parseLong() != 0; + + case BC_DOUBLE_ZERO: + return false; + + case BC_DOUBLE_ONE: + return true; + + case BC_DOUBLE_BYTE: + return read() != 0; + + case BC_DOUBLE_SHORT: + return (0x100 * read() + read()) != 0; + + case BC_DOUBLE_MILL: + { + int mills = parseInt(); + + return mills != 0; + } + + case 'D': + return parseDouble() != 0.0; + + case 'N': + return false; + + default: + throw expect("boolean", tag); + } + } + + /** + * Reads a short + * + *
    +   * I b32 b24 b16 b8
    +   * 
    + */ + public short readShort() + throws IOException + { + return (short) readInt(); + } + + /** + * Reads an integer + * + *
    +   * I b32 b24 b16 b8
    +   * 
    + */ + public final int readInt() + throws IOException + { + //int tag = _offset < _length ? (_buffer[_offset++] & 0xff) : read(); + int tag = read(); + + switch (tag) { + case 'N': + return 0; + + case 'F': + return 0; + + case 'T': + return 1; + + // direct integer + case 0x80: case 0x81: case 0x82: case 0x83: + case 0x84: case 0x85: case 0x86: case 0x87: + case 0x88: case 0x89: case 0x8a: case 0x8b: + case 0x8c: case 0x8d: case 0x8e: case 0x8f: + + case 0x90: case 0x91: case 0x92: case 0x93: + case 0x94: case 0x95: case 0x96: case 0x97: + case 0x98: case 0x99: case 0x9a: case 0x9b: + case 0x9c: case 0x9d: case 0x9e: case 0x9f: + + case 0xa0: case 0xa1: case 0xa2: case 0xa3: + case 0xa4: case 0xa5: case 0xa6: case 0xa7: + case 0xa8: case 0xa9: case 0xaa: case 0xab: + case 0xac: case 0xad: case 0xae: case 0xaf: + + case 0xb0: case 0xb1: case 0xb2: case 0xb3: + case 0xb4: case 0xb5: case 0xb6: case 0xb7: + case 0xb8: case 0xb9: case 0xba: case 0xbb: + case 0xbc: case 0xbd: case 0xbe: case 0xbf: + return tag - BC_INT_ZERO; + + /* byte int */ + case 0xc0: case 0xc1: case 0xc2: case 0xc3: + case 0xc4: case 0xc5: case 0xc6: case 0xc7: + case 0xc8: case 0xc9: case 0xca: case 0xcb: + case 0xcc: case 0xcd: case 0xce: case 0xcf: + return ((tag - BC_INT_BYTE_ZERO) << 8) + read(); + + /* short int */ + case 0xd0: case 0xd1: case 0xd2: case 0xd3: + case 0xd4: case 0xd5: case 0xd6: case 0xd7: + return ((tag - BC_INT_SHORT_ZERO) << 16) + 256 * read() + read(); + + case 'I': + case BC_LONG_INT: + return ((read() << 24) + + (read() << 16) + + (read() << 8) + + read()); + + // direct long + case 0xd8: case 0xd9: case 0xda: case 0xdb: + case 0xdc: case 0xdd: case 0xde: case 0xdf: + + case 0xe0: case 0xe1: case 0xe2: case 0xe3: + case 0xe4: case 0xe5: case 0xe6: case 0xe7: + case 0xe8: case 0xe9: case 0xea: case 0xeb: + case 0xec: case 0xed: case 0xee: case 0xef: + return tag - BC_LONG_ZERO; + + /* byte long */ + case 0xf0: case 0xf1: case 0xf2: case 0xf3: + case 0xf4: case 0xf5: case 0xf6: case 0xf7: + case 0xf8: case 0xf9: case 0xfa: case 0xfb: + case 0xfc: case 0xfd: case 0xfe: case 0xff: + return ((tag - BC_LONG_BYTE_ZERO) << 8) + read(); + + /* short long */ + case 0x38: case 0x39: case 0x3a: case 0x3b: + case 0x3c: case 0x3d: case 0x3e: case 0x3f: + return ((tag - BC_LONG_SHORT_ZERO) << 16) + 256 * read() + read(); + + case 'L': + return (int) parseLong(); + + case BC_DOUBLE_ZERO: + return 0; + + case BC_DOUBLE_ONE: + return 1; + + //case LONG_BYTE: + case BC_DOUBLE_BYTE: + return (byte) (_offset < _length ? _buffer[_offset++] : read()); + + //case INT_SHORT: + //case LONG_SHORT: + case BC_DOUBLE_SHORT: + return (short) (256 * read() + read()); + + case BC_DOUBLE_MILL: + { + int mills = parseInt(); + + return (int) (0.001 * mills); + } + + case 'D': + return (int) parseDouble(); + + default: + throw expect("integer", tag); + } + } + + /** + * Reads a long + * + *
    +   * L b64 b56 b48 b40 b32 b24 b16 b8
    +   * 
    + */ + public long readLong() + throws IOException + { + int tag = read(); + + switch (tag) { + case 'N': + return 0; + + case 'F': + return 0; + + case 'T': + return 1; + + // direct integer + case 0x80: case 0x81: case 0x82: case 0x83: + case 0x84: case 0x85: case 0x86: case 0x87: + case 0x88: case 0x89: case 0x8a: case 0x8b: + case 0x8c: case 0x8d: case 0x8e: case 0x8f: + + case 0x90: case 0x91: case 0x92: case 0x93: + case 0x94: case 0x95: case 0x96: case 0x97: + case 0x98: case 0x99: case 0x9a: case 0x9b: + case 0x9c: case 0x9d: case 0x9e: case 0x9f: + + case 0xa0: case 0xa1: case 0xa2: case 0xa3: + case 0xa4: case 0xa5: case 0xa6: case 0xa7: + case 0xa8: case 0xa9: case 0xaa: case 0xab: + case 0xac: case 0xad: case 0xae: case 0xaf: + + case 0xb0: case 0xb1: case 0xb2: case 0xb3: + case 0xb4: case 0xb5: case 0xb6: case 0xb7: + case 0xb8: case 0xb9: case 0xba: case 0xbb: + case 0xbc: case 0xbd: case 0xbe: case 0xbf: + return tag - BC_INT_ZERO; + + /* byte int */ + case 0xc0: case 0xc1: case 0xc2: case 0xc3: + case 0xc4: case 0xc5: case 0xc6: case 0xc7: + case 0xc8: case 0xc9: case 0xca: case 0xcb: + case 0xcc: case 0xcd: case 0xce: case 0xcf: + return ((tag - BC_INT_BYTE_ZERO) << 8) + read(); + + /* short int */ + case 0xd0: case 0xd1: case 0xd2: case 0xd3: + case 0xd4: case 0xd5: case 0xd6: case 0xd7: + return ((tag - BC_INT_SHORT_ZERO) << 16) + 256 * read() + read(); + + //case LONG_BYTE: + case BC_DOUBLE_BYTE: + return (byte) (_offset < _length ? _buffer[_offset++] : read()); + + //case INT_SHORT: + //case LONG_SHORT: + case BC_DOUBLE_SHORT: + return (short) (256 * read() + read()); + + case 'I': + case BC_LONG_INT: + return parseInt(); + + // direct long + case 0xd8: case 0xd9: case 0xda: case 0xdb: + case 0xdc: case 0xdd: case 0xde: case 0xdf: + + case 0xe0: case 0xe1: case 0xe2: case 0xe3: + case 0xe4: case 0xe5: case 0xe6: case 0xe7: + case 0xe8: case 0xe9: case 0xea: case 0xeb: + case 0xec: case 0xed: case 0xee: case 0xef: + return tag - BC_LONG_ZERO; + + /* byte long */ + case 0xf0: case 0xf1: case 0xf2: case 0xf3: + case 0xf4: case 0xf5: case 0xf6: case 0xf7: + case 0xf8: case 0xf9: case 0xfa: case 0xfb: + case 0xfc: case 0xfd: case 0xfe: case 0xff: + return ((tag - BC_LONG_BYTE_ZERO) << 8) + read(); + + /* short long */ + case 0x38: case 0x39: case 0x3a: case 0x3b: + case 0x3c: case 0x3d: case 0x3e: case 0x3f: + return ((tag - BC_LONG_SHORT_ZERO) << 16) + 256 * read() + read(); + + case 'L': + return parseLong(); + + case BC_DOUBLE_ZERO: + return 0; + + case BC_DOUBLE_ONE: + return 1; + + case BC_DOUBLE_MILL: + { + int mills = parseInt(); + + return (long) (0.001 * mills); + } + + case 'D': + return (long) parseDouble(); + + default: + throw expect("long", tag); + } + } + + /** + * Reads a float + * + *
    +   * D b64 b56 b48 b40 b32 b24 b16 b8
    +   * 
    + */ + public float readFloat() + throws IOException + { + return (float) readDouble(); + } + + /** + * Reads a double + * + *
    +   * D b64 b56 b48 b40 b32 b24 b16 b8
    +   * 
    + */ + public double readDouble() + throws IOException + { + int tag = read(); + + switch (tag) { + case 'N': + return 0; + + case 'F': + return 0; + + case 'T': + return 1; + + // direct integer + case 0x80: case 0x81: case 0x82: case 0x83: + case 0x84: case 0x85: case 0x86: case 0x87: + case 0x88: case 0x89: case 0x8a: case 0x8b: + case 0x8c: case 0x8d: case 0x8e: case 0x8f: + + case 0x90: case 0x91: case 0x92: case 0x93: + case 0x94: case 0x95: case 0x96: case 0x97: + case 0x98: case 0x99: case 0x9a: case 0x9b: + case 0x9c: case 0x9d: case 0x9e: case 0x9f: + + case 0xa0: case 0xa1: case 0xa2: case 0xa3: + case 0xa4: case 0xa5: case 0xa6: case 0xa7: + case 0xa8: case 0xa9: case 0xaa: case 0xab: + case 0xac: case 0xad: case 0xae: case 0xaf: + + case 0xb0: case 0xb1: case 0xb2: case 0xb3: + case 0xb4: case 0xb5: case 0xb6: case 0xb7: + case 0xb8: case 0xb9: case 0xba: case 0xbb: + case 0xbc: case 0xbd: case 0xbe: case 0xbf: + return tag - 0x90; + + /* byte int */ + case 0xc0: case 0xc1: case 0xc2: case 0xc3: + case 0xc4: case 0xc5: case 0xc6: case 0xc7: + case 0xc8: case 0xc9: case 0xca: case 0xcb: + case 0xcc: case 0xcd: case 0xce: case 0xcf: + return ((tag - BC_INT_BYTE_ZERO) << 8) + read(); + + /* short int */ + case 0xd0: case 0xd1: case 0xd2: case 0xd3: + case 0xd4: case 0xd5: case 0xd6: case 0xd7: + return ((tag - BC_INT_SHORT_ZERO) << 16) + 256 * read() + read(); + + case 'I': + case BC_LONG_INT: + return parseInt(); + + // direct long + case 0xd8: case 0xd9: case 0xda: case 0xdb: + case 0xdc: case 0xdd: case 0xde: case 0xdf: + + case 0xe0: case 0xe1: case 0xe2: case 0xe3: + case 0xe4: case 0xe5: case 0xe6: case 0xe7: + case 0xe8: case 0xe9: case 0xea: case 0xeb: + case 0xec: case 0xed: case 0xee: case 0xef: + return tag - BC_LONG_ZERO; + + /* byte long */ + case 0xf0: case 0xf1: case 0xf2: case 0xf3: + case 0xf4: case 0xf5: case 0xf6: case 0xf7: + case 0xf8: case 0xf9: case 0xfa: case 0xfb: + case 0xfc: case 0xfd: case 0xfe: case 0xff: + return ((tag - BC_LONG_BYTE_ZERO) << 8) + read(); + + /* short long */ + case 0x38: case 0x39: case 0x3a: case 0x3b: + case 0x3c: case 0x3d: case 0x3e: case 0x3f: + return ((tag - BC_LONG_SHORT_ZERO) << 16) + 256 * read() + read(); + + case 'L': + return (double) parseLong(); + + case BC_DOUBLE_ZERO: + return 0; + + case BC_DOUBLE_ONE: + return 1; + + case BC_DOUBLE_BYTE: + return (byte) (_offset < _length ? _buffer[_offset++] : read()); + + case BC_DOUBLE_SHORT: + return (short) (256 * read() + read()); + + case BC_DOUBLE_MILL: + { + int mills = parseInt(); + + return 0.001 * mills; + } + + case 'D': + return parseDouble(); + + default: + throw expect("double", tag); + } + } + + /** + * Reads a date. + * + *
    +   * T b64 b56 b48 b40 b32 b24 b16 b8
    +   * 
    + */ + public long readUTCDate() + throws IOException + { + int tag = read(); + + if (tag == BC_DATE) { + return parseLong(); + } + else if (tag == BC_DATE_MINUTE) { + return parseInt() * 60000L; + } + else + throw expect("date", tag); + } + + /** + * Reads a byte from the stream. + */ + public int readChar() + throws IOException + { + if (_chunkLength > 0) { + _chunkLength--; + if (_chunkLength == 0 && _isLastChunk) + _chunkLength = END_OF_DATA; + + int ch = parseUTF8Char(); + return ch; + } + else if (_chunkLength == END_OF_DATA) { + _chunkLength = 0; + return -1; + } + + int tag = read(); + + switch (tag) { + case 'N': + return -1; + + case 'S': + case BC_STRING_CHUNK: + _isLastChunk = tag == 'S'; + _chunkLength = (read() << 8) + read(); + + _chunkLength--; + int value = parseUTF8Char(); + + // special code so successive read byte won't + // be read as a single object. + if (_chunkLength == 0 && _isLastChunk) + _chunkLength = END_OF_DATA; + + return value; + + default: + throw expect("char", tag); + } + } + + /** + * Reads a byte array from the stream. + */ + public int readString(char []buffer, int offset, int length) + throws IOException + { + int readLength = 0; + + if (_chunkLength == END_OF_DATA) { + _chunkLength = 0; + return -1; + } + else if (_chunkLength == 0) { + int tag = read(); + + switch (tag) { + case 'N': + return -1; + + case 'S': + case BC_STRING_CHUNK: + _isLastChunk = tag == 'S'; + _chunkLength = (read() << 8) + read(); + break; + + case 0x00: case 0x01: case 0x02: case 0x03: + case 0x04: case 0x05: case 0x06: case 0x07: + case 0x08: case 0x09: case 0x0a: case 0x0b: + case 0x0c: case 0x0d: case 0x0e: case 0x0f: + + case 0x10: case 0x11: case 0x12: case 0x13: + case 0x14: case 0x15: case 0x16: case 0x17: + case 0x18: case 0x19: case 0x1a: case 0x1b: + case 0x1c: case 0x1d: case 0x1e: case 0x1f: + _isLastChunk = true; + _chunkLength = tag - 0x00; + break; + + case 0x30: case 0x31: case 0x32: case 0x33: + _isLastChunk = true; + _chunkLength = (tag - 0x30) * 256 + read(); + break; + + default: + throw expect("string", tag); + } + } + + while (length > 0) { + if (_chunkLength > 0) { + buffer[offset++] = (char) parseUTF8Char(); + _chunkLength--; + length--; + readLength++; + } + else if (_isLastChunk) { + if (readLength == 0) + return -1; + else { + _chunkLength = END_OF_DATA; + return readLength; + } + } + else { + int tag = read(); + + switch (tag) { + case 'S': + case BC_STRING_CHUNK: + _isLastChunk = tag == 'S'; + _chunkLength = (read() << 8) + read(); + break; + + case 0x00: case 0x01: case 0x02: case 0x03: + case 0x04: case 0x05: case 0x06: case 0x07: + case 0x08: case 0x09: case 0x0a: case 0x0b: + case 0x0c: case 0x0d: case 0x0e: case 0x0f: + + case 0x10: case 0x11: case 0x12: case 0x13: + case 0x14: case 0x15: case 0x16: case 0x17: + case 0x18: case 0x19: case 0x1a: case 0x1b: + case 0x1c: case 0x1d: case 0x1e: case 0x1f: + _isLastChunk = true; + _chunkLength = tag - 0x00; + break; + + case 0x30: case 0x31: case 0x32: case 0x33: + _isLastChunk = true; + _chunkLength = (tag - 0x30) * 256 + read(); + break; + + default: + throw expect("string", tag); + } + } + } + + if (readLength == 0) + return -1; + else if (_chunkLength > 0 || ! _isLastChunk) + return readLength; + else { + _chunkLength = END_OF_DATA; + return readLength; + } + } + + /** + * Reads a string + * + *
    +   * S b16 b8 string value
    +   * 
    + */ + public String readString() + throws IOException + { + int tag = read(); + + switch (tag) { + case 'N': + return null; + case 'T': + return "true"; + case 'F': + return "false"; + + // direct integer + case 0x80: case 0x81: case 0x82: case 0x83: + case 0x84: case 0x85: case 0x86: case 0x87: + case 0x88: case 0x89: case 0x8a: case 0x8b: + case 0x8c: case 0x8d: case 0x8e: case 0x8f: + + case 0x90: case 0x91: case 0x92: case 0x93: + case 0x94: case 0x95: case 0x96: case 0x97: + case 0x98: case 0x99: case 0x9a: case 0x9b: + case 0x9c: case 0x9d: case 0x9e: case 0x9f: + + case 0xa0: case 0xa1: case 0xa2: case 0xa3: + case 0xa4: case 0xa5: case 0xa6: case 0xa7: + case 0xa8: case 0xa9: case 0xaa: case 0xab: + case 0xac: case 0xad: case 0xae: case 0xaf: + + case 0xb0: case 0xb1: case 0xb2: case 0xb3: + case 0xb4: case 0xb5: case 0xb6: case 0xb7: + case 0xb8: case 0xb9: case 0xba: case 0xbb: + case 0xbc: case 0xbd: case 0xbe: case 0xbf: + return String.valueOf((tag - 0x90)); + + /* byte int */ + case 0xc0: case 0xc1: case 0xc2: case 0xc3: + case 0xc4: case 0xc5: case 0xc6: case 0xc7: + case 0xc8: case 0xc9: case 0xca: case 0xcb: + case 0xcc: case 0xcd: case 0xce: case 0xcf: + return String.valueOf(((tag - BC_INT_BYTE_ZERO) << 8) + read()); + + /* short int */ + case 0xd0: case 0xd1: case 0xd2: case 0xd3: + case 0xd4: case 0xd5: case 0xd6: case 0xd7: + return String.valueOf(((tag - BC_INT_SHORT_ZERO) << 16) + + 256 * read() + read()); + + case 'I': + case BC_LONG_INT: + return String.valueOf(parseInt()); + + // direct long + case 0xd8: case 0xd9: case 0xda: case 0xdb: + case 0xdc: case 0xdd: case 0xde: case 0xdf: + + case 0xe0: case 0xe1: case 0xe2: case 0xe3: + case 0xe4: case 0xe5: case 0xe6: case 0xe7: + case 0xe8: case 0xe9: case 0xea: case 0xeb: + case 0xec: case 0xed: case 0xee: case 0xef: + return String.valueOf(tag - BC_LONG_ZERO); + + /* byte long */ + case 0xf0: case 0xf1: case 0xf2: case 0xf3: + case 0xf4: case 0xf5: case 0xf6: case 0xf7: + case 0xf8: case 0xf9: case 0xfa: case 0xfb: + case 0xfc: case 0xfd: case 0xfe: case 0xff: + return String.valueOf(((tag - BC_LONG_BYTE_ZERO) << 8) + read()); + + /* short long */ + case 0x38: case 0x39: case 0x3a: case 0x3b: + case 0x3c: case 0x3d: case 0x3e: case 0x3f: + return String.valueOf(((tag - BC_LONG_SHORT_ZERO) << 16) + + 256 * read() + read()); + + case 'L': + return String.valueOf(parseLong()); + + case BC_DOUBLE_ZERO: + return "0.0"; + + case BC_DOUBLE_ONE: + return "1.0"; + + case BC_DOUBLE_BYTE: + return String.valueOf((byte) (_offset < _length + ? _buffer[_offset++] + : read())); + + case BC_DOUBLE_SHORT: + return String.valueOf(((short) (256 * read() + read()))); + + case BC_DOUBLE_MILL: + { + int mills = parseInt(); + + return String.valueOf(0.001 * mills); + } + + case 'D': + return String.valueOf(parseDouble()); + + case 'S': + case BC_STRING_CHUNK: + _isLastChunk = tag == 'S'; + _chunkLength = (read() << 8) + read(); + + _sbuf.setLength(0); + int ch; + + while ((ch = parseChar()) >= 0) + _sbuf.append((char) ch); + + return _sbuf.toString(); + + // 0-byte string + case 0x00: case 0x01: case 0x02: case 0x03: + case 0x04: case 0x05: case 0x06: case 0x07: + case 0x08: case 0x09: case 0x0a: case 0x0b: + case 0x0c: case 0x0d: case 0x0e: case 0x0f: + + case 0x10: case 0x11: case 0x12: case 0x13: + case 0x14: case 0x15: case 0x16: case 0x17: + case 0x18: case 0x19: case 0x1a: case 0x1b: + case 0x1c: case 0x1d: case 0x1e: case 0x1f: + _isLastChunk = true; + _chunkLength = tag - 0x00; + + _sbuf.setLength(0); + + while ((ch = parseChar()) >= 0) { + _sbuf.append((char) ch); + } + + return _sbuf.toString(); + + case 0x30: case 0x31: case 0x32: case 0x33: + _isLastChunk = true; + _chunkLength = (tag - 0x30) * 256 + read(); + + _sbuf.setLength(0); + + while ((ch = parseChar()) >= 0) + _sbuf.append((char) ch); + + return _sbuf.toString(); + + default: + throw expect("string", tag); + } + } + + /** + * Reads a byte array + * + *
    +   * B b16 b8 data value
    +   * 
    + */ + public byte []readBytes() + throws IOException + { + int tag = read(); + + switch (tag) { + case 'N': + return null; + + case BC_BINARY: + case BC_BINARY_CHUNK: + _isLastChunk = tag == BC_BINARY; + _chunkLength = (read() << 8) + read(); + + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + + int data; + while ((data = parseByte()) >= 0) + bos.write(data); + + return bos.toByteArray(); + + case 0x20: case 0x21: case 0x22: case 0x23: + case 0x24: case 0x25: case 0x26: case 0x27: + case 0x28: case 0x29: case 0x2a: case 0x2b: + case 0x2c: case 0x2d: case 0x2e: case 0x2f: + { + _isLastChunk = true; + _chunkLength = tag - 0x20; + + byte []buffer = new byte[_chunkLength]; + + int offset = 0; + while (offset < _chunkLength) { + int sublen = read(buffer, 0, _chunkLength - offset); + + if (sublen <= 0) + break; + + offset += sublen; + } + + return buffer; + } + + case 0x34: case 0x35: case 0x36: case 0x37: + { + _isLastChunk = true; + _chunkLength = (tag - 0x34) * 256 + read(); + + byte []buffer = new byte[_chunkLength]; + + int offset = 0; + while (offset < _chunkLength) { + int sublen = read(buffer, 0, _chunkLength - offset); + + if (sublen <= 0) + break; + + offset += sublen; + } + + return buffer; + } + + default: + throw expect("bytes", tag); + } + } + + /** + * Reads a byte from the stream. + */ + public int readByte() + throws IOException + { + if (_chunkLength > 0) { + _chunkLength--; + if (_chunkLength == 0 && _isLastChunk) + _chunkLength = END_OF_DATA; + + return read(); + } + else if (_chunkLength == END_OF_DATA) { + _chunkLength = 0; + return -1; + } + + int tag = read(); + + switch (tag) { + case 'N': + return -1; + + case 'B': + case BC_BINARY_CHUNK: + { + _isLastChunk = tag == 'B'; + _chunkLength = (read() << 8) + read(); + + int value = parseByte(); + + // special code so successive read byte won't + // be read as a single object. + if (_chunkLength == 0 && _isLastChunk) + _chunkLength = END_OF_DATA; + + return value; + } + + case 0x20: case 0x21: case 0x22: case 0x23: + case 0x24: case 0x25: case 0x26: case 0x27: + case 0x28: case 0x29: case 0x2a: case 0x2b: + case 0x2c: case 0x2d: case 0x2e: case 0x2f: + { + _isLastChunk = true; + _chunkLength = tag - 0x20; + + int value = parseByte(); + + // special code so successive read byte won't + // be read as a single object. + if (_chunkLength == 0) + _chunkLength = END_OF_DATA; + + return value; + } + + case 0x34: case 0x35: case 0x36: case 0x37: + { + _isLastChunk = true; + _chunkLength = (tag - 0x34) * 256 + read(); + + int value = parseByte(); + + // special code so successive read byte won't + // be read as a single object. + if (_chunkLength == 0) + _chunkLength = END_OF_DATA; + + return value; + } + + default: + throw expect("binary", tag); + } + } + + /** + * Reads a byte array from the stream. + */ + public int readBytes(byte []buffer, int offset, int length) + throws IOException + { + int readLength = 0; + + if (_chunkLength == END_OF_DATA) { + _chunkLength = 0; + return -1; + } + else if (_chunkLength == 0) { + int tag = read(); + + switch (tag) { + case 'N': + return -1; + + case 'B': + case BC_BINARY_CHUNK: + _isLastChunk = tag == 'B'; + _chunkLength = (read() << 8) + read(); + break; + + case 0x20: case 0x21: case 0x22: case 0x23: + case 0x24: case 0x25: case 0x26: case 0x27: + case 0x28: case 0x29: case 0x2a: case 0x2b: + case 0x2c: case 0x2d: case 0x2e: case 0x2f: + { + _isLastChunk = true; + _chunkLength = tag - 0x20; + break; + } + + case 0x34: case 0x35: case 0x36: case 0x37: + { + _isLastChunk = true; + _chunkLength = (tag - 0x34) * 256 + read(); + break; + } + + default: + throw expect("binary", tag); + } + } + + while (length > 0) { + if (_chunkLength > 0) { + buffer[offset++] = (byte) read(); + _chunkLength--; + length--; + readLength++; + } + else if (_isLastChunk) { + if (readLength == 0) + return -1; + else { + _chunkLength = END_OF_DATA; + return readLength; + } + } + else { + int tag = read(); + + switch (tag) { + case 'B': + case BC_BINARY_CHUNK: + _isLastChunk = tag == 'B'; + _chunkLength = (read() << 8) + read(); + break; + + default: + throw expect("binary", tag); + } + } + } + + if (readLength == 0) + return -1; + else if (_chunkLength > 0 || ! _isLastChunk) + return readLength; + else { + _chunkLength = END_OF_DATA; + return readLength; + } + } + + /** + * Reads a fault. + */ + private HashMap readFault() + throws IOException + { + HashMap map = new HashMap(); + + int code = read(); + for (; code > 0 && code != 'Z'; code = read()) { + _offset--; + + Object key = readObject(); + Object value = readObject(); + + if (key != null && value != null) + map.put(key, value); + } + + if (code != 'Z') + throw expect("fault", code); + + return map; + } + + /** + * Reads an object from the input stream with an expected type. + */ + public Object readObject(Class cl) + throws IOException + { + if (cl == null || cl == Object.class) + return readObject(); + + int tag = _offset < _length ? (_buffer[_offset++] & 0xff) : read(); + + switch (tag) { + case 'N': + return null; + + case 'H': + { + Deserializer reader = findSerializerFactory().getDeserializer(cl); + + return reader.readMap(this); + } + + case 'M': + { + String type = readType(); + + // hessian/3bb3 + if ("".equals(type)) { + Deserializer reader; + reader = findSerializerFactory().getDeserializer(cl); + + return reader.readMap(this); + } + else { + Deserializer reader; + reader = findSerializerFactory().getObjectDeserializer(type, cl); + + return reader.readMap(this); + } + } + + case 'C': + { + readObjectDefinition(cl); + + return readObject(cl); + } + + case 0x60: case 0x61: case 0x62: case 0x63: + case 0x64: case 0x65: case 0x66: case 0x67: + case 0x68: case 0x69: case 0x6a: case 0x6b: + case 0x6c: case 0x6d: case 0x6e: case 0x6f: + { + int ref = tag - 0x60; + int size = _classDefs.size(); + + if (ref < 0 || size <= ref) + throw new HessianProtocolException("'" + ref + "' is an unknown class definition"); + + ObjectDefinition def = _classDefs.get(ref); + + return readObjectInstance(cl, def); + } + + case 'O': + { + int ref = readInt(); + int size = _classDefs.size(); + + if (ref < 0 || size <= ref) + throw new HessianProtocolException("'" + ref + "' is an unknown class definition"); + + ObjectDefinition def = _classDefs.get(ref); + + return readObjectInstance(cl, def); + } + + case BC_LIST_VARIABLE: + { + String type = readType(); + + Deserializer reader; + reader = findSerializerFactory().getListDeserializer(type, cl); + + Object v = reader.readList(this, -1); + + return v; + } + + case BC_LIST_FIXED: + { + String type = readType(); + int length = readInt(); + + Deserializer reader; + reader = findSerializerFactory().getListDeserializer(type, cl); + + Object v = reader.readLengthList(this, length); + + return v; + } + + case 0x70: case 0x71: case 0x72: case 0x73: + case 0x74: case 0x75: case 0x76: case 0x77: + { + int length = tag - 0x70; + + String type = readType(); + + Deserializer reader; + reader = findSerializerFactory().getListDeserializer(type, cl); + + Object v = reader.readLengthList(this, length); + + return v; + } + + case BC_LIST_VARIABLE_UNTYPED: + { + Deserializer reader; + reader = findSerializerFactory().getListDeserializer(null, cl); + + Object v = reader.readList(this, -1); + + return v; + } + + case BC_LIST_FIXED_UNTYPED: + { + int length = readInt(); + + Deserializer reader; + reader = findSerializerFactory().getListDeserializer(null, cl); + + Object v = reader.readLengthList(this, length); + + return v; + } + + case 0x78: case 0x79: case 0x7a: case 0x7b: + case 0x7c: case 0x7d: case 0x7e: case 0x7f: + { + int length = tag - 0x78; + + Deserializer reader; + reader = findSerializerFactory().getListDeserializer(null, cl); + + Object v = reader.readLengthList(this, length); + + return v; + } + + case BC_REF: + { + int ref = readInt(); + + return _refs.get(ref); + } + } + + if (tag >= 0) + _offset--; + + // hessian/3b2i vs hessian/3406 + // return readObject(); + Object value = findSerializerFactory().getDeserializer(cl).readObject(this); + return value; + } + + /** + * Reads an arbitrary object from the input stream when the type + * is unknown. + */ + public Object readObject() + throws IOException + { + int tag = _offset < _length ? (_buffer[_offset++] & 0xff) : read(); + + switch (tag) { + case 'N': + return null; + + case 'T': + return Boolean.valueOf(true); + + case 'F': + return Boolean.valueOf(false); + + // direct integer + case 0x80: case 0x81: case 0x82: case 0x83: + case 0x84: case 0x85: case 0x86: case 0x87: + case 0x88: case 0x89: case 0x8a: case 0x8b: + case 0x8c: case 0x8d: case 0x8e: case 0x8f: + + case 0x90: case 0x91: case 0x92: case 0x93: + case 0x94: case 0x95: case 0x96: case 0x97: + case 0x98: case 0x99: case 0x9a: case 0x9b: + case 0x9c: case 0x9d: case 0x9e: case 0x9f: + + case 0xa0: case 0xa1: case 0xa2: case 0xa3: + case 0xa4: case 0xa5: case 0xa6: case 0xa7: + case 0xa8: case 0xa9: case 0xaa: case 0xab: + case 0xac: case 0xad: case 0xae: case 0xaf: + + case 0xb0: case 0xb1: case 0xb2: case 0xb3: + case 0xb4: case 0xb5: case 0xb6: case 0xb7: + case 0xb8: case 0xb9: case 0xba: case 0xbb: + case 0xbc: case 0xbd: case 0xbe: case 0xbf: + return Integer.valueOf(tag - BC_INT_ZERO); + + /* byte int */ + case 0xc0: case 0xc1: case 0xc2: case 0xc3: + case 0xc4: case 0xc5: case 0xc6: case 0xc7: + case 0xc8: case 0xc9: case 0xca: case 0xcb: + case 0xcc: case 0xcd: case 0xce: case 0xcf: + return Integer.valueOf(((tag - BC_INT_BYTE_ZERO) << 8) + read()); + + /* short int */ + case 0xd0: case 0xd1: case 0xd2: case 0xd3: + case 0xd4: case 0xd5: case 0xd6: case 0xd7: + return Integer.valueOf(((tag - BC_INT_SHORT_ZERO) << 16) + + 256 * read() + read()); + + case 'I': + return Integer.valueOf(parseInt()); + + // direct long + case 0xd8: case 0xd9: case 0xda: case 0xdb: + case 0xdc: case 0xdd: case 0xde: case 0xdf: + + case 0xe0: case 0xe1: case 0xe2: case 0xe3: + case 0xe4: case 0xe5: case 0xe6: case 0xe7: + case 0xe8: case 0xe9: case 0xea: case 0xeb: + case 0xec: case 0xed: case 0xee: case 0xef: + return Long.valueOf(tag - BC_LONG_ZERO); + + /* byte long */ + case 0xf0: case 0xf1: case 0xf2: case 0xf3: + case 0xf4: case 0xf5: case 0xf6: case 0xf7: + case 0xf8: case 0xf9: case 0xfa: case 0xfb: + case 0xfc: case 0xfd: case 0xfe: case 0xff: + return Long.valueOf(((tag - BC_LONG_BYTE_ZERO) << 8) + read()); + + /* short long */ + case 0x38: case 0x39: case 0x3a: case 0x3b: + case 0x3c: case 0x3d: case 0x3e: case 0x3f: + return Long.valueOf(((tag - BC_LONG_SHORT_ZERO) << 16) + 256 * read() + read()); + + case BC_LONG_INT: + return Long.valueOf(parseInt()); + + case 'L': + return Long.valueOf(parseLong()); + + case BC_DOUBLE_ZERO: + return Double.valueOf(0); + + case BC_DOUBLE_ONE: + return Double.valueOf(1); + + case BC_DOUBLE_BYTE: + return Double.valueOf((byte) read()); + + case BC_DOUBLE_SHORT: + return Double.valueOf((short) (256 * read() + read())); + + case BC_DOUBLE_MILL: + { + int mills = parseInt(); + + return Double.valueOf(0.001 * mills); + } + + case 'D': + return Double.valueOf(parseDouble()); + + case BC_DATE: + return new Date(parseLong()); + + case BC_DATE_MINUTE: + return new Date(parseInt() * 60000L); + + case BC_STRING_CHUNK: + case 'S': + { + _isLastChunk = tag == 'S'; + _chunkLength = (read() << 8) + read(); + + int data; + _sbuf.setLength(0); + + while ((data = parseChar()) >= 0) + _sbuf.append((char) data); + + return _sbuf.toString(); + } + + case 0x00: case 0x01: case 0x02: case 0x03: + case 0x04: case 0x05: case 0x06: case 0x07: + case 0x08: case 0x09: case 0x0a: case 0x0b: + case 0x0c: case 0x0d: case 0x0e: case 0x0f: + + case 0x10: case 0x11: case 0x12: case 0x13: + case 0x14: case 0x15: case 0x16: case 0x17: + case 0x18: case 0x19: case 0x1a: case 0x1b: + case 0x1c: case 0x1d: case 0x1e: case 0x1f: + { + _isLastChunk = true; + _chunkLength = tag - 0x00; + + int data; + _sbuf.setLength(0); + + while ((data = parseChar()) >= 0) + _sbuf.append((char) data); + + return _sbuf.toString(); + } + + case 0x30: case 0x31: case 0x32: case 0x33: + { + _isLastChunk = true; + _chunkLength = (tag - 0x30) * 256 + read(); + + _sbuf.setLength(0); + + int ch; + while ((ch = parseChar()) >= 0) + _sbuf.append((char) ch); + + return _sbuf.toString(); + } + + case BC_BINARY_CHUNK: + case 'B': + { + _isLastChunk = tag == 'B'; + _chunkLength = (read() << 8) + read(); + + int data; + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + + while ((data = parseByte()) >= 0) + bos.write(data); + + return bos.toByteArray(); + } + + case 0x20: case 0x21: case 0x22: case 0x23: + case 0x24: case 0x25: case 0x26: case 0x27: + case 0x28: case 0x29: case 0x2a: case 0x2b: + case 0x2c: case 0x2d: case 0x2e: case 0x2f: + { + _isLastChunk = true; + int len = tag - 0x20; + _chunkLength = 0; + + byte []data = new byte[len]; + + for (int i = 0; i < len; i++) + data[i] = (byte) read(); + + return data; + } + + case 0x34: case 0x35: case 0x36: case 0x37: + { + _isLastChunk = true; + int len = (tag - 0x34) * 256 + read(); + _chunkLength = 0; + + byte []buffer = new byte[len]; + + for (int i = 0; i < len; i++) { + buffer[i] = (byte) read(); + } + + return buffer; + } + + case BC_LIST_VARIABLE: + { + // variable length list + String type = readType(); + + return findSerializerFactory().readList(this, -1, type); + } + + case BC_LIST_VARIABLE_UNTYPED: + { + return findSerializerFactory().readList(this, -1, null); + } + + case BC_LIST_FIXED: + { + // fixed length lists + String type = readType(); + int length = readInt(); + + Deserializer reader; + reader = findSerializerFactory().getListDeserializer(type, null); + + return reader.readLengthList(this, length); + } + + case BC_LIST_FIXED_UNTYPED: + { + // fixed length lists + int length = readInt(); + + Deserializer reader; + reader = findSerializerFactory().getListDeserializer(null, null); + + return reader.readLengthList(this, length); + } + + // compact fixed list + case 0x70: case 0x71: case 0x72: case 0x73: + case 0x74: case 0x75: case 0x76: case 0x77: + { + // fixed length lists + String type = readType(); + int length = tag - 0x70; + + Deserializer reader; + reader = findSerializerFactory().getListDeserializer(type, null); + + return reader.readLengthList(this, length); + } + + // compact fixed untyped list + case 0x78: case 0x79: case 0x7a: case 0x7b: + case 0x7c: case 0x7d: case 0x7e: case 0x7f: + { + // fixed length lists + int length = tag - 0x78; + + Deserializer reader; + reader = findSerializerFactory().getListDeserializer(null, null); + + return reader.readLengthList(this, length); + } + + case 'H': + { + return findSerializerFactory().readMap(this, null); + } + + case 'M': + { + String type = readType(); + + return findSerializerFactory().readMap(this, type); + } + + case 'C': + { + readObjectDefinition(null); + + return readObject(); + } + + case 0x60: case 0x61: case 0x62: case 0x63: + case 0x64: case 0x65: case 0x66: case 0x67: + case 0x68: case 0x69: case 0x6a: case 0x6b: + case 0x6c: case 0x6d: case 0x6e: case 0x6f: + { + int ref = tag - 0x60; + + if (_classDefs.size() <= ref) + throw error("No classes defined at reference '" + + Integer.toHexString(tag) + "'"); + + ObjectDefinition def = _classDefs.get(ref); + + return readObjectInstance(null, def); + } + + case 'O': + { + int ref = readInt(); + + if (_classDefs.size() <= ref) + throw error("Illegal object reference #" + ref); + + ObjectDefinition def = _classDefs.get(ref); + + return readObjectInstance(null, def); + } + + case BC_REF: + { + int ref = readInt(); + + return _refs.get(ref); + } + + default: + if (tag < 0) + throw new EOFException("readObject: unexpected end of file"); + else + throw error("readObject: unknown code " + codeName(tag)); + } + } + + /** + * Reads an object definition: + * + *
    +   * O string  (string)* *
    +   * 
    + */ + private void readObjectDefinition(Class cl) + throws IOException + { + String type = readString(); + int len = readInt(); + + SerializerFactory factory = findSerializerFactory(); + + Deserializer reader = factory.getObjectDeserializer(type, null); + + Object []fields = reader.createFields(len); + String []fieldNames = new String[len]; + + for (int i = 0; i < len; i++) { + String name = readString(); + + fields[i] = reader.createField(name); + fieldNames[i] = name; + } + + ObjectDefinition def + = new ObjectDefinition(type, reader, fields, fieldNames); + + _classDefs.add(def); + } + + private Object readObjectInstance(Class cl, + ObjectDefinition def) + throws IOException + { + String type = def.getType(); + Deserializer reader = def.getReader(); + Object []fields = def.getFields(); + + SerializerFactory factory = findSerializerFactory(); + + if (cl != reader.getType() && cl != null) { + reader = factory.getObjectDeserializer(type, cl); + + return reader.readObject(this, def.getFieldNames()); + } + else { + return reader.readObject(this, fields); + } + } + + /** + * Reads a remote object. + */ + public Object readRemote() + throws IOException + { + String type = readType(); + String url = readString(); + + return resolveRemote(type, url); + } + + /** + * Reads a reference. + */ + public Object readRef() + throws IOException + { + return _refs.get(parseInt()); + } + + /** + * Reads the start of a list. + */ + public int readListStart() + throws IOException + { + return read(); + } + + /** + * Reads the start of a list. + */ + public int readMapStart() + throws IOException + { + return read(); + } + + /** + * Returns true if this is the end of a list or a map. + */ + public boolean isEnd() + throws IOException + { + int code; + + if (_offset < _length) + code = (_buffer[_offset] & 0xff); + else { + code = read(); + + if (code >= 0) + _offset--; + } + + return (code < 0 || code == 'Z'); + } + + /** + * Reads the end byte. + */ + public void readEnd() + throws IOException + { + int code = _offset < _length ? (_buffer[_offset++] & 0xff) : read(); + + if (code == 'Z') + return; + else if (code < 0) + throw error("unexpected end of file"); + else + throw error("unknown code:" + codeName(code)); + } + + /** + * Reads the end byte. + */ + public void readMapEnd() + throws IOException + { + int code = _offset < _length ? (_buffer[_offset++] & 0xff) : read(); + + if (code != 'Z') + throw error("expected end of map ('Z') at '" + codeName(code) + "'"); + } + + /** + * Reads the end byte. + */ + public void readListEnd() + throws IOException + { + int code = _offset < _length ? (_buffer[_offset++] & 0xff) : read(); + + if (code != 'Z') + throw error("expected end of list ('Z') at '" + codeName(code) + "'"); + } + + /** + * Adds a list/map reference. + */ + public int addRef(Object ref) + { + if (_refs == null) + _refs = new ArrayList(); + + _refs.add(ref); + + return _refs.size() - 1; + } + + /** + * Adds a list/map reference. + */ + public void setRef(int i, Object ref) + { + _refs.set(i, ref); + } + + /** + * Resets the references for streaming. + */ + public void resetReferences() + { + _refs.clear(); + } + + public void reset() + { + resetReferences(); + + _classDefs.clear(); + _types.clear(); + } + + public void resetBuffer() + { + int offset = _offset; + _offset = 0; + + int length = _length; + _length = 0; + + if (length > 0 && offset != length) + throw new IllegalStateException("offset=" + offset + " length=" + length); + } + + public Object readStreamingObject() + throws IOException + { + if (_refs != null) + _refs.clear(); + + return readObject(); + } + + /** + * Resolves a remote object. + */ + public Object resolveRemote(String type, String url) + throws IOException + { + HessianRemoteResolver resolver = getRemoteResolver(); + + if (resolver != null) + return resolver.lookup(type, url); + else + return new HessianRemote(type, url); + } + + /** + * Parses a type from the stream. + * + *
    +   * type ::= string
    +   * type ::= int
    +   * 
    + */ + public String readType() + throws IOException + { + int code = _offset < _length ? (_buffer[_offset++] & 0xff) : read(); + _offset--; + + switch (code) { + case 0x00: case 0x01: case 0x02: case 0x03: + case 0x04: case 0x05: case 0x06: case 0x07: + case 0x08: case 0x09: case 0x0a: case 0x0b: + case 0x0c: case 0x0d: case 0x0e: case 0x0f: + + case 0x10: case 0x11: case 0x12: case 0x13: + case 0x14: case 0x15: case 0x16: case 0x17: + case 0x18: case 0x19: case 0x1a: case 0x1b: + case 0x1c: case 0x1d: case 0x1e: case 0x1f: + + case 0x30: case 0x31: case 0x32: case 0x33: + case BC_STRING_CHUNK: case 'S': + { + String type = readString(); + + if (_types == null) + _types = new ArrayList(); + + _types.add(type); + + return type; + } + + default: + { + int ref = readInt(); + + if (_types.size() <= ref) + throw new IndexOutOfBoundsException("type ref #" + ref + " is greater than the number of valid types (" + _types.size() + ")"); + + return (String) _types.get(ref); + } + } + } + + /** + * Parses the length for an array + * + *
    +   * l b32 b24 b16 b8
    +   * 
    + */ + public int readLength() + throws IOException + { + throw new UnsupportedOperationException(); + } + + /** + * Parses a 32-bit integer value from the stream. + * + *
    +   * b32 b24 b16 b8
    +   * 
    + */ + private int parseInt() + throws IOException + { + int offset = _offset; + + if (offset + 3 < _length) { + byte []buffer = _buffer; + + int b32 = buffer[offset + 0] & 0xff; + int b24 = buffer[offset + 1] & 0xff; + int b16 = buffer[offset + 2] & 0xff; + int b8 = buffer[offset + 3] & 0xff; + + _offset = offset + 4; + + return (b32 << 24) + (b24 << 16) + (b16 << 8) + b8; + } + else { + int b32 = read(); + int b24 = read(); + int b16 = read(); + int b8 = read(); + + return (b32 << 24) + (b24 << 16) + (b16 << 8) + b8; + } + } + + /** + * Parses a 64-bit long value from the stream. + * + *
    +   * b64 b56 b48 b40 b32 b24 b16 b8
    +   * 
    + */ + private long parseLong() + throws IOException + { + long b64 = read(); + long b56 = read(); + long b48 = read(); + long b40 = read(); + long b32 = read(); + long b24 = read(); + long b16 = read(); + long b8 = read(); + + return ((b64 << 56) + + (b56 << 48) + + (b48 << 40) + + (b40 << 32) + + (b32 << 24) + + (b24 << 16) + + (b16 << 8) + + b8); + } + + /** + * Parses a 64-bit double value from the stream. + * + *
    +   * b64 b56 b48 b40 b32 b24 b16 b8
    +   * 
    + */ + private double parseDouble() + throws IOException + { + long bits = parseLong(); + + return Double.longBitsToDouble(bits); + } + + org.w3c.dom.Node parseXML() + throws IOException + { + throw new UnsupportedOperationException(); + } + + /** + * Reads a character from the underlying stream. + */ + private int parseChar() + throws IOException + { + while (_chunkLength <= 0) { + if (_isLastChunk) + return -1; + + int code = _offset < _length ? (_buffer[_offset++] & 0xff) : read(); + + switch (code) { + case BC_STRING_CHUNK: + _isLastChunk = false; + + _chunkLength = (read() << 8) + read(); + break; + + case 'S': + _isLastChunk = true; + + _chunkLength = (read() << 8) + read(); + break; + + case 0x00: case 0x01: case 0x02: case 0x03: + case 0x04: case 0x05: case 0x06: case 0x07: + case 0x08: case 0x09: case 0x0a: case 0x0b: + case 0x0c: case 0x0d: case 0x0e: case 0x0f: + + case 0x10: case 0x11: case 0x12: case 0x13: + case 0x14: case 0x15: case 0x16: case 0x17: + case 0x18: case 0x19: case 0x1a: case 0x1b: + case 0x1c: case 0x1d: case 0x1e: case 0x1f: + _isLastChunk = true; + _chunkLength = code - 0x00; + break; + + case 0x30: case 0x31: case 0x32: case 0x33: + _isLastChunk = true; + _chunkLength = (code - 0x30) * 256 + read(); + break; + + default: + throw expect("string", code); + } + + } + + _chunkLength--; + + return parseUTF8Char(); + } + + /** + * Parses a single UTF8 character. + */ + private int parseUTF8Char() + throws IOException + { + int ch = _offset < _length ? (_buffer[_offset++] & 0xff) : read(); + + if (ch < 0x80) + return ch; + else if ((ch & 0xe0) == 0xc0) { + int ch1 = read(); + int v = ((ch & 0x1f) << 6) + (ch1 & 0x3f); + + return v; + } + else if ((ch & 0xf0) == 0xe0) { + int ch1 = read(); + int ch2 = read(); + int v = ((ch & 0x0f) << 12) + ((ch1 & 0x3f) << 6) + (ch2 & 0x3f); + + return v; + } + else + throw error("bad utf-8 encoding at " + codeName(ch)); + } + + /** + * Reads a byte from the underlying stream. + */ + private int parseByte() + throws IOException + { + while (_chunkLength <= 0) { + if (_isLastChunk) { + return -1; + } + + int code = read(); + + switch (code) { + case BC_BINARY_CHUNK: + _isLastChunk = false; + + _chunkLength = (read() << 8) + read(); + break; + + case 'B': + _isLastChunk = true; + + _chunkLength = (read() << 8) + read(); + break; + + case 0x20: case 0x21: case 0x22: case 0x23: + case 0x24: case 0x25: case 0x26: case 0x27: + case 0x28: case 0x29: case 0x2a: case 0x2b: + case 0x2c: case 0x2d: case 0x2e: case 0x2f: + _isLastChunk = true; + + _chunkLength = code - 0x20; + break; + + case 0x34: case 0x35: case 0x36: case 0x37: + _isLastChunk = true; + _chunkLength = (code - 0x34) * 256 + read(); + break; + + default: + throw expect("byte[]", code); + } + } + + _chunkLength--; + + return read(); + } + + /** + * Reads bytes based on an input stream. + */ + public InputStream readInputStream() + throws IOException + { + int tag = read(); + + switch (tag) { + case 'N': + return null; + + case BC_BINARY: + case BC_BINARY_CHUNK: + _isLastChunk = tag == BC_BINARY; + _chunkLength = (read() << 8) + read(); + break; + + case 0x20: case 0x21: case 0x22: case 0x23: + case 0x24: case 0x25: case 0x26: case 0x27: + case 0x28: case 0x29: case 0x2a: case 0x2b: + case 0x2c: case 0x2d: case 0x2e: case 0x2f: + _isLastChunk = true; + _chunkLength = tag - 0x20; + break; + + case 0x34: case 0x35: case 0x36: case 0x37: + _isLastChunk = true; + _chunkLength = (tag - 0x34) * 256 + read(); + break; + + default: + throw expect("binary", tag); + } + + return new ReadInputStream(); + } + + /** + * Reads bytes from the underlying stream. + */ + int read(byte []buffer, int offset, int length) + throws IOException + { + int readLength = 0; + + while (length > 0) { + while (_chunkLength <= 0) { + if (_isLastChunk) + return readLength == 0 ? -1 : readLength; + + int code = read(); + + switch (code) { + case BC_BINARY_CHUNK: + _isLastChunk = false; + + _chunkLength = (read() << 8) + read(); + break; + + case BC_BINARY: + _isLastChunk = true; + + _chunkLength = (read() << 8) + read(); + break; + + case 0x20: case 0x21: case 0x22: case 0x23: + case 0x24: case 0x25: case 0x26: case 0x27: + case 0x28: case 0x29: case 0x2a: case 0x2b: + case 0x2c: case 0x2d: case 0x2e: case 0x2f: + _isLastChunk = true; + _chunkLength = code - 0x20; + break; + + case 0x34: case 0x35: case 0x36: case 0x37: + _isLastChunk = true; + _chunkLength = (code - 0x34) * 256 + read(); + break; + + default: + throw expect("byte[]", code); + } + } + + int sublen = _chunkLength; + if (length < sublen) + sublen = length; + + if (_length <= _offset && ! readBuffer()) + return -1; + + if (_length - _offset < sublen) + sublen = _length - _offset; + + System.arraycopy(_buffer, _offset, buffer, offset, sublen); + + _offset += sublen; + + offset += sublen; + readLength += sublen; + length -= sublen; + _chunkLength -= sublen; + } + + return readLength; + } + + /** + * Normally, shouldn't be called externally, but needed for QA, e.g. + * ejb/3b01. + */ + public final int read() + throws IOException + { + if (_length <= _offset && ! readBuffer()) + return -1; + + return _buffer[_offset++] & 0xff; + } + + protected void unread() + { + if (_offset <= 0) + throw new IllegalStateException(); + + _offset--; + } + + private final boolean readBuffer() + throws IOException + { + byte []buffer = _buffer; + int offset = _offset; + int length = _length; + + if (offset < length) { + System.arraycopy(buffer, offset, buffer, 0, length - offset); + offset = length - offset; + } + else + offset = 0; + + int len = 0; + if (_is != null) + len = _is.read(buffer, offset, SIZE - offset); + + if (len <= 0) { + _length = offset; + _offset = 0; + + return offset > 0; + } + + _length = offset + len; + _offset = 0; + + return true; + } + + public Reader getReader() + { + return null; + } + + protected IOException expect(String expect, int ch) + throws IOException + { + if (ch < 0) + return error("expected " + expect + " at end of file"); + else { + _offset--; + + try { + int offset = _offset; + String context + = buildDebugContext(_buffer, 0, _length, offset); + + Object obj = readObject(); + + if (obj != null) { + return error("expected " + expect + + " at 0x" + Integer.toHexString(ch & 0xff) + + " " + obj.getClass().getName() + " (" + obj + ")" + + "\n " + context + ""); + } + else + return error("expected " + expect + + " at 0x" + Integer.toHexString(ch & 0xff) + " null"); + } catch (Exception e) { + log.log(Level.FINE, e.toString(), e); + + return error("expected " + expect + + " at 0x" + Integer.toHexString(ch & 0xff)); + } + } + } + + private String buildDebugContext(byte []buffer, int offset, int length, + int errorOffset) + { + StringBuilder sb = new StringBuilder(); + + sb.append("["); + for (int i = 0; i < errorOffset; i++) { + int ch = buffer[offset + i]; + addDebugChar(sb, ch); + } + sb.append("] "); + addDebugChar(sb, buffer[offset + errorOffset]); + sb.append(" ["); + for (int i = errorOffset + 1; i < length; i++) { + int ch = buffer[offset + i]; + addDebugChar(sb, ch); + } + sb.append("]"); + + return sb.toString(); + } + + private void addDebugChar(StringBuilder sb, int ch) + { + if (ch >= 0x20 && ch < 0x7f) { + sb.append((char) ch); + } + else if (ch == '\n') + sb.append((char) ch); + else + sb.append(String.format("\\x%02x", ch & 0xff)); + } + + protected String codeName(int ch) + { + if (ch < 0) + return "end of file"; + else + return "0x" + Integer.toHexString(ch & 0xff) + " (" + (char) + ch + ")"; + } + + protected IOException error(String message) + { + if (_method != null) + return new HessianProtocolException(_method + ": " + message); + else + return new HessianProtocolException(message); + } + + public void close() + throws IOException + { + InputStream is = _is; + _is = null; + + if (_isCloseStreamOnClose && is != null) + is.close(); + } + + class ReadInputStream extends InputStream { + boolean _isClosed = false; + + public int read() + throws IOException + { + if (_isClosed) + return -1; + + int ch = parseByte(); + if (ch < 0) + _isClosed = true; + + return ch; + } + + public int read(byte []buffer, int offset, int length) + throws IOException + { + if (_isClosed) + return -1; + + int len = Hessian2Input.this.read(buffer, offset, length); + if (len < 0) + _isClosed = true; + + return len; + } + + public void close() + throws IOException + { + while (read() >= 0) { + } + } + }; + + final static class ObjectDefinition { + private final String _type; + private final Deserializer _reader; + private final Object []_fields; + private final String []_fieldNames; + + ObjectDefinition(String type, + Deserializer reader, + Object []fields, + String []fieldNames) + { + _type = type; + _reader = reader; + _fields = fields; + _fieldNames = fieldNames; + } + + String getType() + { + return _type; + } + + Deserializer getReader() + { + return _reader; + } + + Object []getFields() + { + return _fields; + } + + String []getFieldNames() + { + return _fieldNames; + } + } + + static { + try { + _detailMessageField = Throwable.class.getDeclaredField("detailMessage"); + _detailMessageField.setAccessible(true); + } catch (Throwable e) { + } + } +} diff --git a/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/Hessian2Output.java b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/Hessian2Output.java new file mode 100644 index 0000000000..c9fc90ec1c --- /dev/null +++ b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/Hessian2Output.java @@ -0,0 +1,1733 @@ +/* + * Copyright (c) 2001-2008 Caucho Technology, Inc. All rights reserved. + * + * The Apache Software License, Version 1.1 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Caucho Technology (http://www.caucho.com/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Burlap", "Resin", and "Caucho" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * info@caucho.com. + * + * 5. Products derived from this software may not be called "Resin" + * nor may "Resin" appear in their names without prior written + * permission of Caucho Technology. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Scott Ferguson + */ + +package com.caucho.hessian4.io; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.HashMap; + +import org.jboss.netty.channel.ChannelFuture; + +import com.caucho.hessian4.util.IdentityIntMap; + +/** + * Output stream for Hessian 2 requests. + * + *

    Since HessianOutput does not depend on any classes other than + * in the JDK, it can be extracted independently into a smaller package. + * + *

    HessianOutput is unbuffered, so any client needs to provide + * its own buffering. + * + *

    + * OutputStream os = ...; // from http connection
    + * Hessian2Output out = new Hessian2Output(os);
    + * String value;
    + *
    + * out.startCall("hello", 1); // start hello call
    + * out.writeString("arg1");   // write a string argument
    + * out.completeCall();        // complete the call
    + * 
    + */ +public class Hessian2Output + extends AbstractHessianOutput + implements Hessian2Constants +{ + public final static int SIZE = 1024*1024; + + // the output stream/ + protected OutputStream _os; + + // map of references + private final IdentityIntMap _refs + = new IdentityIntMap(256); + + private boolean _isCloseStreamOnClose; + + // map of classes + private final IdentityIntMap _classRefs + = new IdentityIntMap(256); + + // map of types + private HashMap _typeRefs; + + private final byte []_buffer = new byte[SIZE]; + private int _offset; + + private boolean _isPacket; + + /** + * Creates a new Hessian output stream, initialized with an + * underlying output stream. + * + * @param os the underlying output stream. + */ + public Hessian2Output(OutputStream os) + { + init(os); + } + + public void init(OutputStream os) + { + _os = os; + reset(); + + } + + public void setCloseStreamOnClose(boolean isClose) + { + _isCloseStreamOnClose = isClose; + } + + public boolean isCloseStreamOnClose() + { + return _isCloseStreamOnClose; + } + + /** + * Writes a complete method call. + */ + @Override + public void call(String method, Object []args) + throws IOException + { + writeVersion(); + + int length = args != null ? args.length : 0; + + startCall(method, length); + + for (int i = 0; i < length; i++) + writeObject(args[i]); + + completeCall(); + + flush(); + } + + /** + * Starts the method call. Clients would use startCall + * instead of call if they wanted finer control over + * writing the arguments, or needed to write headers. + * + *
    +   * C
    +   * string # method name
    +   * int    # arg count
    +   * 
    + * + * @param method the method name to call. + */ + public void startCall(String method, int length) + throws IOException + { + int offset = _offset; + + if (SIZE < offset + 32) { + flushBuffer(); + offset = _offset; + } + + byte []buffer = _buffer; + + buffer[_offset++] = (byte) 'C'; + + writeString(method); + writeInt(length); + } + + /** + * Writes the call tag. This would be followed by the + * method and the arguments + * + *
    +   * C
    +   * 
    + * + * @param method the method name to call. + */ + public void startCall() + throws IOException + { + flushIfFull(); + + _buffer[_offset++] = (byte) 'C'; + } + + /** + * Starts an envelope. + * + *
    +   * E major minor
    +   * m b16 b8 method-name
    +   * 
    + * + * @param method the method name to call. + */ + public void startEnvelope(String method) + throws IOException + { + int offset = _offset; + + if (SIZE < offset + 32) { + flushBuffer(); + offset = _offset; + } + + _buffer[_offset++] = (byte) 'E'; + + writeString(method); + } + + /** + * Completes an envelope. + * + *

    A successful completion will have a single value: + * + *

    +   * Z
    +   * 
    + */ + public void completeEnvelope() + throws IOException + { + flushIfFull(); + + _buffer[_offset++] = (byte) 'Z'; + } + + /** + * Writes the method tag. + * + *
    +   * string
    +   * 
    + * + * @param method the method name to call. + */ + public void writeMethod(String method) + throws IOException + { + writeString(method); + } + + /** + * Completes. + * + *
    +   * z
    +   * 
    + */ + public void completeCall() + throws IOException + { + /* + flushIfFull(); + + _buffer[_offset++] = (byte) 'Z'; + */ + } + + /** + * Starts the reply + * + *

    A successful completion will have a single value: + * + *

    +   * R
    +   * 
    + */ + public void startReply() + throws IOException + { + writeVersion(); + + flushIfFull(); + + _buffer[_offset++] = (byte) 'R'; + } + + public void writeVersion() + throws IOException + { + flushIfFull(); + + _buffer[_offset++] = (byte) 'H'; + _buffer[_offset++] = (byte) 2; + _buffer[_offset++] = (byte) 0; + } + + /** + * Completes reading the reply + * + *

    A successful completion will have a single value: + * + *

    +   * z
    +   * 
    + */ + public void completeReply() + throws IOException + { + } + + /** + * Starts a packet + * + *

    A message contains several objects encapsulated by a length

    + * + *
    +   * p x02 x00
    +   * 
    + */ + public void startMessage() + throws IOException + { + flushIfFull(); + + _buffer[_offset++] = (byte) 'p'; + _buffer[_offset++] = (byte) 2; + _buffer[_offset++] = (byte) 0; + } + + /** + * Completes reading the message + * + *

    A successful completion will have a single value: + * + *

    +   * z
    +   * 
    + */ + public void completeMessage() + throws IOException + { + flushIfFull(); + + _buffer[_offset++] = (byte) 'z'; + } + + /** + * Writes a fault. The fault will be written + * as a descriptive string followed by an object: + * + *
    +   * F map
    +   * 
    + * + *
    +   * F H
    +   * \x04code
    +   * \x10the fault code
    +   *
    +   * \x07message
    +   * \x11the fault message
    +   *
    +   * \x06detail
    +   * M\xnnjavax.ejb.FinderException
    +   *     ...
    +   * Z
    +   * Z
    +   * 
    + * + * @param code the fault code, a three digit + */ + public void writeFault(String code, String message, Object detail) + throws IOException + { + flushIfFull(); + + writeVersion(); + + _buffer[_offset++] = (byte) 'F'; + _buffer[_offset++] = (byte) 'H'; + + _refs.put(new Object(), _refs.size(), false); + + writeString("code"); + writeString(code); + + writeString("message"); + writeString(message); + + if (detail != null) { + writeString("detail"); + writeObject(detail); + } + + flushIfFull(); + _buffer[_offset++] = (byte) 'Z'; + } + + /** + * Writes any object to the output stream. + */ + public void writeObject(Object object) + throws IOException + { + if (object == null) { + writeNull(); + return; + } + + Serializer serializer + = findSerializerFactory().getObjectSerializer(object.getClass()); + + serializer.writeObject(object, this); + } + + /** + * Writes the list header to the stream. List writers will call + * writeListBegin followed by the list contents and then + * call writeListEnd. + * + *
    +   * list ::= V type value* Z
    +   *      ::= v type int value*
    +   * 
    + * + * @return true for variable lists, false for fixed lists + */ + public boolean writeListBegin(int length, String type) + throws IOException + { + flushIfFull(); + + if (length < 0) { + if (type != null) { + _buffer[_offset++] = (byte) BC_LIST_VARIABLE; + writeType(type); + } + else + _buffer[_offset++] = (byte) BC_LIST_VARIABLE_UNTYPED; + + return true; + } + else if (length <= LIST_DIRECT_MAX) { + if (type != null) { + _buffer[_offset++] = (byte) (BC_LIST_DIRECT + length); + writeType(type); + } + else { + _buffer[_offset++] = (byte) (BC_LIST_DIRECT_UNTYPED + length); + } + + return false; + } + else { + if (type != null) { + _buffer[_offset++] = (byte) BC_LIST_FIXED; + writeType(type); + } + else { + _buffer[_offset++] = (byte) BC_LIST_FIXED_UNTYPED; + } + + writeInt(length); + + return false; + } + } + + /** + * Writes the tail of the list to the stream for a variable-length list. + */ + public void writeListEnd() + throws IOException + { + flushIfFull(); + + _buffer[_offset++] = (byte) BC_END; + } + + /** + * Writes the map header to the stream. Map writers will call + * writeMapBegin followed by the map contents and then + * call writeMapEnd. + * + *
    +   * map ::= M type ( )* Z
    +   *     ::= H ( )* Z
    +   * 
    + */ + public void writeMapBegin(String type) + throws IOException + { + if (SIZE < _offset + 32) + flushBuffer(); + + if (type != null) { + _buffer[_offset++] = BC_MAP; + + writeType(type); + } + else + _buffer[_offset++] = BC_MAP_UNTYPED; + } + + /** + * Writes the tail of the map to the stream. + */ + public void writeMapEnd() + throws IOException + { + if (SIZE < _offset + 32) + flushBuffer(); + + _buffer[_offset++] = (byte) BC_END; + } + + /** + * Writes the object definition + * + *
    +   * C <string> <int> <string>*
    +   * 
    + */ + @Override + public int writeObjectBegin(String type) + throws IOException + { + int newRef = _classRefs.size(); + int ref = _classRefs.put(type, newRef, false); + + if (newRef != ref) { + if (SIZE < _offset + 32) + flushBuffer(); + + if (ref <= OBJECT_DIRECT_MAX) { + _buffer[_offset++] = (byte) (BC_OBJECT_DIRECT + ref); + } + else { + _buffer[_offset++] = (byte) 'O'; + writeInt(ref); + } + + return ref; + } + else { + if (SIZE < _offset + 32) + flushBuffer(); + + _buffer[_offset++] = (byte) 'C'; + + writeString(type); + + return -1; + } + } + + /** + * Writes the tail of the class definition to the stream. + */ + public void writeClassFieldLength(int len) + throws IOException + { + writeInt(len); + } + + /** + * Writes the tail of the object definition to the stream. + */ + public void writeObjectEnd() + throws IOException + { + } + + /** + *
    +   * type ::= string
    +   *      ::= int
    +   * 
    + */ + private void writeType(String type) + throws IOException + { + flushIfFull(); + + int len = type.length(); + if (len == 0) { + throw new IllegalArgumentException("empty type is not allowed"); + } + + if (_typeRefs == null) + _typeRefs = new HashMap(); + + Integer typeRefV = (Integer) _typeRefs.get(type); + + if (typeRefV != null) { + int typeRef = typeRefV.intValue(); + + writeInt(typeRef); + } + else { + _typeRefs.put(type, Integer.valueOf(_typeRefs.size())); + + writeString(type); + } + } + + /** + * Writes a boolean value to the stream. The boolean will be written + * with the following syntax: + * + *
    +   * T
    +   * F
    +   * 
    + * + * @param value the boolean value to write. + */ + public void writeBoolean(boolean value) + throws IOException + { + if (SIZE < _offset + 16) + flushBuffer(); + + if (value) + _buffer[_offset++] = (byte) 'T'; + else + _buffer[_offset++] = (byte) 'F'; + } + + /** + * Writes an integer value to the stream. The integer will be written + * with the following syntax: + * + *
    +   * I b32 b24 b16 b8
    +   * 
    + * + * @param value the integer value to write. + */ + public void writeInt(int value) + throws IOException + { + int offset = _offset; + byte []buffer = _buffer; + + if (SIZE <= offset + 16) { + flushBuffer(); + offset = _offset; + } + + if (INT_DIRECT_MIN <= value && value <= INT_DIRECT_MAX) + buffer[offset++] = (byte) (value + BC_INT_ZERO); + else if (INT_BYTE_MIN <= value && value <= INT_BYTE_MAX) { + buffer[offset++] = (byte) (BC_INT_BYTE_ZERO + (value >> 8)); + buffer[offset++] = (byte) (value); + } + else if (INT_SHORT_MIN <= value && value <= INT_SHORT_MAX) { + buffer[offset++] = (byte) (BC_INT_SHORT_ZERO + (value >> 16)); + buffer[offset++] = (byte) (value >> 8); + buffer[offset++] = (byte) (value); + } + else { + buffer[offset++] = (byte) ('I'); + buffer[offset++] = (byte) (value >> 24); + buffer[offset++] = (byte) (value >> 16); + buffer[offset++] = (byte) (value >> 8); + buffer[offset++] = (byte) (value); + } + + _offset = offset; + } + + /** + * Writes a long value to the stream. The long will be written + * with the following syntax: + * + *
    +   * L b64 b56 b48 b40 b32 b24 b16 b8
    +   * 
    + * + * @param value the long value to write. + */ + public void writeLong(long value) + throws IOException + { + int offset = _offset; + byte []buffer = _buffer; + + if (SIZE <= offset + 16) { + flushBuffer(); + offset = _offset; + } + + if (LONG_DIRECT_MIN <= value && value <= LONG_DIRECT_MAX) { + buffer[offset++] = (byte) (value + BC_LONG_ZERO); + } + else if (LONG_BYTE_MIN <= value && value <= LONG_BYTE_MAX) { + buffer[offset++] = (byte) (BC_LONG_BYTE_ZERO + (value >> 8)); + buffer[offset++] = (byte) (value); + } + else if (LONG_SHORT_MIN <= value && value <= LONG_SHORT_MAX) { + buffer[offset++] = (byte) (BC_LONG_SHORT_ZERO + (value >> 16)); + buffer[offset++] = (byte) (value >> 8); + buffer[offset++] = (byte) (value); + } + else if (-0x80000000L <= value && value <= 0x7fffffffL) { + buffer[offset + 0] = (byte) BC_LONG_INT; + buffer[offset + 1] = (byte) (value >> 24); + buffer[offset + 2] = (byte) (value >> 16); + buffer[offset + 3] = (byte) (value >> 8); + buffer[offset + 4] = (byte) (value); + + offset += 5; + } + else { + buffer[offset + 0] = (byte) 'L'; + buffer[offset + 1] = (byte) (value >> 56); + buffer[offset + 2] = (byte) (value >> 48); + buffer[offset + 3] = (byte) (value >> 40); + buffer[offset + 4] = (byte) (value >> 32); + buffer[offset + 5] = (byte) (value >> 24); + buffer[offset + 6] = (byte) (value >> 16); + buffer[offset + 7] = (byte) (value >> 8); + buffer[offset + 8] = (byte) (value); + + offset += 9; + } + + _offset = offset; + } + + /** + * Writes a double value to the stream. The double will be written + * with the following syntax: + * + *
    +   * D b64 b56 b48 b40 b32 b24 b16 b8
    +   * 
    + * + * @param value the double value to write. + */ + public void writeDouble(double value) + throws IOException + { + int offset = _offset; + byte []buffer = _buffer; + + if (SIZE <= offset + 16) { + flushBuffer(); + offset = _offset; + } + + int intValue = (int) value; + + if (intValue == value) { + if (intValue == 0) { + buffer[offset++] = (byte) BC_DOUBLE_ZERO; + + _offset = offset; + + return; + } + else if (intValue == 1) { + buffer[offset++] = (byte) BC_DOUBLE_ONE; + + _offset = offset; + + return; + } + else if (-0x80 <= intValue && intValue < 0x80) { + buffer[offset++] = (byte) BC_DOUBLE_BYTE; + buffer[offset++] = (byte) intValue; + + _offset = offset; + + return; + } + else if (-0x8000 <= intValue && intValue < 0x8000) { + buffer[offset + 0] = (byte) BC_DOUBLE_SHORT; + buffer[offset + 1] = (byte) (intValue >> 8); + buffer[offset + 2] = (byte) intValue; + + _offset = offset + 3; + + return; + } + } + + int mills = (int) (value * 1000); + + if (0.001 * mills == value) { + buffer[offset + 0] = (byte) (BC_DOUBLE_MILL); + buffer[offset + 1] = (byte) (mills >> 24); + buffer[offset + 2] = (byte) (mills >> 16); + buffer[offset + 3] = (byte) (mills >> 8); + buffer[offset + 4] = (byte) (mills); + + _offset = offset + 5; + + return; + } + + long bits = Double.doubleToLongBits(value); + + buffer[offset + 0] = (byte) 'D'; + buffer[offset + 1] = (byte) (bits >> 56); + buffer[offset + 2] = (byte) (bits >> 48); + buffer[offset + 3] = (byte) (bits >> 40); + buffer[offset + 4] = (byte) (bits >> 32); + buffer[offset + 5] = (byte) (bits >> 24); + buffer[offset + 6] = (byte) (bits >> 16); + buffer[offset + 7] = (byte) (bits >> 8); + buffer[offset + 8] = (byte) (bits); + + _offset = offset + 9; + } + + /** + * Writes a date to the stream. + * + *
    +   * date ::= d   b7 b6 b5 b4 b3 b2 b1 b0
    +   *      ::= x65 b3 b2 b1 b0
    +   * 
    + * + * @param time the date in milliseconds from the epoch in UTC + */ + public void writeUTCDate(long time) + throws IOException + { + if (SIZE < _offset + 32) + flushBuffer(); + + int offset = _offset; + byte []buffer = _buffer; + + if (time % 60000L == 0) { + // compact date ::= x65 b3 b2 b1 b0 + + long minutes = time / 60000L; + + if ((minutes >> 31) == 0 || (minutes >> 31) == -1) { + buffer[offset++] = (byte) BC_DATE_MINUTE; + buffer[offset++] = ((byte) (minutes >> 24)); + buffer[offset++] = ((byte) (minutes >> 16)); + buffer[offset++] = ((byte) (minutes >> 8)); + buffer[offset++] = ((byte) (minutes >> 0)); + + _offset = offset; + return; + } + } + + buffer[offset++] = (byte) BC_DATE; + buffer[offset++] = ((byte) (time >> 56)); + buffer[offset++] = ((byte) (time >> 48)); + buffer[offset++] = ((byte) (time >> 40)); + buffer[offset++] = ((byte) (time >> 32)); + buffer[offset++] = ((byte) (time >> 24)); + buffer[offset++] = ((byte) (time >> 16)); + buffer[offset++] = ((byte) (time >> 8)); + buffer[offset++] = ((byte) (time)); + + _offset = offset; + } + + /** + * Writes a null value to the stream. + * The null will be written with the following syntax + * + *
    +   * N
    +   * 
    + * + * @param value the string value to write. + */ + public void writeNull() + throws IOException + { + int offset = _offset; + byte []buffer = _buffer; + + if (SIZE <= offset + 16) { + flushBuffer(); + offset = _offset; + } + + buffer[offset++] = 'N'; + + _offset = offset; + } + + /** + * Writes a string value to the stream using UTF-8 encoding. + * The string will be written with the following syntax: + * + *
    +   * S b16 b8 string-value
    +   * 
    + * + * If the value is null, it will be written as + * + *
    +   * N
    +   * 
    + * + * @param value the string value to write. + */ + public void writeString(String value) + throws IOException + { + int offset = _offset; + byte []buffer = _buffer; + + if (SIZE <= offset + 16) { + flushBuffer(); + offset = _offset; + } + + if (value == null) { + buffer[offset++] = (byte) 'N'; + + _offset = offset; + } + else { + int length = value.length(); + int strOffset = 0; + + while (length > 0x8000) { + int sublen = 0x8000; + + offset = _offset; + + if (SIZE <= offset + 16) { + flushBuffer(); + offset = _offset; + } + + // chunk can't end in high surrogate + char tail = value.charAt(strOffset + sublen - 1); + + if (0xd800 <= tail && tail <= 0xdbff) + sublen--; + + buffer[offset + 0] = (byte) BC_STRING_CHUNK; + buffer[offset + 1] = (byte) (sublen >> 8); + buffer[offset + 2] = (byte) (sublen); + + _offset = offset + 3; + + printString(value, strOffset, sublen); + + length -= sublen; + strOffset += sublen; + } + + offset = _offset; + + if (SIZE <= offset + 16) { + flushBuffer(); + offset = _offset; + } + + if (length <= STRING_DIRECT_MAX) { + buffer[offset++] = (byte) (BC_STRING_DIRECT + length); + } + else if (length <= STRING_SHORT_MAX) { + buffer[offset++] = (byte) (BC_STRING_SHORT + (length >> 8)); + buffer[offset++] = (byte) (length); + } + else { + buffer[offset++] = (byte) ('S'); + buffer[offset++] = (byte) (length >> 8); + buffer[offset++] = (byte) (length); + } + + _offset = offset; + + printString(value, strOffset, length); + } + } + + /** + * Writes a string value to the stream using UTF-8 encoding. + * The string will be written with the following syntax: + * + *
    +   * S b16 b8 string-value
    +   * 
    + * + * If the value is null, it will be written as + * + *
    +   * N
    +   * 
    + * + * @param value the string value to write. + */ + public void writeString(char []buffer, int offset, int length) + throws IOException + { + if (buffer == null) { + if (SIZE < _offset + 16) + flushBuffer(); + + _buffer[_offset++] = (byte) ('N'); + } + else { + while (length > 0x8000) { + int sublen = 0x8000; + + if (SIZE < _offset + 16) + flushBuffer(); + + // chunk can't end in high surrogate + char tail = buffer[offset + sublen - 1]; + + if (0xd800 <= tail && tail <= 0xdbff) + sublen--; + + _buffer[_offset++] = (byte) BC_STRING_CHUNK; + _buffer[_offset++] = (byte) (sublen >> 8); + _buffer[_offset++] = (byte) (sublen); + + printString(buffer, offset, sublen); + + length -= sublen; + offset += sublen; + } + + if (SIZE < _offset + 16) + flushBuffer(); + + if (length <= STRING_DIRECT_MAX) { + _buffer[_offset++] = (byte) (BC_STRING_DIRECT + length); + } + else if (length <= STRING_SHORT_MAX) { + _buffer[_offset++] = (byte) (BC_STRING_SHORT + (length >> 8)); + _buffer[_offset++] = (byte) length; + } + else { + _buffer[_offset++] = (byte) ('S'); + _buffer[_offset++] = (byte) (length >> 8); + _buffer[_offset++] = (byte) (length); + } + + printString(buffer, offset, length); + } + } + + /** + * Writes a byte array to the stream. + * The array will be written with the following syntax: + * + *
    +   * B b16 b18 bytes
    +   * 
    + * + * If the value is null, it will be written as + * + *
    +   * N
    +   * 
    + * + * @param value the string value to write. + */ + public void writeBytes(byte []buffer) + throws IOException + { + if (buffer == null) { + if (SIZE < _offset + 16) + flushBuffer(); + + _buffer[_offset++] = 'N'; + } + else + writeBytes(buffer, 0, buffer.length); + } + + /** + * Writes a byte array to the stream. + * The array will be written with the following syntax: + * + *
    +   * B b16 b18 bytes
    +   * 
    + * + * If the value is null, it will be written as + * + *
    +   * N
    +   * 
    + * + * @param value the string value to write. + */ + public void writeBytes(byte []buffer, int offset, int length) + throws IOException + { + if (buffer == null) { + if (SIZE < _offset + 16) + flushBuffer(); + + _buffer[_offset++] = (byte) 'N'; + } + else { + while (length > 0x8000) { + int sublen = 0x8000; + + if (SIZE < _offset + 3) + flushBuffer(); + + _buffer[_offset++] = (byte) BC_BINARY_CHUNK; + _buffer[_offset++] = (byte) (sublen >> 8); + _buffer[_offset++] = (byte) sublen; + + if (SIZE < _offset + sublen) + flushBuffer(); + + System.arraycopy(buffer, offset, _buffer, _offset, sublen); + _offset += sublen; + + length -= sublen; + offset += sublen; + + } + + /*while (SIZE - _offset - 3 < length) { + int sublen = SIZE - _offset - 3; + + if (sublen < 16) { + flushBuffer(); + + sublen = SIZE - _offset - 3; + + if (length < sublen) + sublen = length; + } + + _buffer[_offset++] = (byte) BC_BINARY_CHUNK; + _buffer[_offset++] = (byte) (sublen >> 8); + _buffer[_offset++] = (byte) sublen; + + System.arraycopy(buffer, offset, _buffer, _offset, sublen); + _offset += sublen; + + length -= sublen; + offset += sublen; + + flushBuffer(); + } + */ + + if (SIZE < _offset + 16) + flushBuffer(); + + if (length <= BINARY_DIRECT_MAX) { + _buffer[_offset++] = (byte) (BC_BINARY_DIRECT + length); + } + else if (length <= BINARY_SHORT_MAX) { + _buffer[_offset++] = (byte) (BC_BINARY_SHORT + (length >> 8)); + _buffer[_offset++] = (byte) (length); + } + else { + _buffer[_offset++] = (byte) 'B'; + _buffer[_offset++] = (byte) (length >> 8); + _buffer[_offset++] = (byte) (length); + } + + if (SIZE < _offset + length) + flushBuffer(); + System.arraycopy(buffer, offset, _buffer, _offset, length); + + _offset += length; + } + } + + /** + * Writes a byte buffer to the stream. + * + *
    +   * 
    + */ + public void writeByteBufferStart() + throws IOException + { + } + + /** + * Writes a byte buffer to the stream. + * + *
    +   * b b16 b18 bytes
    +   * 
    + */ + public void writeByteBufferPart(byte []buffer, int offset, int length) + throws IOException + { + while (length > 0) { + flushIfFull(); + + int sublen = _buffer.length - _offset; + + if (length < sublen) + sublen = length; + + _buffer[_offset++] = BC_BINARY_CHUNK; + _buffer[_offset++] = (byte) (sublen >> 8); + _buffer[_offset++] = (byte) sublen; + + System.arraycopy(buffer, offset, _buffer, _offset, sublen); + + _offset += sublen; + length -= sublen; + offset += sublen; + } + } + + /** + * Writes a byte buffer to the stream. + * + *
    +   * b b16 b18 bytes
    +   * 
    + */ + public void writeByteBufferEnd(byte []buffer, int offset, int length) + throws IOException + { + writeBytes(buffer, offset, length); + } + + /** + * Returns an output stream to write binary data. + */ + public OutputStream getBytesOutputStream() + throws IOException + { + return new BytesOutputStream(); + } + + /** + * Writes a full output stream. + */ + @Override + public void writeByteStream(InputStream is) + throws IOException + { + while (true) { + int len = SIZE - _offset - 3; + + if (len < 16) { + flushBuffer(); + len = SIZE - _offset - 3; + } + + len = is.read(_buffer, _offset + 3, len); + + if (len <= 0) { + _buffer[_offset++] = BC_BINARY_DIRECT; + return; + } + + _buffer[_offset + 0] = (byte) BC_BINARY_CHUNK; + _buffer[_offset + 1] = (byte) (len >> 8); + _buffer[_offset + 2] = (byte) (len); + + _offset += len + 3; + } + } + + /** + * Writes a reference. + * + *
    +   * x51 <int>
    +   * 
    + * + * @param value the integer value to write. + */ + @Override + protected void writeRef(int value) + throws IOException + { + if (SIZE < _offset + 16) + flushBuffer(); + + _buffer[_offset++] = (byte) BC_REF; + + writeInt(value); + } + + /** + * If the object has already been written, just write its ref. + * + * @return true if we're writing a ref. + */ + public boolean addRef(Object object) + throws IOException + { + int newRef = _refs.size(); + + int ref = _refs.put(object, newRef, false); + + if (ref != newRef) { + writeRef(ref); + + return true; + } + else { + return false; + } + } + + /** + * Removes a reference. + */ + /* + private boolean removeRef(Object obj) + throws IOException + { + if (_refs != null) { + _refs.put(obj, -1); + + return true; + } + else + return false; + } + */ + + /** + * Replaces a reference from one object to another. + */ + public boolean replaceRef(Object oldRef, Object newRef) + throws IOException + { + int value = _refs.get(oldRef); + + if (value >= 0) { + _refs.put(newRef, value, true); + + return true; + } + else + return false; + } + + /** + * Starts the streaming message + * + *

    A streaming message starts with 'P'

    + * + *
    +   * P x02 x00
    +   * 
    + */ + public void writeStreamingObject(Object obj) + throws IOException + { + startPacket(); + + writeObject(obj); + + endPacket(); + } + + /** + * Starts a streaming packet + * + *

    A streaming contains a set of chunks, ending with a zero chunk. + * Each chunk is a length followed by data where the length is + * encoded by (b1xxxxxxxx)* b0xxxxxxxx

    + */ + public void startPacket() + throws IOException + { + if (_refs != null) + _refs.clear(); + + flushBuffer(); + + _isPacket = true; + _offset = 3; + _buffer[0] = (byte) 0x55; + _buffer[1] = (byte) 0x55; + _buffer[2] = (byte) 0x55; + } + + public void endPacket() + throws IOException + { + int offset = _offset; + + OutputStream os = _os; + + if (os == null) { + _offset = 0; + return; + } + + int len = offset - 3; + + _buffer[0] = (byte) (0x80); + _buffer[1] = (byte) (0x80 + ((len >> 7) & 0x7f)); + _buffer[2] = (byte) (len & 0x7f); + + // end chunk + _buffer[offset++] = (byte) 0x80; + _buffer[offset++] = (byte) 0x00; + + _isPacket = false; + _offset = 0; + + if (os != null) { + if (len == 0) { + os.write(_buffer, 1, 2); + } + else if (len < 0x80) { + os.write(_buffer, 1, offset - 1); + } + else { + os.write(_buffer, 0, offset); + } + } + } + + /** + * Prints a string to the stream, encoded as UTF-8 with preceeding length + * + * @param v the string to print. + */ + public void printLenString(String v) + throws IOException + { + if (SIZE < _offset + 16) + flushBuffer(); + + if (v == null) { + _buffer[_offset++] = (byte) (0); + _buffer[_offset++] = (byte) (0); + } + else { + int len = v.length(); + _buffer[_offset++] = (byte) (len >> 8); + _buffer[_offset++] = (byte) (len); + + printString(v, 0, len); + } + } + + /** + * Prints a string to the stream, encoded as UTF-8 + * + * @param v the string to print. + */ + public void printString(String v) + throws IOException + { + printString(v, 0, v.length()); + } + + /** + * Prints a string to the stream, encoded as UTF-8 + * + * @param v the string to print. + */ + public void printString(String v, int strOffset, int length) + throws IOException + { + int offset = _offset; + byte []buffer = _buffer; + + for (int i = 0; i < length; i++) { + if (SIZE <= offset + 16) { + _offset = offset; + flushBuffer(); + offset = _offset; + } + + char ch = v.charAt(i + strOffset); + + if (ch < 0x80) + buffer[offset++] = (byte) (ch); + else if (ch < 0x800) { + buffer[offset++] = (byte) (0xc0 + ((ch >> 6) & 0x1f)); + buffer[offset++] = (byte) (0x80 + (ch & 0x3f)); + } + else { + buffer[offset++] = (byte) (0xe0 + ((ch >> 12) & 0xf)); + buffer[offset++] = (byte) (0x80 + ((ch >> 6) & 0x3f)); + buffer[offset++] = (byte) (0x80 + (ch & 0x3f)); + } + } + + _offset = offset; + } + + /** + * Prints a string to the stream, encoded as UTF-8 + * + * @param v the string to print. + */ + public void printString(char []v, int strOffset, int length) + throws IOException + { + int offset = _offset; + byte []buffer = _buffer; + + for (int i = 0; i < length; i++) { + if (SIZE <= offset + 16) { + _offset = offset; + flushBuffer(); + offset = _offset; + } + + char ch = v[i + strOffset]; + + if (ch < 0x80) + buffer[offset++] = (byte) (ch); + else if (ch < 0x800) { + buffer[offset++] = (byte) (0xc0 + ((ch >> 6) & 0x1f)); + buffer[offset++] = (byte) (0x80 + (ch & 0x3f)); + } + else { + buffer[offset++] = (byte) (0xe0 + ((ch >> 12) & 0xf)); + buffer[offset++] = (byte) (0x80 + ((ch >> 6) & 0x3f)); + buffer[offset++] = (byte) (0x80 + (ch & 0x3f)); + } + } + + _offset = offset; + } + + private final void flushIfFull() + throws IOException + { + int offset = _offset; + + if (SIZE < offset + 32) { + flushBuffer(); + } + } + + public final void flush() + throws IOException +{ + flushBuffer(); + _os.flush(); +} + + public final void flush(ChannelFuture future) + throws IOException +{ + flushBuffer(); + ((FlushableOutput)_os).flush(future); +} + + public final void flushBuffer() + throws IOException + { + if (_offset == 0) + return; + int offset = _offset; + + OutputStream os = _os; + + if (! _isPacket && offset > 0) { + _offset = 0; + if (os != null) + os.write(_buffer, 0, offset); + } + else if (_isPacket && offset > 3) { + int len = offset - 3; + _buffer[0] = (byte) 0x80; + _buffer[1] = (byte) (0x80 + ((len >> 7) & 0x7f)); + _buffer[2] = (byte) (len & 0x7f); + _offset = 3; + + if (os != null) + os.write(_buffer, 0, offset); + + _buffer[0] = (byte) 0x56; + _buffer[1] = (byte) 0x56; + _buffer[2] = (byte) 0x56; + + } + } + + public void close() + throws IOException + { + // hessian/3a8c + flush(); + + OutputStream os = _os; + _os = null; + + if (os != null) { + if (_isCloseStreamOnClose) + os.close(); + } + } + + public void free() + { + reset(); + + _os = null; + _isCloseStreamOnClose = false; + } + + /** + * Resets the references for streaming. + */ + @Override + public void resetReferences() + { + if (_refs != null) + _refs.clear(); + } + + /** + * Resets all counters and references + */ + public void reset() + { + if (_refs != null) + _refs.clear(); + + _classRefs.clear(); + _typeRefs = null; + _offset = 0; + _isPacket = false; + if (_os instanceof FlushableOutput) + ((FlushableOutput)_os).reset(); + } + + class BytesOutputStream extends OutputStream { + private int _startOffset; + + BytesOutputStream() + throws IOException + { + if (SIZE < _offset + 16) { + Hessian2Output.this.flushBuffer(); + } + + _startOffset = _offset; + _offset += 3; // skip 'b' xNN xNN + } + + @Override + public void write(int ch) + throws IOException + { + if (SIZE <= _offset) { + int length = (_offset - _startOffset) - 3; + + _buffer[_startOffset] = (byte) BC_BINARY_CHUNK; + _buffer[_startOffset + 1] = (byte) (length >> 8); + _buffer[_startOffset + 2] = (byte) (length); + + Hessian2Output.this.flushBuffer(); + + _startOffset = _offset; + _offset += 3; + } + + _buffer[_offset++] = (byte) ch; + } + + @Override + public void write(byte []buffer, int offset, int length) + throws IOException + { + while (length > 0) { + int sublen = SIZE - _offset; + + if (length < sublen) + sublen = length; + + if (sublen > 0) { + System.arraycopy(buffer, offset, _buffer, _offset, sublen); + _offset += sublen; + } + + length -= sublen; + offset += sublen; + + if (SIZE <= _offset) { + int chunkLength = (_offset - _startOffset) - 3; + + _buffer[_startOffset] = (byte) BC_BINARY_CHUNK; + _buffer[_startOffset + 1] = (byte) (chunkLength >> 8); + _buffer[_startOffset + 2] = (byte) (chunkLength); + + Hessian2Output.this.flushBuffer(); + + _startOffset = _offset; + _offset += 3; + } + } + } + + @Override + public void close() + throws IOException + { + int startOffset = _startOffset; + _startOffset = -1; + + if (startOffset < 0) + return; + + int length = (_offset - startOffset) - 3; + + _buffer[startOffset] = (byte) 'B'; + _buffer[startOffset + 1] = (byte) (length >> 8); + _buffer[startOffset + 2] = (byte) (length); + + Hessian2Output.this.flushBuffer(); + } + } +} diff --git a/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/Hessian2StreamingInput.java b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/Hessian2StreamingInput.java new file mode 100644 index 0000000000..a1452c707b --- /dev/null +++ b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/Hessian2StreamingInput.java @@ -0,0 +1,276 @@ +/* + * Copyright (c) 2001-2008 Caucho Technology, Inc. All rights reserved. + * + * The Apache Software License, Version 1.1 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Caucho Technology (http://www.caucho.com/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Burlap", "Resin", and "Caucho" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * info@caucho.com. + * + * 5. Products derived from this software may not be called "Resin" + * nor may "Resin" appear in their names without prior written + * permission of Caucho Technology. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Scott Ferguson + */ + +package com.caucho.hessian4.io; + +import java.io.IOException; +import java.io.InputStream; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Input stream for Hessian 2 streaming requests using WebSocket. + */ +public class Hessian2StreamingInput +{ + private static final Logger log + = Logger.getLogger(Hessian2StreamingInput.class.getName()); + + private StreamingInputStream _is; + private Hessian2Input _in; + + /** + * Creates a new Hessian input stream, initialized with an + * underlying input stream. + * + * @param is the underlying output stream. + */ + public Hessian2StreamingInput(InputStream is) + { + _is = new StreamingInputStream(is); + _in = new Hessian2Input(_is); + } + + public void setSerializerFactory(SerializerFactory factory) + { + _in.setSerializerFactory(factory); + } + + public boolean isDataAvailable() + { + StreamingInputStream is = _is; + + return is != null && is.isDataAvailable(); + } + + public Hessian2Input startPacket() + throws IOException + { + if (_is.startPacket()) { + _in.resetReferences(); + _in.resetBuffer(); // XXX: + return _in; + } + else + return null; + } + + public void endPacket() + throws IOException + { + _is.endPacket(); + _in.resetBuffer(); // XXX: + } + + public Hessian2Input getHessianInput() + { + return _in; + } + + /** + * Read the next object + */ + public Object readObject() + throws IOException + { + _is.startPacket(); + + Object obj = _in.readStreamingObject(); + + _is.endPacket(); + + return obj; + } + + /** + * Close the output. + */ + public void close() + throws IOException + { + _in.close(); + } + + static class StreamingInputStream extends InputStream { + private InputStream _is; + + private int _length; + private boolean _isPacketEnd; + + StreamingInputStream(InputStream is) + { + _is = is; + } + + public boolean isDataAvailable() + { + try { + return _is != null && _is.available() > 0; + } catch (IOException e) { + log.log(Level.FINER, e.toString(), e); + + return true; + } + } + + public boolean startPacket() + throws IOException + { + // skip zero-length packets + do { + _isPacketEnd = false; + } while ((_length = readChunkLength(_is)) == 0); + + return _length > 0; + } + + public void endPacket() + throws IOException + { + while (! _isPacketEnd) { + if (_length <= 0) + _length = readChunkLength(_is); + + if (_length > 0) + _is.skip(_length); + } + } + + public int read() + throws IOException + { + if (_isPacketEnd) + throw new IllegalStateException(); + + InputStream is = _is; + + if (_length == 0) { + _length = readChunkLength(is); + + if (_length <= 0) + return -1; + } + + _length--; + + return is.read(); + } + + public int read(byte []buffer, int offset, int length) + throws IOException + { + if (_isPacketEnd) + throw new IllegalStateException(); + + InputStream is = _is; + + if (_length <= 0) { + _length = readChunkLength(is); + + if (_length <= 0) + return -1; + } + + int sublen = _length; + if (length < sublen) + sublen = length; + + sublen = is.read(buffer, offset, sublen); + + if (sublen < 0) + return -1; + + _length -= sublen; + + return sublen; + } + + private int readChunkLength(InputStream is) + throws IOException + { + if (_isPacketEnd) + return -1; + + int length = 0; + + int code = is.read(); + + if (code < 0) { + _isPacketEnd = true; + return -1; + } + else if ((code & 0x80) != 0x80) { + int len = 256; + StringBuilder sb = new StringBuilder(); + int ch; + + while ((len-- > 0 && is.available() > 0 && (ch = is.read()) >= 0)) + sb.append((char) ch); + + throw new IllegalStateException("WebSocket binary must begin with a 0x80 packet at 0x" + Integer.toHexString(code) + + " ("+ (char) code + ")" + + " context[" + sb + "]"); + } + + while ((code = is.read()) >= 0) { + length = (length << 7) + (code & 0x7f); + + if ((code & 0x80) == 0) { + if (length == 0) + _isPacketEnd = true; + + return length; + } + } + + _isPacketEnd = true; + + return -1; + } + } +} diff --git a/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/Hessian2StreamingOutput.java b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/Hessian2StreamingOutput.java new file mode 100644 index 0000000000..ee831f92ae --- /dev/null +++ b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/Hessian2StreamingOutput.java @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2001-2008 Caucho Technology, Inc. All rights reserved. + * + * The Apache Software License, Version 1.1 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Caucho Technology (http://www.caucho.com/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Burlap", "Resin", and "Caucho" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * info@caucho.com. + * + * 5. Products derived from this software may not be called "Resin" + * nor may "Resin" appear in their names without prior written + * permission of Caucho Technology. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Scott Ferguson + */ + +package com.caucho.hessian4.io; + +import java.io.IOException; +import java.io.OutputStream; + +/** + * Output stream for Hessian 2 streaming requests. + */ +public class Hessian2StreamingOutput +{ + private Hessian2Output _out; + + /** + * Creates a new Hessian output stream, initialized with an + * underlying output stream. + * + * @param os the underlying output stream. + */ + public Hessian2StreamingOutput(OutputStream os) + { + _out = new Hessian2Output(os); + } + + public Hessian2StreamingOutput(Hessian2Output out) + { + _out = out; + } + + public Hessian2Output getHessian2Output() + { + return _out; + } + + public void setCloseStreamOnClose(boolean isClose) + { + _out.setCloseStreamOnClose(isClose); + } + + public boolean isCloseStreamOnClose() + { + return _out.isCloseStreamOnClose(); + } + + /** + * Writes any object to the output stream. + */ + public void writeObject(Object object) + throws IOException + { + _out.writeStreamingObject(object); + } + + /** + * Flushes the output. + */ + public void flush() + throws IOException + { + _out.flush(); + } + + /** + * Close the output. + */ + public void close() + throws IOException + { + _out.close(); + } +} diff --git a/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/HessianDebugInputStream.java b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/HessianDebugInputStream.java new file mode 100644 index 0000000000..314fbcf3f6 --- /dev/null +++ b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/HessianDebugInputStream.java @@ -0,0 +1,196 @@ +/* + * Copyright (c) 2001-2008 Caucho Technology, Inc. All rights reserved. + * + * The Apache Software License, Version 1.1 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Caucho Technology (http://www.caucho.com/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Hessian", "Resin", and "Caucho" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * info@caucho.com. + * + * 5. Products derived from this software may not be called "Resin" + * nor may "Resin" appear in their names without prior written + * permission of Caucho Technology. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Scott Ferguson + */ + +package com.caucho.hessian4.io; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.PrintWriter; +import java.io.Writer; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Debugging input stream for Hessian requests. + */ +public class HessianDebugInputStream extends InputStream +{ + private InputStream _is; + + private HessianDebugState _state; + + /** + * Creates an uninitialized Hessian input stream. + */ + public HessianDebugInputStream(InputStream is, OutputStream os) + { + this(is, new PrintWriter(os)); + } + + /** + * Creates an uninitialized Hessian input stream. + */ + public HessianDebugInputStream(InputStream is, PrintWriter dbg) + { + _is = is; + + if (dbg == null) + dbg = new PrintWriter(System.out); + + _state = new HessianDebugState(dbg); + } + + /** + * Creates an uninitialized Hessian input stream. + */ + public HessianDebugInputStream(InputStream is, Logger log, Level level) + { + this(is, new PrintWriter(new LogWriter(log, level))); + } + + public void startTop2() + { + _state.startTop2(); + } + + public void startData1() + { + _state.startData1(); + } + + public void startStreaming() + { + _state.startStreaming(); + } + + public void setDepth(int depth) + { + _state.setDepth(depth); + } + + /** + * Reads a character. + */ + public int read() + throws IOException + { + int ch; + + InputStream is = _is; + + if (is == null) + return -1; + else { + ch = is.read(); + } + + _state.next(ch); + + return ch; + } + + /** + * closes the stream. + */ + public void close() + throws IOException + { + InputStream is = _is; + _is = null; + + if (is != null) + is.close(); + + _state.println(); + } + + static class LogWriter extends Writer { + private Logger _log; + private Level _level; + private StringBuilder _sb = new StringBuilder(); + + LogWriter(Logger log, Level level) + { + _log = log; + _level = level; + } + + public void write(char ch) + { + if (ch == '\n' && _sb.length() > 0) { + _log.log(_level, _sb.toString()); + _sb.setLength(0); + } + else + _sb.append((char) ch); + } + + public void write(char []buffer, int offset, int length) + { + for (int i = 0; i < length; i++) { + char ch = buffer[offset + i]; + + if (ch == '\n' && _sb.length() > 0) { + _log.log(_level, _sb.toString()); + _sb.setLength(0); + } + else + _sb.append((char) ch); + } + } + + public void flush() + { + } + + public void close() + { + } + } +} diff --git a/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/HessianDebugOutputStream.java b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/HessianDebugOutputStream.java new file mode 100644 index 0000000000..b6c96ed9dd --- /dev/null +++ b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/HessianDebugOutputStream.java @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2001-2004 Caucho Technology, Inc. All rights reserved. + * + * The Apache Software License, Version 1.1 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Caucho Technology (http://www.caucho.com/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Hessian", "Resin", and "Caucho" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * info@caucho.com. + * + * 5. Products derived from this software may not be called "Resin" + * nor may "Resin" appear in their names without prior written + * permission of Caucho Technology. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Scott Ferguson + */ + +package com.caucho.hessian4.io; + +import java.io.IOException; +import java.io.OutputStream; +import java.io.PrintWriter; +import java.io.Writer; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Debugging output stream for Hessian requests. + */ +public class HessianDebugOutputStream extends OutputStream +{ + private OutputStream _os; + + private HessianDebugState _state; + + /** + * Creates an uninitialized Hessian input stream. + */ + public HessianDebugOutputStream(OutputStream os, PrintWriter dbg) + { + _os = os; + + _state = new HessianDebugState(dbg); + } + + /** + * Creates an uninitialized Hessian input stream. + */ + public HessianDebugOutputStream(OutputStream os, Logger log, Level level) + { + this(os, new PrintWriter(new LogWriter(log, level))); + } + + public void startTop2() + { + _state.startTop2(); + } + + public void startStreaming() + { + _state.startStreaming(); + } + + /** + * Writes a character. + */ + public void write(int ch) + throws IOException + { + ch = ch & 0xff; + + _os.write(ch); + + _state.next(ch); + } + + public void flush() + throws IOException + { + _os.flush(); + } + + /** + * closes the stream. + */ + public void close() + throws IOException + { + OutputStream os = _os; + _os = null; + + if (os != null) { + _state.next(-1); + os.close(); + } + + _state.println(); + } + + static class LogWriter extends Writer { + private Logger _log; + private Level _level; + private StringBuilder _sb = new StringBuilder(); + + LogWriter(Logger log, Level level) + { + _log = log; + _level = level; + } + + public void write(char ch) + { + if (ch == '\n' && _sb.length() > 0) { + _log.log(_level, _sb.toString()); + _sb.setLength(0); + } + else + _sb.append((char) ch); + } + + public void write(char []buffer, int offset, int length) + { + for (int i = 0; i < length; i++) { + char ch = buffer[offset + i]; + + if (ch == '\n' && _sb.length() > 0) { + _log.log(_level, _sb.toString()); + _sb.setLength(0); + } + else + _sb.append((char) ch); + } + } + + public void flush() + { + } + + public void close() + { + } + } +} diff --git a/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/HessianDebugState.java b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/HessianDebugState.java new file mode 100644 index 0000000000..f7a61486fa --- /dev/null +++ b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/HessianDebugState.java @@ -0,0 +1,2506 @@ +/* + * Copyright (c) 2001-2004 Caucho Technology, Inc. All rights reserved. + * + * The Apache Software License, Version 1.1 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Caucho Technology (http://www.caucho.com/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Hessian", "Resin", and "Caucho" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * info@caucho.com. + * + * 5. Products derived from this software may not be called "Resin" + * nor may "Resin" appear in their names without prior written + * permission of Caucho Technology. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Scott Ferguson + */ + +package com.caucho.hessian4.io; + +import java.io.IOException; +import java.io.PrintWriter; +import java.util.ArrayList; + +/** + * Debugging input stream for Hessian requests. + */ +public class HessianDebugState implements Hessian2Constants +{ + private PrintWriter _dbg; + + private State _state; + private ArrayList _stateStack = new ArrayList(); + + private ArrayList _objectDefList + = new ArrayList(); + + private ArrayList _typeDefList + = new ArrayList(); + + private int _refId; + private boolean _isNewline = true; + private boolean _isObject = false; + private int _column; + + private int _depth = 0; + + /** + * Creates an uninitialized Hessian input stream. + */ + public HessianDebugState(PrintWriter dbg) + { + _dbg = dbg; + + _state = new InitialState(); + } + + public void startTop2() + { + _state = new Top2State(); + } + + public void startData1() + { + _state = new InitialState1(); + } + + public void startStreaming() + { + _state = new StreamingState(new InitialState(), false); + } + + /** + * Reads a character. + */ + public void next(int ch) + throws IOException + { + _state = _state.next(ch); + } + + void pushStack(State state) + { + _stateStack.add(state); + } + + State popStack() + { + return _stateStack.remove(_stateStack.size() - 1); + } + + public void setDepth(int depth) + { + _depth = depth; + } + + public int getDepth() + { + return _depth; + } + + void println() + { + if (! _isNewline) { + _dbg.println(); + _dbg.flush(); + } + + _isNewline = true; + _column = 0; + } + + static boolean isString(int ch) + { + switch (ch) { + case 0x00: case 0x01: case 0x02: case 0x03: + case 0x04: case 0x05: case 0x06: case 0x07: + case 0x08: case 0x09: case 0x0a: case 0x0b: + case 0x0c: case 0x0d: case 0x0e: case 0x0f: + + case 0x10: case 0x11: case 0x12: case 0x13: + case 0x14: case 0x15: case 0x16: case 0x17: + case 0x18: case 0x19: case 0x1a: case 0x1b: + case 0x1c: case 0x1d: case 0x1e: case 0x1f: + + case 0x30: case 0x31: case 0x32: case 0x33: + + case 'R': + case 'S': + return true; + + default: + return false; + } + } + + static boolean isInteger(int ch) + { + switch (ch) { + case 0x80: case 0x81: case 0x82: case 0x83: + case 0x84: case 0x85: case 0x86: case 0x87: + case 0x88: case 0x89: case 0x8a: case 0x8b: + case 0x8c: case 0x8d: case 0x8e: case 0x8f: + + case 0x90: case 0x91: case 0x92: case 0x93: + case 0x94: case 0x95: case 0x96: case 0x97: + case 0x98: case 0x99: case 0x9a: case 0x9b: + case 0x9c: case 0x9d: case 0x9e: case 0x9f: + + case 0xa0: case 0xa1: case 0xa2: case 0xa3: + case 0xa4: case 0xa5: case 0xa6: case 0xa7: + case 0xa8: case 0xa9: case 0xaa: case 0xab: + case 0xac: case 0xad: case 0xae: case 0xaf: + + case 0xb0: case 0xb1: case 0xb2: case 0xb3: + case 0xb4: case 0xb5: case 0xb6: case 0xb7: + case 0xb8: case 0xb9: case 0xba: case 0xbb: + case 0xbc: case 0xbd: case 0xbe: case 0xbf: + + case 0xc0: case 0xc1: case 0xc2: case 0xc3: + case 0xc4: case 0xc5: case 0xc6: case 0xc7: + case 0xc8: case 0xc9: case 0xca: case 0xcb: + case 0xcc: case 0xcd: case 0xce: case 0xcf: + + case 0xd0: case 0xd1: case 0xd2: case 0xd3: + case 0xd4: case 0xd5: case 0xd6: case 0xd7: + + case 'I': + return true; + + default: + return false; + } + } + + abstract class State { + State _next; + + State() + { + } + + State(State next) + { + _next = next; + } + + abstract State next(int ch); + + boolean isShift(Object value) + { + return false; + } + + State shift(Object value) + { + return this; + } + + int depth() + { + if (_next != null) + return _next.depth(); + else + return HessianDebugState.this.getDepth(); + } + + void printIndent(int depth) + { + if (_isNewline) { + for (int i = _column; i < depth() + depth; i++) { + _dbg.print(" "); + _column++; + } + } + } + + void print(String string) + { + print(0, string); + } + + void print(int depth, String string) + { + printIndent(depth); + + _dbg.print(string); + _isNewline = false; + _isObject = false; + + int p = string.lastIndexOf('\n'); + if (p > 0) + _column = string.length() - p - 1; + else + _column += string.length(); + } + + void println(String string) + { + println(0, string); + } + + void println(int depth, String string) + { + printIndent(depth); + + _dbg.println(string); + _dbg.flush(); + _isNewline = true; + _isObject = false; + _column = 0; + } + + void println() + { + if (! _isNewline) { + _dbg.println(); + _dbg.flush(); + } + + _isNewline = true; + _isObject = false; + _column = 0; + } + + void printObject(String string) + { + if (_isObject) + println(); + + printIndent(0); + + _dbg.print(string); + _dbg.flush(); + + _column += string.length(); + + _isNewline = false; + _isObject = true; + } + + protected State nextObject(int ch) + { + switch (ch) { + case -1: + println(); + return this; + + case 'N': + if (isShift(null)) + return shift(null); + else { + printObject("null"); + return this; + } + + case 'T': + if (isShift(Boolean.TRUE)) + return shift(Boolean.TRUE); + else { + printObject("true"); + return this; + } + + case 'F': + if (isShift(Boolean.FALSE)) + return shift(Boolean.FALSE); + else { + printObject("false"); + return this; + } + + case 0x80: case 0x81: case 0x82: case 0x83: + case 0x84: case 0x85: case 0x86: case 0x87: + case 0x88: case 0x89: case 0x8a: case 0x8b: + case 0x8c: case 0x8d: case 0x8e: case 0x8f: + + case 0x90: case 0x91: case 0x92: case 0x93: + case 0x94: case 0x95: case 0x96: case 0x97: + case 0x98: case 0x99: case 0x9a: case 0x9b: + case 0x9c: case 0x9d: case 0x9e: case 0x9f: + + case 0xa0: case 0xa1: case 0xa2: case 0xa3: + case 0xa4: case 0xa5: case 0xa6: case 0xa7: + case 0xa8: case 0xa9: case 0xaa: case 0xab: + case 0xac: case 0xad: case 0xae: case 0xaf: + + case 0xb0: case 0xb1: case 0xb2: case 0xb3: + case 0xb4: case 0xb5: case 0xb6: case 0xb7: + case 0xb8: case 0xb9: case 0xba: case 0xbb: + case 0xbc: case 0xbd: case 0xbe: case 0xbf: + { + Integer value = new Integer(ch - 0x90); + + if (isShift(value)) + return shift(value); + else { + printObject(value.toString()); + return this; + } + } + + case 0xc0: case 0xc1: case 0xc2: case 0xc3: + case 0xc4: case 0xc5: case 0xc6: case 0xc7: + case 0xc8: case 0xc9: case 0xca: case 0xcb: + case 0xcc: case 0xcd: case 0xce: case 0xcf: + return new IntegerState(this, "int", ch - 0xc8, 3); + + case 0xd0: case 0xd1: case 0xd2: case 0xd3: + case 0xd4: case 0xd5: case 0xd6: case 0xd7: + return new IntegerState(this, "int", ch - 0xd4, 2); + + case 'I': + return new IntegerState(this, "int"); + + case 0xd8: case 0xd9: case 0xda: case 0xdb: + case 0xdc: case 0xdd: case 0xde: case 0xdf: + case 0xe0: case 0xe1: case 0xe2: case 0xe3: + case 0xe4: case 0xe5: case 0xe6: case 0xe7: + case 0xe8: case 0xe9: case 0xea: case 0xeb: + case 0xec: case 0xed: case 0xee: case 0xef: + { + Long value = new Long(ch - 0xe0); + + if (isShift(value)) + return shift(value); + else { + printObject(value.toString() + "L"); + return this; + } + } + + case 0xf0: case 0xf1: case 0xf2: case 0xf3: + case 0xf4: case 0xf5: case 0xf6: case 0xf7: + case 0xf8: case 0xf9: case 0xfa: case 0xfb: + case 0xfc: case 0xfd: case 0xfe: case 0xff: + return new LongState(this, "long", ch - 0xf8, 7); + + case 0x38: case 0x39: case 0x3a: case 0x3b: + case 0x3c: case 0x3d: case 0x3e: case 0x3f: + return new LongState(this, "long", ch - 0x3c, 6); + + case BC_LONG_INT: + return new LongState(this, "long", 0, 4); + + case 'L': + return new LongState(this, "long"); + + case 0x5b: case 0x5c: + { + Double value = new Double(ch - 0x5b); + + if (isShift(value)) + return shift(value); + else { + printObject(value.toString()); + return this; + } + } + + case 0x5d: + return new DoubleIntegerState(this, 3); + + case 0x5e: + return new DoubleIntegerState(this, 2); + + case 0x5f: + return new MillsState(this); + + case 'D': + return new DoubleState(this); + + case 'Q': + return new RefState(this); + + case BC_DATE: + return new DateState(this); + + case BC_DATE_MINUTE: + return new DateState(this, true); + + case 0x00: + { + String value = "\"\""; + + if (isShift(value)) + return shift(value); + else { + printObject(value.toString()); + return this; + } + } + + case 0x01: case 0x02: case 0x03: + case 0x04: case 0x05: case 0x06: case 0x07: + case 0x08: case 0x09: case 0x0a: case 0x0b: + case 0x0c: case 0x0d: case 0x0e: case 0x0f: + + case 0x10: case 0x11: case 0x12: case 0x13: + case 0x14: case 0x15: case 0x16: case 0x17: + case 0x18: case 0x19: case 0x1a: case 0x1b: + case 0x1c: case 0x1d: case 0x1e: case 0x1f: + return new StringState(this, 'S', ch); + + case 0x30: case 0x31: case 0x32: case 0x33: + return new StringState(this, 'S', ch - 0x30, true); + + case 'R': + return new StringState(this, 'S', false); + + case 'S': + return new StringState(this, 'S', true); + + case 0x20: + { + String value = "binary(0)"; + + if (isShift(value)) + return shift(value); + else { + printObject(value.toString()); + return this; + } + } + + case 0x21: case 0x22: case 0x23: + case 0x24: case 0x25: case 0x26: case 0x27: + case 0x28: case 0x29: case 0x2a: case 0x2b: + case 0x2c: case 0x2d: case 0x2e: case 0x2f: + return new BinaryState(this, 'B', ch - 0x20); + + case 0x34: case 0x35: case 0x36: case 0x37: + return new BinaryState(this, 'B', ch - 0x34, true); + + case 'A': + return new BinaryState(this, 'B', false); + + case 'B': + return new BinaryState(this, 'B', true); + + case 'M': + return new MapState(this, _refId++); + + case 'H': + return new MapState(this, _refId++, false); + + case BC_LIST_VARIABLE: + return new ListState(this, _refId++, true); + + case BC_LIST_VARIABLE_UNTYPED: + return new ListState(this, _refId++, false); + + case BC_LIST_FIXED: + return new CompactListState(this, _refId++, true); + + case BC_LIST_FIXED_UNTYPED: + return new CompactListState(this, _refId++, false); + + case 0x70: case 0x71: case 0x72: case 0x73: + case 0x74: case 0x75: case 0x76: case 0x77: + return new CompactListState(this, _refId++, true, ch - 0x70); + + case 0x78: case 0x79: case 0x7a: case 0x7b: + case 0x7c: case 0x7d: case 0x7e: case 0x7f: + return new CompactListState(this, _refId++, false, ch - 0x78); + + case 'C': + return new ObjectDefState(this); + + case 0x60: case 0x61: case 0x62: case 0x63: + case 0x64: case 0x65: case 0x66: case 0x67: + case 0x68: case 0x69: case 0x6a: case 0x6b: + case 0x6c: case 0x6d: case 0x6e: case 0x6f: + return new ObjectState(this, _refId++, ch - 0x60); + + case 'O': + return new ObjectState(this, _refId++); + + default: + return this; + } + } + } + + abstract class State1 extends State { + State1() + { + } + + State1(State next) + { + super(next); + } + + protected State nextObject(int ch) + { + switch (ch) { + case -1: + println(); + return this; + + case 'N': + if (isShift(null)) + return shift(null); + else { + printObject("null"); + return this; + } + + case 'T': + if (isShift(Boolean.TRUE)) + return shift(Boolean.TRUE); + else { + printObject("true"); + return this; + } + + case 'F': + if (isShift(Boolean.FALSE)) + return shift(Boolean.FALSE); + else { + printObject("false"); + return this; + } + + case 'I': + return new IntegerState(this, "int"); + + case 'L': + return new LongState(this, "long"); + + case 'D': + return new DoubleState(this); + + case 'Q': + return new RefState(this); + + case 'd': + return new DateState(this); + + case 's': + return new StringState(this, 'S', false); + + case 'S': + return new StringState(this, 'S', true); + + case 'b': + return new BinaryState(this, 'B', false); + + case 'B': + return new BinaryState(this, 'B', true); + + case 'M': + return new MapState1(this, _refId++); + + case 'V': + return new ListState1(this, _refId++); + + case 'R': + return new IntegerState(new RefState1(this), "ref"); + + default: + printObject("x" + String.format("%02x", ch)); + return this; + } + } + } + + class InitialState extends State { + State next(int ch) + { + return nextObject(ch); + } + } + + class InitialState1 extends State1 { + State next(int ch) + { + return nextObject(ch); + } + } + + class Top1State extends State1 { + State next(int ch) + { + println(); + + if (ch == 'r') { + return new ReplyState1(this); + } + else if (ch == 'c') { + return new CallState1(this); + } + else + return nextObject(ch); + } + } + + class Top2State extends State { + State next(int ch) + { + println(); + + if (ch == 'R') { + return new Reply2State(this); + } + else if (ch == 'F') { + return new Fault2State(this); + } + else if (ch == 'C') { + return new Call2State(this); + } + else if (ch == 'H') { + return new Hessian2State(this); + } + else if (ch == 'r') { + return new ReplyState1(this); + } + else if (ch == 'c') { + return new CallState1(this); + } + else + return nextObject(ch); + } + } + + class IntegerState extends State { + String _typeCode; + + int _length; + int _value; + + IntegerState(State next, String typeCode) + { + super(next); + + _typeCode = typeCode; + } + + IntegerState(State next, String typeCode, int value, int length) + { + super(next); + + _typeCode = typeCode; + + _value = value; + _length = length; + } + + State next(int ch) + { + _value = 256 * _value + (ch & 0xff); + + if (++_length == 4) { + Integer value = new Integer(_value); + + if (_next.isShift(value)) + return _next.shift(value); + else { + printObject(value.toString()); + + return _next; + } + } + else + return this; + } + } + + class LongState extends State { + String _typeCode; + + int _length; + long _value; + + LongState(State next, String typeCode) + { + super(next); + + _typeCode = typeCode; + } + + LongState(State next, String typeCode, long value, int length) + { + super(next); + + _typeCode = typeCode; + + _value = value; + _length = length; + } + + State next(int ch) + { + _value = 256 * _value + (ch & 0xff); + + if (++_length == 8) { + Long value = new Long(_value); + + if (_next.isShift(value)) + return _next.shift(value); + else { + printObject(value.toString() + "L"); + + return _next; + } + } + else + return this; + } + } + + class DoubleIntegerState extends State { + int _length; + int _value; + boolean _isFirst = true; + + DoubleIntegerState(State next, int length) + { + super(next); + + _length = length; + } + + State next(int ch) + { + if (_isFirst) + _value = (byte) ch; + else + _value = 256 * _value + (ch & 0xff); + + _isFirst = false; + + if (++_length == 4) { + Double value = new Double(_value); + + if (_next.isShift(value)) + return _next.shift(value); + else { + printObject(value.toString()); + + return _next; + } + } + else + return this; + } + } + + class RefState extends State { + String _typeCode; + + int _length; + int _value; + + RefState(State next) + { + super(next); + } + + RefState(State next, String typeCode) + { + super(next); + + _typeCode = typeCode; + } + + RefState(State next, String typeCode, int value, int length) + { + super(next); + + _typeCode = typeCode; + + _value = value; + _length = length; + } + + @Override + boolean isShift(Object o) + { + return true; + } + + @Override + State shift(Object o) + { + println("ref #" + o); + + return _next; + } + + @Override + State next(int ch) + { + return nextObject(ch); + } + } + + class RefState1 extends State { + String _typeCode; + + RefState1(State next) + { + super(next); + } + + @Override + boolean isShift(Object o) + { + return true; + } + + @Override + State shift(Object o) + { + println("ref #" + o); + + return _next; + } + + @Override + State next(int ch) + { + return nextObject(ch); + } + } + + class DateState extends State { + int _length; + long _value; + boolean _isMinute; + + DateState(State next) + { + super(next); + } + + DateState(State next, boolean isMinute) + { + super(next); + + _length = 4; + _isMinute = isMinute; + } + + + State next(int ch) + { + _value = 256 * _value + (ch & 0xff); + + if (++_length == 8) { + java.util.Date value; + + if (_isMinute) + value = new java.util.Date(_value * 60000L); + else + value = new java.util.Date(_value); + + if (_next.isShift(value)) + return _next.shift(value); + else { + printObject(value.toString()); + + return _next; + } + } + else + return this; + } + } + + class DoubleState extends State { + int _length; + long _value; + + DoubleState(State next) + { + super(next); + } + + State next(int ch) + { + _value = 256 * _value + (ch & 0xff); + + if (++_length == 8) { + Double value = Double.longBitsToDouble(_value); + + if (_next.isShift(value)) + return _next.shift(value); + else { + printObject(value.toString()); + + return _next; + } + } + else + return this; + } + } + + class MillsState extends State { + int _length; + int _value; + + MillsState(State next) + { + super(next); + } + + State next(int ch) + { + _value = 256 * _value + (ch & 0xff); + + if (++_length == 4) { + Double value = 0.001 * _value; + + if (_next.isShift(value)) + return _next.shift(value); + else { + printObject(value.toString()); + + return _next; + } + } + else + return this; + } + } + + class StringState extends State { + private static final int TOP = 0; + private static final int UTF_2_1 = 1; + private static final int UTF_3_1 = 2; + private static final int UTF_3_2 = 3; + + char _typeCode; + + StringBuilder _value = new StringBuilder(); + int _lengthIndex; + int _length; + boolean _isLastChunk; + + int _utfState; + char _ch; + + StringState(State next, char typeCode, boolean isLastChunk) + { + super(next); + + _typeCode = typeCode; + _isLastChunk = isLastChunk; + } + + StringState(State next, char typeCode, int length) + { + super(next); + + _typeCode = typeCode; + _isLastChunk = true; + _length = length; + _lengthIndex = 2; + } + + StringState(State next, char typeCode, int length, boolean isLastChunk) + { + super(next); + + _typeCode = typeCode; + _isLastChunk = isLastChunk; + _length = length; + _lengthIndex = 1; + } + + State next(int ch) + { + if (_lengthIndex < 2) { + _length = 256 * _length + (ch & 0xff); + + if (++_lengthIndex == 2 && _length == 0 && _isLastChunk) { + if (_next.isShift(_value.toString())) + return _next.shift(_value.toString()); + else { + printObject("\"" + _value + "\""); + return _next; + } + } + else + return this; + } + else if (_length == 0) { + if (ch == 's' || ch == 'x') { + _isLastChunk = false; + _lengthIndex = 0; + return this; + } + else if (ch == 'S' || ch == 'X') { + _isLastChunk = true; + _lengthIndex = 0; + return this; + } + else if (ch == 0x00) { + if (_next.isShift(_value.toString())) + return _next.shift(_value.toString()); + else { + printObject("\"" + _value + "\""); + return _next; + } + } + else if (0x00 <= ch && ch < 0x20) { + _isLastChunk = true; + _lengthIndex = 2; + _length = ch & 0xff; + return this; + } + else if (0x30 <= ch && ch < 0x34) { + _isLastChunk = true; + _lengthIndex = 1; + _length = (ch - 0x30); + return this; + } + else { + println(String.valueOf((char) ch) + ": unexpected character"); + return _next; + } + } + + switch (_utfState) { + case TOP: + if (ch < 0x80) { + _length--; + + _value.append((char) ch); + } + else if (ch < 0xe0) { + _ch = (char) ((ch & 0x1f) << 6); + _utfState = UTF_2_1; + } + else { + _ch = (char) ((ch & 0xf) << 12); + _utfState = UTF_3_1; + } + break; + + case UTF_2_1: + case UTF_3_2: + _ch += ch & 0x3f; + _value.append(_ch); + _length--; + _utfState = TOP; + break; + + case UTF_3_1: + _ch += (char) ((ch & 0x3f) << 6); + _utfState = UTF_3_2; + break; + } + + if (_length == 0 && _isLastChunk) { + if (_next.isShift(_value.toString())) + return _next.shift(_value.toString()); + else { + printObject("\"" + _value + "\""); + + return _next; + } + } + else + return this; + } + } + + class BinaryState extends State { + char _typeCode; + + int _totalLength; + + int _lengthIndex; + int _length; + boolean _isLastChunk; + + BinaryState(State next, char typeCode, boolean isLastChunk) + { + super(next); + + _typeCode = typeCode; + _isLastChunk = isLastChunk; + } + + BinaryState(State next, char typeCode, int length) + { + super(next); + + _typeCode = typeCode; + _isLastChunk = true; + _length = length; + _lengthIndex = 2; + } + + BinaryState(State next, char typeCode, int length, boolean isLastChunk) + { + super(next); + + _typeCode = typeCode; + _isLastChunk = isLastChunk; + _length = length; + _lengthIndex = 1; + } + + State next(int ch) + { + if (_lengthIndex < 2) { + _length = 256 * _length + (ch & 0xff); + + if (++_lengthIndex == 2 && _length == 0 && _isLastChunk) { + String value = "binary(" + _totalLength + ")"; + + if (_next.isShift(value)) + return _next.shift(value); + else { + printObject(value); + return _next; + } + } + else + return this; + } + else if (_length == 0) { + if (ch == 'b') { + _isLastChunk = false; + _lengthIndex = 0; + return this; + } + else if (ch == 'B') { + _isLastChunk = true; + _lengthIndex = 0; + return this; + } + else if (ch == 0x20) { + String value = "binary(" + _totalLength + ")"; + + if (_next.isShift(value)) + return _next.shift(value); + else { + printObject(value); + return _next; + } + } + else if (0x20 <=ch && ch < 0x30) { + _isLastChunk = true; + _lengthIndex = 2; + _length = (ch & 0xff) - 0x20; + return this; + } + else { + println(String.valueOf((char) ch) + ": unexpected character"); + return _next; + } + } + + _length--; + _totalLength++; + + if (_length == 0 && _isLastChunk) { + String value = "binary(" + _totalLength + ")"; + + if (_next.isShift(value)) + return _next.shift(value); + else { + printObject(value); + + return _next; + } + } + else + return this; + } + } + + class MapState extends State { + private static final int TYPE = 0; + private static final int KEY = 1; + private static final int VALUE = 2; + + private int _refId; + + private int _state; + private int _valueDepth; + private boolean _hasData; + + MapState(State next, int refId) + { + super(next); + + _refId = refId; + _state = TYPE; + } + + MapState(State next, int refId, boolean isType) + { + super(next); + + _refId = refId; + + if (isType) + _state = TYPE; + else { + printObject("map (#" + _refId + ")"); + _state = VALUE; + } + } + + @Override + boolean isShift(Object value) + { + return _state == TYPE; + } + + @Override + State shift(Object type) + { + if (_state == TYPE) { + if (type instanceof String) { + _typeDefList.add((String) type); + } + else if (type instanceof Integer) { + int iValue = (Integer) type; + + if (iValue >= 0 && iValue < _typeDefList.size()) + type = _typeDefList.get(iValue); + } + + printObject("map " + type + " (#" + _refId + ")"); + + _state = VALUE; + + return this; + } + else + throw new IllegalStateException(); + } + + @Override + int depth() + { + if (_state == TYPE) + return _next.depth(); + else if (_state == KEY) + return _next.depth() + 2; + else + return _valueDepth; + } + + State next(int ch) + { + switch (_state) { + case TYPE: + return nextObject(ch); + + case VALUE: + if (ch == 'Z') { + if (_hasData) + println(); + + return _next; + } + else { + if (_hasData) + println(); + + _hasData = true; + _state = KEY; + + return nextObject(ch); + } + + case KEY: + print(" => "); + _isObject = false; + _valueDepth = _column; + + _state = VALUE; + + return nextObject(ch); + + default: + throw new IllegalStateException(); + } + } + } + + class MapState1 extends State1 { + private static final int TYPE = 0; + private static final int KEY = 1; + private static final int VALUE = 2; + + private int _refId; + + private int _state; + private int _valueDepth; + private boolean _hasData; + + MapState1(State next, int refId) + { + super(next); + + _refId = refId; + _state = TYPE; + } + + MapState1(State next, int refId, boolean isType) + { + super(next); + + _refId = refId; + + if (isType) + _state = TYPE; + else { + printObject("map (#" + _refId + ")"); + _state = VALUE; + } + } + + @Override + boolean isShift(Object value) + { + return _state == TYPE; + } + + @Override + State shift(Object type) + { + if (_state == TYPE) { + if (type instanceof String) { + _typeDefList.add((String) type); + } + else if (type instanceof Integer) { + int iValue = (Integer) type; + + if (iValue >= 0 && iValue < _typeDefList.size()) + type = _typeDefList.get(iValue); + } + + printObject("map " + type + " (#" + _refId + ")"); + + _state = VALUE; + + return this; + } + else + throw new IllegalStateException(); + } + + @Override + int depth() + { + if (_state == TYPE) + return _next.depth(); + else if (_state == KEY) + return _next.depth() + 2; + else + return _valueDepth; + } + + State next(int ch) + { + switch (_state) { + case TYPE: + if (ch == 't') { + return new StringState(this, 't', true); + } + else if (ch == 'z') { + println("map (#" + _refId + ")"); + return _next; + } + else { + println("map (#" + _refId + ")"); + _hasData = true; + _state = KEY; + return nextObject(ch); + } + + case VALUE: + if (ch == 'z') { + if (_hasData) + println(); + + return _next; + } + else { + if (_hasData) + println(); + + _hasData = true; + _state = KEY; + + return nextObject(ch); + } + + case KEY: + print(" => "); + _isObject = false; + _valueDepth = _column; + + _state = VALUE; + + return nextObject(ch); + + default: + throw new IllegalStateException(); + } + } + } + + class ObjectDefState extends State { + private static final int TYPE = 1; + private static final int COUNT = 2; + private static final int FIELD = 3; + private static final int COMPLETE = 4; + + private int _refId; + + private int _state; + private boolean _hasData; + private int _count; + + private String _type; + private ArrayList _fields = new ArrayList(); + + ObjectDefState(State next) + { + super(next); + + _state = TYPE; + } + + @Override + boolean isShift(Object value) + { + return true; + } + + @Override + State shift(Object object) + { + if (_state == TYPE) { + _type = (String) object; + + print("/* defun " + _type + " ["); + + _objectDefList.add(new ObjectDef(_type, _fields)); + + _state = COUNT; + } + else if (_state == COUNT) { + _count = (Integer) object; + + _state = FIELD; + } + else if (_state == FIELD) { + String field = (String) object; + + _count--; + + _fields.add(field); + + if (_fields.size() == 1) + print(field); + else + print(", " + field); + } + else { + throw new UnsupportedOperationException(); + } + + return this; + } + + @Override + int depth() + { + if (_state <= TYPE) + return _next.depth(); + else + return _next.depth() + 2; + } + + State next(int ch) + { + switch (_state) { + case TYPE: + return nextObject(ch); + + case COUNT: + return nextObject(ch); + + case FIELD: + if (_count == 0) { + println("] */"); + _next.printIndent(0); + + return _next.nextObject(ch); + } + else + return nextObject(ch); + + default: + throw new IllegalStateException(); + } + } + } + + class ObjectState extends State { + private static final int TYPE = 0; + private static final int FIELD = 1; + + private int _refId; + + private int _state; + private ObjectDef _def; + private int _count; + private int _fieldDepth; + + ObjectState(State next, int refId) + { + super(next); + + _refId = refId; + _state = TYPE; + } + + ObjectState(State next, int refId, int def) + { + super(next); + + _refId = refId; + _state = FIELD; + + if (def < 0 || _objectDefList.size() <= def) { + throw new IllegalStateException(def + " is an unknown object type"); + } + + _def = _objectDefList.get(def); + + println("object " + _def.getType() + " (#" + _refId + ")"); + } + + @Override + boolean isShift(Object value) + { + if (_state == TYPE) + return true; + else + return false; + } + + @Override + State shift(Object object) + { + if (_state == TYPE) { + int def = (Integer) object; + + _def = _objectDefList.get(def); + + println("object " + _def.getType() + " (#" + _refId + ")"); + + _state = FIELD; + + if (_def.getFields().size() == 0) + return _next; + } + + return this; + } + + @Override + int depth() + { + if (_state <= TYPE) + return _next.depth(); + else + return _fieldDepth; + } + + State next(int ch) + { + switch (_state) { + case TYPE: + return nextObject(ch); + + case FIELD: + if (_def.getFields().size() <= _count) + return _next.next(ch); + + _fieldDepth = _next.depth() + 2; + println(); + print(_def.getFields().get(_count++) + ": "); + + _fieldDepth = _column; + + _isObject = false; + return nextObject(ch); + + default: + throw new IllegalStateException(); + } + } + } + + class ListState1 extends State1 { + private static final int TYPE = 0; + private static final int LENGTH = 1; + private static final int VALUE = 2; + + private int _refId; + + private int _state; + private boolean _hasData; + private int _count; + private int _valueDepth; + + ListState1(State next, int refId) + { + super(next); + + _refId = refId; + + _state = TYPE; + } + + @Override + boolean isShift(Object value) + { + return _state == TYPE || _state == LENGTH; + } + + @Override + State shift(Object object) + { + if (_state == TYPE) { + Object type = object; + + if (type instanceof String) { + _typeDefList.add((String) type); + } + else if (object instanceof Integer) { + int index = (Integer) object; + + if (index >= 0 && index < _typeDefList.size()) + type = _typeDefList.get(index); + else + type = "type-unknown(" + index + ")"; + } + + printObject("list " + type + "(#" + _refId + ")"); + + _state = VALUE; + + return this; + } + else if (_state == LENGTH) { + _state = VALUE; + + return this; + } + else + return this; + } + + @Override + int depth() + { + if (_state <= LENGTH) + return _next.depth(); + else if (_state == VALUE) + return _valueDepth; + else + return _next.depth() + 2; + } + + State next(int ch) + { + switch (_state) { + case TYPE: + if (ch == 'z') { + printObject("list (#" + _refId + ")"); + + return _next; + } + else if (ch == 't') { + return new StringState(this, 't', true); + } + else { + printObject("list (#" + _refId + ")"); + printObject(" " + _count++ + ": "); + _valueDepth = _column; + _isObject = false; + _state = VALUE; + + return nextObject(ch); + } + + case VALUE: + if (ch == 'z') { + if (_count > 0) + println(); + + return _next; + } + else { + _valueDepth = _next.depth() + 2; + println(); + printObject(_count++ + ": "); + _valueDepth = _column; + _isObject = false; + + return nextObject(ch); + } + + default: + throw new IllegalStateException(); + } + } + } + + class ListState extends State { + private static final int TYPE = 0; + private static final int LENGTH = 1; + private static final int VALUE = 2; + + private int _refId; + + private int _state; + private boolean _hasData; + private int _count; + private int _valueDepth; + + ListState(State next, int refId, boolean isType) + { + super(next); + + _refId = refId; + + if (isType) + _state = TYPE; + else { + printObject("list (#" + _refId + ")"); + _state = VALUE; + } + } + + @Override + boolean isShift(Object value) + { + return _state == TYPE || _state == LENGTH; + } + + @Override + State shift(Object object) + { + if (_state == TYPE) { + Object type = object; + + if (type instanceof String) { + _typeDefList.add((String) type); + } + else if (object instanceof Integer) { + int index = (Integer) object; + + if (index >= 0 && index < _typeDefList.size()) + type = _typeDefList.get(index); + else + type = "type-unknown(" + index + ")"; + } + + printObject("list " + type + "(#" + _refId + ")"); + + _state = VALUE; + + return this; + } + else if (_state == LENGTH) { + _state = VALUE; + + return this; + } + else + return this; + } + + @Override + int depth() + { + if (_state <= LENGTH) + return _next.depth(); + else if (_state == VALUE) + return _valueDepth; + else + return _next.depth() + 2; + } + + State next(int ch) + { + switch (_state) { + case TYPE: + return nextObject(ch); + + case VALUE: + if (ch == 'Z') { + if (_count > 0) + println(); + + return _next; + } + else { + _valueDepth = _next.depth() + 2; + println(); + printObject(_count++ + ": "); + _valueDepth = _column; + _isObject = false; + + return nextObject(ch); + } + + default: + throw new IllegalStateException(); + } + } + } + + class CompactListState extends State { + private static final int TYPE = 0; + private static final int LENGTH = 1; + private static final int VALUE = 2; + + private int _refId; + + private boolean _isTyped; + private boolean _isLength; + + private int _state; + private boolean _hasData; + private int _length; + private int _count; + private int _valueDepth; + + CompactListState(State next, int refId, boolean isTyped) + { + super(next); + + _isTyped = isTyped; + _refId = refId; + + if (isTyped) + _state = TYPE; + else + _state = LENGTH; + } + + CompactListState(State next, int refId, boolean isTyped, int length) + { + super(next); + + _isTyped = isTyped; + _refId = refId; + _length = length; + + _isLength = true; + + if (isTyped) + _state = TYPE; + else { + printObject("list (#" + _refId + ")"); + + _state = VALUE; + } + } + + @Override + boolean isShift(Object value) + { + return _state == TYPE || _state == LENGTH; + } + + @Override + State shift(Object object) + { + if (_state == TYPE) { + Object type = object; + + if (object instanceof Integer) { + int index = (Integer) object; + + if (index >= 0 && index < _typeDefList.size()) + type = _typeDefList.get(index); + else + type = "type-unknown(" + index + ")"; + } + else if (object instanceof String) + _typeDefList.add((String) object); + + printObject("list " + type + " (#" + _refId + ")"); + + if (_isLength) { + _state = VALUE; + + if (_length == 0) + return _next; + } + else + _state = LENGTH; + + return this; + } + else if (_state == LENGTH) { + _length = (Integer) object; + + if (! _isTyped) + printObject("list (#" + _refId + ")"); + + _state = VALUE; + + if (_length == 0) + return _next; + else + return this; + } + else + return this; + } + + @Override + int depth() + { + if (_state <= LENGTH) + return _next.depth(); + else if (_state == VALUE) + return _valueDepth; + else + return _next.depth() + 2; + } + + State next(int ch) + { + switch (_state) { + case TYPE: + return nextObject(ch); + + case LENGTH: + return nextObject(ch); + + case VALUE: + if (_length <= _count) + return _next.next(ch); + else { + _valueDepth = _next.depth() + 2; + println(); + printObject(_count++ + ": "); + _valueDepth = _column; + _isObject = false; + + return nextObject(ch); + } + + default: + throw new IllegalStateException(); + } + } + } + + class Hessian2State extends State { + private static final int MAJOR = 0; + private static final int MINOR = 1; + + private int _state; + private int _major; + private int _minor; + + Hessian2State(State next) + { + super(next); + } + + int depth() + { + return _next.depth() + 2; + } + + State next(int ch) + { + switch (_state) { + case MAJOR: + _major = ch; + _state = MINOR; + return this; + + case MINOR: + _minor = ch; + println(-2, "Hessian " + _major + "." + _minor); + return _next; + + default: + throw new IllegalStateException(); + } + } + } + + class CallState1 extends State1 { + private static final int MAJOR = 0; + private static final int MINOR = 1; + private static final int HEADER = 2; + private static final int METHOD = 3; + private static final int VALUE = 4; + private static final int ARG = 5; + + private int _state; + private int _major; + private int _minor; + + CallState1(State next) + { + super(next); + } + + int depth() + { + return _next.depth() + 2; + } + + State next(int ch) + { + switch (_state) { + case MAJOR: + _major = ch; + _state = MINOR; + return this; + + case MINOR: + _minor = ch; + _state = HEADER; + println(-2, "call " + _major + "." + _minor); + return this; + + case HEADER: + if (ch == 'H') { + println(); + print("header "); + _isObject = false; + _state = VALUE; + return new StringState(this, 'H', true); + } + else if (ch == 'm') { + println(); + print("method "); + _isObject = false; + _state = ARG; + return new StringState(this, 'm', true); + } + else { + println((char) ch + ": unexpected char"); + return popStack(); + } + + case VALUE: + print(" => "); + _isObject = false; + _state = HEADER; + return nextObject(ch); + + case ARG: + if (ch == 'z') { + println(); + return _next; + } + else + return nextObject(ch); + + default: + throw new IllegalStateException(); + } + } + } + + class Call2State extends State { + private static final int METHOD = 0; + private static final int COUNT = 1; + private static final int ARG = 2; + + private int _state = METHOD; + private int _i; + private int _count; + + Call2State(State next) + { + super(next); + } + + int depth() + { + return _next.depth() + 5; + } + + @Override + boolean isShift(Object value) + { + return _state != ARG; + } + + @Override + State shift(Object object) + { + if (_state == METHOD) { + println(-5, "Call " + object); + + _state = COUNT; + return this; + } + else if (_state == COUNT) { + Integer count = (Integer) object; + + _count = count; + + _state = ARG; + + if (_count == 0) { + return _next; + } + else + return this; + } + else { + return this; + } + } + + @Override + State next(int ch) + { + switch (_state) { + case COUNT: + return nextObject(ch); + + case METHOD: + return nextObject(ch); + + case ARG: + if (_count <= _i) { + println(); + return _next.next(ch); + } + else { + println(); + print(-3, _i++ + ": "); + + return nextObject(ch); + } + + default: + throw new IllegalStateException(); + } + } + } + + class ReplyState1 extends State1 { + private static final int MAJOR = 0; + private static final int MINOR = 1; + private static final int HEADER = 2; + private static final int VALUE = 3; + private static final int END = 4; + + private int _state; + private int _major; + private int _minor; + + ReplyState1(State next) + { + _next = next; + } + + int depth() + { + return _next.depth() + 2; + } + + State next(int ch) + { + switch (_state) { + case MAJOR: + if (ch == 't' || ch == 'S') + return new RemoteState(this).next(ch); + + _major = ch; + _state = MINOR; + return this; + + case MINOR: + _minor = ch; + _state = HEADER; + println(-2, "reply " + _major + "." + _minor); + return this; + + case HEADER: + if (ch == 'H') { + _state = VALUE; + return new StringState(this, 'H', true); + } + else if (ch == 'f') { + print("fault "); + _isObject = false; + _state = END; + return new MapState(this, 0); + } + else { + _state = END; + return nextObject(ch); + } + + case VALUE: + _state = HEADER; + return nextObject(ch); + + case END: + println(); + if (ch == 'z') { + return _next; + } + else + return _next.next(ch); + + default: + throw new IllegalStateException(); + } + } + } + + class Reply2State extends State { + Reply2State(State next) + { + super(next); + + println(-2, "Reply"); + } + + int depth() + { + return _next.depth() + 2; + } + + @Override + State next(int ch) + { + if (ch < 0) { + println(); + return _next; + } + else { + return nextObject(ch); + } + } + } + + class Fault2State extends State { + Fault2State(State next) + { + super(next); + + println(-2, "Fault"); + } + + int depth() + { + return _next.depth() + 2; + } + + @Override + State next(int ch) + { + return nextObject(ch); + } + } + + class IndirectState extends State { + IndirectState(State next) + { + super(next); + } + + boolean isShift(Object object) + { + return _next.isShift(object); + } + + State shift(Object object) + { + return _next.shift(object); + } + + State next(int ch) + { + return nextObject(ch); + } + } + + class RemoteState extends State { + private static final int TYPE = 0; + private static final int VALUE = 1; + private static final int END = 2; + + private int _state; + private int _major; + private int _minor; + + RemoteState(State next) + { + super(next); + } + + State next(int ch) + { + switch (_state) { + case TYPE: + println(-1, "remote"); + if (ch == 't') { + _state = VALUE; + return new StringState(this, 't', false); + } + else { + _state = END; + return nextObject(ch); + } + + case VALUE: + _state = END; + return _next.nextObject(ch); + + case END: + return _next.next(ch); + + default: + throw new IllegalStateException(); + } + } + } + + class StreamingState extends State { + private int _digit; + private int _code; + private int _length; + private boolean _isLast; + private boolean _isFirst = true; + + private State _childState; + + StreamingState(State next, boolean isLast) + { + super(next); + + _isLast = isLast; + _childState = new InitialState(); + } + + State next(int ch) + { + if (_digit >= 0) { + if (_digit == 0) { + _code = ch; + _digit = 1; + _length = 0; + + return this; + } + else if ((ch & 0x80) == 0x80) { + _length = 128 * _length + (ch & 0x7f); + + return this; + } + else { + _length = 128 * _length + (ch & 0x7f); + _digit = -1; + } + + if (_isFirst) + println(-1, "packet-start(" + _length + ")"); + _isFirst = false; + + if (_length == 0) { + _isFirst = true; + println(-1, ""); + println(-1, "packet-end"); + _refId = 0; + _digit = 0; + } + + return this; + } + else if (_length == 0) { + _digit = 0; + + return this; + } + + _childState = _childState.next(ch); + + _length--; + + if (_length <= 0) { + _digit = 0; + _length = 0; + return this; + } + else + return this; + } + } + + static class ObjectDef { + private String _type; + private ArrayList _fields; + + ObjectDef(String type, ArrayList fields) + { + _type = type; + _fields = fields; + } + + String getType() + { + return _type; + } + + ArrayList getFields() + { + return _fields; + } + } +} diff --git a/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/HessianEnvelope.java b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/HessianEnvelope.java new file mode 100644 index 0000000000..9f21cd06c9 --- /dev/null +++ b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/HessianEnvelope.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2001-2004 Caucho Technology, Inc. All rights reserved. + * + * The Apache Software License, Version 1.1 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Caucho Technology (http://www.caucho.com/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Hessian", "Resin", and "Caucho" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * info@caucho.com. + * + * 5. Products derived from this software may not be called "Resin" + * nor may "Resin" appear in their names without prior written + * permission of Caucho Technology. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Scott Ferguson + */ + +package com.caucho.hessian4.io; + +import java.io.IOException; + +/** + * Factory class for wrapping and unwrapping hessian streams. + */ +abstract public class HessianEnvelope { + /** + * Wrap the Hessian output stream in an envelope. + */ + abstract public Hessian2Output wrap(Hessian2Output out) + throws IOException; + + /** + * Unwrap the Hessian input stream with this envelope. It is an + * error if the actual envelope does not match the expected envelope + * class. + */ + abstract public Hessian2Input unwrap(Hessian2Input in) + throws IOException; + + /** + * Unwrap the envelope after having read the envelope code ('E') and + * the envelope method. Called by the EnvelopeFactory for dynamic + * reading of the envelopes. + */ + abstract public Hessian2Input unwrapHeaders(Hessian2Input in) + throws IOException; +} diff --git a/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/HessianFactory.java b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/HessianFactory.java new file mode 100644 index 0000000000..6b59b1a9ea --- /dev/null +++ b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/HessianFactory.java @@ -0,0 +1,225 @@ +/* + * Copyright (c) 2001-2004 Caucho Technology, Inc. All rights reserved. + * + * The Apache Software License, Version 1.1 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Caucho Technology (http://www.caucho.com/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Hessian", "Resin", and "Caucho" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * info@caucho.com. + * + * 5. Products derived from this software may not be called "Resin" + * nor may "Resin" appear in their names without prior written + * permission of Caucho Technology. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Scott Ferguson + */ + +package com.caucho.hessian4.io; + +import java.io.InputStream; +import java.io.OutputStream; +import java.util.logging.Level; +import java.util.logging.Logger; + +import com.caucho.hessian4.util.HessianFreeList; + +/** + * Factory for creating HessianInput and HessianOutput streams. + */ +public class HessianFactory +{ + public static final Logger log + = Logger.getLogger(HessianFactory.class.getName()); + + private SerializerFactory _serializerFactory; + private SerializerFactory _defaultSerializerFactory; + + private final HessianFreeList _freeHessian2Output + = new HessianFreeList(32); + + private final HessianFreeList _freeHessianOutput + = new HessianFreeList(32); + + private final HessianFreeList _freeHessian2Input + = new HessianFreeList(32); + + private final HessianFreeList _freeHessianInput + = new HessianFreeList(32); + + public HessianFactory() + { + _defaultSerializerFactory = SerializerFactory.createDefault(); + _serializerFactory = _defaultSerializerFactory; + } + + public void setSerializerFactory(SerializerFactory factory) + { + _serializerFactory = factory; + } + + public SerializerFactory getSerializerFactory() + { + // the default serializer factory cannot be modified by external + // callers + if (_serializerFactory == _defaultSerializerFactory) { + _serializerFactory = new SerializerFactory(); + } + + return _serializerFactory; + } + + /** + * Creates a new Hessian 2.0 deserializer. + */ + public Hessian2Input createHessian2Input(InputStream is) + { + Hessian2Input in = new Hessian2Input(is); + in.setSerializerFactory(_serializerFactory); + + return in; + } + + /** + * Frees a Hessian 2.0 deserializer + */ + public void freeHessian2Input(Hessian2Input in) + { + /* + if (in == null) + return; + + in.free(); + + _freeHessian2Input.free(in); + */ + } + + /** + * Creates a new Hessian 2.0 deserializer. + */ + public Hessian2StreamingInput createHessian2StreamingInput(InputStream is) + { + Hessian2StreamingInput in = new Hessian2StreamingInput(is); + in.setSerializerFactory(_serializerFactory); + + return in; + } + + /** + * Frees a Hessian 2.0 deserializer + */ + public void freeHessian2StreamingInput(Hessian2StreamingInput in) + { + } + + /** + * Creates a new Hessian 1.0 deserializer. + */ + public HessianInput createHessianInput(InputStream is) + { + return new HessianInput(is); + } + + /** + * Creates a new Hessian 2.0 serializer. + */ + public Hessian2Output createHessian2Output(OutputStream os) + { + Hessian2Output out = _freeHessian2Output.allocate(); + + if (out != null) + out.init(os); + else + out = new Hessian2Output(os); + + out.setSerializerFactory(_serializerFactory); + + return out; + } + + /** + * Frees a Hessian 2.0 serializer + */ + public void freeHessian2Output(Hessian2Output out) + { + if (out == null) + return; + + out.free(); + + _freeHessian2Output.free(out); + } + + /** + * Creates a new Hessian 2.0 serializer. + */ + public Hessian2StreamingOutput createHessian2StreamingOutput(OutputStream os) + { + Hessian2Output out = createHessian2Output(os); + + return new Hessian2StreamingOutput(out); + } + + /** + * Frees a Hessian 2.0 serializer + */ + public void freeHessian2StreamingOutput(Hessian2StreamingOutput out) + { + if (out == null) + return; + + freeHessian2Output(out.getHessian2Output()); + } + + /** + * Creates a new Hessian 1.0 serializer. + */ + public HessianOutput createHessianOutput(OutputStream os) + { + return new HessianOutput(os); + } + + public OutputStream createHessian2DebugOutput(OutputStream os, + Logger log, + Level level) + { + HessianDebugOutputStream out + = new HessianDebugOutputStream(os, log, level); + + out.startTop2(); + + return out; + } +} diff --git a/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/HessianFieldException.java b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/HessianFieldException.java new file mode 100644 index 0000000000..19a97946d0 --- /dev/null +++ b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/HessianFieldException.java @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2001-2004 Caucho Technology, Inc. All rights reserved. + * + * The Apache Software License, Version 1.1 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Caucho Technology (http://www.caucho.com/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Burlap", "Resin", and "Caucho" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * info@caucho.com. + * + * 5. Products derived from this software may not be called "Resin" + * nor may "Resin" appear in their names without prior written + * permission of Caucho Technology. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Scott Ferguson + */ + +package com.caucho.hessian4.io; + + +/** + * Exception during field reading. + */ +public class HessianFieldException extends HessianProtocolException { + /** + * Zero-arg constructor. + */ + public HessianFieldException() + { + } + + /** + * Create the exception. + */ + public HessianFieldException(String message) + { + super(message); + } + + /** + * Create the exception. + */ + public HessianFieldException(String message, Throwable cause) + { + super(message, cause); + } + + /** + * Create the exception. + */ + public HessianFieldException(Throwable cause) + { + super(cause); + } +} diff --git a/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/HessianHandle.java b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/HessianHandle.java new file mode 100644 index 0000000000..268d2197f3 --- /dev/null +++ b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/HessianHandle.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2001-2004 Caucho Technology, Inc. All rights reserved. + * + * The Apache Software License, Version 1.1 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Caucho Technology (http://www.caucho.com/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Hessian", "Resin", and "Caucho" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * info@caucho.com. + * + * 5. Products derived from this software may not be called "Resin" + * nor may "Resin" appear in their names without prior written + * permission of Caucho Technology. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Scott Ferguson + */ + +package com.caucho.hessian4.io; + +/** + * Marks a type as a handle + */ +public interface HessianHandle { +} diff --git a/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/HessianInput.java b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/HessianInput.java new file mode 100644 index 0000000000..5856b87312 --- /dev/null +++ b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/HessianInput.java @@ -0,0 +1,1707 @@ +/* + * Copyright (c) 2001-2004 Caucho Technology, Inc. All rights reserved. + * + * The Apache Software License, Version 1.1 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Caucho Technology (http://www.caucho.com/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Hessian", "Resin", and "Caucho" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * info@caucho.com. + * + * 5. Products derived from this software may not be called "Resin" + * nor may "Resin" appear in their names without prior written + * permission of Caucho Technology. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Scott Ferguson + */ + +package com.caucho.hessian4.io; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; + +/** + * Input stream for Hessian requests. + * + *

    HessianInput is unbuffered, so any client needs to provide + * its own buffering. + * + *

    + * InputStream is = ...; // from http connection
    + * HessianInput in = new HessianInput(is);
    + * String value;
    + *
    + * in.startReply();         // read reply header
    + * value = in.readString(); // read string value
    + * in.completeReply();      // read reply footer
    + * 
    + */ +public class HessianInput extends AbstractHessianInput { + private static int END_OF_DATA = -2; + + private static Field _detailMessageField; + + // factory for deserializing objects in the input stream + protected SerializerFactory _serializerFactory; + + protected ArrayList _refs; + + // the underlying input stream + private InputStream _is; + // a peek character + protected int _peek = -1; + + // the method for a call + private String _method; + + private Reader _chunkReader; + private InputStream _chunkInputStream; + + private Throwable _replyFault; + + private StringBuffer _sbuf = new StringBuffer(); + + // true if this is the last chunk + private boolean _isLastChunk; + // the chunk length + private int _chunkLength; + + /** + * Creates an uninitialized Hessian input stream. + */ + public HessianInput() + { + } + + /** + * Creates a new Hessian input stream, initialized with an + * underlying input stream. + * + * @param is the underlying input stream. + */ + public HessianInput(InputStream is) + { + init(is); + } + + /** + * Sets the serializer factory. + */ + public void setSerializerFactory(SerializerFactory factory) + { + _serializerFactory = factory; + } + + /** + * Gets the serializer factory. + */ + public SerializerFactory getSerializerFactory() + { + return _serializerFactory; + } + + /** + * Initialize the hessian stream with the underlying input stream. + */ + public void init(InputStream is) + { + _is = is; + _method = null; + _isLastChunk = true; + _chunkLength = 0; + _peek = -1; + _refs = null; + _replyFault = null; + + if (_serializerFactory == null) + _serializerFactory = new SerializerFactory(); + } + + /** + * Returns the calls method + */ + public String getMethod() + { + return _method; + } + + /** + * Returns any reply fault. + */ + public Throwable getReplyFault() + { + return _replyFault; + } + + /** + * Starts reading the call + * + *
    +   * c major minor
    +   * 
    + */ + public int readCall() + throws IOException + { + int tag = read(); + + if (tag != 'c') + throw error("expected hessian call ('c') at " + codeName(tag)); + + int major = read(); + int minor = read(); + + return (major << 16) + minor; + } + + /** + * For backward compatibility with HessianSkeleton + */ + public void skipOptionalCall() + throws IOException + { + int tag = read(); + + if (tag == 'c') { + read(); + read(); + } + else + _peek = tag; + } + + /** + * Starts reading the call + * + *

    A successful completion will have a single value: + * + *

    +   * m b16 b8 method
    +   * 
    + */ + public String readMethod() + throws IOException + { + int tag = read(); + + if (tag != 'm') + throw error("expected hessian method ('m') at " + codeName(tag)); + int d1 = read(); + int d2 = read(); + + _isLastChunk = true; + _chunkLength = d1 * 256 + d2; + _sbuf.setLength(0); + int ch; + while ((ch = parseChar()) >= 0) + _sbuf.append((char) ch); + + _method = _sbuf.toString(); + + return _method; + } + + /** + * Starts reading the call, including the headers. + * + *

    The call expects the following protocol data + * + *

    +   * c major minor
    +   * m b16 b8 method
    +   * 
    + */ + public void startCall() + throws IOException + { + readCall(); + + while (readHeader() != null) { + readObject(); + } + + readMethod(); + } + + /** + * Completes reading the call + * + *

    A successful completion will have a single value: + * + *

    +   * z
    +   * 
    + */ + public void completeCall() + throws IOException + { + int tag = read(); + + if (tag == 'z') { + } + else + throw error("expected end of call ('z') at " + codeName(tag) + ". Check method arguments and ensure method overloading is enabled if necessary"); + } + + /** + * Reads a reply as an object. + * If the reply has a fault, throws the exception. + */ + public Object readReply(Class expectedClass) + throws Throwable + { + int tag = read(); + + if (tag != 'r') + error("expected hessian reply at " + codeName(tag)); + + int major = read(); + int minor = read(); + + tag = read(); + if (tag == 'f') + throw prepareFault(); + else { + _peek = tag; + + Object value = readObject(expectedClass); + + completeValueReply(); + + return value; + } + } + + /** + * Starts reading the reply + * + *

    A successful completion will have a single value: + * + *

    +   * r
    +   * 
    + */ + public void startReply() + throws Throwable + { + int tag = read(); + + if (tag != 'r') + error("expected hessian reply at " + codeName(tag)); + + int major = read(); + int minor = read(); + + startReplyBody(); + } + + public void startReplyBody() + throws Throwable + { + int tag = read(); + + if (tag == 'f') + throw prepareFault(); + else + _peek = tag; + } + + /** + * Prepares the fault. + */ + private Throwable prepareFault() + throws IOException + { + HashMap fault = readFault(); + + Object detail = fault.get("detail"); + String message = (String) fault.get("message"); + + if (detail instanceof Throwable) { + _replyFault = (Throwable) detail; + + if (message != null && _detailMessageField != null) { + try { + _detailMessageField.set(_replyFault, message); + } catch (Throwable e) { + } + } + + return _replyFault; + } + + else { + String code = (String) fault.get("code"); + + _replyFault = new HessianServiceException(message, code, detail); + + return _replyFault; + } + } + + /** + * Completes reading the call + * + *

    A successful completion will have a single value: + * + *

    +   * z
    +   * 
    + */ + public void completeReply() + throws IOException + { + int tag = read(); + + if (tag != 'z') + error("expected end of reply at " + codeName(tag)); + } + + /** + * Completes reading the call + * + *

    A successful completion will have a single value: + * + *

    +   * z
    +   * 
    + */ + public void completeValueReply() + throws IOException + { + int tag = read(); + + if (tag != 'z') + error("expected end of reply at " + codeName(tag)); + } + + /** + * Reads a header, returning null if there are no headers. + * + *
    +   * H b16 b8 value
    +   * 
    + */ + public String readHeader() + throws IOException + { + int tag = read(); + + if (tag == 'H') { + _isLastChunk = true; + _chunkLength = (read() << 8) + read(); + + _sbuf.setLength(0); + int ch; + while ((ch = parseChar()) >= 0) + _sbuf.append((char) ch); + + return _sbuf.toString(); + } + + _peek = tag; + + return null; + } + + /** + * Reads a null + * + *
    +   * N
    +   * 
    + */ + public void readNull() + throws IOException + { + int tag = read(); + + switch (tag) { + case 'N': return; + + default: + throw expect("null", tag); + } + } + + /** + * Reads a boolean + * + *
    +   * T
    +   * F
    +   * 
    + */ + public boolean readBoolean() + throws IOException + { + int tag = read(); + + switch (tag) { + case 'T': return true; + case 'F': return false; + case 'I': return parseInt() == 0; + case 'L': return parseLong() == 0; + case 'D': return parseDouble() == 0.0; + case 'N': return false; + + default: + throw expect("boolean", tag); + } + } + + /** + * Reads a byte + * + *
    +   * I b32 b24 b16 b8
    +   * 
    + */ + /* + public byte readByte() + throws IOException + { + return (byte) readInt(); + } + */ + + /** + * Reads a short + * + *
    +   * I b32 b24 b16 b8
    +   * 
    + */ + public short readShort() + throws IOException + { + return (short) readInt(); + } + + /** + * Reads an integer + * + *
    +   * I b32 b24 b16 b8
    +   * 
    + */ + public int readInt() + throws IOException + { + int tag = read(); + + switch (tag) { + case 'T': return 1; + case 'F': return 0; + case 'I': return parseInt(); + case 'L': return (int) parseLong(); + case 'D': return (int) parseDouble(); + + default: + throw expect("int", tag); + } + } + + /** + * Reads a long + * + *
    +   * L b64 b56 b48 b40 b32 b24 b16 b8
    +   * 
    + */ + public long readLong() + throws IOException + { + int tag = read(); + + switch (tag) { + case 'T': return 1; + case 'F': return 0; + case 'I': return parseInt(); + case 'L': return parseLong(); + case 'D': return (long) parseDouble(); + + default: + throw expect("long", tag); + } + } + + /** + * Reads a float + * + *
    +   * D b64 b56 b48 b40 b32 b24 b16 b8
    +   * 
    + */ + public float readFloat() + throws IOException + { + return (float) readDouble(); + } + + /** + * Reads a double + * + *
    +   * D b64 b56 b48 b40 b32 b24 b16 b8
    +   * 
    + */ + public double readDouble() + throws IOException + { + int tag = read(); + + switch (tag) { + case 'T': return 1; + case 'F': return 0; + case 'I': return parseInt(); + case 'L': return (double) parseLong(); + case 'D': return parseDouble(); + + default: + throw expect("long", tag); + } + } + + /** + * Reads a date. + * + *
    +   * T b64 b56 b48 b40 b32 b24 b16 b8
    +   * 
    + */ + public long readUTCDate() + throws IOException + { + int tag = read(); + + if (tag != 'd') + throw error("expected date at " + codeName(tag)); + + long b64 = read(); + long b56 = read(); + long b48 = read(); + long b40 = read(); + long b32 = read(); + long b24 = read(); + long b16 = read(); + long b8 = read(); + + return ((b64 << 56) + + (b56 << 48) + + (b48 << 40) + + (b40 << 32) + + (b32 << 24) + + (b24 << 16) + + (b16 << 8) + + b8); + } + + /** + * Reads a byte from the stream. + */ + public int readChar() + throws IOException + { + if (_chunkLength > 0) { + _chunkLength--; + if (_chunkLength == 0 && _isLastChunk) + _chunkLength = END_OF_DATA; + + int ch = parseUTF8Char(); + return ch; + } + else if (_chunkLength == END_OF_DATA) { + _chunkLength = 0; + return -1; + } + + int tag = read(); + + switch (tag) { + case 'N': + return -1; + + case 'S': + case 's': + case 'X': + case 'x': + _isLastChunk = tag == 'S' || tag == 'X'; + _chunkLength = (read() << 8) + read(); + + _chunkLength--; + int value = parseUTF8Char(); + + // special code so successive read byte won't + // be read as a single object. + if (_chunkLength == 0 && _isLastChunk) + _chunkLength = END_OF_DATA; + + return value; + + default: + throw new IOException("expected 'S' at " + (char) tag); + } + } + + /** + * Reads a byte array from the stream. + */ + public int readString(char []buffer, int offset, int length) + throws IOException + { + int readLength = 0; + + if (_chunkLength == END_OF_DATA) { + _chunkLength = 0; + return -1; + } + else if (_chunkLength == 0) { + int tag = read(); + + switch (tag) { + case 'N': + return -1; + + case 'S': + case 's': + case 'X': + case 'x': + _isLastChunk = tag == 'S' || tag == 'X'; + _chunkLength = (read() << 8) + read(); + break; + + default: + throw new IOException("expected 'S' at " + (char) tag); + } + } + + while (length > 0) { + if (_chunkLength > 0) { + buffer[offset++] = (char) parseUTF8Char(); + _chunkLength--; + length--; + readLength++; + } + else if (_isLastChunk) { + if (readLength == 0) + return -1; + else { + _chunkLength = END_OF_DATA; + return readLength; + } + } + else { + int tag = read(); + + switch (tag) { + case 'S': + case 's': + case 'X': + case 'x': + _isLastChunk = tag == 'S' || tag == 'X'; + _chunkLength = (read() << 8) + read(); + break; + + default: + throw new IOException("expected 'S' at " + (char) tag); + } + } + } + + if (readLength == 0) + return -1; + else if (_chunkLength > 0 || ! _isLastChunk) + return readLength; + else { + _chunkLength = END_OF_DATA; + return readLength; + } + } + + /** + * Reads a string + * + *
    +   * S b16 b8 string value
    +   * 
    + */ + public String readString() + throws IOException + { + int tag = read(); + + switch (tag) { + case 'N': + return null; + + case 'I': + return String.valueOf(parseInt()); + case 'L': + return String.valueOf(parseLong()); + case 'D': + return String.valueOf(parseDouble()); + + case 'S': + case 's': + case 'X': + case 'x': + _isLastChunk = tag == 'S' || tag == 'X'; + _chunkLength = (read() << 8) + read(); + + _sbuf.setLength(0); + int ch; + + while ((ch = parseChar()) >= 0) + _sbuf.append((char) ch); + + return _sbuf.toString(); + + default: + throw expect("string", tag); + } + } + + /** + * Reads an XML node. + * + *
    +   * S b16 b8 string value
    +   * 
    + */ + public org.w3c.dom.Node readNode() + throws IOException + { + int tag = read(); + + switch (tag) { + case 'N': + return null; + + case 'S': + case 's': + case 'X': + case 'x': + _isLastChunk = tag == 'S' || tag == 'X'; + _chunkLength = (read() << 8) + read(); + + throw error("Can't handle string in this context"); + + default: + throw expect("string", tag); + } + } + + /** + * Reads a byte array + * + *
    +   * B b16 b8 data value
    +   * 
    + */ + public byte []readBytes() + throws IOException + { + int tag = read(); + + switch (tag) { + case 'N': + return null; + + case 'B': + case 'b': + _isLastChunk = tag == 'B'; + _chunkLength = (read() << 8) + read(); + + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + + int data; + while ((data = parseByte()) >= 0) + bos.write(data); + + return bos.toByteArray(); + + default: + throw expect("bytes", tag); + } + } + + /** + * Reads a byte from the stream. + */ + public int readByte() + throws IOException + { + if (_chunkLength > 0) { + _chunkLength--; + if (_chunkLength == 0 && _isLastChunk) + _chunkLength = END_OF_DATA; + + return read(); + } + else if (_chunkLength == END_OF_DATA) { + _chunkLength = 0; + return -1; + } + + int tag = read(); + + switch (tag) { + case 'N': + return -1; + + case 'B': + case 'b': + _isLastChunk = tag == 'B'; + _chunkLength = (read() << 8) + read(); + + int value = parseByte(); + + // special code so successive read byte won't + // be read as a single object. + if (_chunkLength == 0 && _isLastChunk) + _chunkLength = END_OF_DATA; + + return value; + + default: + throw new IOException("expected 'B' at " + (char) tag); + } + } + + /** + * Reads a byte array from the stream. + */ + public int readBytes(byte []buffer, int offset, int length) + throws IOException + { + int readLength = 0; + + if (_chunkLength == END_OF_DATA) { + _chunkLength = 0; + return -1; + } + else if (_chunkLength == 0) { + int tag = read(); + + switch (tag) { + case 'N': + return -1; + + case 'B': + case 'b': + _isLastChunk = tag == 'B'; + _chunkLength = (read() << 8) + read(); + break; + + default: + throw new IOException("expected 'B' at " + (char) tag); + } + } + + while (length > 0) { + if (_chunkLength > 0) { + buffer[offset++] = (byte) read(); + _chunkLength--; + length--; + readLength++; + } + else if (_isLastChunk) { + if (readLength == 0) + return -1; + else { + _chunkLength = END_OF_DATA; + return readLength; + } + } + else { + int tag = read(); + + switch (tag) { + case 'B': + case 'b': + _isLastChunk = tag == 'B'; + _chunkLength = (read() << 8) + read(); + break; + + default: + throw new IOException("expected 'B' at " + (char) tag); + } + } + } + + if (readLength == 0) + return -1; + else if (_chunkLength > 0 || ! _isLastChunk) + return readLength; + else { + _chunkLength = END_OF_DATA; + return readLength; + } + } + + /** + * Reads a fault. + */ + private HashMap readFault() + throws IOException + { + HashMap map = new HashMap(); + + int code = read(); + for (; code > 0 && code != 'z'; code = read()) { + _peek = code; + + Object key = readObject(); + Object value = readObject(); + + if (key != null && value != null) + map.put(key, value); + } + + if (code != 'z') + throw expect("fault", code); + + return map; + } + + /** + * Reads an object from the input stream with an expected type. + */ + public Object readObject(Class cl) + throws IOException + { + if (cl == null || cl == Object.class) + return readObject(); + + int tag = read(); + + switch (tag) { + case 'N': + return null; + + case 'M': + { + String type = readType(); + + // hessian/3386 + if ("".equals(type)) { + Deserializer reader; + reader = _serializerFactory.getDeserializer(cl); + + return reader.readMap(this); + } + else { + Deserializer reader; + reader = _serializerFactory.getObjectDeserializer(type); + + return reader.readMap(this); + } + } + + case 'V': + { + String type = readType(); + int length = readLength(); + + Deserializer reader; + reader = _serializerFactory.getObjectDeserializer(type); + + if (cl != reader.getType() && cl.isAssignableFrom(reader.getType())) + return reader.readList(this, length); + + reader = _serializerFactory.getDeserializer(cl); + + Object v = reader.readList(this, length); + + return v; + } + + case 'R': + { + int ref = parseInt(); + + return _refs.get(ref); + } + + case 'r': + { + String type = readType(); + String url = readString(); + + return resolveRemote(type, url); + } + } + + _peek = tag; + + // hessian/332i vs hessian/3406 + //return readObject(); + + Object value = _serializerFactory.getDeserializer(cl).readObject(this); + + return value; + } + + /** + * Reads an arbitrary object from the input stream when the type + * is unknown. + */ + public Object readObject() + throws IOException + { + int tag = read(); + + switch (tag) { + case 'N': + return null; + + case 'T': + return Boolean.valueOf(true); + + case 'F': + return Boolean.valueOf(false); + + case 'I': + return Integer.valueOf(parseInt()); + + case 'L': + return Long.valueOf(parseLong()); + + case 'D': + return Double.valueOf(parseDouble()); + + case 'd': + return new Date(parseLong()); + + case 'x': + case 'X': { + _isLastChunk = tag == 'X'; + _chunkLength = (read() << 8) + read(); + + return parseXML(); + } + + case 's': + case 'S': { + _isLastChunk = tag == 'S'; + _chunkLength = (read() << 8) + read(); + + int data; + _sbuf.setLength(0); + + while ((data = parseChar()) >= 0) + _sbuf.append((char) data); + + return _sbuf.toString(); + } + + case 'b': + case 'B': { + _isLastChunk = tag == 'B'; + _chunkLength = (read() << 8) + read(); + + int data; + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + + while ((data = parseByte()) >= 0) + bos.write(data); + + return bos.toByteArray(); + } + + case 'V': { + String type = readType(); + int length = readLength(); + + return _serializerFactory.readList(this, length, type); + } + + case 'M': { + String type = readType(); + + return _serializerFactory.readMap(this, type); + } + + case 'R': { + int ref = parseInt(); + + return _refs.get(ref); + } + + case 'r': { + String type = readType(); + String url = readString(); + + return resolveRemote(type, url); + } + + default: + throw error("unknown code for readObject at " + codeName(tag)); + } + } + + /** + * Reads a remote object. + */ + public Object readRemote() + throws IOException + { + String type = readType(); + String url = readString(); + + return resolveRemote(type, url); + } + + /** + * Reads a reference. + */ + public Object readRef() + throws IOException + { + return _refs.get(parseInt()); + } + + /** + * Reads the start of a list. + */ + public int readListStart() + throws IOException + { + return read(); + } + + /** + * Reads the start of a list. + */ + public int readMapStart() + throws IOException + { + return read(); + } + + /** + * Returns true if this is the end of a list or a map. + */ + public boolean isEnd() + throws IOException + { + int code = read(); + + _peek = code; + + return (code < 0 || code == 'z'); + } + + /** + * Reads the end byte. + */ + public void readEnd() + throws IOException + { + int code = read(); + + if (code != 'z') + throw error("unknown code at " + codeName(code)); + } + + /** + * Reads the end byte. + */ + public void readMapEnd() + throws IOException + { + int code = read(); + + if (code != 'z') + throw error("expected end of map ('z') at " + codeName(code)); + } + + /** + * Reads the end byte. + */ + public void readListEnd() + throws IOException + { + int code = read(); + + if (code != 'z') + throw error("expected end of list ('z') at " + codeName(code)); + } + + /** + * Adds a list/map reference. + */ + public int addRef(Object ref) + { + if (_refs == null) + _refs = new ArrayList(); + + _refs.add(ref); + + return _refs.size() - 1; + } + + /** + * Adds a list/map reference. + */ + public void setRef(int i, Object ref) + { + _refs.set(i, ref); + } + + /** + * Resets the references for streaming. + */ + public void resetReferences() + { + if (_refs != null) + _refs.clear(); + } + + /** + * Resolves a remote object. + */ + public Object resolveRemote(String type, String url) + throws IOException + { + HessianRemoteResolver resolver = getRemoteResolver(); + + if (resolver != null) + return resolver.lookup(type, url); + else + return new HessianRemote(type, url); + } + + /** + * Parses a type from the stream. + * + *
    +   * t b16 b8
    +   * 
    + */ + public String readType() + throws IOException + { + int code = read(); + + if (code != 't') { + _peek = code; + return ""; + } + + _isLastChunk = true; + _chunkLength = (read() << 8) + read(); + + _sbuf.setLength(0); + int ch; + while ((ch = parseChar()) >= 0) + _sbuf.append((char) ch); + + return _sbuf.toString(); + } + + /** + * Parses the length for an array + * + *
    +   * l b32 b24 b16 b8
    +   * 
    + */ + public int readLength() + throws IOException + { + int code = read(); + + if (code != 'l') { + _peek = code; + return -1; + } + + return parseInt(); + } + + /** + * Parses a 32-bit integer value from the stream. + * + *
    +   * b32 b24 b16 b8
    +   * 
    + */ + private int parseInt() + throws IOException + { + int b32 = read(); + int b24 = read(); + int b16 = read(); + int b8 = read(); + + return (b32 << 24) + (b24 << 16) + (b16 << 8) + b8; + } + + /** + * Parses a 64-bit long value from the stream. + * + *
    +   * b64 b56 b48 b40 b32 b24 b16 b8
    +   * 
    + */ + private long parseLong() + throws IOException + { + long b64 = read(); + long b56 = read(); + long b48 = read(); + long b40 = read(); + long b32 = read(); + long b24 = read(); + long b16 = read(); + long b8 = read(); + + return ((b64 << 56) + + (b56 << 48) + + (b48 << 40) + + (b40 << 32) + + (b32 << 24) + + (b24 << 16) + + (b16 << 8) + + b8); + } + + /** + * Parses a 64-bit double value from the stream. + * + *
    +   * b64 b56 b48 b40 b32 b24 b16 b8
    +   * 
    + */ + private double parseDouble() + throws IOException + { + long b64 = read(); + long b56 = read(); + long b48 = read(); + long b40 = read(); + long b32 = read(); + long b24 = read(); + long b16 = read(); + long b8 = read(); + + long bits = ((b64 << 56) + + (b56 << 48) + + (b48 << 40) + + (b40 << 32) + + (b32 << 24) + + (b24 << 16) + + (b16 << 8) + + b8); + + return Double.longBitsToDouble(bits); + } + + org.w3c.dom.Node parseXML() + throws IOException + { + throw new UnsupportedOperationException(); + } + + /** + * Reads a character from the underlying stream. + */ + private int parseChar() + throws IOException + { + while (_chunkLength <= 0) { + if (_isLastChunk) + return -1; + + int code = read(); + + switch (code) { + case 's': + case 'x': + _isLastChunk = false; + + _chunkLength = (read() << 8) + read(); + break; + + case 'S': + case 'X': + _isLastChunk = true; + + _chunkLength = (read() << 8) + read(); + break; + + default: + throw expect("string", code); + } + + } + + _chunkLength--; + + return parseUTF8Char(); + } + + /** + * Parses a single UTF8 character. + */ + private int parseUTF8Char() + throws IOException + { + int ch = read(); + + if (ch < 0x80) + return ch; + else if ((ch & 0xe0) == 0xc0) { + int ch1 = read(); + int v = ((ch & 0x1f) << 6) + (ch1 & 0x3f); + + return v; + } + else if ((ch & 0xf0) == 0xe0) { + int ch1 = read(); + int ch2 = read(); + int v = ((ch & 0x0f) << 12) + ((ch1 & 0x3f) << 6) + (ch2 & 0x3f); + + return v; + } + else + throw error("bad utf-8 encoding at " + codeName(ch)); + } + + /** + * Reads a byte from the underlying stream. + */ + private int parseByte() + throws IOException + { + while (_chunkLength <= 0) { + if (_isLastChunk) { + return -1; + } + + int code = read(); + + switch (code) { + case 'b': + _isLastChunk = false; + + _chunkLength = (read() << 8) + read(); + break; + + case 'B': + _isLastChunk = true; + + _chunkLength = (read() << 8) + read(); + break; + + default: + throw expect("byte[]", code); + } + } + + _chunkLength--; + + return read(); + } + + /** + * Reads bytes based on an input stream. + */ + public InputStream readInputStream() + throws IOException + { + int tag = read(); + + switch (tag) { + case 'N': + return null; + + case 'B': + case 'b': + _isLastChunk = tag == 'B'; + _chunkLength = (read() << 8) + read(); + break; + + default: + throw expect("inputStream", tag); + } + + return new InputStream() { + boolean _isClosed = false; + + public int read() + throws IOException + { + if (_isClosed || _is == null) + return -1; + + int ch = parseByte(); + if (ch < 0) + _isClosed = true; + + return ch; + } + + public int read(byte []buffer, int offset, int length) + throws IOException + { + if (_isClosed || _is == null) + return -1; + + int len = HessianInput.this.read(buffer, offset, length); + if (len < 0) + _isClosed = true; + + return len; + } + + public void close() + throws IOException + { + while (read() >= 0) { + } + + _isClosed = true; + } + }; + } + + /** + * Reads bytes from the underlying stream. + */ + int read(byte []buffer, int offset, int length) + throws IOException + { + int readLength = 0; + + while (length > 0) { + while (_chunkLength <= 0) { + if (_isLastChunk) + return readLength == 0 ? -1 : readLength; + + int code = read(); + + switch (code) { + case 'b': + _isLastChunk = false; + + _chunkLength = (read() << 8) + read(); + break; + + case 'B': + _isLastChunk = true; + + _chunkLength = (read() << 8) + read(); + break; + + default: + throw expect("byte[]", code); + } + } + + int sublen = _chunkLength; + if (length < sublen) + sublen = length; + + sublen = _is.read(buffer, offset, sublen); + offset += sublen; + readLength += sublen; + length -= sublen; + _chunkLength -= sublen; + } + + return readLength; + } + + final int read() + throws IOException + { + if (_peek >= 0) { + int value = _peek; + _peek = -1; + return value; + } + + int ch = _is.read(); + + return ch; + } + + public void close() + { + _is = null; + } + + public Reader getReader() + { + return null; + } + + protected IOException expect(String expect, int ch) + { + return error("expected " + expect + " at " + codeName(ch)); + } + + protected String codeName(int ch) + { + if (ch < 0) + return "end of file"; + else + return "0x" + Integer.toHexString(ch & 0xff) + " (" + (char) + ch + ")"; + } + + protected IOException error(String message) + { + if (_method != null) + return new HessianProtocolException(_method + ": " + message); + else + return new HessianProtocolException(message); + } + + static { + try { + _detailMessageField = Throwable.class.getDeclaredField("detailMessage"); + _detailMessageField.setAccessible(true); + } catch (Throwable e) { + } + } +} diff --git a/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/HessianInputFactory.java b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/HessianInputFactory.java new file mode 100644 index 0000000000..918c7efbfe --- /dev/null +++ b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/HessianInputFactory.java @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2001-2004 Caucho Technology, Inc. All rights reserved. + * + * The Apache Software License, Version 1.1 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Caucho Technology (http://www.caucho.com/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Hessian", "Resin", and "Caucho" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * info@caucho.com. + * + * 5. Products derived from this software may not be called "Resin" + * nor may "Resin" appear in their names without prior written + * permission of Caucho Technology. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Scott Ferguson + */ + +package com.caucho.hessian4.io; + +import java.io.IOException; +import java.io.InputStream; +import java.util.logging.Logger; + +public class HessianInputFactory +{ + public static final Logger log + = Logger.getLogger(HessianInputFactory.class.getName()); + + private HessianFactory _factory = new HessianFactory(); + + public void setSerializerFactory(SerializerFactory factory) + { + _factory.setSerializerFactory(factory); + } + + public SerializerFactory getSerializerFactory() + { + return _factory.getSerializerFactory(); + } + + public HeaderType readHeader(InputStream is) + throws IOException + { + int code = is.read(); + + int major = is.read(); + int minor = is.read(); + + switch (code) { + case -1: + throw new IOException("Unexpected end of file for Hessian message"); + + case 'c': + if (major >= 2) + return HeaderType.CALL_1_REPLY_2; + else + return HeaderType.CALL_1_REPLY_1; + case 'r': + return HeaderType.REPLY_1; + + case 'H': + return HeaderType.HESSIAN_2; + + default: + throw new IOException((char) code + " 0x" + Integer.toHexString(code) + " is an unknown Hessian message code."); + } + } + + public AbstractHessianInput open(InputStream is) + throws IOException + { + int code = is.read(); + + int major = is.read(); + int minor = is.read(); + + switch (code) { + case 'c': + case 'C': + case 'r': + case 'R': + if (major >= 2) { + return _factory.createHessian2Input(is); + } + else { + return _factory.createHessianInput(is); + } + + default: + throw new IOException((char) code + " is an unknown Hessian message code."); + } + } + + public enum HeaderType { + CALL_1_REPLY_1, + CALL_1_REPLY_2, + HESSIAN_2, + REPLY_1, + REPLY_2; + + public boolean isCall1() + { + switch (this) { + case CALL_1_REPLY_1: + case CALL_1_REPLY_2: + return true; + default: + return false; + } + } + + public boolean isCall2() + { + switch (this) { + case HESSIAN_2: + return true; + default: + return false; + } + } + + public boolean isReply1() + { + switch (this) { + case CALL_1_REPLY_1: + return true; + default: + return false; + } + } + + public boolean isReply2() + { + switch (this) { + case CALL_1_REPLY_2: + case HESSIAN_2: + return true; + default: + return false; + } + } + } +} diff --git a/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/HessianMethodSerializationException.java b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/HessianMethodSerializationException.java new file mode 100644 index 0000000000..41e14a9442 --- /dev/null +++ b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/HessianMethodSerializationException.java @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2001-2008 Caucho Technology, Inc. All rights reserved. + * + * The Apache Software License, Version 1.1 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Caucho Technology (http://www.caucho.com/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Burlap", "Resin", and "Caucho" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * info@caucho.com. + * + * 5. Products derived from this software may not be called "Resin" + * nor may "Resin" appear in their names without prior written + * permission of Caucho Technology. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Scott Ferguson + */ + +package com.caucho.hessian4.io; + +import com.caucho.hessian4.HessianException; + +/** + * Exception for faults when the fault doesn't return a java exception. + * This exception is required for MicroHessianInput. + */ +public class HessianMethodSerializationException extends HessianException { + /** + * Zero-arg constructor. + */ + public HessianMethodSerializationException() + { + } + + /** + * Create the exception. + */ + public HessianMethodSerializationException(String message) + { + super(message); + } + + /** + * Create the exception. + */ + public HessianMethodSerializationException(String message, Throwable cause) + { + super(message, cause); + } + + /** + * Create the exception. + */ + public HessianMethodSerializationException(Throwable cause) + { + super(cause); + } +} diff --git a/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/HessianOutput.java b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/HessianOutput.java new file mode 100644 index 0000000000..b7922e37e5 --- /dev/null +++ b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/HessianOutput.java @@ -0,0 +1,956 @@ +/* + * Copyright (c) 2001-2008 Caucho Technology, Inc. All rights reserved. + * + * The Apache Software License, Version 1.1 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Caucho Technology (http://www.caucho.com/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Burlap", "Resin", and "Caucho" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * info@caucho.com. + * + * 5. Products derived from this software may not be called "Resin" + * nor may "Resin" appear in their names without prior written + * permission of Caucho Technology. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Scott Ferguson + */ + +package com.caucho.hessian4.io; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.IdentityHashMap; + +/** + * Output stream for Hessian requests, compatible with microedition + * Java. It only uses classes and types available in JDK. + * + *

    Since HessianOutput does not depend on any classes other than + * in the JDK, it can be extracted independently into a smaller package. + * + *

    HessianOutput is unbuffered, so any client needs to provide + * its own buffering. + * + *

    + * OutputStream os = ...; // from http connection
    + * HessianOutput out = new HessianOutput(os);
    + * String value;
    + *
    + * out.startCall("hello");  // start hello call
    + * out.writeString("arg1"); // write a string argument
    + * out.completeCall();      // complete the call
    + * 
    + */ +public class HessianOutput extends AbstractHessianOutput { + // the output stream/ + protected OutputStream os; + // map of references + private IdentityHashMap _refs; + private int _version = 1; + + /** + * Creates a new Hessian output stream, initialized with an + * underlying output stream. + * + * @param os the underlying output stream. + */ + public HessianOutput(OutputStream os) + { + init(os); + } + + /** + * Creates an uninitialized Hessian output stream. + */ + public HessianOutput() + { + } + + /** + * Initializes the output + */ + public void init(OutputStream os) + { + this.os = os; + + _refs = null; + + if (_serializerFactory == null) + _serializerFactory = new SerializerFactory(); + } + + /** + * Sets the client's version. + */ + public void setVersion(int version) + { + _version = version; + } + + /** + * Writes a complete method call. + */ + public void call(String method, Object []args) + throws IOException + { + int length = args != null ? args.length : 0; + + startCall(method, length); + + for (int i = 0; i < length; i++) + writeObject(args[i]); + + completeCall(); + } + + /** + * Starts the method call. Clients would use startCall + * instead of call if they wanted finer control over + * writing the arguments, or needed to write headers. + * + *
    +   * c major minor
    +   * m b16 b8 method-name
    +   * 
    + * + * @param method the method name to call. + */ + public void startCall(String method, int length) + throws IOException + { + os.write('c'); + os.write(_version); + os.write(0); + + os.write('m'); + int len = method.length(); + os.write(len >> 8); + os.write(len); + printString(method, 0, len); + } + + /** + * Writes the call tag. This would be followed by the + * headers and the method tag. + * + *
    +   * c major minor
    +   * 
    + * + * @param method the method name to call. + */ + public void startCall() + throws IOException + { + os.write('c'); + os.write(0); + os.write(1); + } + + /** + * Writes the method tag. + * + *
    +   * m b16 b8 method-name
    +   * 
    + * + * @param method the method name to call. + */ + public void writeMethod(String method) + throws IOException + { + os.write('m'); + int len = method.length(); + os.write(len >> 8); + os.write(len); + printString(method, 0, len); + } + + /** + * Completes. + * + *
    +   * z
    +   * 
    + */ + public void completeCall() + throws IOException + { + os.write('z'); + } + + /** + * Starts the reply + * + *

    A successful completion will have a single value: + * + *

    +   * r
    +   * 
    + */ + public void startReply() + throws IOException + { + os.write('r'); + os.write(1); + os.write(0); + } + + /** + * Completes reading the reply + * + *

    A successful completion will have a single value: + * + *

    +   * z
    +   * 
    + */ + public void completeReply() + throws IOException + { + os.write('z'); + } + + /** + * Writes a header name. The header value must immediately follow. + * + *
    +   * H b16 b8 foo value
    +   * 
    + */ + public void writeHeader(String name) + throws IOException + { + int len = name.length(); + + os.write('H'); + os.write(len >> 8); + os.write(len); + + printString(name); + } + + /** + * Writes a fault. The fault will be written + * as a descriptive string followed by an object: + * + *
    +   * f
    +   * <string>code
    +   * <string>the fault code
    +   *
    +   * <string>message
    +   * <string>the fault mesage
    +   *
    +   * <string>detail
    +   * mt\x00\xnnjavax.ejb.FinderException
    +   *     ...
    +   * z
    +   * z
    +   * 
    + * + * @param code the fault code, a three digit + */ + public void writeFault(String code, String message, Object detail) + throws IOException + { + // hessian/3525 + os.write('r'); + os.write(1); + os.write(0); + + os.write('f'); + writeString("code"); + writeString(code); + + writeString("message"); + writeString(message); + + if (detail != null) { + writeString("detail"); + writeObject(detail); + } + os.write('z'); + + os.write('z'); + } + + /** + * Writes any object to the output stream. + */ + public void writeObject(Object object) + throws IOException + { + if (object == null) { + writeNull(); + return; + } + + Serializer serializer; + + serializer = _serializerFactory.getSerializer(object.getClass()); + + serializer.writeObject(object, this); + } + + /** + * Writes the list header to the stream. List writers will call + * writeListBegin followed by the list contents and then + * call writeListEnd. + * + *
    +   * V
    +   * t b16 b8 type
    +   * l b32 b24 b16 b8
    +   * 
    + */ + public boolean writeListBegin(int length, String type) + throws IOException + { + os.write('V'); + + if (type != null) { + os.write('t'); + printLenString(type); + } + + if (length >= 0) { + os.write('l'); + os.write(length >> 24); + os.write(length >> 16); + os.write(length >> 8); + os.write(length); + } + + return true; + } + + /** + * Writes the tail of the list to the stream. + */ + public void writeListEnd() + throws IOException + { + os.write('z'); + } + + /** + * Writes the map header to the stream. Map writers will call + * writeMapBegin followed by the map contents and then + * call writeMapEnd. + * + *
    +   * Mt b16 b8 ( )z
    +   * 
    + */ + public void writeMapBegin(String type) + throws IOException + { + os.write('M'); + os.write('t'); + printLenString(type); + } + + /** + * Writes the tail of the map to the stream. + */ + public void writeMapEnd() + throws IOException + { + os.write('z'); + } + + /** + * Writes a remote object reference to the stream. The type is the + * type of the remote interface. + * + *
    +   * 'r' 't' b16 b8 type url
    +   * 
    + */ + public void writeRemote(String type, String url) + throws IOException + { + os.write('r'); + os.write('t'); + printLenString(type); + os.write('S'); + printLenString(url); + } + + /** + * Writes a boolean value to the stream. The boolean will be written + * with the following syntax: + * + *
    +   * T
    +   * F
    +   * 
    + * + * @param value the boolean value to write. + */ + public void writeBoolean(boolean value) + throws IOException + { + if (value) + os.write('T'); + else + os.write('F'); + } + + /** + * Writes an integer value to the stream. The integer will be written + * with the following syntax: + * + *
    +   * I b32 b24 b16 b8
    +   * 
    + * + * @param value the integer value to write. + */ + public void writeInt(int value) + throws IOException + { + os.write('I'); + os.write(value >> 24); + os.write(value >> 16); + os.write(value >> 8); + os.write(value); + } + + /** + * Writes a long value to the stream. The long will be written + * with the following syntax: + * + *
    +   * L b64 b56 b48 b40 b32 b24 b16 b8
    +   * 
    + * + * @param value the long value to write. + */ + public void writeLong(long value) + throws IOException + { + os.write('L'); + os.write((byte) (value >> 56)); + os.write((byte) (value >> 48)); + os.write((byte) (value >> 40)); + os.write((byte) (value >> 32)); + os.write((byte) (value >> 24)); + os.write((byte) (value >> 16)); + os.write((byte) (value >> 8)); + os.write((byte) (value)); + } + + /** + * Writes a double value to the stream. The double will be written + * with the following syntax: + * + *
    +   * D b64 b56 b48 b40 b32 b24 b16 b8
    +   * 
    + * + * @param value the double value to write. + */ + public void writeDouble(double value) + throws IOException + { + long bits = Double.doubleToLongBits(value); + + os.write('D'); + os.write((byte) (bits >> 56)); + os.write((byte) (bits >> 48)); + os.write((byte) (bits >> 40)); + os.write((byte) (bits >> 32)); + os.write((byte) (bits >> 24)); + os.write((byte) (bits >> 16)); + os.write((byte) (bits >> 8)); + os.write((byte) (bits)); + } + + /** + * Writes a date to the stream. + * + *
    +   * T  b64 b56 b48 b40 b32 b24 b16 b8
    +   * 
    + * + * @param time the date in milliseconds from the epoch in UTC + */ + public void writeUTCDate(long time) + throws IOException + { + os.write('d'); + os.write((byte) (time >> 56)); + os.write((byte) (time >> 48)); + os.write((byte) (time >> 40)); + os.write((byte) (time >> 32)); + os.write((byte) (time >> 24)); + os.write((byte) (time >> 16)); + os.write((byte) (time >> 8)); + os.write((byte) (time)); + } + + /** + * Writes a null value to the stream. + * The null will be written with the following syntax + * + *
    +   * N
    +   * 
    + * + * @param value the string value to write. + */ + public void writeNull() + throws IOException + { + os.write('N'); + } + + /** + * Writes a string value to the stream using UTF-8 encoding. + * The string will be written with the following syntax: + * + *
    +   * S b16 b8 string-value
    +   * 
    + * + * If the value is null, it will be written as + * + *
    +   * N
    +   * 
    + * + * @param value the string value to write. + */ + public void writeString(String value) + throws IOException + { + if (value == null) { + os.write('N'); + } + else { + int length = value.length(); + int offset = 0; + + while (length > 0x8000) { + int sublen = 0x8000; + + // chunk can't end in high surrogate + char tail = value.charAt(offset + sublen - 1); + + if (0xd800 <= tail && tail <= 0xdbff) + sublen--; + + os.write('s'); + os.write(sublen >> 8); + os.write(sublen); + + printString(value, offset, sublen); + + length -= sublen; + offset += sublen; + } + + os.write('S'); + os.write(length >> 8); + os.write(length); + + printString(value, offset, length); + } + } + + /** + * Writes a string value to the stream using UTF-8 encoding. + * The string will be written with the following syntax: + * + *
    +   * S b16 b8 string-value
    +   * 
    + * + * If the value is null, it will be written as + * + *
    +   * N
    +   * 
    + * + * @param value the string value to write. + */ + public void writeString(char []buffer, int offset, int length) + throws IOException + { + if (buffer == null) { + os.write('N'); + } + else { + while (length > 0x8000) { + int sublen = 0x8000; + + // chunk can't end in high surrogate + char tail = buffer[offset + sublen - 1]; + + if (0xd800 <= tail && tail <= 0xdbff) + sublen--; + + os.write('s'); + os.write(sublen >> 8); + os.write(sublen); + + printString(buffer, offset, sublen); + + length -= sublen; + offset += sublen; + } + + os.write('S'); + os.write(length >> 8); + os.write(length); + + printString(buffer, offset, length); + } + } + + /** + * Writes a byte array to the stream. + * The array will be written with the following syntax: + * + *
    +   * B b16 b18 bytes
    +   * 
    + * + * If the value is null, it will be written as + * + *
    +   * N
    +   * 
    + * + * @param value the string value to write. + */ + public void writeBytes(byte []buffer) + throws IOException + { + if (buffer == null) + os.write('N'); + else + writeBytes(buffer, 0, buffer.length); + } + + /** + * Writes a byte array to the stream. + * The array will be written with the following syntax: + * + *
    +   * B b16 b18 bytes
    +   * 
    + * + * If the value is null, it will be written as + * + *
    +   * N
    +   * 
    + * + * @param value the string value to write. + */ + public void writeBytes(byte []buffer, int offset, int length) + throws IOException + { + if (buffer == null) { + os.write('N'); + } + else { + while (length > 0x8000) { + int sublen = 0x8000; + + os.write('b'); + os.write(sublen >> 8); + os.write(sublen); + + os.write(buffer, offset, sublen); + + length -= sublen; + offset += sublen; + } + + os.write('B'); + os.write(length >> 8); + os.write(length); + os.write(buffer, offset, length); + } + } + + /** + * Writes a byte buffer to the stream. + * + *
    +   * 
    + */ + public void writeByteBufferStart() + throws IOException + { + } + + /** + * Writes a byte buffer to the stream. + * + *
    +   * b b16 b18 bytes
    +   * 
    + */ + public void writeByteBufferPart(byte []buffer, int offset, int length) + throws IOException + { + while (length > 0) { + int sublen = length; + + if (0x8000 < sublen) + sublen = 0x8000; + + os.write('b'); + os.write(sublen >> 8); + os.write(sublen); + + os.write(buffer, offset, sublen); + + length -= sublen; + offset += sublen; + } + } + + /** + * Writes a byte buffer to the stream. + * + *
    +   * b b16 b18 bytes
    +   * 
    + */ + public void writeByteBufferEnd(byte []buffer, int offset, int length) + throws IOException + { + writeBytes(buffer, offset, length); + } + + /** + * Writes a reference. + * + *
    +   * R b32 b24 b16 b8
    +   * 
    + * + * @param value the integer value to write. + */ + public void writeRef(int value) + throws IOException + { + os.write('R'); + os.write(value >> 24); + os.write(value >> 16); + os.write(value >> 8); + os.write(value); + } + + /** + * Writes a placeholder. + * + *
    +   * P
    +   * 
    + */ + public void writePlaceholder() + throws IOException + { + os.write('P'); + } + + /** + * If the object has already been written, just write its ref. + * + * @return true if we're writing a ref. + */ + public boolean addRef(Object object) + throws IOException + { + if (_refs == null) + _refs = new IdentityHashMap(); + + Integer ref = (Integer) _refs.get(object); + + if (ref != null) { + int value = ref.intValue(); + + writeRef(value); + return true; + } + else { + _refs.put(object, new Integer(_refs.size())); + + return false; + } + } + + /** + * Resets the references for streaming. + */ + public void resetReferences() + { + if (_refs != null) + _refs.clear(); + } + + /** + * Removes a reference. + */ + public boolean removeRef(Object obj) + throws IOException + { + if (_refs != null) { + _refs.remove(obj); + + return true; + } + else + return false; + } + + /** + * Replaces a reference from one object to another. + */ + public boolean replaceRef(Object oldRef, Object newRef) + throws IOException + { + Integer value = (Integer) _refs.remove(oldRef); + + if (value != null) { + _refs.put(newRef, value); + return true; + } + else + return false; + } + + /** + * Prints a string to the stream, encoded as UTF-8 with preceeding length + * + * @param v the string to print. + */ + public void printLenString(String v) + throws IOException + { + if (v == null) { + os.write(0); + os.write(0); + } + else { + int len = v.length(); + os.write(len >> 8); + os.write(len); + + printString(v, 0, len); + } + } + + /** + * Prints a string to the stream, encoded as UTF-8 + * + * @param v the string to print. + */ + public void printString(String v) + throws IOException + { + printString(v, 0, v.length()); + } + + /** + * Prints a string to the stream, encoded as UTF-8 + * + * @param v the string to print. + */ + public void printString(String v, int offset, int length) + throws IOException + { + for (int i = 0; i < length; i++) { + char ch = v.charAt(i + offset); + + if (ch < 0x80) + os.write(ch); + else if (ch < 0x800) { + os.write(0xc0 + ((ch >> 6) & 0x1f)); + os.write(0x80 + (ch & 0x3f)); + } + else { + os.write(0xe0 + ((ch >> 12) & 0xf)); + os.write(0x80 + ((ch >> 6) & 0x3f)); + os.write(0x80 + (ch & 0x3f)); + } + } + } + + /** + * Prints a string to the stream, encoded as UTF-8 + * + * @param v the string to print. + */ + public void printString(char []v, int offset, int length) + throws IOException + { + for (int i = 0; i < length; i++) { + char ch = v[i + offset]; + + if (ch < 0x80) + os.write(ch); + else if (ch < 0x800) { + os.write(0xc0 + ((ch >> 6) & 0x1f)); + os.write(0x80 + (ch & 0x3f)); + } + else { + os.write(0xe0 + ((ch >> 12) & 0xf)); + os.write(0x80 + ((ch >> 6) & 0x3f)); + os.write(0x80 + (ch & 0x3f)); + } + } + } + + public void flush() + throws IOException + { + if (this.os != null) + this.os.flush(); + } + + public void close() + throws IOException + { + if (this.os != null) + this.os.flush(); + } +} diff --git a/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/HessianProtocolException.java b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/HessianProtocolException.java new file mode 100644 index 0000000000..9a0a3f80ac --- /dev/null +++ b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/HessianProtocolException.java @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2001-2008 Caucho Technology, Inc. All rights reserved. + * + * The Apache Software License, Version 1.1 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Caucho Technology (http://www.caucho.com/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Burlap", "Resin", and "Caucho" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * info@caucho.com. + * + * 5. Products derived from this software may not be called "Resin" + * nor may "Resin" appear in their names without prior written + * permission of Caucho Technology. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Scott Ferguson + */ + +package com.caucho.hessian4.io; + +import java.io.IOException; + +/** + * Exception for faults when the fault doesn't return a java exception. + * This exception is required for MicroHessianInput. + */ +public class HessianProtocolException extends IOException { + private Throwable rootCause; + + /** + * Zero-arg constructor. + */ + public HessianProtocolException() + { + } + + /** + * Create the exception. + */ + public HessianProtocolException(String message) + { + super(message); + } + + /** + * Create the exception. + */ + public HessianProtocolException(String message, Throwable rootCause) + { + super(message); + + this.rootCause = rootCause; + } + + /** + * Create the exception. + */ + public HessianProtocolException(Throwable rootCause) + { + super(String.valueOf(rootCause)); + + this.rootCause = rootCause; + } + + /** + * Returns the underlying cause. + */ + public Throwable getRootCause() + { + return rootCause; + } + + /** + * Returns the underlying cause. + */ + public Throwable getCause() + { + return getRootCause(); + } +} diff --git a/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/HessianRemote.java b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/HessianRemote.java new file mode 100644 index 0000000000..c8e3cb493b --- /dev/null +++ b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/HessianRemote.java @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2001-2004 Caucho Technology, Inc. All rights reserved. + * + * The Apache Software License, Version 1.1 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Caucho Technology (http://www.caucho.com/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Hessian", "Resin", and "Caucho" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * info@caucho.com. + * + * 5. Products derived from this software may not be called "Resin" + * nor may "Resin" appear in their names without prior written + * permission of Caucho Technology. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Scott Ferguson + */ + +package com.caucho.hessian4.io; + +/** + * Encapsulates a remote address when no stub is available, e.g. for + * Java MicroEdition. + */ +public class HessianRemote implements java.io.Serializable { + private String type; + private String url; + + /** + * Creates a new Hessian remote object. + * + * @param type the remote stub interface + * @param url the remote url + */ + public HessianRemote(String type, String url) + { + this.type = type; + this.url = url; + } + + /** + * Creates an uninitialized Hessian remote. + */ + public HessianRemote() + { + } + + /** + * Returns the remote api class name. + */ + public String getType() + { + return type; + } + + /** + * Returns the remote URL. + */ + public String getURL() + { + return url; + } + + /** + * Sets the remote URL. + */ + public void setURL(String url) + { + this.url = url; + } + + /** + * Defines the hashcode. + */ + public int hashCode() + { + return url.hashCode(); + } + + /** + * Defines equality + */ + public boolean equals(Object obj) + { + if (! (obj instanceof HessianRemote)) + return false; + + HessianRemote remote = (HessianRemote) obj; + + return url.equals(remote.url); + } + + /** + * Readable version of the remote. + */ + public String toString() + { + return "HessianRemote[" + url + "]"; + } +} diff --git a/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/HessianRemoteObject.java b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/HessianRemoteObject.java new file mode 100644 index 0000000000..84acae53df --- /dev/null +++ b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/HessianRemoteObject.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2001-2004 Caucho Technology, Inc. All rights reserved. + * + * The Apache Software License, Version 1.1 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Caucho Technology (http://www.caucho.com/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Hessian", "Resin", and "Caucho" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * info@caucho.com. + * + * 5. Products derived from this software may not be called "Resin" + * nor may "Resin" appear in their names without prior written + * permission of Caucho Technology. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Scott Ferguson + */ + +package com.caucho.hessian4.io; + +/** + * Interface for any hessian remote object. + */ +public interface HessianRemoteObject { + public String getHessianType(); + public String getHessianURL(); +} diff --git a/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/HessianRemoteResolver.java b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/HessianRemoteResolver.java new file mode 100644 index 0000000000..4c774854cd --- /dev/null +++ b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/HessianRemoteResolver.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2001-2004 Caucho Technology, Inc. All rights reserved. + * + * The Apache Software License, Version 1.1 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Caucho Technology (http://www.caucho.com/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Hessian", "Resin", and "Caucho" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * info@caucho.com. + * + * 5. Products derived from this software may not be called "Resin" + * nor may "Resin" appear in their names without prior written + * permission of Caucho Technology. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Scott Ferguson + */ + +package com.caucho.hessian4.io; + +import java.io.IOException; + +/** + * Looks up remote objects. The default just returns a HessianRemote object. + */ +public interface HessianRemoteResolver { + /** + * Looks up a proxy object. + */ + public Object lookup(String type, String url) + throws IOException; +} diff --git a/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/HessianSerializerInput.java b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/HessianSerializerInput.java new file mode 100644 index 0000000000..161d89b5fb --- /dev/null +++ b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/HessianSerializerInput.java @@ -0,0 +1,184 @@ +/* + * Copyright (c) 2001-2004 Caucho Technology, Inc. All rights reserved. + * + * The Apache Software License, Version 1.1 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Caucho Technology (http://www.caucho.com/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Burlap", "Resin", and "Caucho" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * info@caucho.com. + * + * 5. Products derived from this software may not be called "Resin" + * nor may "Resin" appear in their names without prior written + * permission of Caucho Technology. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Scott Ferguson + */ + +package com.caucho.hessian4.io; + +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.HashMap; + +/** + * Input stream for Hessian requests, deserializing objects using the + * java.io.Serialization protocol. + * + *

    HessianSerializerInput is unbuffered, so any client needs to provide + * its own buffering. + * + *

    Serialization

    + * + *
    + * InputStream is = new FileInputStream("test.xml");
    + * HessianOutput in = new HessianSerializerOutput(is);
    + *
    + * Object obj = in.readObject();
    + * is.close();
    + * 
    + * + *

    Parsing a Hessian reply

    + * + *
    + * InputStream is = ...; // from http connection
    + * HessianInput in = new HessianSerializerInput(is);
    + * String value;
    + *
    + * in.startReply();         // read reply header
    + * value = in.readString(); // read string value
    + * in.completeReply();      // read reply footer
    + * 
    + */ +public class HessianSerializerInput extends Hessian2Input { + /** + * Creates a new Hessian input stream, initialized with an + * underlying input stream. + * + * @param is the underlying input stream. + */ + public HessianSerializerInput(InputStream is) + { + super(is); + } + + /** + * Creates an uninitialized Hessian input stream. + */ + public HessianSerializerInput() + { + super(null); + } + + /** + * Reads an object from the input stream. cl is known not to be + * a Map. + */ + protected Object readObjectImpl(Class cl) + throws IOException + { + try { + Object obj = cl.newInstance(); + + if (_refs == null) + _refs = new ArrayList(); + _refs.add(obj); + + HashMap fieldMap = getFieldMap(cl); + + int code = read(); + for (; code >= 0 && code != 'z'; code = read()) { + unread(); + + Object key = readObject(); + + Field field = (Field) fieldMap.get(key); + + if (field != null) { + Object value = readObject(field.getType()); + field.set(obj, value); + } + else { + Object value = readObject(); + } + } + + if (code != 'z') + throw expect("map", code); + + // if there's a readResolve method, call it + try { + Method method = cl.getMethod("readResolve", new Class[0]); + return method.invoke(obj, new Object[0]); + } catch (Exception e) { + } + + return obj; + } catch (IOException e) { + throw e; + } catch (Exception e) { + throw new IOExceptionWrapper(e); + } + } + + /** + * Creates a map of the classes fields. + */ + protected HashMap getFieldMap(Class cl) + { + HashMap fieldMap = new HashMap(); + + for (; cl != null; cl = cl.getSuperclass()) { + Field []fields = cl.getDeclaredFields(); + for (int i = 0; i < fields.length; i++) { + Field field = fields[i]; + + if (Modifier.isTransient(field.getModifiers()) || + Modifier.isStatic(field.getModifiers())) + continue; + + // XXX: could parameterize the handler to only deal with public + field.setAccessible(true); + + fieldMap.put(field.getName(), field); + } + } + + return fieldMap; + } +} diff --git a/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/HessianSerializerOutput.java b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/HessianSerializerOutput.java new file mode 100644 index 0000000000..1b8b9b8d4f --- /dev/null +++ b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/HessianSerializerOutput.java @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2001-2004 Caucho Technology, Inc. All rights reserved. + * + * The Apache Software License, Version 1.1 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Caucho Technology (http://www.caucho.com/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Hessian", "Resin", and "Caucho" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * info@caucho.com. + * + * 5. Products derived from this software may not be called "Resin" + * nor may "Resin" appear in their names without prior written + * permission of Caucho Technology. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Scott Ferguson + */ + +package com.caucho.hessian4.io; + +import java.io.IOException; +import java.io.OutputStream; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; + +/** + * Output stream for Hessian requests. + * + *

    HessianOutput is unbuffered, so any client needs to provide + * its own buffering. + * + *

    Serialization

    + * + *
    + * OutputStream os = new FileOutputStream("test.xml");
    + * HessianOutput out = new HessianSerializerOutput(os);
    + *
    + * out.writeObject(obj);
    + * os.close();
    + * 
    + * + *

    Writing an RPC Call

    + * + *
    + * OutputStream os = ...; // from http connection
    + * HessianOutput out = new HessianSerializerOutput(os);
    + * String value;
    + *
    + * out.startCall("hello");  // start hello call
    + * out.writeString("arg1"); // write a string argument
    + * out.completeCall();      // complete the call
    + * 
    + */ +public class HessianSerializerOutput extends Hessian2Output { + /** + * Creates a new Hessian output stream, initialized with an + * underlying output stream. + * + * @param os the underlying output stream. + */ + public HessianSerializerOutput(OutputStream os) + { + super(os); + } + + /** + * Creates an uninitialized Hessian output stream. + */ + public HessianSerializerOutput() + { + super(null); + } + + /** + * Applications which override this can do custom serialization. + * + * @param object the object to write. + */ + public void writeObjectImpl(Object obj) + throws IOException + { + Class cl = obj.getClass(); + + try { + Method method = cl.getMethod("writeReplace", new Class[0]); + Object repl = method.invoke(obj, new Object[0]); + + writeObject(repl); + return; + } catch (Exception e) { + } + + try { + writeMapBegin(cl.getName()); + for (; cl != null; cl = cl.getSuperclass()) { + Field []fields = cl.getDeclaredFields(); + for (int i = 0; i < fields.length; i++) { + Field field = fields[i]; + + if (Modifier.isTransient(field.getModifiers()) || + Modifier.isStatic(field.getModifiers())) + continue; + + // XXX: could parameterize the handler to only deal with public + field.setAccessible(true); + + writeString(field.getName()); + writeObject(field.get(obj)); + } + } + writeMapEnd(); + } catch (IllegalAccessException e) { + throw new IOExceptionWrapper(e); + } + } +} diff --git a/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/HessianServiceException.java b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/HessianServiceException.java new file mode 100644 index 0000000000..432aded172 --- /dev/null +++ b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/HessianServiceException.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2001-2004 Caucho Technology, Inc. All rights reserved. + * + * The Apache Software License, Version 1.1 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Caucho Technology (http://www.caucho.com/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Hessian", "Resin", and "Caucho" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * info@caucho.com. + * + * 5. Products derived from this software may not be called "Resin" + * nor may "Resin" appear in their names without prior written + * permission of Caucho Technology. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Scott Ferguson + */ + +package com.caucho.hessian4.io; + +/** + * Exception for faults when the fault doesn't return a java exception. + * This exception is required for MicroHessianInput. + */ +public class HessianServiceException extends Exception { + private String code; + private Object detail; + + /** + * Zero-arg constructor. + */ + public HessianServiceException() + { + } + + /** + * Create the exception. + */ + public HessianServiceException(String message, String code, Object detail) + { + super(message); + this.code = code; + this.detail = detail; + } + + /** + * Returns the code. + */ + public String getCode() + { + return code; + } + + /** + * Returns the detail. + */ + public Object getDetail() + { + return detail; + } +} diff --git a/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/IOExceptionWrapper.java b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/IOExceptionWrapper.java new file mode 100644 index 0000000000..f2671987a6 --- /dev/null +++ b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/IOExceptionWrapper.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2001-2004 Caucho Technology, Inc. All rights reserved. + * + * The Apache Software License, Version 1.1 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Caucho Technology (http://www.caucho.com/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Hessian", "Resin", and "Caucho" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * info@caucho.com. + * + * 5. Products derived from this software may not be called "Resin" + * nor may "Resin" appear in their names without prior written + * permission of Caucho Technology. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Scott Ferguson + */ + +package com.caucho.hessian4.io; + +import java.io.IOException; + +/** + * Exception wrapper for IO. + */ +public class IOExceptionWrapper extends IOException { + private Throwable _cause; + + public IOExceptionWrapper(Throwable cause) + { + super(cause.toString()); + + _cause = cause; + } + + public IOExceptionWrapper(String msg, Throwable cause) + { + super(msg); + + _cause = cause; + } + + public Throwable getCause() + { + return _cause; + } +} diff --git a/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/InputStreamDeserializer.java b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/InputStreamDeserializer.java new file mode 100644 index 0000000000..f2679c11f5 --- /dev/null +++ b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/InputStreamDeserializer.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2001-2004 Caucho Technology, Inc. All rights reserved. + * + * The Apache Software License, Version 1.1 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Caucho Technology (http://www.caucho.com/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Burlap", "Resin", and "Caucho" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * info@caucho.com. + * + * 5. Products derived from this software may not be called "Resin" + * nor may "Resin" appear in their names without prior written + * permission of Caucho Technology. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Scott Ferguson + */ + +package com.caucho.hessian4.io; + +import java.io.IOException; + +/** + * Serializing a stream object. + */ +public class InputStreamDeserializer extends AbstractDeserializer { + public static final InputStreamDeserializer DESER + = new InputStreamDeserializer(); + + public InputStreamDeserializer() + { + } + + public Object readObject(AbstractHessianInput in) + throws IOException + { + return in.readInputStream(); + } +} diff --git a/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/InputStreamSerializer.java b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/InputStreamSerializer.java new file mode 100644 index 0000000000..a82e3c850a --- /dev/null +++ b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/InputStreamSerializer.java @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2001-2004 Caucho Technology, Inc. All rights reserved. + * + * The Apache Software License, Version 1.1 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Caucho Technology (http://www.caucho.com/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Burlap", "Resin", and "Caucho" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * info@caucho.com. + * + * 5. Products derived from this software may not be called "Resin" + * nor may "Resin" appear in their names without prior written + * permission of Caucho Technology. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Scott Ferguson + */ + +package com.caucho.hessian4.io; + +import java.io.IOException; +import java.io.InputStream; + +/** + * Serializing a stream object. + */ +public class InputStreamSerializer extends AbstractSerializer { + public InputStreamSerializer() + { + } + + public void writeObject(Object obj, AbstractHessianOutput out) + throws IOException + { + InputStream is = (InputStream) obj; + + if (is == null) + out.writeNull(); + else { + out.writeByteStream(is); + } + } +} diff --git a/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/IteratorSerializer.java b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/IteratorSerializer.java new file mode 100644 index 0000000000..ce548b9132 --- /dev/null +++ b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/IteratorSerializer.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2001-2004 Caucho Technology, Inc. All rights reserved. + * + * The Apache Software License, Version 1.1 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Caucho Technology (http://www.caucho.com/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Hessian", "Resin", and "Caucho" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * info@caucho.com. + * + * 5. Products derived from this software may not be called "Resin" + * nor may "Resin" appear in their names without prior written + * permission of Caucho Technology. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Scott Ferguson + */ + +package com.caucho.hessian4.io; + +import java.io.IOException; +import java.util.Iterator; + +/** + * Serializing a JDK 1.2 Iterator. + */ +public class IteratorSerializer extends AbstractSerializer { + private static IteratorSerializer _serializer; + + public static IteratorSerializer create() + { + if (_serializer == null) + _serializer = new IteratorSerializer(); + + return _serializer; + } + + public void writeObject(Object obj, AbstractHessianOutput out) + throws IOException + { + Iterator iter = (Iterator) obj; + + boolean hasEnd = out.writeListBegin(-1, null); + + while (iter.hasNext()) { + Object value = iter.next(); + + out.writeObject(value); + } + + if (hasEnd) + out.writeListEnd(); + } +} diff --git a/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/JavaDeserializer.java b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/JavaDeserializer.java new file mode 100644 index 0000000000..8c3fd87125 --- /dev/null +++ b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/JavaDeserializer.java @@ -0,0 +1,753 @@ +/* + * Copyright (c) 2001-2008 Caucho Technology, Inc. All rights reserved. + * + * The Apache Software License, Version 1.1 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Caucho Technology (http://www.caucho.com/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Burlap", "Resin", and "Caucho" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * info@caucho.com. + * + * 5. Products derived from this software may not be called "Resin" + * nor may "Resin" appear in their names without prior written + * permission of Caucho Technology. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Scott Ferguson + */ + +package com.caucho.hessian4.io; + +import java.io.IOException; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.HashMap; + + +/** + * Serializing an object for known object types. + */ +public class JavaDeserializer extends AbstractMapDeserializer { + private Class _type; + private HashMap _fieldMap; + private Method _readResolve; + private Constructor _constructor; + private Object []_constructorArgs; + + public JavaDeserializer(Class cl) + { + _type = cl; + _fieldMap = getFieldMap(cl); + + _readResolve = getReadResolve(cl); + + if (_readResolve != null) { + _readResolve.setAccessible(true); + } + + Constructor []constructors = cl.getDeclaredConstructors(); + long bestCost = Long.MAX_VALUE; + + for (int i = 0; i < constructors.length; i++) { + Class []param = constructors[i].getParameterTypes(); + long cost = 0; + + for (int j = 0; j < param.length; j++) { + cost = 4 * cost; + + if (Object.class.equals(param[j])) + cost += 1; + else if (String.class.equals(param[j])) + cost += 2; + else if (int.class.equals(param[j])) + cost += 3; + else if (long.class.equals(param[j])) + cost += 4; + else if (param[j].isPrimitive()) + cost += 5; + else + cost += 6; + } + + if (cost < 0 || cost > (1 << 48)) + cost = 1 << 48; + + cost += (long) param.length << 48; + + if (cost < bestCost) { + _constructor = constructors[i]; + bestCost = cost; + } + } + + if (_constructor != null) { + _constructor.setAccessible(true); + Class []params = _constructor.getParameterTypes(); + _constructorArgs = new Object[params.length]; + for (int i = 0; i < params.length; i++) { + _constructorArgs[i] = getParamArg(params[i]); + } + } + } + + @Override + public Class getType() + { + return _type; + } + + @Override + public boolean isReadResolve() + { + return _readResolve != null; + } + + public Object readMap(AbstractHessianInput in) + throws IOException + { + try { + Object obj = instantiate(); + + return readMap(in, obj); + } catch (IOException e) { + throw e; + } catch (RuntimeException e) { + throw e; + } catch (Exception e) { + throw new IOExceptionWrapper(_type.getName() + ":" + e.getMessage(), e); + } + } + + @Override + public Object []createFields(int len) + { + return new FieldDeserializer[len]; + } + + public Object createField(String name) + { + Object reader = _fieldMap.get(name); + + if (reader == null) + reader = NullFieldDeserializer.DESER; + + return reader; + } + + @Override + public Object readObject(AbstractHessianInput in, + Object []fields) + throws IOException + { + try { + Object obj = instantiate(); + + return readObject(in, obj, (FieldDeserializer []) fields); + } catch (IOException e) { + throw e; + } catch (RuntimeException e) { + throw e; + } catch (Exception e) { + throw new IOExceptionWrapper(_type.getName() + ":" + e.getMessage(), e); + } + } + + @Override + public Object readObject(AbstractHessianInput in, + String []fieldNames) + throws IOException + { + try { + Object obj = instantiate(); + + return readObject(in, obj, fieldNames); + } catch (IOException e) { + throw e; + } catch (RuntimeException e) { + throw e; + } catch (Exception e) { + throw new IOExceptionWrapper(_type.getName() + ":" + e.getMessage(), e); + } + } + + /** + * Returns the readResolve method + */ + protected Method getReadResolve(Class cl) + { + for (; cl != null; cl = cl.getSuperclass()) { + Method []methods = cl.getDeclaredMethods(); + + for (int i = 0; i < methods.length; i++) { + Method method = methods[i]; + + if (method.getName().equals("readResolve") + && method.getParameterTypes().length == 0) + return method; + } + } + + return null; + } + + public Object readMap(AbstractHessianInput in, Object obj) + throws IOException + { + try { + int ref = in.addRef(obj); + + while (! in.isEnd()) { + Object key = in.readObject(); + + FieldDeserializer deser = _fieldMap.get(key); + + if (deser != null) + deser.deserialize(in, obj); + else + in.readObject(); + } + + in.readMapEnd(); + + Object resolve = resolve(in, obj); + + if (obj != resolve) + in.setRef(ref, resolve); + + return resolve; + } catch (IOException e) { + throw e; + } catch (Exception e) { + throw new IOExceptionWrapper(e); + } + } + + private Object readObject(AbstractHessianInput in, + Object obj, + FieldDeserializer []fields) + throws IOException + { + try { + int ref = in.addRef(obj); + + for (FieldDeserializer reader : fields) { + reader.deserialize(in, obj); + } + + Object resolve = resolve(in, obj); + + if (obj != resolve) + in.setRef(ref, resolve); + + return resolve; + } catch (IOException e) { + throw e; + } catch (Exception e) { + throw new IOExceptionWrapper(obj.getClass().getName() + ":" + e, e); + } + } + + public Object readObject(AbstractHessianInput in, + Object obj, + String []fieldNames) + throws IOException + { + try { + int ref = in.addRef(obj); + + for (String fieldName : fieldNames) { + FieldDeserializer reader = _fieldMap.get(fieldName); + + if (reader != null) + reader.deserialize(in, obj); + else + in.readObject(); + } + + Object resolve = resolve(in, obj); + + if (obj != resolve) + in.setRef(ref, resolve); + + return resolve; + } catch (IOException e) { + throw e; + } catch (Exception e) { + throw new IOExceptionWrapper(obj.getClass().getName() + ":" + e, e); + } + } + + protected Object resolve(AbstractHessianInput in, Object obj) + throws Exception + { + // if there's a readResolve method, call it + try { + if (_readResolve != null) + return _readResolve.invoke(obj, new Object[0]); + } catch (InvocationTargetException e) { + if (e.getCause() instanceof Exception) + throw (Exception) e.getCause(); + else + throw e; + } + + return obj; + } + + protected Object instantiate() + throws Exception + { + try { + if (_constructor != null) + return _constructor.newInstance(_constructorArgs); + else + return _type.newInstance(); + } catch (Exception e) { + throw new HessianProtocolException("'" + _type.getName() + "' could not be instantiated", e); + } + } + + /** + * Creates a map of the classes fields. + */ + protected HashMap getFieldMap(Class cl) + { + HashMap fieldMap + = new HashMap(); + + for (; cl != null; cl = cl.getSuperclass()) { + Field []fields = cl.getDeclaredFields(); + for (int i = 0; i < fields.length; i++) { + Field field = fields[i]; + + if (Modifier.isTransient(field.getModifiers()) + || Modifier.isStatic(field.getModifiers())) + continue; + else if (fieldMap.get(field.getName()) != null) + continue; + + // XXX: could parameterize the handler to only deal with public + try { + field.setAccessible(true); + } catch (Throwable e) { + e.printStackTrace(); + } + + Class type = field.getType(); + FieldDeserializer deser; + + if (String.class.equals(type)) + deser = new StringFieldDeserializer(field); + else if (byte.class.equals(type)) { + deser = new ByteFieldDeserializer(field); + } + else if (short.class.equals(type)) { + deser = new ShortFieldDeserializer(field); + } + else if (int.class.equals(type)) { + deser = new IntFieldDeserializer(field); + } + else if (long.class.equals(type)) { + deser = new LongFieldDeserializer(field); + } + else if (float.class.equals(type)) { + deser = new FloatFieldDeserializer(field); + } + else if (double.class.equals(type)) { + deser = new DoubleFieldDeserializer(field); + } + else if (boolean.class.equals(type)) { + deser = new BooleanFieldDeserializer(field); + } + else if (java.sql.Date.class.equals(type)) { + deser = new SqlDateFieldDeserializer(field); + } + else if (java.sql.Timestamp.class.equals(type)) { + deser = new SqlTimestampFieldDeserializer(field); + } + else if (java.sql.Time.class.equals(type)) { + deser = new SqlTimeFieldDeserializer(field); + } + else { + deser = new ObjectFieldDeserializer(field); + } + + fieldMap.put(field.getName(), deser); + } + } + + return fieldMap; + } + + /** + * Creates a map of the classes fields. + */ + protected static Object getParamArg(Class cl) + { + if (! cl.isPrimitive()) + return null; + else if (boolean.class.equals(cl)) + return Boolean.FALSE; + else if (byte.class.equals(cl)) + return new Byte((byte) 0); + else if (short.class.equals(cl)) + return new Short((short) 0); + else if (char.class.equals(cl)) + return new Character((char) 0); + else if (int.class.equals(cl)) + return Integer.valueOf(0); + else if (long.class.equals(cl)) + return Long.valueOf(0); + else if (float.class.equals(cl)) + return Float.valueOf(0); + else if (double.class.equals(cl)) + return Double.valueOf(0); + else + throw new UnsupportedOperationException(); + } + + abstract static class FieldDeserializer { + abstract void deserialize(AbstractHessianInput in, Object obj) + throws IOException; + } + + static class NullFieldDeserializer { + static NullFieldDeserializer DESER = new NullFieldDeserializer(); + void deserialize(AbstractHessianInput in, Object obj) + throws IOException + { + in.readObject(); + } + } + + static class ObjectFieldDeserializer extends FieldDeserializer { + private final Field _field; + + ObjectFieldDeserializer(Field field) + { + _field = field; + } + + void deserialize(AbstractHessianInput in, Object obj) + throws IOException + { + Object value = null; + + try { + value = in.readObject(_field.getType()); + + _field.set(obj, value); + } catch (Exception e) { + logDeserializeError(_field, obj, value, e); + } + } + } + + static class BooleanFieldDeserializer extends FieldDeserializer { + private final Field _field; + + BooleanFieldDeserializer(Field field) + { + _field = field; + } + + void deserialize(AbstractHessianInput in, Object obj) + throws IOException + { + boolean value = false; + + try { + value = in.readBoolean(); + + _field.setBoolean(obj, value); + } catch (Exception e) { + logDeserializeError(_field, obj, value, e); + } + } + } + + static class ByteFieldDeserializer extends FieldDeserializer { + private final Field _field; + + ByteFieldDeserializer(Field field) + { + _field = field; + } + + void deserialize(AbstractHessianInput in, Object obj) + throws IOException + { + int value = 0; + + try { + value = in.readInt(); + + _field.setByte(obj, (byte) value); + } catch (Exception e) { + logDeserializeError(_field, obj, value, e); + } + } + } + + static class ShortFieldDeserializer extends FieldDeserializer { + private final Field _field; + + ShortFieldDeserializer(Field field) + { + _field = field; + } + + void deserialize(AbstractHessianInput in, Object obj) + throws IOException + { + int value = 0; + + try { + value = in.readInt(); + + _field.setShort(obj, (short) value); + } catch (Exception e) { + logDeserializeError(_field, obj, value, e); + } + } + } + + static class IntFieldDeserializer extends FieldDeserializer { + private final Field _field; + + IntFieldDeserializer(Field field) + { + _field = field; + } + + void deserialize(AbstractHessianInput in, Object obj) + throws IOException + { + int value = 0; + + try { + value = in.readInt(); + + _field.setInt(obj, value); + } catch (Exception e) { + logDeserializeError(_field, obj, value, e); + } + } + } + + static class LongFieldDeserializer extends FieldDeserializer { + private final Field _field; + + LongFieldDeserializer(Field field) + { + _field = field; + } + + void deserialize(AbstractHessianInput in, Object obj) + throws IOException + { + long value = 0; + + try { + value = in.readLong(); + + _field.setLong(obj, value); + } catch (Exception e) { + logDeserializeError(_field, obj, value, e); + } + } + } + + static class FloatFieldDeserializer extends FieldDeserializer { + private final Field _field; + + FloatFieldDeserializer(Field field) + { + _field = field; + } + + void deserialize(AbstractHessianInput in, Object obj) + throws IOException + { + double value = 0; + + try { + value = in.readDouble(); + + _field.setFloat(obj, (float) value); + } catch (Exception e) { + logDeserializeError(_field, obj, value, e); + } + } + } + + static class DoubleFieldDeserializer extends FieldDeserializer { + private final Field _field; + + DoubleFieldDeserializer(Field field) + { + _field = field; + } + + void deserialize(AbstractHessianInput in, Object obj) + throws IOException + { + double value = 0; + + try { + value = in.readDouble(); + + _field.setDouble(obj, value); + } catch (Exception e) { + logDeserializeError(_field, obj, value, e); + } + } + } + + static class StringFieldDeserializer extends FieldDeserializer { + private final Field _field; + + StringFieldDeserializer(Field field) + { + _field = field; + } + + void deserialize(AbstractHessianInput in, Object obj) + throws IOException + { + String value = null; + + try { + value = in.readString(); + + _field.set(obj, value); + } catch (Exception e) { + logDeserializeError(_field, obj, value, e); + } + } + } + + static class SqlDateFieldDeserializer extends FieldDeserializer { + private final Field _field; + + SqlDateFieldDeserializer(Field field) + { + _field = field; + } + + void deserialize(AbstractHessianInput in, Object obj) + throws IOException + { + java.sql.Date value = null; + + try { + java.util.Date date = (java.util.Date) in.readObject(); + value = new java.sql.Date(date.getTime()); + + _field.set(obj, value); + } catch (Exception e) { + logDeserializeError(_field, obj, value, e); + } + } + } + + static class SqlTimestampFieldDeserializer extends FieldDeserializer { + private final Field _field; + + SqlTimestampFieldDeserializer(Field field) + { + _field = field; + } + + void deserialize(AbstractHessianInput in, Object obj) + throws IOException + { + java.sql.Timestamp value = null; + + try { + java.util.Date date = (java.util.Date) in.readObject(); + value = new java.sql.Timestamp(date.getTime()); + + _field.set(obj, value); + } catch (Exception e) { + logDeserializeError(_field, obj, value, e); + } + } + } + + static class SqlTimeFieldDeserializer extends FieldDeserializer { + private final Field _field; + + SqlTimeFieldDeserializer(Field field) + { + _field = field; + } + + void deserialize(AbstractHessianInput in, Object obj) + throws IOException + { + java.sql.Time value = null; + + try { + java.util.Date date = (java.util.Date) in.readObject(); + value = new java.sql.Time(date.getTime()); + + _field.set(obj, value); + } catch (Exception e) { + logDeserializeError(_field, obj, value, e); + } + } + } + + static void logDeserializeError(Field field, Object obj, Object value, + Throwable e) + throws IOException + { + String fieldName = (field.getDeclaringClass().getName() + + "." + field.getName()); + + if (e instanceof HessianFieldException) + throw (HessianFieldException) e; + else if (e instanceof IOException) + throw new HessianFieldException(fieldName + ": " + e.getMessage(), e); + + if (value != null) + throw new HessianFieldException(fieldName + ": " + value.getClass().getName() + " (" + value + ")" + + " cannot be assigned to '" + field.getType().getName() + "'", e); + else + throw new HessianFieldException(fieldName + ": " + field.getType().getName() + " cannot be assigned from null", e); + } +} diff --git a/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/JavaSerializer.java b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/JavaSerializer.java new file mode 100644 index 0000000000..b726b13b0e --- /dev/null +++ b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/JavaSerializer.java @@ -0,0 +1,450 @@ +/* + * Copyright (c) 2001-2008 Caucho Technology, Inc. All rights reserved. + * + * The Apache Software License, Version 1.1 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Caucho Technology (http://www.caucho.com/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Burlap", "Resin", and "Caucho" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * info@caucho.com. + * + * 5. Products derived from this software may not be called "Resin" + * nor may "Resin" appear in their names without prior written + * permission of Caucho Technology. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Scott Ferguson + */ + +package com.caucho.hessian4.io; + +import java.io.IOException; +import java.lang.ref.SoftReference; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.WeakHashMap; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Serializing an object for known object types. + */ +public class JavaSerializer extends AbstractSerializer +{ + private static final Logger log + = Logger.getLogger(JavaSerializer.class.getName()); + + private static final WeakHashMap,SoftReference> _serializerMap + = new WeakHashMap,SoftReference>(); + + private Field []_fields; + private FieldSerializer []_fieldSerializers; + + private Object _writeReplaceFactory; + private Method _writeReplace; + + public JavaSerializer(Class cl) + { + introspect(cl); + + _writeReplace = getWriteReplace(cl); + } + + public static Serializer create(Class cl) + { + synchronized (_serializerMap) { + SoftReference baseRef + = _serializerMap.get(cl); + + JavaSerializer base = baseRef != null ? baseRef.get() : null; + + if (base == null) { + base = new JavaSerializer(cl); + baseRef = new SoftReference(base); + _serializerMap.put(cl, baseRef); + } + + return base; + } + } + + protected void introspect(Class cl) + { + if (_writeReplace != null) + _writeReplace.setAccessible(true); + + ArrayList primitiveFields = new ArrayList(); + ArrayList compoundFields = new ArrayList(); + + for (; cl != null; cl = cl.getSuperclass()) { + Field []fields = cl.getDeclaredFields(); + for (int i = 0; i < fields.length; i++) { + Field field = fields[i]; + + if (Modifier.isTransient(field.getModifiers()) + || Modifier.isStatic(field.getModifiers())) + continue; + + // XXX: could parameterize the handler to only deal with public + field.setAccessible(true); + + if (field.getType().isPrimitive() + || (field.getType().getName().startsWith("java.lang.") + && ! field.getType().equals(Object.class))) + primitiveFields.add(field); + else + compoundFields.add(field); + } + } + + ArrayList fields = new ArrayList(); + fields.addAll(primitiveFields); + fields.addAll(compoundFields); + + _fields = new Field[fields.size()]; + fields.toArray(_fields); + + _fieldSerializers = new FieldSerializer[_fields.length]; + + for (int i = 0; i < _fields.length; i++) { + _fieldSerializers[i] = getFieldSerializer(_fields[i].getType()); + } + } + + /** + * Returns the writeReplace method + */ + protected static Method getWriteReplace(Class cl) + { + for (; cl != null; cl = cl.getSuperclass()) { + Method []methods = cl.getDeclaredMethods(); + + for (int i = 0; i < methods.length; i++) { + Method method = methods[i]; + + if (method.getName().equals("writeReplace") + && method.getParameterTypes().length == 0) + return method; + } + } + + return null; + } + + /** + * Returns the writeReplace method + */ + protected Method getWriteReplace(Class cl, Class param) + { + for (; cl != null; cl = cl.getSuperclass()) { + for (Method method : cl.getDeclaredMethods()) { + if (method.getName().equals("writeReplace") + && method.getParameterTypes().length == 1 + && param.equals(method.getParameterTypes()[0])) + return method; + } + } + + return null; + } + + public void writeObject(Object obj, AbstractHessianOutput out) + throws IOException + { + if (out.addRef(obj)) { + return; + } + + Class cl = obj.getClass(); + + try { + if (_writeReplace != null) { + Object repl; + + if (_writeReplaceFactory != null) + repl = _writeReplace.invoke(_writeReplaceFactory, obj); + else + repl = _writeReplace.invoke(obj); + + // out.removeRef(obj); + + out.writeObject(repl); + + out.replaceRef(repl, obj); + + return; + } + } catch (RuntimeException e) { + throw e; + } catch (Exception e) { + // log.log(Level.FINE, e.toString(), e); + throw new RuntimeException(e); + } + + int ref = out.writeObjectBegin(cl.getName()); + + if (ref < -1) { + writeObject10(obj, out); + } + else { + if (ref == -1) { + writeDefinition20(out); + out.writeObjectBegin(cl.getName()); + } + + writeInstance(obj, out); + } + } + + protected void writeObject10(Object obj, AbstractHessianOutput out) + throws IOException + { + for (int i = 0; i < _fields.length; i++) { + Field field = _fields[i]; + + out.writeString(field.getName()); + + _fieldSerializers[i].serialize(out, obj, field); + } + + out.writeMapEnd(); + } + + private void writeDefinition20(AbstractHessianOutput out) + throws IOException + { + out.writeClassFieldLength(_fields.length); + + for (int i = 0; i < _fields.length; i++) { + Field field = _fields[i]; + + out.writeString(field.getName()); + } + } + + public void writeInstance(Object obj, AbstractHessianOutput out) + throws IOException + { + try { + for (int i = 0; i < _fields.length; i++) { + Field field = _fields[i]; + + _fieldSerializers[i].serialize(out, obj, field); + } + } catch (RuntimeException e) { + throw new RuntimeException(e.getMessage() + "\n class: " + + obj.getClass().getName() + + " (object=" + obj + ")", + e); + } catch (IOException e) { + throw new IOExceptionWrapper(e.getMessage() + "\n class: " + + obj.getClass().getName() + + " (object=" + obj + ")", + e); + } + } + + private static FieldSerializer getFieldSerializer(Class type) + { + if (int.class.equals(type) + || byte.class.equals(type) + || short.class.equals(type) + || int.class.equals(type)) { + return IntFieldSerializer.SER; + } + else if (long.class.equals(type)) { + return LongFieldSerializer.SER; + } + else if (double.class.equals(type) || + float.class.equals(type)) { + return DoubleFieldSerializer.SER; + } + else if (boolean.class.equals(type)) { + return BooleanFieldSerializer.SER; + } + else if (String.class.equals(type)) { + return StringFieldSerializer.SER; + } + else if (java.util.Date.class.equals(type) + || java.sql.Date.class.equals(type) + || java.sql.Timestamp.class.equals(type) + || java.sql.Time.class.equals(type)) { + return DateFieldSerializer.SER; + } + else + return FieldSerializer.SER; + } + + static class FieldSerializer { + static final FieldSerializer SER = new FieldSerializer(); + + void serialize(AbstractHessianOutput out, Object obj, Field field) + throws IOException + { + Object value = null; + + try { + value = field.get(obj); + } catch (IllegalAccessException e) { + log.log(Level.FINE, e.toString(), e); + } + + try { + out.writeObject(value); + } catch (RuntimeException e) { + throw new RuntimeException(e.getMessage() + "\n field: " + + field.getDeclaringClass().getName() + + '.' + field.getName(), + e); + } catch (IOException e) { + throw new IOExceptionWrapper(e.getMessage() + "\n field: " + + field.getDeclaringClass().getName() + + '.' + field.getName(), + e); + } + } + } + + static class BooleanFieldSerializer extends FieldSerializer { + static final FieldSerializer SER = new BooleanFieldSerializer(); + + void serialize(AbstractHessianOutput out, Object obj, Field field) + throws IOException + { + boolean value = false; + + try { + value = field.getBoolean(obj); + } catch (IllegalAccessException e) { + log.log(Level.FINE, e.toString(), e); + } + + out.writeBoolean(value); + } + } + + static class IntFieldSerializer extends FieldSerializer { + static final FieldSerializer SER = new IntFieldSerializer(); + + void serialize(AbstractHessianOutput out, Object obj, Field field) + throws IOException + { + int value = 0; + + try { + value = field.getInt(obj); + } catch (IllegalAccessException e) { + log.log(Level.FINE, e.toString(), e); + } + + out.writeInt(value); + } + } + + static class LongFieldSerializer extends FieldSerializer { + static final FieldSerializer SER = new LongFieldSerializer(); + + void serialize(AbstractHessianOutput out, Object obj, Field field) + throws IOException + { + long value = 0; + + try { + value = field.getLong(obj); + } catch (IllegalAccessException e) { + log.log(Level.FINE, e.toString(), e); + } + + out.writeLong(value); + } + } + + static class DoubleFieldSerializer extends FieldSerializer { + static final FieldSerializer SER = new DoubleFieldSerializer(); + + void serialize(AbstractHessianOutput out, Object obj, Field field) + throws IOException + { + double value = 0; + + try { + value = field.getDouble(obj); + } catch (IllegalAccessException e) { + log.log(Level.FINE, e.toString(), e); + } + + out.writeDouble(value); + } + } + + static class StringFieldSerializer extends FieldSerializer { + static final FieldSerializer SER = new StringFieldSerializer(); + + void serialize(AbstractHessianOutput out, Object obj, Field field) + throws IOException + { + String value = null; + + try { + value = (String) field.get(obj); + } catch (IllegalAccessException e) { + log.log(Level.FINE, e.toString(), e); + } + + out.writeString(value); + } + } + + static class DateFieldSerializer extends FieldSerializer { + static final FieldSerializer SER = new DateFieldSerializer(); + + void serialize(AbstractHessianOutput out, Object obj, Field field) + throws IOException + { + java.util.Date value = null; + + try { + value = (java.util.Date) field.get(obj); + } catch (IllegalAccessException e) { + log.log(Level.FINE, e.toString(), e); + } + + if (value == null) + out.writeNull(); + else + out.writeUTCDate(value.getTime()); + } + } +} diff --git a/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/LocaleHandle.java b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/LocaleHandle.java new file mode 100644 index 0000000000..b96d7b95d9 --- /dev/null +++ b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/LocaleHandle.java @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2001-2008 Caucho Technology, Inc. All rights reserved. + * + * The Apache Software License, Version 1.1 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Caucho Technology (http://www.caucho.com/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Hessian", "Resin", and "Caucho" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * info@caucho.com. + * + * 5. Products derived from this software may not be called "Resin" + * nor may "Resin" appear in their names without prior written + * permission of Caucho Technology. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Scott Ferguson + */ + +package com.caucho.hessian4.io; + +import java.util.Locale; + +/** + * Handle for a locale object. + */ +public class LocaleHandle implements java.io.Serializable, HessianHandle { + private String value; + + public LocaleHandle(String locale) + { + this.value = locale; + } + + private Object readResolve() + { + String s = this.value; + + if (s == null) + return null; + + int len = s.length(); + char ch = ' '; + + int i = 0; + for (; + i < len && ('a' <= (ch = s.charAt(i)) && ch <= 'z' + || 'A' <= ch && ch <= 'Z' + || '0' <= ch && ch <= '9'); + i++) { + } + + String language = s.substring(0, i); + String country = null; + String var = null; + + if (ch == '-' || ch == '_') { + int head = ++i; + + for (; + i < len && ('a' <= (ch = s.charAt(i)) && ch <= 'z' + || 'A' <= ch && ch <= 'Z' + || '0' <= ch && ch <= '9'); + i++) { + } + + country = s.substring(head, i); + } + + if (ch == '-' || ch == '_') { + int head = ++i; + + for (; + i < len && ('a' <= (ch = s.charAt(i)) && ch <= 'z' + || 'A' <= ch && ch <= 'Z' + || '0' <= ch && ch <= '9'); + i++) { + } + + var = s.substring(head, i); + } + + if (var != null) + return new Locale(language, country, var); + else if (country != null) + return new Locale(language, country); + else + return new Locale(language); + } +} diff --git a/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/LocaleSerializer.java b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/LocaleSerializer.java new file mode 100644 index 0000000000..0bb8c5eb85 --- /dev/null +++ b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/LocaleSerializer.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2001-2004 Caucho Technology, Inc. All rights reserved. + * + * The Apache Software License, Version 1.1 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Caucho Technology (http://www.caucho.com/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Hessian", "Resin", and "Caucho" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * info@caucho.com. + * + * 5. Products derived from this software may not be called "Resin" + * nor may "Resin" appear in their names without prior written + * permission of Caucho Technology. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Scott Ferguson + */ + +package com.caucho.hessian4.io; + +import java.io.IOException; +import java.util.Locale; + +/** + * Serializing a locale. + */ +public class LocaleSerializer extends AbstractSerializer { + private static LocaleSerializer SERIALIZER = new LocaleSerializer(); + + public static LocaleSerializer create() + { + return SERIALIZER; + } + + public void writeObject(Object obj, AbstractHessianOutput out) + throws IOException + { + if (obj == null) + out.writeNull(); + else { + Locale locale = (Locale) obj; + + out.writeObject(new LocaleHandle(locale.toString())); + } + } +} diff --git a/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/MapDeserializer.java b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/MapDeserializer.java new file mode 100644 index 0000000000..f752b350b9 --- /dev/null +++ b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/MapDeserializer.java @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2001-2004 Caucho Technology, Inc. All rights reserved. + * + * The Apache Software License, Version 1.1 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Caucho Technology (http://www.caucho.com/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Hessian", "Resin", and "Caucho" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * info@caucho.com. + * + * 5. Products derived from this software may not be called "Resin" + * nor may "Resin" appear in their names without prior written + * permission of Caucho Technology. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Scott Ferguson + */ + +package com.caucho.hessian4.io; + +import java.io.IOException; +import java.lang.reflect.Constructor; +import java.util.HashMap; +import java.util.Map; +import java.util.SortedMap; +import java.util.TreeMap; + +/** + * Deserializing a JDK 1.2 Map. + */ +public class MapDeserializer extends AbstractMapDeserializer { + private Class _type; + private Constructor _ctor; + + public MapDeserializer(Class type) + { + if (type == null) + type = HashMap.class; + + _type = type; + + Constructor []ctors = type.getConstructors(); + for (int i = 0; i < ctors.length; i++) { + if (ctors[i].getParameterTypes().length == 0) + _ctor = ctors[i]; + } + + if (_ctor == null) { + try { + _ctor = HashMap.class.getConstructor(new Class[0]); + } catch (Exception e) { + throw new IllegalStateException(e); + } + } + } + + public Class getType() + { + if (_type != null) + return _type; + else + return HashMap.class; + } + + public Object readMap(AbstractHessianInput in) + throws IOException + { + Map map; + + if (_type == null) + map = new HashMap(); + else if (_type.equals(Map.class)) + map = new HashMap(); + else if (_type.equals(SortedMap.class)) + map = new TreeMap(); + else { + try { + map = (Map) _ctor.newInstance(); + } catch (Exception e) { + throw new IOExceptionWrapper(e); + } + } + + in.addRef(map); + + while (! in.isEnd()) { + map.put(in.readObject(), in.readObject()); + } + + in.readEnd(); + + return map; + } + + @Override + public Object readObject(AbstractHessianInput in, + Object []fields) + throws IOException + { + String []fieldNames = (String []) fields; + Map map = createMap(); + + int ref = in.addRef(map); + + for (int i = 0; i < fieldNames.length; i++) { + String name = fieldNames[i]; + + map.put(name, in.readObject()); + } + + return map; + } + + private Map createMap() + throws IOException + { + + if (_type == null) + return new HashMap(); + else if (_type.equals(Map.class)) + return new HashMap(); + else if (_type.equals(SortedMap.class)) + return new TreeMap(); + else { + try { + return (Map) _ctor.newInstance(); + } catch (Exception e) { + throw new IOExceptionWrapper(e); + } + } + } +} diff --git a/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/MapSerializer.java b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/MapSerializer.java new file mode 100644 index 0000000000..c3fa7dde79 --- /dev/null +++ b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/MapSerializer.java @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2001-2004 Caucho Technology, Inc. All rights reserved. + * + * The Apache Software License, Version 1.1 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Caucho Technology (http://www.caucho.com/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Burlap", "Resin", and "Caucho" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * info@caucho.com. + * + * 5. Products derived from this software may not be called "Resin" + * nor may "Resin" appear in their names without prior written + * permission of Caucho Technology. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Scott Ferguson + */ + +package com.caucho.hessian4.io; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +/** + * Serializing a JDK 1.2 java.util.Map. + */ +public class MapSerializer extends AbstractSerializer { + private boolean _isSendJavaType = true; + + /** + * Set true if the java type of the collection should be sent. + */ + public void setSendJavaType(boolean sendJavaType) + { + _isSendJavaType = sendJavaType; + } + + /** + * Return true if the java type of the collection should be sent. + */ + public boolean getSendJavaType() + { + return _isSendJavaType; + } + + public void writeObject(Object obj, AbstractHessianOutput out) + throws IOException + { + if (out.addRef(obj)) + return; + + Map map = (Map) obj; + + Class cl = obj.getClass(); + + if (cl.equals(HashMap.class) + || ! _isSendJavaType + || ! (obj instanceof java.io.Serializable)) + out.writeMapBegin(null); + else + out.writeMapBegin(obj.getClass().getName()); + + Iterator iter = map.entrySet().iterator(); + while (iter.hasNext()) { + Map.Entry entry = (Map.Entry) iter.next(); + + out.writeObject(entry.getKey()); + out.writeObject(entry.getValue()); + } + out.writeMapEnd(); + } +} diff --git a/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/ObjectDeserializer.java b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/ObjectDeserializer.java new file mode 100644 index 0000000000..3633d8a07c --- /dev/null +++ b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/ObjectDeserializer.java @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2001-2008 Caucho Technology, Inc. All rights reserved. + * + * The Apache Software License, Version 1.1 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Caucho Technology (http://www.caucho.com/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Burlap", "Resin", and "Caucho" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * info@caucho.com. + * + * 5. Products derived from this software may not be called "Resin" + * nor may "Resin" appear in their names without prior written + * permission of Caucho Technology. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Scott Ferguson + */ + +package com.caucho.hessian4.io; + +import java.io.IOException; + +/** + * Serializing an object for known object types. + */ +public class ObjectDeserializer extends AbstractDeserializer { + private Class _cl; + + public ObjectDeserializer(Class cl) + { + _cl = cl; + } + + public Class getType() + { + return _cl; + } + + @Override + public Object readObject(AbstractHessianInput in) + throws IOException + { + return in.readObject(); + } + + @Override + public Object readObject(AbstractHessianInput in, Object []fields) + throws IOException + { + throw new UnsupportedOperationException(String.valueOf(this)); + } + + @Override + public Object readList(AbstractHessianInput in, int length) + throws IOException + { + throw new UnsupportedOperationException(String.valueOf(this)); + } + + @Override + public Object readLengthList(AbstractHessianInput in, int length) + throws IOException + { + throw new UnsupportedOperationException(String.valueOf(this)); + } + + @Override + public String toString() + { + return getClass().getSimpleName() + "[" + _cl + "]"; + } +} diff --git a/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/ObjectHandleSerializer.java b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/ObjectHandleSerializer.java new file mode 100644 index 0000000000..fa30618677 --- /dev/null +++ b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/ObjectHandleSerializer.java @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2001-2004 Caucho Technology, Inc. All rights reserved. + * + * The Apache Software License, Version 1.1 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Caucho Technology (http://www.caucho.com/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Hessian", "Resin", and "Caucho" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * info@caucho.com. + * + * 5. Products derived from this software may not be called "Resin" + * nor may "Resin" appear in their names without prior written + * permission of Caucho Technology. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Scott Ferguson + */ + +package com.caucho.hessian4.io; + +import java.io.IOException; + +/** + * Serializing a remote object. + */ +public class ObjectHandleSerializer extends AbstractSerializer { + public static final Serializer SER = new ObjectHandleSerializer(); + + public void writeObject(Object obj, AbstractHessianOutput out) + throws IOException + { + if (obj == null) + out.writeNull(); + else { + if (out.addRef(obj)) + return; + + int ref = out.writeObjectBegin("object"); + + if (ref < -1) { + out.writeMapEnd(); + } + else { + if (ref == -1) { + out.writeInt(0); + out.writeObjectBegin("object"); + } + } + } + } +} diff --git a/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/ObjectNameDeserializer.java b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/ObjectNameDeserializer.java new file mode 100644 index 0000000000..40a3a75ba0 --- /dev/null +++ b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/ObjectNameDeserializer.java @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2001-2004 Caucho Technology, Inc. All rights reserved. + * + * The Apache Software License, Version 1.1 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Caucho Technology (http://www.caucho.com/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Hessian", "Resin", and "Caucho" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * info@caucho.com. + * + * 5. Products derived from this software may not be called "Resin" + * nor may "Resin" appear in their names without prior written + * permission of Caucho Technology. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Scott Ferguson + */ + +package com.caucho.hessian4.io; + +import javax.management.ObjectName; + +import com.caucho.hessian4.HessianException; + +/** + * Deserializing an ObjectName + */ +public class ObjectNameDeserializer extends AbstractStringValueDeserializer { + + static final String CLASS_NAME = "javax.management.ObjectName"; + + @Override + public Class getType() + { + try + { + return Class.forName(CLASS_NAME); + } + catch (ClassNotFoundException e) + { + e.printStackTrace(); + return null; + } + } + + @Override + protected Object create(String value) + { + try { + return new ObjectName(value); + } catch (RuntimeException e) { + throw e; + } catch (Exception e) { + throw new HessianException(e); + } + } +} diff --git a/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/ObjectSerializer.java b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/ObjectSerializer.java new file mode 100644 index 0000000000..2242b6ac9a --- /dev/null +++ b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/ObjectSerializer.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2001-2004 Caucho Technology, Inc. All rights reserved. + * + * The Apache Software License, Version 1.1 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Caucho Technology (http://www.caucho.com/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Burlap", "Resin", and "Caucho" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * info@caucho.com. + * + * 5. Products derived from this software may not be called "Resin" + * nor may "Resin" appear in their names without prior written + * permission of Caucho Technology. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Scott Ferguson + */ + +package com.caucho.hessian4.io; + + +/** + * Serializing an object. + */ +public interface ObjectSerializer { + public Serializer getObjectSerializer(); +} diff --git a/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/RemoteDeserializer.java b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/RemoteDeserializer.java new file mode 100644 index 0000000000..6c699a6874 --- /dev/null +++ b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/RemoteDeserializer.java @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2001-2008 Caucho Technology, Inc. All rights reserved. + * + * The Apache Software License, Version 1.1 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Caucho Technology (http://www.caucho.com/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Burlap", "Resin", and "Caucho" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * info@caucho.com. + * + * 5. Products derived from this software may not be called "Resin" + * nor may "Resin" appear in their names without prior written + * permission of Caucho Technology. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Scott Ferguson + */ + +package com.caucho.hessian4.io; + +import java.util.logging.Logger; + +/** + * Serializing an object for known object types. + */ +public class RemoteDeserializer extends JavaDeserializer { + private static final Logger log + = Logger.getLogger(RemoteDeserializer.class.getName()); + + public static final Deserializer DESER = new RemoteDeserializer(); + + public RemoteDeserializer() + { + super(HessianRemote.class); + } + + @Override + public boolean isReadResolve() + { + return true; + } + + @Override + protected Object resolve(AbstractHessianInput in, Object obj) + throws Exception + { + HessianRemote remote = (HessianRemote) obj; + HessianRemoteResolver resolver = in.getRemoteResolver(); + + if (resolver != null) { + Object proxy = resolver.lookup(remote.getType(), remote.getURL()); + + return proxy; + } + else + return remote; + } +} diff --git a/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/RemoteSerializer.java b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/RemoteSerializer.java new file mode 100644 index 0000000000..a60233bfbb --- /dev/null +++ b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/RemoteSerializer.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2001-2004 Caucho Technology, Inc. All rights reserved. + * + * The Apache Software License, Version 1.1 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Caucho Technology (http://www.caucho.com/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Hessian", "Resin", and "Caucho" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * info@caucho.com. + * + * 5. Products derived from this software may not be called "Resin" + * nor may "Resin" appear in their names without prior written + * permission of Caucho Technology. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Scott Ferguson + */ + +package com.caucho.hessian4.io; + +//import com.caucho.burlap.io.BurlapRemoteObject; + +import java.io.IOException; + +/** + * Serializing a remote object. + */ +public class RemoteSerializer extends AbstractSerializer { + public void writeObject(Object obj, AbstractHessianOutput out) + throws IOException + { + HessianRemoteObject remoteObject = (HessianRemoteObject) obj; + + out.writeObject(new HessianRemote(remoteObject.getHessianType(), + remoteObject.getHessianURL())); + } +} diff --git a/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/Serializer.java b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/Serializer.java new file mode 100644 index 0000000000..df2b814130 --- /dev/null +++ b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/Serializer.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2001-2004 Caucho Technology, Inc. All rights reserved. + * + * The Apache Software License, Version 1.1 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Caucho Technology (http://www.caucho.com/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Burlap", "Resin", and "Caucho" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * info@caucho.com. + * + * 5. Products derived from this software may not be called "Resin" + * nor may "Resin" appear in their names without prior written + * permission of Caucho Technology. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Scott Ferguson + */ + +package com.caucho.hessian4.io; + +import java.io.IOException; + +/** + * Serializing an object. + */ +public interface Serializer { + public void writeObject(Object obj, AbstractHessianOutput out) + throws IOException; +} diff --git a/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/SerializerFactory.java b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/SerializerFactory.java new file mode 100644 index 0000000000..b63e728ea6 --- /dev/null +++ b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/SerializerFactory.java @@ -0,0 +1,749 @@ +/* + * Copyright (c) 2001-2008 Caucho Technology, Inc. All rights reserved. + * + * The Apache Software License, Version 1.1 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Caucho Technology (http://www.caucho.com/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Burlap", "Resin", and "Caucho" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * info@caucho.com. + * + * 5. Products derived from this software may not be called "Resin" + * nor may "Resin" appear in their names without prior written + * permission of Caucho Technology. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Scott Ferguson + */ + +package com.caucho.hessian4.io; + +//import com.caucho.burlap.io.BurlapRemoteObject; + +import java.io.IOException; +import java.io.InputStream; +import java.io.Serializable; +import java.lang.annotation.Annotation; +import java.lang.ref.SoftReference; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Collection; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.WeakHashMap; +import java.util.concurrent.ConcurrentHashMap; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Factory for returning serialization methods. + */ +public class SerializerFactory extends AbstractSerializerFactory +{ + private static final Logger log + = Logger.getLogger(SerializerFactory.class.getName()); + + private static final Deserializer OBJECT_DESERIALIZER + = new BasicDeserializer(BasicDeserializer.OBJECT); + + private static final ClassLoader _systemClassLoader; + + private static final HashMap _staticTypeMap; + + private static final + WeakHashMap> + _defaultFactoryRefMap + = new WeakHashMap>(); + + private ContextSerializerFactory _contextFactory; + private ClassLoader _loader; + + protected Serializer _defaultSerializer; + + // Additional factories + protected ArrayList _factories = new ArrayList(); + + protected CollectionSerializer _collectionSerializer; + protected MapSerializer _mapSerializer; + + private Deserializer _hashMapDeserializer; + private Deserializer _arrayListDeserializer; + private ConcurrentHashMap _cachedSerializerMap; + private ConcurrentHashMap _cachedDeserializerMap; + private HashMap _cachedTypeDeserializerMap; + + private boolean _isAllowNonSerializable; + private boolean _isEnableUnsafeSerializer + = false; + + public SerializerFactory() + { + this(Thread.currentThread().getContextClassLoader()); + } + + public SerializerFactory(ClassLoader loader) + { + _loader = loader; + + _contextFactory = ContextSerializerFactory.create(loader); + } + + public static SerializerFactory createDefault() + { + ClassLoader loader = Thread.currentThread().getContextClassLoader(); + + synchronized (_defaultFactoryRefMap) { + SoftReference factoryRef + = _defaultFactoryRefMap.get(loader); + + SerializerFactory factory = null; + + if (factoryRef != null) + factory = factoryRef.get(); + + if (factory == null) { + factory = new SerializerFactory(); + + factoryRef = new SoftReference(factory); + + _defaultFactoryRefMap.put(loader, factoryRef); + } + + return factory; + } + } + + public ClassLoader getClassLoader() + { + return _loader; + } + + /** + * Set true if the collection serializer should send the java type. + */ + public void setSendCollectionType(boolean isSendType) + { + if (_collectionSerializer == null) + _collectionSerializer = new CollectionSerializer(); + + _collectionSerializer.setSendJavaType(isSendType); + + if (_mapSerializer == null) + _mapSerializer = new MapSerializer(); + + _mapSerializer.setSendJavaType(isSendType); + } + + /** + * Adds a factory. + */ + public void addFactory(AbstractSerializerFactory factory) + { + _factories.add(factory); + } + + /** + * If true, non-serializable objects are allowed. + */ + public void setAllowNonSerializable(boolean allow) + { + _isAllowNonSerializable = allow; + } + + /** + * If true, non-serializable objects are allowed. + */ + public boolean isAllowNonSerializable() + { + return _isAllowNonSerializable; + } + + /** + * Returns the serializer for a class. + * + * @param cl the class of the object that needs to be serialized. + * + * @return a serializer object for the serialization. + */ + public Serializer getObjectSerializer(Class cl) + throws HessianProtocolException + { + Serializer serializer = getSerializer(cl); + + if (serializer instanceof ObjectSerializer) + return ((ObjectSerializer) serializer).getObjectSerializer(); + else + return serializer; + } + + /** + * Returns the serializer for a class. + * + * @param cl the class of the object that needs to be serialized. + * + * @return a serializer object for the serialization. + */ + public Serializer getSerializer(Class cl) + throws HessianProtocolException + { + Serializer serializer; + + if (_cachedSerializerMap != null) { + serializer = (Serializer) _cachedSerializerMap.get(cl); + + if (serializer != null) + return serializer; + } + + serializer = loadSerializer(cl); + + if (_cachedSerializerMap == null) + _cachedSerializerMap = new ConcurrentHashMap(8); + + if (serializer != null) + _cachedSerializerMap.put(cl, serializer); + + return serializer; + } + + protected Serializer loadSerializer(Class cl) + throws HessianProtocolException + { + Serializer serializer = null; + + for (int i = 0; + _factories != null && i < _factories.size(); + i++) { + AbstractSerializerFactory factory; + + factory = (AbstractSerializerFactory) _factories.get(i); + + serializer = factory.getSerializer(cl); + + if (serializer != null) + return serializer; + } + + serializer = _contextFactory.getSerializer(cl.getName()); + + if (serializer != null) + return serializer; + + ClassLoader loader = cl.getClassLoader(); + + if (loader == null) + loader = _systemClassLoader; + + ContextSerializerFactory factory = null; + + factory = ContextSerializerFactory.create(loader); + + serializer = factory.getCustomSerializer(cl); + + if (serializer != null) + return serializer; + + if (HessianRemoteObject.class.isAssignableFrom(cl)) + return new RemoteSerializer(); + + //else if (BurlapRemoteObject.class.isAssignableFrom(cl)) + // return new RemoteSerializer(); + + else if (JavaSerializer.getWriteReplace(cl) != null) + return new WriteReplaceSerializer(cl, _loader); + + else if (Map.class.isAssignableFrom(cl)) { + if (_mapSerializer == null) + _mapSerializer = new MapSerializer(); + + return _mapSerializer; + } + else if (Collection.class.isAssignableFrom(cl)) { + if (_collectionSerializer == null) { + _collectionSerializer = new CollectionSerializer(); + } + + return _collectionSerializer; + } + + else if (cl.isArray()) + return new ArraySerializer(); + + else if (Throwable.class.isAssignableFrom(cl)) + return new ThrowableSerializer(cl, getClassLoader()); + + else if (InputStream.class.isAssignableFrom(cl)) + return new InputStreamSerializer(); + + else if (Iterator.class.isAssignableFrom(cl)) + return IteratorSerializer.create(); + + else if (Calendar.class.isAssignableFrom(cl)) + return CalendarSerializer.SER; + + else if (Enumeration.class.isAssignableFrom(cl)) + return EnumerationSerializer.create(); + + else if (Enum.class.isAssignableFrom(cl)) + return new EnumSerializer(cl); + + else if (Annotation.class.isAssignableFrom(cl)) + return new AnnotationSerializer(cl); + + return getDefaultSerializer(cl); + } + + /** + * Returns the default serializer for a class that isn't matched + * directly. Application can override this method to produce + * bean-style serialization instead of field serialization. + * + * @param cl the class of the object that needs to be serialized. + * + * @return a serializer object for the serialization. + */ + protected Serializer getDefaultSerializer(Class cl) + { + if (_defaultSerializer != null) + return _defaultSerializer; + + if (! Serializable.class.isAssignableFrom(cl) + && ! _isAllowNonSerializable) { + throw new IllegalStateException("Serialized class " + cl.getName() + " must implement java.io.Serializable"); + } + + if (_isEnableUnsafeSerializer + && JavaSerializer.getWriteReplace(cl) == null) { + return null; + } + else + return JavaSerializer.create(cl); + } + + /** + * Returns the deserializer for a class. + * + * @param cl the class of the object that needs to be deserialized. + * + * @return a deserializer object for the serialization. + */ + public Deserializer getDeserializer(Class cl) + throws HessianProtocolException + { + Deserializer deserializer; + + if (_cachedDeserializerMap != null) { + deserializer = (Deserializer) _cachedDeserializerMap.get(cl); + + if (deserializer != null) + return deserializer; + } + + deserializer = loadDeserializer(cl); + + if (_cachedDeserializerMap == null) + _cachedDeserializerMap = new ConcurrentHashMap(8); + + if (deserializer != null) + _cachedDeserializerMap.put(cl, deserializer); + + return deserializer; + } + + protected Deserializer loadDeserializer(Class cl) + throws HessianProtocolException + { + Deserializer deserializer = null; + + for (int i = 0; + deserializer == null && _factories != null && i < _factories.size(); + i++) { + AbstractSerializerFactory factory; + factory = (AbstractSerializerFactory) _factories.get(i); + + deserializer = factory.getDeserializer(cl); + } + if (deserializer != null) + return deserializer; + + deserializer = _contextFactory.getDeserializer(cl.getName()); + + if (deserializer != null) + return deserializer; + + ContextSerializerFactory factory = null; + + if (cl.getClassLoader() != null) + factory = ContextSerializerFactory.create(cl.getClassLoader()); + else + factory = ContextSerializerFactory.create(_systemClassLoader); + + deserializer = factory.getCustomDeserializer(cl); + + if (deserializer != null) + return deserializer; + + if (Collection.class.isAssignableFrom(cl)) + deserializer = new CollectionDeserializer(cl); + + else if (Map.class.isAssignableFrom(cl)) + deserializer = new MapDeserializer(cl); + + else if (Annotation.class.isAssignableFrom(cl)) + deserializer = new AnnotationDeserializer(cl); + + else if (cl.isInterface()) + deserializer = new ObjectDeserializer(cl); + + else if (cl.isArray()) + deserializer = new ArrayDeserializer(cl.getComponentType()); + + else if (Enumeration.class.isAssignableFrom(cl)) + deserializer = EnumerationDeserializer.create(); + + else if (Enum.class.isAssignableFrom(cl)) + deserializer = new EnumDeserializer(cl); + + else if (Class.class.equals(cl)) + deserializer = new ClassDeserializer(_loader); + + else + deserializer = getDefaultDeserializer(cl); + + return deserializer; + } + + /** + * Returns a custom serializer the class + * + * @param cl the class of the object that needs to be serialized. + * + * @return a serializer object for the serialization. + */ + protected Deserializer getCustomDeserializer(Class cl) + { + try { + Class serClass = Class.forName(cl.getName() + "HessianDeserializer", + false, cl.getClassLoader()); + + Deserializer ser = (Deserializer) serClass.newInstance(); + + return ser; + } catch (ClassNotFoundException e) { + log.log(Level.FINEST, e.toString(), e); + + return null; + } catch (Exception e) { + log.log(Level.FINE, e.toString(), e); + + return null; + } + } + + /** + * Returns the default serializer for a class that isn't matched + * directly. Application can override this method to produce + * bean-style serialization instead of field serialization. + * + * @param cl the class of the object that needs to be serialized. + * + * @return a serializer object for the serialization. + */ + protected Deserializer getDefaultDeserializer(Class cl) + { + if (InputStream.class.equals(cl)) + return InputStreamDeserializer.DESER; + + if (_isEnableUnsafeSerializer) { + return null; + } + else + return new JavaDeserializer(cl); + } + + /** + * Reads the object as a list. + */ + public Object readList(AbstractHessianInput in, int length, String type) + throws HessianProtocolException, IOException + { + Deserializer deserializer = getDeserializer(type); + + if (deserializer != null) + return deserializer.readList(in, length); + else + return new CollectionDeserializer(ArrayList.class).readList(in, length); + } + + /** + * Reads the object as a map. + */ + public Object readMap(AbstractHessianInput in, String type) + throws HessianProtocolException, IOException + { + Deserializer deserializer = getDeserializer(type); + + if (deserializer != null) + return deserializer.readMap(in); + else if (_hashMapDeserializer != null) + return _hashMapDeserializer.readMap(in); + else { + _hashMapDeserializer = new MapDeserializer(HashMap.class); + + return _hashMapDeserializer.readMap(in); + } + } + + /** + * Reads the object as a map. + */ + public Object readObject(AbstractHessianInput in, + String type, + String []fieldNames) + throws HessianProtocolException, IOException + { + Deserializer deserializer = getDeserializer(type); + + if (deserializer != null) + return deserializer.readObject(in, fieldNames); + else if (_hashMapDeserializer != null) + return _hashMapDeserializer.readObject(in, fieldNames); + else { + _hashMapDeserializer = new MapDeserializer(HashMap.class); + + return _hashMapDeserializer.readObject(in, fieldNames); + } + } + + /** + * Reads the object as a map. + */ + public Deserializer getObjectDeserializer(String type, Class cl) + throws HessianProtocolException + { + Deserializer reader = getObjectDeserializer(type); + + if (cl == null + || cl.equals(reader.getType()) + || cl.isAssignableFrom(reader.getType()) + || reader.isReadResolve() + || HessianHandle.class.isAssignableFrom(reader.getType())) { + return reader; + } + + if (log.isLoggable(Level.FINE)) { + log.fine("hessian: expected deserializer '" + cl.getName() + "' at '" + type + "' (" + + reader.getType().getName() + ")"); + } + + return getDeserializer(cl); + } + + /** + * Reads the object as a map. + */ + public Deserializer getObjectDeserializer(String type) + throws HessianProtocolException + { + Deserializer deserializer = getDeserializer(type); + + if (deserializer != null) + return deserializer; + else if (_hashMapDeserializer != null) + return _hashMapDeserializer; + else { + _hashMapDeserializer = new MapDeserializer(HashMap.class); + + return _hashMapDeserializer; + } + } + + /** + * Reads the object as a map. + */ + public Deserializer getListDeserializer(String type, Class cl) + throws HessianProtocolException + { + Deserializer reader = getListDeserializer(type); + + if (cl == null + || cl.equals(reader.getType()) + || cl.isAssignableFrom(reader.getType())) { + return reader; + } + + if (log.isLoggable(Level.FINE)) { + log.fine("hessian: expected '" + cl.getName() + "' at '" + type + "' (" + + reader.getType().getName() + ")"); + } + + return getDeserializer(cl); + } + + /** + * Reads the object as a map. + */ + public Deserializer getListDeserializer(String type) + throws HessianProtocolException + { + Deserializer deserializer = getDeserializer(type); + + if (deserializer != null) + return deserializer; + else if (_arrayListDeserializer != null) + return _arrayListDeserializer; + else { + _arrayListDeserializer = new CollectionDeserializer(ArrayList.class); + + return _arrayListDeserializer; + } + } + + /** + * Returns a deserializer based on a string type. + */ + public Deserializer getDeserializer(String type) + throws HessianProtocolException + { + if (type == null || type.equals("")) + return null; + + Deserializer deserializer; + + if (_cachedTypeDeserializerMap != null) { + synchronized (_cachedTypeDeserializerMap) { + deserializer = (Deserializer) _cachedTypeDeserializerMap.get(type); + } + + if (deserializer != null) + return deserializer; + } + + + deserializer = (Deserializer) _staticTypeMap.get(type); + if (deserializer != null) + return deserializer; + + if (type.startsWith("[")) { + Deserializer subDeserializer = getDeserializer(type.substring(1)); + + if (subDeserializer != null) + deserializer = new ArrayDeserializer(subDeserializer.getType()); + else + deserializer = new ArrayDeserializer(Object.class); + } + else { + try { + + //Class cl = Class.forName(type, false, _loader); + Class cl = Class.forName(type, false, getClass().getClassLoader()); + deserializer = getDeserializer(cl); + } catch (Exception e) { + log.warning("Hessian/Burlap: '" + type + "' is an unknown class in " + _loader + ":\n" + e); + + log.log(Level.FINER, e.toString(), e); + } + } + + if (deserializer != null) { + if (_cachedTypeDeserializerMap == null) + _cachedTypeDeserializerMap = new HashMap(8); + + synchronized (_cachedTypeDeserializerMap) { + _cachedTypeDeserializerMap.put(type, deserializer); + } + } + + return deserializer; + } + + private static void addBasic(Class cl, String typeName, int type) + { + Deserializer deserializer = new BasicDeserializer(type); + _staticTypeMap.put(typeName, deserializer); + } + + static { + _staticTypeMap = new HashMap(); + + addBasic(void.class, "void", BasicSerializer.NULL); + + addBasic(Boolean.class, "boolean", BasicSerializer.BOOLEAN); + addBasic(Byte.class, "byte", BasicSerializer.BYTE); + addBasic(Short.class, "short", BasicSerializer.SHORT); + addBasic(Integer.class, "int", BasicSerializer.INTEGER); + addBasic(Long.class, "long", BasicSerializer.LONG); + addBasic(Float.class, "float", BasicSerializer.FLOAT); + addBasic(Double.class, "double", BasicSerializer.DOUBLE); + addBasic(Character.class, "char", BasicSerializer.CHARACTER_OBJECT); + addBasic(String.class, "string", BasicSerializer.STRING); + addBasic(Object.class, "object", BasicSerializer.OBJECT); + addBasic(java.util.Date.class, "date", BasicSerializer.DATE); + + addBasic(boolean.class, "boolean", BasicSerializer.BOOLEAN); + addBasic(byte.class, "byte", BasicSerializer.BYTE); + addBasic(short.class, "short", BasicSerializer.SHORT); + addBasic(int.class, "int", BasicSerializer.INTEGER); + addBasic(long.class, "long", BasicSerializer.LONG); + addBasic(float.class, "float", BasicSerializer.FLOAT); + addBasic(double.class, "double", BasicSerializer.DOUBLE); + addBasic(char.class, "char", BasicSerializer.CHARACTER); + + addBasic(boolean[].class, "[boolean", BasicSerializer.BOOLEAN_ARRAY); + addBasic(byte[].class, "[byte", BasicSerializer.BYTE_ARRAY); + addBasic(short[].class, "[short", BasicSerializer.SHORT_ARRAY); + addBasic(int[].class, "[int", BasicSerializer.INTEGER_ARRAY); + addBasic(long[].class, "[long", BasicSerializer.LONG_ARRAY); + addBasic(float[].class, "[float", BasicSerializer.FLOAT_ARRAY); + addBasic(double[].class, "[double", BasicSerializer.DOUBLE_ARRAY); + addBasic(char[].class, "[char", BasicSerializer.CHARACTER_ARRAY); + addBasic(String[].class, "[string", BasicSerializer.STRING_ARRAY); + addBasic(Object[].class, "[object", BasicSerializer.OBJECT_ARRAY); + + Deserializer objectDeserializer = new JavaDeserializer(Object.class); + _staticTypeMap.put("object", objectDeserializer); + _staticTypeMap.put(HessianRemote.class.getName(), + RemoteDeserializer.DESER); + + + ClassLoader systemClassLoader = null; + try { + systemClassLoader = ClassLoader.getSystemClassLoader(); + } catch (Exception e) { + } + + _systemClassLoader = systemClassLoader; + } +} diff --git a/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/ShortHandle.java b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/ShortHandle.java new file mode 100644 index 0000000000..4b11508e24 --- /dev/null +++ b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/ShortHandle.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2001-2004 Caucho Technology, Inc. All rights reserved. + * + * The Apache Software License, Version 1.1 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Caucho Technology (http://www.caucho.com/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Burlap", "Resin", and "Caucho" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * info@caucho.com. + * + * 5. Products derived from this software may not be called "Resin" + * nor may "Resin" appear in their names without prior written + * permission of Caucho Technology. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Scott Ferguson + */ + +package com.caucho.hessian4.io; + +import java.io.Serializable; + +/** + * Handle for Java Short objects. + */ +public class ShortHandle implements Serializable { + private short _value; + + private ShortHandle() + { + } + + public ShortHandle(short value) + { + _value = value; + } + + public short getValue() + { + return _value; + } + + public Object readResolve() + { + return new Short(_value); + } + + public String toString() + { + return getClass().getSimpleName() + "[" + _value + "]"; + } +} diff --git a/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/SqlDateDeserializer.java b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/SqlDateDeserializer.java new file mode 100644 index 0000000000..c7126413e5 --- /dev/null +++ b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/SqlDateDeserializer.java @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2001-2004 Caucho Technology, Inc. All rights reserved. + * + * The Apache Software License, Version 1.1 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Caucho Technology (http://www.caucho.com/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Hessian", "Resin", and "Caucho" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * info@caucho.com. + * + * 5. Products derived from this software may not be called "Resin" + * nor may "Resin" appear in their names without prior written + * permission of Caucho Technology. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Scott Ferguson + */ + +package com.caucho.hessian4.io; + +import java.io.IOException; +import java.lang.reflect.Constructor; + +import com.caucho.hessian4.HessianException; + +/** + * Deserializing a string valued object + */ +public class SqlDateDeserializer extends AbstractDeserializer { + private Class _cl; + private Constructor _constructor; + + public SqlDateDeserializer(Class cl) + { + try { + _cl = cl; + _constructor = cl.getConstructor(new Class[] { long.class }); + } catch (NoSuchMethodException e) { + throw new HessianException(e); + } + } + + public Class getType() + { + return _cl; + } + + public Object readMap(AbstractHessianInput in) + throws IOException + { + int ref = in.addRef(null); + + long initValue = Long.MIN_VALUE; + + while (! in.isEnd()) { + String key = in.readString(); + + if (key.equals("value")) + initValue = in.readUTCDate(); + else + in.readString(); + } + + in.readMapEnd(); + + Object value = create(initValue); + + in.setRef(ref, value); + + return value; + } + + public Object readObject(AbstractHessianInput in, + Object []fields) + throws IOException + { + String []fieldNames = (String []) fields; + + int ref = in.addRef(null); + + long initValue = Long.MIN_VALUE; + + for (int i = 0; i < fieldNames.length; i++) { + String key = fieldNames[i]; + + if (key.equals("value")) + initValue = in.readUTCDate(); + else + in.readObject(); + } + + Object value = create(initValue); + + in.setRef(ref, value); + + return value; + } + + private Object create(long initValue) + throws IOException + { + if (initValue == Long.MIN_VALUE) + throw new IOException(_cl.getName() + " expects name."); + + try { + return _constructor.newInstance(new Object[] { new Long(initValue) }); + } catch (Exception e) { + throw new IOExceptionWrapper(e); + } + } +} diff --git a/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/SqlDateSerializer.java b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/SqlDateSerializer.java new file mode 100644 index 0000000000..0af4de28d6 --- /dev/null +++ b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/SqlDateSerializer.java @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2001-2004 Caucho Technology, Inc. All rights reserved. + * + * The Apache Software License, Version 1.1 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Caucho Technology (http://www.caucho.com/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Hessian", "Resin", and "Caucho" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * info@caucho.com. + * + * 5. Products derived from this software may not be called "Resin" + * nor may "Resin" appear in their names without prior written + * permission of Caucho Technology. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Scott Ferguson + */ + +package com.caucho.hessian4.io; + +import java.io.IOException; +import java.util.Date; + +/** + * Serializing a sql date object. + */ +public class SqlDateSerializer extends AbstractSerializer +{ + public void writeObject(Object obj, AbstractHessianOutput out) + throws IOException + { + if (obj == null) + out.writeNull(); + else { + Class cl = obj.getClass(); + + if (out.addRef(obj)) + return; + + int ref = out.writeObjectBegin(cl.getName()); + + if (ref < -1) { + out.writeString("value"); + out.writeUTCDate(((Date) obj).getTime()); + out.writeMapEnd(); + } + else { + if (ref == -1) { + out.writeInt(1); + out.writeString("value"); + out.writeObjectBegin(cl.getName()); + } + + out.writeUTCDate(((Date) obj).getTime()); + } + } + } +} diff --git a/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/StackTraceElementDeserializer.java b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/StackTraceElementDeserializer.java new file mode 100644 index 0000000000..6bab3d9930 --- /dev/null +++ b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/StackTraceElementDeserializer.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2001-2004 Caucho Technology, Inc. All rights reserved. + * + * The Apache Software License, Version 1.1 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Caucho Technology (http://www.caucho.com/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Hessian", "Resin", and "Caucho" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * info@caucho.com. + * + * 5. Products derived from this software may not be called "Resin" + * nor may "Resin" appear in their names without prior written + * permission of Caucho Technology. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Scott Ferguson + */ + +package com.caucho.hessian4.io; + + +/** + * Deserializing a JDK 1.4 StackTraceElement + */ +public class StackTraceElementDeserializer extends JavaDeserializer { + public StackTraceElementDeserializer() + { + super(StackTraceElement.class); + } + + @Override + protected Object instantiate() + throws Exception + { + return new StackTraceElement("", "", "", 0); + } +} diff --git a/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/StringValueDeserializer.java b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/StringValueDeserializer.java new file mode 100644 index 0000000000..0d12fdc759 --- /dev/null +++ b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/StringValueDeserializer.java @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2001-2004 Caucho Technology, Inc. All rights reserved. + * + * The Apache Software License, Version 1.1 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Caucho Technology (http://www.caucho.com/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Hessian", "Resin", and "Caucho" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * info@caucho.com. + * + * 5. Products derived from this software may not be called "Resin" + * nor may "Resin" appear in their names without prior written + * permission of Caucho Technology. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Scott Ferguson + */ + +package com.caucho.hessian4.io; + +import java.io.IOException; +import java.lang.reflect.Constructor; + +import com.caucho.hessian4.HessianException; + +/** + * Deserializing a string valued object + */ +public class StringValueDeserializer extends AbstractStringValueDeserializer { + private Class _cl; + private Constructor _constructor; + + public StringValueDeserializer(Class cl) + { + try { + _cl = cl; + _constructor = cl.getConstructor(new Class[] { String.class }); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + @Override + public Class getType() + { + return _cl; + } + + @Override + protected Object create(String value) + throws IOException + { + if (value == null) + throw new IOException(_cl.getName() + " expects name."); + + try { + return _constructor.newInstance(new Object[] { value }); + } catch (Exception e) { + throw new HessianException(_cl.getName() + ": value=" + value + "\n" + e, + e); + } + } +} diff --git a/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/StringValueSerializer.java b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/StringValueSerializer.java new file mode 100644 index 0000000000..d86163450a --- /dev/null +++ b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/StringValueSerializer.java @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2001-2004 Caucho Technology, Inc. All rights reserved. + * + * The Apache Software License, Version 1.1 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Caucho Technology (http://www.caucho.com/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Hessian", "Resin", and "Caucho" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * info@caucho.com. + * + * 5. Products derived from this software may not be called "Resin" + * nor may "Resin" appear in their names without prior written + * permission of Caucho Technology. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Scott Ferguson + */ + +package com.caucho.hessian4.io; + +import java.io.IOException; + +/** + * Serializing a remote object. + */ +public class StringValueSerializer extends AbstractSerializer { + public static final Serializer SER = new StringValueSerializer(); + + public void writeObject(Object obj, AbstractHessianOutput out) + throws IOException + { + if (obj == null) + out.writeNull(); + else { + if (out.addRef(obj)) + return; + + Class cl = obj.getClass(); + + int ref = out.writeObjectBegin(cl.getName()); + + if (ref < -1) { + out.writeString("value"); + out.writeString(obj.toString()); + out.writeMapEnd(); + } + else { + if (ref == -1) { + out.writeInt(1); + out.writeString("value"); + out.writeObjectBegin(cl.getName()); + } + + out.writeString(obj.toString()); + } + } + } +} diff --git a/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/ThrowableSerializer.java b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/ThrowableSerializer.java new file mode 100644 index 0000000000..6cb3f89cd0 --- /dev/null +++ b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/ThrowableSerializer.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2001-2004 Caucho Technology, Inc. All rights reserved. + * + * The Apache Software License, Version 1.1 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Caucho Technology (http://www.caucho.com/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Burlap", "Resin", and "Caucho" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * info@caucho.com. + * + * 5. Products derived from this software may not be called "Resin" + * nor may "Resin" appear in their names without prior written + * permission of Caucho Technology. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Scott Ferguson + */ + +package com.caucho.hessian4.io; + +import java.io.IOException; + +/** + * Serializing an object for known object types. + */ +public class ThrowableSerializer extends JavaSerializer { + public ThrowableSerializer(Class cl, ClassLoader loader) + { + super(cl); + } + + public void writeObject(Object obj, AbstractHessianOutput out) + throws IOException + { + Throwable e = (Throwable) obj; + + e.getStackTrace(); + + super.writeObject(obj, out); + } +} diff --git a/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/ValueDeserializer.java b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/ValueDeserializer.java new file mode 100644 index 0000000000..4a59c7423a --- /dev/null +++ b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/ValueDeserializer.java @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2001-2004 Caucho Technology, Inc. All rights reserved. + * + * The Apache Software License, Version 1.1 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Caucho Technology (http://www.caucho.com/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Hessian", "Resin", and "Caucho" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * info@caucho.com. + * + * 5. Products derived from this software may not be called "Resin" + * nor may "Resin" appear in their names without prior written + * permission of Caucho Technology. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Scott Ferguson + */ + +package com.caucho.hessian4.io; + +import java.io.IOException; + +/** + * Deserializing a string valued object + */ +abstract public class ValueDeserializer extends AbstractDeserializer { + public Object readMap(AbstractHessianInput in) + throws IOException + { + String initValue = null; + + while (! in.isEnd()) { + String key = in.readString(); + + if (key.equals("value")) + initValue = in.readString(); + else + in.readObject(); + } + + in.readMapEnd(); + + return create(initValue); + } + + public Object readObject(AbstractHessianInput in, String []fieldNames) + throws IOException + { + String initValue = null; + + for (int i = 0; i < fieldNames.length; i++) { + if ("value".equals(fieldNames[i])) + initValue = in.readString(); + else + in.readObject(); + } + + return create(initValue); + } + + abstract Object create(String value) + throws IOException; +} diff --git a/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/WriteReplaceSerializer.java b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/WriteReplaceSerializer.java new file mode 100644 index 0000000000..08fd1bc913 --- /dev/null +++ b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/io/WriteReplaceSerializer.java @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2001-2008 Caucho Technology, Inc. All rights reserved. + * + * The Apache Software License, Version 1.1 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Caucho Technology (http://www.caucho.com/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Burlap", "Resin", and "Caucho" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * info@caucho.com. + * + * 5. Products derived from this software may not be called "Resin" + * nor may "Resin" appear in their names without prior written + * permission of Caucho Technology. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Scott Ferguson + */ + +package com.caucho.hessian4.io; + +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.logging.Level; +import java.util.logging.Logger; + +import com.caucho.hessian4.HessianException; + +/** + * Serializing an object for known object types. + */ +public class WriteReplaceSerializer extends AbstractSerializer +{ + private static final Logger log + = Logger.getLogger(WriteReplaceSerializer.class.getName()); + + private static Object []NULL_ARGS = new Object[0]; + + private Object _writeReplaceFactory; + private Method _writeReplace; + + public WriteReplaceSerializer(Class cl, + ClassLoader loader) + { + introspectWriteReplace(cl, loader); + } + + private void introspectWriteReplace(Class cl, ClassLoader loader) + { + try { + String className = cl.getName() + "HessianSerializer"; + + Class serializerClass = Class.forName(className, false, loader); + + Object serializerObject = serializerClass.newInstance(); + + Method writeReplace = getWriteReplace(serializerClass, cl); + + if (writeReplace != null) { + _writeReplaceFactory = serializerObject; + _writeReplace = writeReplace; + } + } catch (ClassNotFoundException e) { + } catch (Exception e) { + log.log(Level.FINER, e.toString(), e); + } + + _writeReplace = getWriteReplace(cl); + if (_writeReplace != null) + _writeReplace.setAccessible(true); + } + + /** + * Returns the writeReplace method + */ + protected static Method getWriteReplace(Class cl, Class param) + { + for (; cl != null; cl = cl.getSuperclass()) { + for (Method method : cl.getDeclaredMethods()) { + if (method.getName().equals("writeReplace") + && method.getParameterTypes().length == 1 + && param.equals(method.getParameterTypes()[0])) + return method; + } + } + + return null; + } + + /** + * Returns the writeReplace method + */ + protected static Method getWriteReplace(Class cl) + { + for (; cl != null; cl = cl.getSuperclass()) { + Method []methods = cl.getDeclaredMethods(); + + for (int i = 0; i < methods.length; i++) { + Method method = methods[i]; + + if (method.getName().equals("writeReplace") && + method.getParameterTypes().length == 0) + return method; + } + } + + return null; + } + + public void writeObject(Object obj, AbstractHessianOutput out) + throws IOException + { + if (out.addRef(obj)) { + return; + } + + Class cl = obj.getClass(); + + try { + Object repl; + + if (_writeReplaceFactory != null) + repl = _writeReplace.invoke(_writeReplaceFactory, obj); + else + repl = _writeReplace.invoke(obj); + + if (obj == repl) + throw new HessianException(this + ": Hessian writeReplace error. The writeReplace method (" + _writeReplace + ") must not return the same object: " + obj); + + out.removeRef(obj); + + out.writeObject(repl); + + out.replaceRef(repl, obj); + } catch (RuntimeException e) { + throw e; + } catch (InvocationTargetException e) { + throw new RuntimeException(e.getCause()); + } catch (Exception e) { + throw new RuntimeException(e); + } + } +} diff --git a/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/server/HessianSkeleton.java b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/server/HessianSkeleton.java new file mode 100644 index 0000000000..a0ff58a8f4 --- /dev/null +++ b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/server/HessianSkeleton.java @@ -0,0 +1,376 @@ +/* + * Copyright (c) 2001-2009 Caucho Technology, Inc. All rights reserved. + * + * The Apache Software License, Version 1.1 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Caucho Technology (http://www.caucho.com/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Hessian", "Resin", and "Caucho" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * info@caucho.com. + * + * 5. Products derived from this software may not be called "Resin" + * nor may "Resin" appear in their names without prior written + * permission of Caucho Technology. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Scott Ferguson + */ + +package com.caucho.hessian4.server; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.PrintWriter; +import java.io.Writer; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.logging.Level; +import java.util.logging.Logger; + +import com.caucho.hessian4.io.AbstractHessianInput; +import com.caucho.hessian4.io.AbstractHessianOutput; +import com.caucho.hessian4.io.Hessian2Input; +import com.caucho.hessian4.io.Hessian2Output; +import com.caucho.hessian4.io.HessianDebugInputStream; +import com.caucho.hessian4.io.HessianDebugOutputStream; +import com.caucho.hessian4.io.HessianInput; +import com.caucho.hessian4.io.HessianInputFactory; +import com.caucho.hessian4.io.HessianOutput; +import com.caucho.hessian4.io.SerializerFactory; +import com.caucho.hessian4.services.server.AbstractSkeleton; + +/** + * Proxy class for Hessian services. + */ +public class HessianSkeleton extends AbstractSkeleton { + private static final Logger log + = Logger.getLogger(HessianSkeleton.class.getName()); + + private boolean _isDebug; + private HessianInputFactory _inputFactory = new HessianInputFactory(); + + private Object _service; + + /** + * Create a new hessian skeleton. + * + * @param service the underlying service object. + * @param apiClass the API interface + */ + public HessianSkeleton(Object service, Class apiClass) + { + super(apiClass); + + if (service == null) + service = this; + + _service = service; + + if (! apiClass.isAssignableFrom(service.getClass())) + throw new IllegalArgumentException("Service " + service + " must be an instance of " + apiClass.getName()); + } + + /** + * Create a new hessian skeleton. + * + * @param service the underlying service object. + * @param apiClass the API interface + */ + public HessianSkeleton(Class apiClass) + { + super(apiClass); + } + + public void setDebug(boolean isDebug) + { + _isDebug = isDebug; + } + + public boolean isDebug() + { + return _isDebug; + } + + /** + * Invoke the object with the request from the input stream. + * + * @param in the Hessian input stream + * @param out the Hessian output stream + */ + public void invoke(InputStream is, OutputStream os) + throws Exception + { + invoke(is, os, null); + } + + /** + * Invoke the object with the request from the input stream. + * + * @param in the Hessian input stream + * @param out the Hessian output stream + */ + public void invoke(InputStream is, OutputStream os, + SerializerFactory serializerFactory) + throws Exception + { + boolean isDebug = false; + + if (isDebugInvoke()) { + isDebug = true; + + PrintWriter dbg = createDebugPrintWriter(); + HessianDebugInputStream dIs = new HessianDebugInputStream(is, dbg); + dIs.startTop2(); + is = dIs; + HessianDebugOutputStream dOs = new HessianDebugOutputStream(os, dbg); + dOs.startTop2(); + os = dOs; + } + + HessianInputFactory.HeaderType header = _inputFactory.readHeader(is); + + AbstractHessianInput in; + AbstractHessianOutput out; + + switch (header) { + case CALL_1_REPLY_1: + in = new HessianInput(is); + out = new HessianOutput(os); + break; + + case CALL_1_REPLY_2: + in = new HessianInput(is); + out = new Hessian2Output(os); + break; + + case HESSIAN_2: + in = new Hessian2Input(is); + in.readCall(); + out = new Hessian2Output(os); + break; + + default: + throw new IllegalStateException(header + " is an unknown Hessian call"); + } + + if (serializerFactory != null) { + in.setSerializerFactory(serializerFactory); + out.setSerializerFactory(serializerFactory); + } + + try { + invoke(_service, in, out); + } finally { + in.close(); + out.close(); + + if (isDebug) + os.close(); + } + } + + /** + * Invoke the object with the request from the input stream. + * + * @param in the Hessian input stream + * @param out the Hessian output stream + */ + public void invoke(AbstractHessianInput in, AbstractHessianOutput out) + throws Exception + { + invoke(_service, in, out); + } + + /** + * Invoke the object with the request from the input stream. + * + * @param in the Hessian input stream + * @param out the Hessian output stream + */ + public void invoke(Object service, + AbstractHessianInput in, + AbstractHessianOutput out) + throws Exception + { + //ServiceContext context = ServiceContext.getContext(); + + // backward compatibility for some frameworks that don't read + // the call type first + in.skipOptionalCall(); + + // Hessian 1.0 backward compatibility + String header; + while ((header = in.readHeader()) != null) { + Object value = in.readObject(); + + //context.addHeader(header, value); + } + + String methodName = in.readMethod(); + int argLength = in.readMethodArgLength(); + + Method method; + + method = getMethod(methodName + "__" + argLength); + + if (method == null) + method = getMethod(methodName); + + if (method != null) { + } + else if ("_hessian_getAttribute".equals(methodName)) { + String attrName = in.readString(); + in.completeCall(); + + String value = null; + + if ("java.api.class".equals(attrName)) + value = getAPIClassName(); + else if ("java.home.class".equals(attrName)) + value = getHomeClassName(); + else if ("java.object.class".equals(attrName)) + value = getObjectClassName(); + + out.writeReply(value); + out.close(); + return; + } + else if (method == null) { + out.writeFault("NoSuchMethodException", + "The service has no method named: " + in.getMethod(), + null); + out.close(); + return; + } + + Class []args = method.getParameterTypes(); + + if (argLength != args.length && argLength >= 0) { + out.writeFault("NoSuchMethod", + "method " + method + " argument length mismatch, received length=" + argLength, + null); + out.close(); + return; + } + + Object []values = new Object[args.length]; + + for (int i = 0; i < args.length; i++) { + // XXX: needs Marshal object + values[i] = in.readObject(args[i]); + } + + Object result = null; + + try { + result = method.invoke(service, values); + } catch (Exception e) { + Throwable e1 = e; + if (e1 instanceof InvocationTargetException) + e1 = ((InvocationTargetException) e).getTargetException(); + + log.log(Level.FINE, this + " " + e1.toString(), e1); + + out.writeFault("ServiceException", e.getMessage(), e); + out.close(); + return; + } + + // The complete call needs to be after the invoke to handle a + // trailing InputStream + in.completeCall(); + + out.writeReply(result); + + out.close(); + } + + protected boolean isDebugInvoke() + { + return (log.isLoggable(Level.FINEST) + || isDebug() && log.isLoggable(Level.FINE)); + } + + /** + * Creates the PrintWriter for debug output. The default is to + * write to java.util.Logging. + */ + protected PrintWriter createDebugPrintWriter() + throws IOException + { + return new PrintWriter(new LogWriter(log)); + } + + static class LogWriter extends Writer { + private Logger _log; + private StringBuilder _sb = new StringBuilder(); + + LogWriter(Logger log) + { + _log = log; + } + + public void write(char ch) + { + if (ch == '\n' && _sb.length() > 0) { + _log.fine(_sb.toString()); + _sb.setLength(0); + } + else + _sb.append((char) ch); + } + + public void write(char []buffer, int offset, int length) + { + for (int i = 0; i < length; i++) { + char ch = buffer[offset + i]; + + if (ch == '\n' && _sb.length() > 0) { + _log.fine(_sb.toString()); + _sb.setLength(0); + } + else + _sb.append((char) ch); + } + } + + public void flush() + { + } + + public void close() + { + } + } +} diff --git a/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/services/client/ServiceProxyFactory.java b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/services/client/ServiceProxyFactory.java new file mode 100644 index 0000000000..08c71e6fab --- /dev/null +++ b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/services/client/ServiceProxyFactory.java @@ -0,0 +1,81 @@ +/* + * The Apache Software License, Version 1.1 + * + * Copyright (c) 2001-2004 Caucho Technology, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Caucho Technology (http://www.caucho.com/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Burlap", "Resin", and "Caucho" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * info@caucho.com. + * + * 5. Products derived from this software may not be called "Resin" + * nor may "Resin" appear in their names without prior written + * permission of Caucho Technology. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Scott Ferguson + */ + +package com.caucho.hessian4.services.client; + +/** + * Factory for creating client stubs. The returned stub will + * call the remote object for all methods. + * + *
    + * URL url = new URL("http://localhost:8080/ejb/hello");
    + * HelloHome hello = (HelloHome) factory.create(HelloHome.class, url);
    + * 
    + * + * After creation, the stub can be like a regular Java class. Because + * it makes remote calls, it can throw more exceptions than a Java class. + * In particular, it may throw protocol exceptions. + */ +public interface ServiceProxyFactory { + /** + * Creates a new proxy with the specified URL. The returned object + * is a proxy with the interface specified by api. + * + *
    +   * String url = "http://localhost:8080/ejb/hello");
    +   * HelloHome hello = (HelloHome) factory.create(HelloHome.class, url);
    +   * 
    + * + * @param api the interface the proxy class needs to implement + * @param url the URL where the client object is located. + * + * @return a proxy to the object with the specified interface. + */ + public Object create(Class api, String url) + throws java.net.MalformedURLException; +} diff --git a/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/services/server/AbstractSkeleton.java b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/services/server/AbstractSkeleton.java new file mode 100644 index 0000000000..004488a927 --- /dev/null +++ b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/services/server/AbstractSkeleton.java @@ -0,0 +1,221 @@ +/* + * Copyright (c) 2001-2004 Caucho Technology, Inc. All rights reserved. + * + * The Apache Software License, Version 1.1 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Caucho Technology (http://www.caucho.com/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Hessian", "Resin", and "Caucho" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * info@caucho.com. + * + * 5. Products derived from this software may not be called "Resin" + * nor may "Resin" appear in their names without prior written + * permission of Caucho Technology. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Scott Ferguson + */ + +package com.caucho.hessian4.services.server; + +import java.io.InputStream; +import java.lang.reflect.Method; +import java.util.HashMap; + +/** + * Proxy class for Hessian services. + */ +abstract public class AbstractSkeleton { + private Class _apiClass; + private Class _homeClass; + private Class _objectClass; + + private HashMap _methodMap = new HashMap(); + + /** + * Create a new hessian skeleton. + * + * @param apiClass the API interface + */ + protected AbstractSkeleton(Class apiClass) + { + _apiClass = apiClass; + + Method []methodList = apiClass.getMethods(); + + for (int i = 0; i < methodList.length; i++) { + Method method = methodList[i]; + + if (_methodMap.get(method.getName()) == null) + _methodMap.put(method.getName(), method); + + Class []param = method.getParameterTypes(); + String mangledName = method.getName() + "__" + param.length; + _methodMap.put(mangledName, method); + + _methodMap.put(mangleName(method, false), methodList[i]); + } + } + + /** + * Returns the API class of the current object. + */ + public String getAPIClassName() + { + return _apiClass.getName(); + } + + /** + * Returns the API class of the factory/home. + */ + public String getHomeClassName() + { + if (_homeClass != null) + return _homeClass.getName(); + else + return getAPIClassName(); + } + + /** + * Sets the home API class. + */ + public void setHomeClass(Class homeAPI) + { + _homeClass = homeAPI; + } + + /** + * Returns the API class of the object URLs + */ + public String getObjectClassName() + { + if (_objectClass != null) + return _objectClass.getName(); + else + return getAPIClassName(); + } + + /** + * Sets the object API class. + */ + public void setObjectClass(Class objectAPI) + { + _objectClass = objectAPI; + } + + /** + * Returns the method by the mangled name. + * + * @param mangledName the name passed by the protocol + */ + protected Method getMethod(String mangledName) + { + return (Method) _methodMap.get(mangledName); + } + + /** + * Creates a unique mangled method name based on the method name and + * the method parameters. + * + * @param method the method to mangle + * @param isFull if true, mangle the full classname + * + * @return a mangled string. + */ + public static String mangleName(Method method, boolean isFull) + { + StringBuffer sb = new StringBuffer(); + + sb.append(method.getName()); + + Class []params = method.getParameterTypes(); + for (int i = 0; i < params.length; i++) { + sb.append('_'); + sb.append(mangleClass(params[i], isFull)); + } + + return sb.toString(); + } + + /** + * Mangles a classname. + */ + public static String mangleClass(Class cl, boolean isFull) + { + String name = cl.getName(); + + if (name.equals("boolean") || name.equals("java.lang.Boolean")) + return "boolean"; + else if (name.equals("int") || name.equals("java.lang.Integer") + || name.equals("short") || name.equals("java.lang.Short") + || name.equals("byte") || name.equals("java.lang.Byte")) + return "int"; + else if (name.equals("long") || name.equals("java.lang.Long")) + return "long"; + else if (name.equals("float") || name.equals("java.lang.Float") + || name.equals("double") || name.equals("java.lang.Double")) + return "double"; + else if (name.equals("java.lang.String") + || name.equals("com.caucho.util.CharBuffer") + || name.equals("char") || name.equals("java.lang.Character") + || name.equals("java.io.Reader")) + return "string"; + else if (name.equals("java.util.Date") + || name.equals("com.caucho.util.QDate")) + return "date"; + else if (InputStream.class.isAssignableFrom(cl) + || name.equals("[B")) + return "binary"; + else if (cl.isArray()) { + return "[" + mangleClass(cl.getComponentType(), isFull); + } + else if (name.equals("org.w3c.dom.Node") + || name.equals("org.w3c.dom.Element") + || name.equals("org.w3c.dom.Document")) + return "xml"; + else if (isFull) + return name; + else { + int p = name.lastIndexOf('.'); + if (p > 0) + return name.substring(p + 1); + else + return name; + } + } + + public String toString() + { + return getClass().getSimpleName() + "[" + _apiClass.getName() + "]"; + } +} diff --git a/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/util/HessianFreeList.java b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/util/HessianFreeList.java new file mode 100644 index 0000000000..c21a0fb7fb --- /dev/null +++ b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/util/HessianFreeList.java @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2001-2008 Caucho Technology, Inc. All rights reserved. + * + * The Apache Software License, Version 1.1 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Caucho Technology (http://www.caucho.com/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Burlap", "Resin", and "Caucho" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * info@caucho.com. + * + * 5. Products derived from this software may not be called "Resin" + * nor may "Resin" appear in their names without prior written + * permission of Caucho Technology. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Scott Ferguson + */ + +package com.caucho.hessian4.util; + +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReferenceArray; + +/** + * FreeList provides a simple class to manage free objects. This is useful + * for large data structures that otherwise would gobble up huge GC time. + * + *

    The free list is bounded. Freeing an object when the list is full will + * do nothing. + */ +public final class HessianFreeList { + private final AtomicReferenceArray _freeStack; + private final AtomicInteger _top = new AtomicInteger(); + + /** + * Create a new free list. + * + * @param initialSize maximum number of free objects to store. + */ + public HessianFreeList(int size) + { + _freeStack = new AtomicReferenceArray(size); + } + + /** + * Try to get an object from the free list. Returns null if the free list + * is empty. + * + * @return the new object or null. + */ + public T allocate() + { + int top = _top.get(); + + if (top > 0 && _top.compareAndSet(top, top - 1)) + return _freeStack.getAndSet(top - 1, null); + else + return null; + } + + /** + * Frees the object. If the free list is full, the object will be garbage + * collected. + * + * @param obj the object to be freed. + */ + public boolean free(T obj) + { + int top = _top.get(); + + if (top < _freeStack.length()) { + boolean isFree = _freeStack.compareAndSet(top, null, obj); + + _top.compareAndSet(top, top + 1); + + return isFree; + } + else + return false; + } + + public boolean allowFree(T obj) + { + return _top.get() < _freeStack.length(); + } + + /** + * Frees the object. If the free list is full, the object will be garbage + * collected. + * + * @param obj the object to be freed. + */ + public void freeCareful(T obj) + { + if (checkDuplicate(obj)) + throw new IllegalStateException("tried to free object twice: " + obj); + + free(obj); + } + + /** + * Debugging to see if the object has already been freed. + */ + public boolean checkDuplicate(T obj) + { + int top = _top.get(); + + for (int i = top - 1; i >= 0; i--) { + if (_freeStack.get(i) == obj) + return true; + } + + return false; + } +} diff --git a/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/util/IdentityIntMap.java b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/util/IdentityIntMap.java new file mode 100644 index 0000000000..01c27cf9eb --- /dev/null +++ b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/util/IdentityIntMap.java @@ -0,0 +1,280 @@ +/* + * Copyright (c) 2001-2008 Caucho Technology, Inc. All rights reserved. + * + * The Apache Software License, Version 1.1 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Caucho Technology (http://www.caucho.com/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Burlap", "Resin", and "Caucho" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * info@caucho.com. + * + * 5. Products derived from this software may not be called "Resin" + * nor may "Resin" appear in their names without prior written + * permission of Caucho Technology. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Scott Ferguson + */ + +package com.caucho.hessian4.util; + +/** + * The IntMap provides a simple hashmap from keys to integers. The API is + * an abbreviation of the HashMap collection API. + * + *

    The convenience of IntMap is avoiding all the silly wrapping of + * integers. + */ +public class IdentityIntMap { + /** + * Encoding of a null entry. Since NULL is equal to Integer.MIN_VALUE, + * it's impossible to distinguish between the two. + */ + public final static int NULL = 0xdeadbeef; // Integer.MIN_VALUE + 1; + + private Object []_keys; + private int []_values; + + private int _size; + private int _prime; + + /** + * Create a new IntMap. Default size is 16. + */ + public IdentityIntMap(int capacity) + { + _keys = new Object[capacity]; + _values = new int[capacity]; + + _prime = getBiggestPrime(_keys.length); + _size = 0; + } + + /** + * Clear the hashmap. + */ + public void clear() + { + final Object []keys = _keys; + final int []values = _values; + + for (int i = keys.length - 1; i >= 0; i--) { + keys[i] = null; + values[i] = 0; + } + + _size = 0; + } + /** + * Returns the current number of entries in the map. + */ + public final int size() + { + return _size; + } + + /** + * Puts a new value in the property table with the appropriate flags + */ + public final int get(Object key) + { + int prime = _prime; + int hash = hashCode(key) % prime; + // int hash = key.hashCode() & mask; + + final Object []keys = _keys; + + while (true) { + Object mapKey = keys[hash]; + + if (mapKey == null) + return NULL; + else if (isEqual(mapKey, key)) + return _values[hash]; + + hash = (hash + 1) % prime; + } + } + + /** + * Puts a new value in the property table with the appropriate flags + */ + public final int put(Object key, int value, boolean isReplace) + { + int prime = _prime; + int hash = hashCode(key) % prime; + //int hash = key.hashCode() % prime; + + Object []keys = _keys; + + while (true) { + Object testKey = keys[hash]; + + if (testKey == null) { + keys[hash] = key; + _values[hash] = value; + + _size++; + + if (keys.length <= 4 * _size) + resize(4 * keys.length); + + return value; + } + else if (!isEqual(key, testKey)) { + hash = (hash + 1) % prime; + + continue; + } + else if (isReplace){ + int old = _values[hash]; + + _values[hash] = value; + + return old; + } + else { + return _values[hash]; + } + } + } + + /** + * Expands the property table + */ + private void resize(int newSize) + { + Object []keys = _keys; + int values[] = _values; + + _keys = new Object[newSize]; + _values = new int[newSize]; + _size = 0; + + _prime = getBiggestPrime(_keys.length); + + for (int i = keys.length - 1; i >= 0; i--) { + Object key = keys[i]; + + if (key != null) { + put(key, values[i], true); + } + } + } + + protected int hashCode(Object value) + { + int result = 0; + if (value instanceof String) + result = value.hashCode(); + else + result = System.identityHashCode(value); + if (result < 0) + result = -result; + return result; + } + + protected boolean isEqual(Object obj1, Object obj2) + { + if (obj1 instanceof String && obj2 instanceof String) + return obj1.equals(obj2); + else + return obj1 == obj2; + } + + public String toString() + { + StringBuffer sbuf = new StringBuffer(); + + sbuf.append("IntMap["); + boolean isFirst = true; + + for (int i = 0; i <= _keys.length; i++) { + if (_keys[i] != null) { + if (! isFirst) + sbuf.append(", "); + + isFirst = false; + sbuf.append(_keys[i]); + sbuf.append(":"); + sbuf.append(_values[i]); + } + } + sbuf.append("]"); + + return sbuf.toString(); + } + + public static final int []PRIMES = + { + 1, /* 1<< 0 = 1 */ + 2, /* 1<< 1 = 2 */ + 3, /* 1<< 2 = 4 */ + 7, /* 1<< 3 = 8 */ + 13, /* 1<< 4 = 16 */ + 31, /* 1<< 5 = 32 */ + 61, /* 1<< 6 = 64 */ + 127, /* 1<< 7 = 128 */ + 251, /* 1<< 8 = 256 */ + 509, /* 1<< 9 = 512 */ + 1021, /* 1<<10 = 1024 */ + 2039, /* 1<<11 = 2048 */ + 4093, /* 1<<12 = 4096 */ + 8191, /* 1<<13 = 8192 */ + 16381, /* 1<<14 = 16384 */ + 32749, /* 1<<15 = 32768 */ + 65521, /* 1<<16 = 65536 */ + 131071, /* 1<<17 = 131072 */ + 262139, /* 1<<18 = 262144 */ + 524287, /* 1<<19 = 524288 */ + 1048573, /* 1<<20 = 1048576 */ + 2097143, /* 1<<21 = 2097152 */ + 4194301, /* 1<<22 = 4194304 */ + 8388593, /* 1<<23 = 8388608 */ + 16777213, /* 1<<24 = 16777216 */ + 33554393, /* 1<<25 = 33554432 */ + 67108859, /* 1<<26 = 67108864 */ + 134217689, /* 1<<27 = 134217728 */ + 268435399, /* 1<<28 = 268435456 */ + }; + + public static int getBiggestPrime(int value) + { + for (int i = PRIMES.length - 1; i >= 0; i--) { + if (PRIMES[i] <= value) + return PRIMES[i]; + } + + return 2; + } +} diff --git a/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/util/IntMap.java b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/util/IntMap.java new file mode 100644 index 0000000000..c7b1bff30b --- /dev/null +++ b/javaUtilities/yajsw/src/hessian/com/caucho/hessian4/util/IntMap.java @@ -0,0 +1,267 @@ +/* + * Copyright (c) 2001-2008 Caucho Technology, Inc. All rights reserved. + * + * The Apache Software License, Version 1.1 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Caucho Technology (http://www.caucho.com/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "Burlap", "Resin", and "Caucho" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * info@caucho.com. + * + * 5. Products derived from this software may not be called "Resin" + * nor may "Resin" appear in their names without prior written + * permission of Caucho Technology. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @author Scott Ferguson + */ + +package com.caucho.hessian4.util; + +/** + * The IntMap provides a simple hashmap from keys to integers. The API is + * an abbreviation of the HashMap collection API. + * + *

    The convenience of IntMap is avoiding all the silly wrapping of + * integers. + */ +public class IntMap { + /** + * Encoding of a null entry. Since NULL is equal to Integer.MIN_VALUE, + * it's impossible to distinguish between the two. + */ + public final static int NULL = 0xdeadbeef; // Integer.MIN_VALUE + 1; + + private Object []_keys; + private int []_values; + + private int _size; + private int _prime; + + /** + * Create a new IntMap. Default size is 16. + */ + public IntMap() + { + int capacity = 1024; + + _keys = new Object[capacity]; + _values = new int[capacity]; + + _prime = getBiggestPrime(_keys.length); + _size = 0; + } + + /** + * Clear the hashmap. + */ + public void clear() + { + final Object []keys = _keys; + final int []values = _values; + + for (int i = keys.length - 1; i >= 0; i--) { + keys[i] = null; + values[i] = 0; + } + + _size = 0; + } + /** + * Returns the current number of entries in the map. + */ + public final int size() + { + return _size; + } + + /** + * Puts a new value in the property table with the appropriate flags + */ + public final int get(Object key) + { + int prime = _prime; + int hash = hashCode(key) % prime; + // int hash = key.hashCode() & mask; + + final Object []keys = _keys; + + while (true) { + Object mapKey = keys[hash]; + + if (mapKey == null) + return NULL; + else if (mapKey == key) + return _values[hash]; + + hash = (hash + 1) % prime; + } + } + + /** + * Puts a new value in the property table with the appropriate flags + */ + public final int put(Object key, int value, boolean isReplace) + { + int prime = _prime; + int hash = hashCode(key) % prime; + // int hash = key.hashCode() % prime; + + Object []keys = _keys; + + while (true) { + Object testKey = keys[hash]; + + if (testKey == null) { + keys[hash] = key; + _values[hash] = value; + + _size++; + + if (keys.length <= 4 * _size) + resize(4 * keys.length); + + return NULL; + } + else if (key != testKey) { + hash = (hash + 1) % prime; + + continue; + } + else if (isReplace){ + int old = _values[hash]; + + _values[hash] = value; + + return old; + } + else { + return _values[hash]; + } + } + } + + /** + * Expands the property table + */ + private void resize(int newSize) + { + Object []keys = _keys; + int values[] = _values; + + _keys = new Object[newSize]; + _values = new int[newSize]; + _size = 0; + + _prime = getBiggestPrime(_keys.length); + + for (int i = keys.length - 1; i >= 0; i--) { + Object key = keys[i]; + + if (key != null) { + put(key, values[i], true); + } + } + } + + protected int hashCode(Object value) + { + return value.hashCode(); + } + + public String toString() + { + StringBuffer sbuf = new StringBuffer(); + + sbuf.append("IntMap["); + boolean isFirst = true; + + for (int i = 0; i <= _keys.length; i++) { + if (_keys[i] != null) { + if (! isFirst) + sbuf.append(", "); + + isFirst = false; + sbuf.append(_keys[i]); + sbuf.append(":"); + sbuf.append(_values[i]); + } + } + sbuf.append("]"); + + return sbuf.toString(); + } + + public static final int []PRIMES = + { + 1, /* 1<< 0 = 1 */ + 2, /* 1<< 1 = 2 */ + 3, /* 1<< 2 = 4 */ + 7, /* 1<< 3 = 8 */ + 13, /* 1<< 4 = 16 */ + 31, /* 1<< 5 = 32 */ + 61, /* 1<< 6 = 64 */ + 127, /* 1<< 7 = 128 */ + 251, /* 1<< 8 = 256 */ + 509, /* 1<< 9 = 512 */ + 1021, /* 1<<10 = 1024 */ + 2039, /* 1<<11 = 2048 */ + 4093, /* 1<<12 = 4096 */ + 8191, /* 1<<13 = 8192 */ + 16381, /* 1<<14 = 16384 */ + 32749, /* 1<<15 = 32768 */ + 65521, /* 1<<16 = 65536 */ + 131071, /* 1<<17 = 131072 */ + 262139, /* 1<<18 = 262144 */ + 524287, /* 1<<19 = 524288 */ + 1048573, /* 1<<20 = 1048576 */ + 2097143, /* 1<<21 = 2097152 */ + 4194301, /* 1<<22 = 4194304 */ + 8388593, /* 1<<23 = 8388608 */ + 16777213, /* 1<<24 = 16777216 */ + 33554393, /* 1<<25 = 33554432 */ + 67108859, /* 1<<26 = 67108864 */ + 134217689, /* 1<<27 = 134217728 */ + 268435399, /* 1<<28 = 268435456 */ + }; + + public static int getBiggestPrime(int value) + { + for (int i = PRIMES.length - 1; i >= 0; i--) { + if (PRIMES[i] <= value) + return PRIMES[i]; + } + + return 2; + } +} diff --git a/javaUtilities/yajsw/src/main/java/com/sun/jna/PlatformEx.java b/javaUtilities/yajsw/src/main/java/com/sun/jna/PlatformEx.java new file mode 100644 index 0000000000..1f95714cc8 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/com/sun/jna/PlatformEx.java @@ -0,0 +1,21 @@ +package com.sun.jna; + +public class PlatformEx +{ + private static boolean winVista = false; + + static + { + String osName = System.getProperty("os.name").toLowerCase(); + if (osName.startsWith("windows")) + { + winVista = osName.contains("vista") || osName.contains(" 7") || osName.contains("2008") || osName.contains(" 8"); + } + } + + public static boolean isWinVista() + { + return winVista; + } + +} diff --git a/javaUtilities/yajsw/src/main/java/com/sun/jna/StringBlock.java b/javaUtilities/yajsw/src/main/java/com/sun/jna/StringBlock.java new file mode 100644 index 0000000000..e8c680c459 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/com/sun/jna/StringBlock.java @@ -0,0 +1,78 @@ +package com.sun.jna; + +/* + * A string block consists of a null-terminated block of null-terminated strings. + * Note that an ANSI environment block is terminated by two zero bytes: + * one for the last string, one more to terminate the block. + * A Unicode environment block is terminated by four zero bytes: + * two for the last string, two more to terminate the block. + */ + +public class StringBlock extends Memory +{ + private boolean wide; + + /** Create a native array of strings. */ + public StringBlock(String[] strings) + { + this(strings, false); + } + + /** Create a native block of strings. */ + public StringBlock(String[] strings, boolean wide) + { + this((Object[]) strings, wide); + } + + /** Create a native block of wide strings. */ + public StringBlock(WString[] strings) + { + this(strings, true); + } + + private StringBlock(Object[] strings, boolean wide) + { + super(calculateLength(strings, wide)); + this.wide = wide; + int offset = 0; + for (int i = 0; i < strings.length; i++) + { + if (strings[i] != null) + { + setString(offset, strings[i].toString(), wide); + if (wide) + offset += (strings[i].toString().length()) * Native.WCHAR_SIZE; + else + offset += (strings[i].toString().getBytes().length); + setString(offset, "\0", wide); + if (wide) + offset += Native.WCHAR_SIZE; + else + offset += 1; + + } + } + setByte(offset, (byte) 0); + if (wide) + setByte(offset + 1, (byte) 0); + } + + private static long calculateLength(Object[] strings, boolean wide) + { + int result = 0; + if (wide) + { + for (Object string : strings) + result += (string.toString().length() + 1) * Native.WCHAR_SIZE; + result += Native.WCHAR_SIZE; + } + else + { + for (Object string : strings) + result += (string.toString().getBytes().length + 1); + result += 1; + } + return result; + } + +} diff --git a/javaUtilities/yajsw/src/main/java/jnacontrib/jna/Advapi32.java b/javaUtilities/yajsw/src/main/java/jnacontrib/jna/Advapi32.java new file mode 100644 index 0000000000..90ef55286d --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/jnacontrib/jna/Advapi32.java @@ -0,0 +1,497 @@ +/* + * Advapi32.java + * + * Created on 6. August 2007, 11:24 + * + * To change this template, choose Tools | Template Manager + * and open the template in the editor. + */ + +package jnacontrib.jna; + +import java.util.ArrayList; +import java.util.List; + +import com.sun.jna.Memory; +import com.sun.jna.Native; +import com.sun.jna.Pointer; +import com.sun.jna.Structure; +import com.sun.jna.WString; +import com.sun.jna.ptr.IntByReference; +import com.sun.jna.ptr.PointerByReference; +import com.sun.jna.win32.StdCallLibrary; + +/** + * + * @author TB + */ +public interface Advapi32 extends StdCallLibrary +{ + Advapi32 INSTANCE = (Advapi32) Native.loadLibrary("Advapi32", Advapi32.class, Options.UNICODE_OPTIONS); + + /* + * BOOL WINAPI LookupAccountName( LPCTSTR lpSystemName, LPCTSTR + * lpAccountName, PSID Sid, LPDWORD cbSid, LPTSTR ReferencedDomainName, + * LPDWORD cchReferencedDomainName, PSID_NAME_USE peUse ); + */ + public boolean LookupAccountName(String lpSystemName, String lpAccountName, byte[] Sid, IntByReference cbSid, char[] ReferencedDomainName, + IntByReference cchReferencedDomainName, PointerByReference peUse); + + /* + * BOOL WINAPI LookupAccountSid( LPCTSTR lpSystemName, PSID lpSid, LPTSTR + * lpName, LPDWORD cchName, LPTSTR lpReferencedDomainName, LPDWORD + * cchReferencedDomainName, PSID_NAME_USE peUse ); + */ + public boolean LookupAccountSid(String lpSystemName, byte[] Sid, char[] lpName, IntByReference cchName, char[] ReferencedDomainName, + IntByReference cchReferencedDomainName, PointerByReference peUse); + + /* + * BOOL ConvertSidToStringSid( PSID Sid, LPTSTR* StringSid ); + */ + public boolean ConvertSidToStringSid(byte[] Sid, PointerByReference StringSid); + + /* + * BOOL WINAPI ConvertStringSidToSid( LPCTSTR StringSid, PSID* Sid ); + */ + public boolean ConvertStringSidToSid(String StringSid, PointerByReference Sid); + + /* + * SC_HANDLE WINAPI OpenSCManager( LPCTSTR lpMachineName, LPCTSTR + * lpDatabaseName, DWORD dwDesiredAccess ); + */ + public Pointer OpenSCManager(String lpMachineName, WString lpDatabaseName, int dwDesiredAccess); + + /* + * BOOL WINAPI CloseServiceHandle( SC_HANDLE hSCObject ); + */ + public boolean CloseServiceHandle(Pointer hSCObject); + + /* + * SC_HANDLE WINAPI OpenService( SC_HANDLE hSCManager, LPCTSTR + * lpServiceName, DWORD dwDesiredAccess ); + */ + public Pointer OpenService(Pointer hSCManager, String lpServiceName, int dwDesiredAccess); + + /* + * BOOL WINAPI StartService( SC_HANDLE hService, DWORD dwNumServiceArgs, + * LPCTSTR* lpServiceArgVectors ); + */ + public boolean StartService(Pointer hService, int dwNumServiceArgs, char[] lpServiceArgVectors); + + /* + * BOOL WINAPI ControlService( SC_HANDLE hService, DWORD dwControl, + * LPSERVICE_STATUS lpServiceStatus ); + */ + public boolean ControlService(Pointer hService, int dwControl, SERVICE_STATUS lpServiceStatus); + + /* + * BOOL WINAPI StartServiceCtrlDispatcher( const SERVICE_TABLE_ENTRY* + * lpServiceTable ); + */ + public boolean StartServiceCtrlDispatcher(Structure[] lpServiceTable); + + /* + * SERVICE_STATUS_HANDLE WINAPI RegisterServiceCtrlHandler( LPCTSTR + * lpServiceName, LPHANDLER_FUNCTION lpHandlerProc ); + */ + public Pointer RegisterServiceCtrlHandler(String lpServiceName, Handler lpHandlerProc); + + /* + * SERVICE_STATUS_HANDLE WINAPI RegisterServiceCtrlHandlerEx( LPCTSTR + * lpServiceName, LPHANDLER_FUNCTION_EX lpHandlerProc, LPVOID lpContext ); + */ + public Pointer RegisterServiceCtrlHandlerEx(String lpServiceName, HandlerEx lpHandlerProc, Pointer lpContext); + + /* + * BOOL WINAPI SetServiceStatus( SERVICE_STATUS_HANDLE hServiceStatus, + * LPSERVICE_STATUS lpServiceStatus ); + */ + public boolean SetServiceStatus(Pointer hServiceStatus, SERVICE_STATUS lpServiceStatus); + + /* + * SC_HANDLE WINAPI CreateService( SC_HANDLE hSCManager, LPCTSTR + * lpServiceName, LPCTSTR lpDisplayName, DWORD dwDesiredAccess, DWORD + * dwServiceType, DWORD dwStartType, DWORD dwErrorControl, LPCTSTR + * lpBinaryPathName, LPCTSTR lpLoadOrderGroup, LPDWORD lpdwTagId, LPCTSTR + * lpDependencies, LPCTSTR lpServiceStartName, LPCTSTR lpPassword ); + */ + public Pointer CreateService(Pointer hSCManager, String lpServiceName, String lpDisplayName, int dwDesiredAccess, int dwServiceType, + int dwStartType, int dwErrorControl, String lpBinaryPathName, String lpLoadOrderGroup, IntByReference lpdwTagId, String lpDependencies, + String lpServiceStartName, String lpPassword); + + /* + * BOOL WINAPI DeleteService( SC_HANDLE hService ); + */ + public boolean DeleteService(Pointer hService); + + /* + * BOOL WINAPI ChangeServiceConfig2( SC_HANDLE hService, DWORD dwInfoLevel, + * LPVOID lpInfo ); + */ + public boolean ChangeServiceConfig2(Pointer hService, int dwInfoLevel, ChangeServiceConfig2Info lpInfo); + + /* + * LONG WINAPI RegOpenKeyEx( HKEY hKey, LPCTSTR lpSubKey, DWORD ulOptions, + * REGSAM samDesired, PHKEY phkResult ); + */ + public int RegOpenKeyEx(int hKey, String lpSubKey, int ulOptions, int samDesired, IntByReference phkResult); + + /* + * LONG WINAPI RegQueryValueEx( HKEY hKey, LPCTSTR lpValueName, LPDWORD + * lpReserved, LPDWORD lpType, LPBYTE lpData, LPDWORD lpcbData ); + */ + public int RegQueryValueEx(int hKey, String lpValueName, IntByReference lpReserved, IntByReference lpType, byte[] lpData, IntByReference lpcbData); + + /* + * LONG WINAPI RegCloseKey( HKEY hKey ); + */ + public int RegCloseKey(int hKey); + + /* + * LONG WINAPI RegDeleteValue( HKEY hKey, LPCTSTR lpValueName ); + */ + public int RegDeleteValue(int hKey, String lpValueName); + + /* + * LONG WINAPI RegSetValueEx( HKEY hKey, LPCTSTR lpValueName, DWORD + * Reserved, DWORD dwType, const BYTE* lpData, DWORD cbData ); + */ + public int RegSetValueEx(int hKey, String lpValueName, int Reserved, int dwType, byte[] lpData, int cbData); + + /* + * LONG WINAPI RegCreateKeyEx( HKEY hKey, LPCTSTR lpSubKey, DWORD Reserved, + * LPTSTR lpClass, DWORD dwOptions, REGSAM samDesired, LPSECURITY_ATTRIBUTES + * lpSecurityAttributes, PHKEY phkResult, LPDWORD lpdwDisposition ); + */ + public int RegCreateKeyEx(int hKey, String lpSubKey, int Reserved, String lpClass, int dwOptions, int samDesired, + WINBASE.SECURITY_ATTRIBUTES lpSecurityAttributes, IntByReference phkResult, IntByReference lpdwDisposition); + + /* + * LONG WINAPI RegDeleteKey( HKEY hKey, LPCTSTR lpSubKey ); + */ + public int RegDeleteKey(int hKey, String name); + + /* + * LONG WINAPI RegEnumKeyEx( HKEY hKey, DWORD dwIndex, LPTSTR lpName, + * LPDWORD lpcName, LPDWORD lpReserved, LPTSTR lpClass, LPDWORD lpcClass, + * PFILETIME lpftLastWriteTime ); + */ + public int RegEnumKeyEx(int hKey, int dwIndex, char[] lpName, IntByReference lpcName, IntByReference reserved, char[] lpClass, + IntByReference lpcClass, WINBASE.FILETIME lpftLastWriteTime); + + /* + * LONG WINAPI RegEnumValue( HKEY hKey, DWORD dwIndex, LPTSTR lpValueName, + * LPDWORD lpcchValueName, LPDWORD lpReserved, LPDWORD lpType, LPBYTE + * lpData, LPDWORD lpcbData ); + */ + public int RegEnumValue(int hKey, int dwIndex, char[] lpValueName, IntByReference lpcchValueName, IntByReference reserved, IntByReference lpType, + byte[] lpData, IntByReference lpcbData); + + interface SERVICE_MAIN_FUNCTION extends StdCallCallback + { + /* + * VOID WINAPI ServiceMain( DWORD dwArgc, LPTSTR* lpszArgv ); + */ + public void callback(int dwArgc, Pointer lpszArgv); + } + + interface Handler extends StdCallCallback + { + /* + * VOID WINAPI Handler( DWORD fdwControl ); + */ + public void callback(int fdwControl); + } + + interface HandlerEx extends StdCallCallback + { + /* + * DWORD WINAPI HandlerEx( DWORD dwControl, DWORD dwEventType, LPVOID + * lpEventData, LPVOID lpContext ); + */ + public int callback(int dwControl, int dwEventType, Pointer lpEventData, Pointer lpContext); + } + + /* + * typedef struct _SERVICE_STATUS { DWORD dwServiceType; DWORD + * dwCurrentState; DWORD dwControlsAccepted; DWORD dwWin32ExitCode; DWORD + * dwServiceSpecificExitCode; DWORD dwCheckPoint; DWORD dwWaitHint; } + * SERVICE_STATUS,LPSERVICE_STATUS; + */ + public static class SERVICE_STATUS extends Structure + { + public int dwServiceType; + public int dwCurrentState; + public int dwControlsAccepted; + public int dwWin32ExitCode; + public int dwServiceSpecificExitCode; + public int dwCheckPoint; + public int dwWaitHint; + } + + /* + * typedef struct _SERVICE_TABLE_ENTRY { LPTSTR lpServiceName; + * LPSERVICE_MAIN_FUNCTION lpServiceProc; } SERVICE_TABLE_ENTRY, + * LPSERVICE_TABLE_ENTRY; + */ + public static class SERVICE_TABLE_ENTRY extends Structure + { + public String lpServiceName; + public SERVICE_MAIN_FUNCTION lpServiceProc; + } + + public static class ChangeServiceConfig2Info extends Structure + { + } + + /* + * YAJSW additions start here + */ + + /* + * BOOL WINAPI QueryServiceConfig( __in SC_HANDLE hService, __out_opt + * LPQUERY_SERVICE_CONFIG lpServiceConfig, __in DWORD cbBufSize, __out + * LPDWORD pcbBytesNeeded ); + */ + boolean QueryServiceConfig(Pointer hService, Memory lpServiceConfig, int cbBufSize, IntByReference pcbBytesNeeded); + + /* + * BOOL WINAPI QueryServiceStatusEx( __in SC_HANDLE hService, __in + * SC_STATUS_TYPE InfoLevel, __out_opt LPBYTE lpBuffer, __in DWORD + * cbBufSize, __out LPDWORD pcbBytesNeeded ); + */ + boolean QueryServiceStatusEx(Pointer hService, short InfoLevel, Memory lpBuffer, int cbBufSize, IntByReference pcbBytesNeeded); + + public static final int SC_STATUS_PROCESS_INFO = 0; + + /* + * BOOL WINAPI QueryServiceConfig2( __in SC_HANDLE hService, __in DWORD + * dwInfoLevel, __out_opt LPBYTE lpBuffer, __in DWORD cbBufSize, __out + * LPDWORD pcbBytesNeeded ); + */ + boolean QueryServiceConfig2(Pointer hService, short InfoLevel, Memory lpBuffer, int cbBufSize, IntByReference pcbBytesNeeded); + + public static final int SERVICE_CONFIG_DESCRIPTION = 1; + + /* + * typedef struct _ENUM_SERVICE_STATUS_PROCESS { LPTSTR lpServiceName; + * LPTSTR lpDisplayName; SERVICE_STATUS_PROCESS ServiceStatusProcess; } + * ENUM_SERVICE_STATUS_PROCESS,LPENUM_SERVICE_STATUS_PROCESS; + */ + + public static class ENUM_SERVICE_STATUS_PROCESS extends Structure + { + public Pointer lpServiceName; + public Pointer lpDisplayName; + public SERVICE_STATUS_PROCESS ServiceStatusProcess; + + public void init(Pointer pointer) + { + useMemory(pointer); + read(); + } + + public ENUM_SERVICE_STATUS_PROCESS next() + { + ENUM_SERVICE_STATUS_PROCESS next = new ENUM_SERVICE_STATUS_PROCESS(); + next.useMemory(getPointer(), size()); + next.read(); + return next; + } + + public String getServiceName() + { + return lpServiceName.getString(0, true); + } + + public String getDisplayName() + { + return lpDisplayName.getString(0, true); + } + + public int getProcessId() + { + return ServiceStatusProcess.dwProcessId; + } + + public int getCurrentState() + { + return ServiceStatusProcess.dwCurrentState; + } + + } + + /* + * typedef struct _SERVICE_STATUS_PROCESS { DWORD dwServiceType; DWORD + * dwCurrentState; DWORD dwControlsAccepted; DWORD dwWin32ExitCode; DWORD + * dwServiceSpecificExitCode; DWORD dwCheckPoint; DWORD dwWaitHint; DWORD + * dwProcessId; DWORD dwServiceFlags; } SERVICE_STATUS_PROCESS, + * LPSERVICE_STATUS_PROCESS; + */ + + public static class SERVICE_STATUS_PROCESS extends Structure + { + public void init(Pointer pointer) + { + useMemory(pointer); + read(); + } + + public int dwServiceType; + public int dwCurrentState; + public int dwControlsAccepted; + public int dwWin32ExitCode; + public int dwServiceSpecificExitCode; + public int dwCheckPoint; + public int dwWaitHint; + public int dwProcessId; + public int dwServiceFlags; + + } + + public static final int SERVICE_RUNNING = 0x00000004; + public static final int SERVICE_STOPPED = 0x00000001; + public static final int SERVICE_PAUSED = 0x00000007; + public static final int SERVICE_START_PENDING = 0x00000002; + public static final int SERVICE_STOP_PENDING = 0x00000003; + + /* + * typedef struct _QUERY_SERVICE_CONFIG { DWORD dwServiceType; DWORD + * dwStartType; DWORD dwErrorControl; LPTSTR lpBinaryPathName; LPTSTR + * lpLoadOrderGroup; DWORD dwTagId; LPTSTR lpDependencies; LPTSTR + * lpServiceStartName; LPTSTR lpDisplayName; } QUERY_SERVICE_CONFIG, + * LPQUERY_SERVICE_CONFIG; + */ + + public static class QUERY_SERVICE_CONFIG extends Structure + { + public void init(Pointer pointer) + { + useMemory(pointer); + read(); + } + + public int dwServiceType; + public int dwStartType; + public int dwErrorControl; + public String lpBinaryPathName; + public String lpLoadOrderGroup; + public int dwTagId; + public Pointer lpDependencies; + public String lpServiceStartName; + public String lpDisplayName; + + public String[] getDependencies() + { + List result = new ArrayList(); + Pointer ptr = lpDependencies; + int offset = 0; + String s = ""; + if (ptr != null) + do + { + s = ptr.getString(offset, true); + if (s != null && !"".equals(s)) + { + result.add(s); + offset += s.getBytes().length * 2 + 2; + } + } + while (s != null && !"".equals(s)); + return result.toArray(new String[0]); + } + + } + + /* + * typedef struct _SERVICE_DESCRIPTION { LPTSTR lpDescription; } + * SERVICE_DESCRIPTION,LPSERVICE_DESCRIPTION; + */ + public static class SERVICE_DESCRIPTION extends ChangeServiceConfig2Info + { + public String lpDescription; + + public void init(Pointer pointer) + { + useMemory(pointer); + read(); + } + } + + /* + * typedef struct _SERVICE_DELAYED_AUTO_START_INFO { + BOOL fDelayedAutostart;} + */ + public static class SERVICE_DELAYED_AUTO_START_INFO extends ChangeServiceConfig2Info + { + public boolean fDelayedAutostart; + + public void init(Pointer pointer) + { + useMemory(pointer); + read(); + } + } + + /* + * typedef struct _SC_ACTION { + SC_ACTION_TYPE Type; + DWORD Delay; +} SC_ACTION, *LPSC_ACTION; + */ + public static class SC_ACTION extends Structure + { + public SC_ACTION() + { + super(); + setAutoWrite(true); + setAutoRead(false); + } + public int Type; + public int Delay; + } + + // SC_ACTION types + public static final int SC_ACTION_NONE = 0; // No action. + public static final int SC_ACTION_REBOOT = 2; // Reboot the computer. + public static final int SC_ACTION_RESTART = 1; // Restart the service. + public static final int SC_ACTION_RUN_COMMAND = 3; // Run a command. + + /* + * typedef struct _SERVICE_FAILURE_ACTIONS { + DWORD dwResetPeriod; + LPTSTR lpRebootMsg; + LPTSTR lpCommand; + DWORD cActions; + SC_ACTION *lpsaActions; +} SERVICE_FAILURE_ACTIONS, *LPSERVICE_FAILURE_ACTIONS; + */ + public static class SERVICE_FAILURE_ACTIONS extends ChangeServiceConfig2Info + { + public int dwResetPeriod; + public String lpRebootMsg; + public String lpCommand; + public int cActions; + public Pointer lpsaActions; + + } + + + /* + * BOOL WINAPI EnumServicesStatusEx( __in SC_HANDLE hSCManager, __in + * SC_ENUM_TYPE InfoLevel, __in DWORD dwServiceType, __in DWORD + * dwServiceState, __out_opt LPBYTE lpServices, __in DWORD cbBufSize, __out + * LPDWORD pcbBytesNeeded, __out LPDWORD lpServicesReturned, __inout_opt + * LPDWORD lpResumeHandle, __in_opt LPCTSTR pszGroupName ); + */ + public boolean EnumServicesStatusExW(Pointer hSCManager, int InfoLevel, int dwServiceType, int dwServiceState, Memory lpServices, int cbBufSize, + IntByReference pcbBytesNeeded, IntByReference lpServicesReturned, IntByReference lpResumeHandle, String pszGroupName); + + public static final int SERVICE_DISABLED = 0x00000004; + public static final int SERVICE_INTERACTIVE_PROCESS = 0x00000100; + public static final int SERVICE_AUTO_START = 0x00000002; + public static final int SERVICE_BOOT_START = 0x00000000; + public static final int SERVICE_DEMAND_START = 0x00000003; + public static final int SERVICE_SYSTEM_START = 0x00000001; + +} diff --git a/javaUtilities/yajsw/src/main/java/jnacontrib/jna/Options.java b/javaUtilities/yajsw/src/main/java/jnacontrib/jna/Options.java new file mode 100644 index 0000000000..6ce637934a --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/jnacontrib/jna/Options.java @@ -0,0 +1,35 @@ +/* + * Options.java + * + * Created on 8. August 2007, 17:07 + * + * To change this template, choose Tools | Template Manager + * and open the template in the editor. + */ + +package jnacontrib.jna; + +import static com.sun.jna.Library.OPTION_FUNCTION_MAPPER; +import static com.sun.jna.Library.OPTION_TYPE_MAPPER; + +import java.util.HashMap; +import java.util.Map; + +import com.sun.jna.win32.W32APIFunctionMapper; +import com.sun.jna.win32.W32APITypeMapper; + +/** + * + * @author TB + */ +public interface Options +{ + Map UNICODE_OPTIONS = new HashMap() + { + { + put(OPTION_TYPE_MAPPER, W32APITypeMapper.UNICODE); + put(OPTION_FUNCTION_MAPPER, W32APIFunctionMapper.UNICODE); + } + private static final long serialVersionUID = 1L; + }; +} diff --git a/javaUtilities/yajsw/src/main/java/jnacontrib/jna/WINBASE.java b/javaUtilities/yajsw/src/main/java/jnacontrib/jna/WINBASE.java new file mode 100644 index 0000000000..b155919405 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/jnacontrib/jna/WINBASE.java @@ -0,0 +1,42 @@ +/* + * WINBASE.java + * + * Created on 5. September 2007, 11:24 + * + * To change this template, choose Tools | Template Manager + * and open the template in the editor. + */ + +package jnacontrib.jna; + +import com.sun.jna.Pointer; +import com.sun.jna.Structure; + +/** + * + * @author TB + */ +public interface WINBASE +{ + /* + * typedef struct _SECURITY_ATTRIBUTES { DWORD nLength; LPVOID + * lpSecurityDescriptor; BOOL bInheritHandle; } SECURITY_ATTRIBUTES, + * PSECURITY_ATTRIBUTES,LPSECURITY_ATTRIBUTES; + */ + public static class SECURITY_ATTRIBUTES extends Structure + { + public int nLength; + public Pointer lpSecurityDescriptor; + public boolean bInheritHandle; + } + + /* + * typedef struct _FILETIME { DWORD dwLowDateTime; DWORD dwHighDateTime; } + * FILETIME, *PFILETIME, *LPFILETIME; + */ + public static class FILETIME extends Structure + { + public int dwLowDateTime; + public int dwHighDateTime; + } +} diff --git a/javaUtilities/yajsw/src/main/java/jnacontrib/jna/WINERROR.java b/javaUtilities/yajsw/src/main/java/jnacontrib/jna/WINERROR.java new file mode 100644 index 0000000000..3193b9ebc3 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/jnacontrib/jna/WINERROR.java @@ -0,0 +1,22 @@ +/* + * WINERROR.java + * + * Created on 7. August 2007, 08:09 + * + * To change this template, choose Tools | Template Manager + * and open the template in the editor. + */ + +package jnacontrib.jna; + +/** + * + * @author TB + */ +public interface WINERROR +{ + public final static int ERROR_SUCCESS = 0; + public final static int NO_ERROR = 0; + public final static int ERROR_FILE_NOT_FOUND = 2; + public final static int ERROR_MORE_DATA = 234; +} diff --git a/javaUtilities/yajsw/src/main/java/jnacontrib/jna/WINNT.java b/javaUtilities/yajsw/src/main/java/jnacontrib/jna/WINNT.java new file mode 100644 index 0000000000..208ace9133 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/jnacontrib/jna/WINNT.java @@ -0,0 +1,126 @@ +/* + * WINNT.java + * + * Created on 8. August 2007, 13:41 + * + * To change this template, choose Tools | Template Manager + * and open the template in the editor. + */ + +package jnacontrib.jna; + +/** + * + * @author TB + */ +public interface WINNT +{ + public final static int DELETE = 0x00010000; + public final static int READ_CONTROL = 0x00020000; + public final static int WRITE_DAC = 0x00040000; + public final static int WRITE_OWNER = 0x00080000; + public final static int SYNCHRONIZE = 0x00100000; + + public final static int STANDARD_RIGHTS_REQUIRED = 0x000F0000; + + public final static int STANDARD_RIGHTS_READ = READ_CONTROL; + public final static int STANDARD_RIGHTS_WRITE = READ_CONTROL; + public final static int STANDARD_RIGHTS_EXECUTE = READ_CONTROL; + + public final static int STANDARD_RIGHTS_ALL = 0x001F0000; + + public final static int SPECIFIC_RIGHTS_ALL = 0x0000FFFF; + + public final static int GENERIC_EXECUTE = 0x20000000; + + public final static int SERVICE_WIN32_OWN_PROCESS = 0x00000010; + + public final static int KEY_QUERY_VALUE = 0x0001; + public final static int KEY_SET_VALUE = 0x0002; + public final static int KEY_CREATE_SUB_KEY = 0x0004; + public final static int KEY_ENUMERATE_SUB_KEYS = 0x0008; + public final static int KEY_NOTIFY = 0x0010; + public final static int KEY_CREATE_LINK = 0x0020; + + public final static int KEY_READ = ((STANDARD_RIGHTS_READ | KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS | KEY_NOTIFY) & (~SYNCHRONIZE)); + public final static int KEY_WRITE = ((STANDARD_RIGHTS_WRITE | KEY_SET_VALUE | KEY_CREATE_SUB_KEY) & (~SYNCHRONIZE)); + + public final static int REG_NONE = 0; // No + // value + // type + public final static int REG_SZ = 1; // Unicode + // nul + // terminated + // string + public final static int REG_EXPAND_SZ = 2; // Unicode + // nul + // terminated + // string + // (with environment variable references) + public final static int REG_BINARY = 3; // Free + // form + // binary + public final static int REG_DWORD = 4; // 32-bit + // number + public final static int REG_DWORD_LITTLE_ENDIAN = 4; // 32-bit + // number + // (same + // as + // REG_DWORD) + public final static int REG_DWORD_BIG_ENDIAN = 5; // 32-bit + // number + public final static int REG_LINK = 6; // Symbolic + // Link + // (unicode) + public final static int REG_MULTI_SZ = 7; // Multiple + // Unicode + // strings + public final static int REG_RESOURCE_LIST = 8; // Resource + // list + // in + // the + // resource + // map + public final static int REG_FULL_RESOURCE_DESCRIPTOR = 9; // Resource + // list + // in + // the + // hardware + // description + public final static int REG_RESOURCE_REQUIREMENTS_LIST = 10; + + public final static int REG_OPTION_RESERVED = 0x00000000; // Parameter + // is + // reserved + public final static int REG_OPTION_NON_VOLATILE = 0x00000000; // Key + // is + // preserved + // when system is rebooted + public final static int REG_OPTION_VOLATILE = 0x00000001; // Key + // is + // not + // preserved + // when system is rebooted + public final static int REG_OPTION_CREATE_LINK = 0x00000002; // Created + // key + // is + // a + // symbolic link + public final static int REG_OPTION_BACKUP_RESTORE = 0x00000004; // open + // for + // backup + // or + // restore + // special access rules + // privilege required + public final static int REG_OPTION_OPEN_LINK = 0x00000008; // Open + // symbolic + // link + + /* + * YAJSW additions start here + */ + + public final static int GENERIC_READ = 0x80000000; + +} diff --git a/javaUtilities/yajsw/src/main/java/jnacontrib/jna/WINREG.java b/javaUtilities/yajsw/src/main/java/jnacontrib/jna/WINREG.java new file mode 100644 index 0000000000..9310abc99f --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/jnacontrib/jna/WINREG.java @@ -0,0 +1,22 @@ +/* + * WINREG.java + * + * Created on 17. August 2007, 14:32 + * + * To change this template, choose Tools | Template Manager + * and open the template in the editor. + */ + +package jnacontrib.jna; + +/** + * + * @author TB + */ +public interface WINREG +{ + public final static int HKEY_CLASSES_ROOT = 0x80000000; + public final static int HKEY_CURRENT_USER = 0x80000001; + public final static int HKEY_LOCAL_MACHINE = 0x80000002; + public final static int HKEY_USERS = 0x80000003; +} diff --git a/javaUtilities/yajsw/src/main/java/jnacontrib/jna/WINSVC.java b/javaUtilities/yajsw/src/main/java/jnacontrib/jna/WINSVC.java new file mode 100644 index 0000000000..72721a2b76 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/jnacontrib/jna/WINSVC.java @@ -0,0 +1,98 @@ +/* + * WINSVC.java + * + * Created on 8. August 2007, 15:07 + * + * To change this template, choose Tools | Template Manager + * and open the template in the editor. + */ + +package jnacontrib.jna; + +/** + * + * @author TB + */ +public interface WINSVC +{ + public final static int SERVICE_CONTROL_STOP = 0x00000001; + public final static int SERVICE_CONTROL_SHUTDOWN = 0x00000005; + + public final static int SERVICE_STOPPED = 0x00000001; + public final static int SERVICE_START_PENDING = 0x00000002; + public final static int SERVICE_STOP_PENDING = 0x00000003; + public final static int SERVICE_RUNNING = 0x00000004; + public final static int SERVICE_CONTINUE_PENDING = 0x00000005; + public final static int SERVICE_PAUSE_PENDING = 0x00000006; + public final static int SERVICE_PAUSED = 0x00000007; + + public final static int SERVICE_ACCEPT_STOP = 0x00000001; + public final static int SERVICE_ACCEPT_PAUSE_CONTINUE = 0x00000002; + public final static int SERVICE_ACCEPT_SHUTDOWN = 0x00000004; + public final static int SERVICE_ACCEPT_PARAMCHANGE = 0x00000008; + public final static int SERVICE_ACCEPT_NETBINDCHANGE = 0x00000010; + + public final static int SC_MANAGER_CONNECT = 0x0001; + public final static int SC_MANAGER_CREATE_SERVICE = 0x0002; + public final static int SC_MANAGER_ENUMERATE_SERVICE = 0x0004; + public final static int SC_MANAGER_LOCK = 0x0008; + public final static int SC_MANAGER_QUERY_LOCK_STATUS = 0x0010; + public final static int SC_MANAGER_MODIFY_BOOT_CONFIG = 0x0020; + + public final static int SC_MANAGER_ALL_ACCESS = WINNT.STANDARD_RIGHTS_REQUIRED | SC_MANAGER_CONNECT | SC_MANAGER_CREATE_SERVICE + | SC_MANAGER_ENUMERATE_SERVICE | SC_MANAGER_LOCK | SC_MANAGER_QUERY_LOCK_STATUS + | SC_MANAGER_MODIFY_BOOT_CONFIG; + + public final static int SERVICE_QUERY_CONFIG = 0x0001; + public final static int SERVICE_CHANGE_CONFIG = 0x0002; + public final static int SERVICE_QUERY_STATUS = 0x0004; + public final static int SERVICE_ENUMERATE_DEPENDENTS = 0x0008; + public final static int SERVICE_START = 0x0010; + public final static int SERVICE_STOP = 0x0020; + public final static int SERVICE_PAUSE_CONTINUE = 0x0040; + public final static int SERVICE_INTERROGATE = 0x0080; + public final static int SERVICE_USER_DEFINED_CONTROL = 0x0100; + + public final static int SERVICE_ALL_ACCESS = WINNT.STANDARD_RIGHTS_REQUIRED | SERVICE_QUERY_CONFIG | SERVICE_CHANGE_CONFIG + | SERVICE_QUERY_STATUS | SERVICE_ENUMERATE_DEPENDENTS | SERVICE_START + | SERVICE_STOP | SERVICE_PAUSE_CONTINUE | SERVICE_INTERROGATE + | SERVICE_USER_DEFINED_CONTROL; + + public final static int SERVICE_CONFIG_DESCRIPTION = 1; + public final static int SERVICE_CONFIG_FAILURE_ACTIONS = 2; + public final static int SERVICE_CONFIG_DELAYED_AUTO_START_INFO = 3; + + public final static int SERVICE_KERNEL_DRIVER = 0x00000001; + public final static int SERVICE_FILE_SYSTEM_DRIVER = 0x00000002; + public final static int SERVICE_ADAPTER = 0x00000004; + public final static int SERVICE_RECOGNIZER_DRIVER = 0x00000008; + + public final static int SERVICE_DRIVER = SERVICE_KERNEL_DRIVER | SERVICE_FILE_SYSTEM_DRIVER | SERVICE_RECOGNIZER_DRIVER; + + public final static int SERVICE_WIN32_OWN_PROCESS = 0x00000010; + public final static int SERVICE_WIN32_SHARE_PROCESS = 0x00000020; + public final static int SERVICE_WIN32 = SERVICE_WIN32_OWN_PROCESS | SERVICE_WIN32_SHARE_PROCESS; + + public final static int SERVICE_INTERACTIVE_PROCESS = 0x00000100; + + public final static int SERVICE_TYPE_ALL = SERVICE_WIN32 | SERVICE_ADAPTER | SERVICE_DRIVER | SERVICE_INTERACTIVE_PROCESS; + + public final static int SERVICE_BOOT_START = 0x00000000; + public final static int SERVICE_SYSTEM_START = 0x00000001; + public final static int SERVICE_AUTO_START = 0x00000002; + public final static int SERVICE_DEMAND_START = 0x00000003; + public final static int SERVICE_DISABLED = 0x00000004; + + public final static int SERVICE_ERROR_IGNORE = 0x00000000; + public final static int SERVICE_ERROR_NORMAL = 0x00000001; + public final static int SERVICE_ERROR_SEVERE = 0x00000002; + public final static int SERVICE_ERROR_CRITICAL = 0x00000003; + + /* + * YAJSW additions start here + */ + + public final static int SC_ENUM_PROCESS_INFO = 0; + public final static int SERVICE_STATE_ALL = 0x00000003; + +} diff --git a/javaUtilities/yajsw/src/main/java/jnacontrib/win32/Registry.java b/javaUtilities/yajsw/src/main/java/jnacontrib/win32/Registry.java new file mode 100644 index 0000000000..2b73944c9f --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/jnacontrib/win32/Registry.java @@ -0,0 +1,564 @@ +/* + * Registry.java + * + * Created on 17. August 2007, 15:07 + * + * To change this template, choose Tools | Template Manager + * and open the template in the editor. + */ + +package jnacontrib.win32; + +import java.io.UnsupportedEncodingException; +import java.util.Arrays; +import java.util.HashMap; +import java.util.TreeMap; +import java.util.TreeSet; + +import jnacontrib.jna.Advapi32; +import jnacontrib.jna.WINBASE; +import jnacontrib.jna.WINERROR; +import jnacontrib.jna.WINNT; +import jnacontrib.jna.WINREG; + +import com.sun.jna.ptr.IntByReference; + +/** + * Methods for accessing the Windows Registry. Only String and DWORD values + * supported at the moment. + */ +public class Registry +{ + public static enum REGISTRY_ROOT_KEY + { + CLASSES_ROOT, CURRENT_USER, LOCAL_MACHINE, USERS + }; + + private final static HashMap rootKeyMap = new HashMap(); + + static + { + rootKeyMap.put(REGISTRY_ROOT_KEY.CLASSES_ROOT, WINREG.HKEY_CLASSES_ROOT); + rootKeyMap.put(REGISTRY_ROOT_KEY.CURRENT_USER, WINREG.HKEY_CURRENT_USER); + rootKeyMap.put(REGISTRY_ROOT_KEY.LOCAL_MACHINE, WINREG.HKEY_LOCAL_MACHINE); + rootKeyMap.put(REGISTRY_ROOT_KEY.USERS, WINREG.HKEY_USERS); + } + + /** + * Testing. + * + * @param args + * arguments + * @throws java.lang.Exception + * on error + */ + public static void main(String[] args) throws Exception + { + } + + /** + * Gets one of the root keys. + * + * @param key + * key type + * @return root key + */ + private static int getRegistryRootKey(REGISTRY_ROOT_KEY key) + { + Advapi32 advapi32; + IntByReference pHandle; + int handle = 0; + + advapi32 = Advapi32.INSTANCE; + pHandle = new IntByReference(); + + if (advapi32.RegOpenKeyEx(rootKeyMap.get(key), null, 0, 0, pHandle) == WINERROR.ERROR_SUCCESS) + { + handle = pHandle.getValue(); + } + return (handle); + } + + /** + * Opens a key. + * + * @param rootKey + * root key + * @param subKeyName + * name of the key + * @param access + * access mode + * @return handle to the key or 0 + */ + private static int openKey(REGISTRY_ROOT_KEY rootKey, String subKeyName, int access) + { + Advapi32 advapi32; + IntByReference pHandle; + int rootKeyHandle; + + advapi32 = Advapi32.INSTANCE; + rootKeyHandle = getRegistryRootKey(rootKey); + pHandle = new IntByReference(); + + if (advapi32.RegOpenKeyEx(rootKeyHandle, subKeyName, 0, access, pHandle) == WINERROR.ERROR_SUCCESS) + { + return (pHandle.getValue()); + + } + else + { + return (0); + } + } + + /** + * Converts a Windows buffer to a Java String. + * + * @param buf + * buffer + * @throws java.io.UnsupportedEncodingException + * on error + * @return String + */ + private static String convertBufferToString(byte[] buf) throws UnsupportedEncodingException + { + return (new String(buf, 0, buf.length - 2, "UTF-16LE")); + } + + /** + * Converts a Windows buffer to an int. + * + * @param buf + * buffer + * @return int + */ + private static int convertBufferToInt(byte[] buf) + { + return (((int) (buf[0] & 0xff)) + (((int) (buf[1] & 0xff)) << 8) + (((int) (buf[2] & 0xff)) << 16) + (((int) (buf[3] & 0xff)) << 24)); + } + + /** + * Read a String value. + * + * @param rootKey + * root key + * @param subKeyName + * key name + * @param name + * value name + * @throws java.io.UnsupportedEncodingException + * on error + * @return String or null + */ + public static String getStringValue(REGISTRY_ROOT_KEY rootKey, String subKeyName, String name) throws UnsupportedEncodingException + { + Advapi32 advapi32; + IntByReference pType, lpcbData; + byte[] lpData = new byte[1]; + int handle = 0; + String ret = null; + + advapi32 = Advapi32.INSTANCE; + pType = new IntByReference(); + lpcbData = new IntByReference(); + handle = openKey(rootKey, subKeyName, WINNT.KEY_READ); + + if (handle != 0) + { + + if (advapi32.RegQueryValueEx(handle, name, null, pType, lpData, lpcbData) == WINERROR.ERROR_MORE_DATA) + { + lpData = new byte[lpcbData.getValue()]; + + if (advapi32.RegQueryValueEx(handle, name, null, pType, lpData, lpcbData) == WINERROR.ERROR_SUCCESS) + { + ret = convertBufferToString(lpData); + } + } + advapi32.RegCloseKey(handle); + } + return (ret); + } + + /** + * Read an int value. + * + * + * @return int or 0 + * @param rootKey + * root key + * @param subKeyName + * key name + * @param name + * value name + */ + public static int getIntValue(REGISTRY_ROOT_KEY rootKey, String subKeyName, String name) + { + Advapi32 advapi32; + IntByReference pType, lpcbData; + byte[] lpData = new byte[1]; + int handle = 0; + int ret = 0; + + advapi32 = Advapi32.INSTANCE; + pType = new IntByReference(); + lpcbData = new IntByReference(); + handle = openKey(rootKey, subKeyName, WINNT.KEY_READ); + + if (handle != 0) + { + + if (advapi32.RegQueryValueEx(handle, name, null, pType, lpData, lpcbData) == WINERROR.ERROR_MORE_DATA) + { + lpData = new byte[lpcbData.getValue()]; + + if (advapi32.RegQueryValueEx(handle, name, null, pType, lpData, lpcbData) == WINERROR.ERROR_SUCCESS) + { + ret = convertBufferToInt(lpData); + } + } + advapi32.RegCloseKey(handle); + } + return (ret); + } + + /** + * Delete a value. + * + * @param rootKey + * root key + * @param subKeyName + * key name + * @param name + * value name + * @return true on success + */ + public static boolean deleteValue(REGISTRY_ROOT_KEY rootKey, String subKeyName, String name) + { + Advapi32 advapi32; + int handle; + boolean ret = true; + + advapi32 = Advapi32.INSTANCE; + + handle = openKey(rootKey, subKeyName, WINNT.KEY_READ | WINNT.KEY_WRITE); + + if (handle != 0) + { + if (advapi32.RegDeleteValue(handle, name) == WINERROR.ERROR_SUCCESS) + { + ret = true; + } + advapi32.RegCloseKey(handle); + } + return (ret); + } + + /** + * Writes a String value. + * + * @param rootKey + * root key + * @param subKeyName + * key name + * @param name + * value name + * @param value + * value + * @throws java.io.UnsupportedEncodingException + * on error + * @return true on success + */ + public static boolean setStringValue(REGISTRY_ROOT_KEY rootKey, String subKeyName, String name, String value) throws UnsupportedEncodingException + { + Advapi32 advapi32; + int handle; + byte[] data; + boolean ret = false; + + data = Arrays.copyOf(value.getBytes("UTF-16LE"), value.length() * 2 + 2); + advapi32 = Advapi32.INSTANCE; + handle = openKey(rootKey, subKeyName, WINNT.KEY_READ | WINNT.KEY_WRITE); + + if (handle != 0) + { + if (advapi32.RegSetValueEx(handle, name, 0, WINNT.REG_SZ, data, data.length) == WINERROR.ERROR_SUCCESS) + { + ret = true; + } + advapi32.RegCloseKey(handle); + } + return (ret); + } + + /** + * Writes an int value. + * + * + * @return true on success + * @param rootKey + * root key + * @param subKeyName + * key name + * @param name + * value name + * @param value + * value + */ + public static boolean setIntValue(REGISTRY_ROOT_KEY rootKey, String subKeyName, String name, int value) + { + Advapi32 advapi32; + int handle; + byte[] data; + boolean ret = false; + + data = new byte[4]; + data[0] = (byte) (value & 0xff); + data[1] = (byte) ((value >> 8) & 0xff); + data[2] = (byte) ((value >> 16) & 0xff); + data[3] = (byte) ((value >> 24) & 0xff); + advapi32 = Advapi32.INSTANCE; + handle = openKey(rootKey, subKeyName, WINNT.KEY_READ | WINNT.KEY_WRITE); + + if (handle != 0) + { + + if (advapi32.RegSetValueEx(handle, name, 0, WINNT.REG_DWORD, data, data.length) == WINERROR.ERROR_SUCCESS) + { + ret = true; + } + advapi32.RegCloseKey(handle); + } + return (ret); + } + + /** + * Check for existence of a value. + * + * @param rootKey + * root key + * @param subKeyName + * key name + * @param name + * value name + * @return true if exists + */ + public static boolean valueExists(REGISTRY_ROOT_KEY rootKey, String subKeyName, String name) + { + Advapi32 advapi32; + IntByReference pType, lpcbData; + byte[] lpData = new byte[1]; + int handle = 0; + boolean ret = false; + + advapi32 = Advapi32.INSTANCE; + pType = new IntByReference(); + lpcbData = new IntByReference(); + handle = openKey(rootKey, subKeyName, WINNT.KEY_READ); + + if (handle != 0) + { + + if (advapi32.RegQueryValueEx(handle, name, null, pType, lpData, lpcbData) != WINERROR.ERROR_FILE_NOT_FOUND) + { + ret = true; + + } + else + { + ret = false; + } + advapi32.RegCloseKey(handle); + } + return (ret); + } + + /** + * Create a new key. + * + * @param rootKey + * root key + * @param parent + * name of parent key + * @param name + * key name + * @return true on success + */ + public static boolean createKey(REGISTRY_ROOT_KEY rootKey, String parent, String name) + { + Advapi32 advapi32; + IntByReference hkResult, dwDisposition; + int handle = 0; + boolean ret = false; + + advapi32 = Advapi32.INSTANCE; + hkResult = new IntByReference(); + dwDisposition = new IntByReference(); + handle = openKey(rootKey, parent, WINNT.KEY_READ); + + if (handle != 0) + { + + if (advapi32.RegCreateKeyEx(handle, name, 0, null, WINNT.REG_OPTION_NON_VOLATILE, WINNT.KEY_READ, null, hkResult, dwDisposition) == WINERROR.ERROR_SUCCESS) + { + ret = true; + advapi32.RegCloseKey(hkResult.getValue()); + + } + else + { + ret = false; + } + advapi32.RegCloseKey(handle); + } + return (ret); + } + + /** + * Delete a key. + * + * @param rootKey + * root key + * @param parent + * name of parent key + * @param name + * key name + * @return true on success + */ + public static boolean deleteKey(REGISTRY_ROOT_KEY rootKey, String parent, String name) + { + Advapi32 advapi32; + int handle = 0; + boolean ret = false; + + advapi32 = Advapi32.INSTANCE; + handle = openKey(rootKey, parent, WINNT.KEY_READ); + + if (handle != 0) + { + + if (advapi32.RegDeleteKey(handle, name) == WINERROR.ERROR_SUCCESS) + { + ret = true; + + } + else + { + ret = false; + } + advapi32.RegCloseKey(handle); + } + return (ret); + } + + /** + * Get all sub keys of a key. + * + * @param rootKey + * root key + * @param parent + * key name + * @return array with all sub key names + */ + public static String[] getSubKeys(REGISTRY_ROOT_KEY rootKey, String parent) + { + Advapi32 advapi32; + int handle = 0, dwIndex; + char[] lpName; + IntByReference lpcName; + WINBASE.FILETIME lpftLastWriteTime; + TreeSet subKeys = new TreeSet(); + + advapi32 = Advapi32.INSTANCE; + handle = openKey(rootKey, parent, WINNT.KEY_READ); + lpName = new char[256]; + lpcName = new IntByReference(256); + lpftLastWriteTime = new WINBASE.FILETIME(); + + if (handle != 0) + { + dwIndex = 0; + + while (advapi32.RegEnumKeyEx(handle, dwIndex, lpName, lpcName, null, null, null, lpftLastWriteTime) == WINERROR.ERROR_SUCCESS) + { + subKeys.add(new String(lpName, 0, lpcName.getValue())); + lpcName.setValue(256); + dwIndex++; + } + advapi32.RegCloseKey(handle); + } + + return (subKeys.toArray(new String[] + {})); + } + + /** + * Get all values under a key. + * + * @param rootKey + * root key + * @param key + * jey name + * @throws java.io.UnsupportedEncodingException + * on error + * @return TreeMap with name and value pairs + */ + public static TreeMap getValues(REGISTRY_ROOT_KEY rootKey, String key) throws UnsupportedEncodingException + { + Advapi32 advapi32; + int handle = 0, dwIndex, result = 0; + char[] lpValueName; + byte[] lpData; + IntByReference lpcchValueName, lpType, lpcbData; + String name; + TreeMap values = new TreeMap(String.CASE_INSENSITIVE_ORDER); + + advapi32 = Advapi32.INSTANCE; + handle = openKey(rootKey, key, WINNT.KEY_READ); + lpValueName = new char[16384]; + lpcchValueName = new IntByReference(16384); + lpType = new IntByReference(); + lpData = new byte[1]; + lpcbData = new IntByReference(); + + if (handle != 0) + { + dwIndex = 0; + + do + { + lpcbData.setValue(0); + result = advapi32.RegEnumValue(handle, dwIndex, lpValueName, lpcchValueName, null, lpType, lpData, lpcbData); + + if (result == WINERROR.ERROR_MORE_DATA) + { + lpData = new byte[lpcbData.getValue()]; + lpcchValueName = new IntByReference(16384); + result = advapi32.RegEnumValue(handle, dwIndex, lpValueName, lpcchValueName, null, lpType, lpData, lpcbData); + + if (result == WINERROR.ERROR_SUCCESS) + { + name = new String(lpValueName, 0, lpcchValueName.getValue()); + + switch (lpType.getValue()) + { + case WINNT.REG_SZ: + values.put(name, convertBufferToString(lpData)); + break; + case WINNT.REG_DWORD: + values.put(name, convertBufferToInt(lpData)); + break; + default: + break; + } + } + } + dwIndex++; + } + while (result == WINERROR.ERROR_SUCCESS); + + advapi32.RegCloseKey(handle); + } + return (values); + } +} diff --git a/javaUtilities/yajsw/src/main/java/jnacontrib/win32/Win32Service.java b/javaUtilities/yajsw/src/main/java/jnacontrib/win32/Win32Service.java new file mode 100644 index 0000000000..ec50e34f6b --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/jnacontrib/win32/Win32Service.java @@ -0,0 +1,841 @@ +/* + * Win32Service.java + * + * Created on 12. September 2007, 12:05 + * + * To change this template, choose Tools | Template Manager + * and open the template in the editor. + */ + +package jnacontrib.win32; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +import jnacontrib.jna.Advapi32; +import jnacontrib.jna.WINERROR; +import jnacontrib.jna.WINNT; +import jnacontrib.jna.WINSVC; +import jnacontrib.jna.Advapi32.ENUM_SERVICE_STATUS_PROCESS; +import jnacontrib.jna.Advapi32.QUERY_SERVICE_CONFIG; +import jnacontrib.jna.Advapi32.SERVICE_DESCRIPTION; +import jnacontrib.jna.Advapi32.SERVICE_FAILURE_ACTIONS; +import jnacontrib.jna.Advapi32.SERVICE_STATUS_PROCESS; + +import org.rzo.yajsw.os.Service; +import org.rzo.yajsw.os.ServiceInfo; +import org.rzo.yajsw.os.ServiceInfoImpl; +import org.rzo.yajsw.util.MyReentrantLock; + +import com.sun.jna.Memory; +import com.sun.jna.Native; +import com.sun.jna.PlatformEx; +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.Kernel32; +import com.sun.jna.platform.win32.Kernel32Util; +import com.sun.jna.ptr.IntByReference; + +/** + * Baseclass for a Win32 service. + */ +abstract public class Win32Service +{ + protected String serviceName; + private ServiceMain serviceMain; + private ServiceControl serviceControl; + private Pointer serviceStatusHandle; + protected Object waitObject = new Object(); + private int stopTimeout = 5000; + private int startupTimeout = 30000; + protected volatile int checkPoint = 0; + private boolean autoReportStartup = true; + private Lock startupLock = new MyReentrantLock(); + private Condition startupCondition = startupLock.newCondition(); + private volatile boolean _stopping = false; + public volatile String _stopReason = null; + private static int lastWinError = -1; + protected boolean _debug = false; + + + /** + * Creates a new instance of Win32Service. + * + * @param serviceName + * internal name of the service + */ + public Win32Service(String serviceName) + { + this.serviceName = serviceName; + } + + public Win32Service() + { + + } + + public void setServiceName(String serviceName) + { + this.serviceName = serviceName; + } + + public String getServiceName() + { + return serviceName; + } + + public void setDebug(boolean value) + { + _debug = value; + } + + /** + * Install the service. + * + * @param displayName + * visible name + * @param description + * description + * @param dependencies + * array of other services to depend on or null + * @param account + * service account or null for LocalSystem + * @param password + * password for service account or null + * @throws java.lang.Exception + * @return true on success + */ + public boolean install(String displayName, String description, String[] dependencies, String account, String password, boolean delayedAutostart) + { + return (install(displayName, description, dependencies, account, password, "java.exe -cp \"" + System.getProperty("java.class.path") + + "\" -Xrs " + this.getClass().getName(), "AUTO_START", false, null)); + } + + /** + * Install the service. + * + * @return true on success + * @param displayName + * visible name + * @param description + * description + * @param dependencies + * array of other services to depend on or null + * @param account + * service account or null for LocalSystem + * @param password + * password for service account or null + * @param command + * command line to start the service + * @throws java.lang.Exception + */ + public boolean install(String displayName, String description, String[] dependencies, String account, String password, String command, + String startType, boolean interactive, Object failureActions) + { + Advapi32 advapi32; + Advapi32.SERVICE_DESCRIPTION desc; + Pointer serviceManager, service; + boolean success = false; + String dep = ""; + + if (dependencies != null) + { + for (String s : dependencies) + { + dep += s + "\0"; + } + } + dep += "\0"; + + desc = new Advapi32.SERVICE_DESCRIPTION(); + desc.lpDescription = description; + + advapi32 = Advapi32.INSTANCE; + serviceManager = openServiceControlManager(null, WINSVC.SC_MANAGER_ALL_ACCESS); + + int winStartType = "DEMAND_START".equals(startType) ? WINSVC.SERVICE_DEMAND_START : WINSVC.SERVICE_AUTO_START; + int dwServiceType = WINSVC.SERVICE_WIN32_OWN_PROCESS; + if (interactive) + dwServiceType |= WINSVC.SERVICE_INTERACTIVE_PROCESS; + if (serviceManager != null) + { + System.out.println("service cmd: "+command); + service = advapi32.CreateService(serviceManager, serviceName, displayName, WINSVC.SERVICE_ALL_ACCESS, dwServiceType, winStartType, + WINSVC.SERVICE_ERROR_NORMAL, command, null, null, dep, account, password); + + if (service != null) + { + if (failureActions != null) + { + success = advapi32.ChangeServiceConfig2(service, WINSVC.SERVICE_CONFIG_FAILURE_ACTIONS, (SERVICE_FAILURE_ACTIONS)failureActions); + if (!success) + { + int err = Native.getLastError(); + System.out.println("ERROR Setting failure actions #"+err+ " "+Kernel32Util.formatMessageFromLastErrorCode(err)); + } + + } + success = advapi32.ChangeServiceConfig2(service, WINSVC.SERVICE_CONFIG_DESCRIPTION, desc); + if (PlatformEx.isWinVista() && "DELAYED_AUTO_START".equals(startType)) + { + Advapi32.SERVICE_DELAYED_AUTO_START_INFO delayedDesc = new Advapi32.SERVICE_DELAYED_AUTO_START_INFO(); + delayedDesc.fDelayedAutostart = true; + success = advapi32.ChangeServiceConfig2(service, WINSVC.SERVICE_CONFIG_DELAYED_AUTO_START_INFO, delayedDesc); + } + advapi32.CloseServiceHandle(service); + } + else + { + int err = Kernel32.INSTANCE.GetLastError(); + System.out.println("error during install " + err); + System.out.println(Kernel32Util.formatMessageFromLastErrorCode(err)); + } + + advapi32.CloseServiceHandle(serviceManager); + } + return (success); + } + + /** + * Uninstall the service. + * + * @throws java.lang.Exception + * @return true on success + */ + public boolean uninstall() + { + Advapi32 advapi32; + Pointer serviceManager, service; + boolean success = false; + + advapi32 = Advapi32.INSTANCE; + serviceManager = openServiceControlManager(null, WINSVC.SC_MANAGER_ALL_ACCESS); + + if (serviceManager != null) + { + service = advapi32.OpenService(serviceManager, serviceName, WINSVC.SERVICE_ALL_ACCESS); + + if (service != null) + { + success = advapi32.DeleteService(service); + advapi32.CloseServiceHandle(service); + } + advapi32.CloseServiceHandle(serviceManager); + } + return (success); + } + + public static ServiceInfo serviceInfo(String name) + { + ServiceInfoImpl result = new ServiceInfoImpl(); + result.setName(name); + + Advapi32 advapi32; + Pointer serviceManager, service; + int state = Service.STATE_UNKNOWN; + + advapi32 = Advapi32.INSTANCE; + + serviceManager = openServiceControlManager(null, WINNT.GENERIC_READ); + + if (serviceManager != null) + { + state = 0; + service = advapi32.OpenService(serviceManager, name, WINNT.GENERIC_READ); + + if (service != null) + { + IntByReference pcbBytesNeeded = new IntByReference(); + state |= Service.STATE_INSTALLED; + // get size required + if (!advapi32.QueryServiceConfig(service, null, 0, pcbBytesNeeded)) + { + // now get the data + int cbBufSize = pcbBytesNeeded.getValue(); + Memory buffer = new Memory(cbBufSize); + buffer.clear(); + if (advapi32.QueryServiceConfig(service, buffer, cbBufSize, pcbBytesNeeded)) + { + QUERY_SERVICE_CONFIG lpServiceConfig = new QUERY_SERVICE_CONFIG(); + lpServiceConfig.init(buffer); + if (lpServiceConfig.dwStartType == Advapi32.SERVICE_DISABLED) + state |= Service.STATE_DISABLED; + if (lpServiceConfig.dwStartType == Advapi32.SERVICE_BOOT_START | lpServiceConfig.dwStartType == Advapi32.SERVICE_SYSTEM_START + | lpServiceConfig.dwStartType == Advapi32.SERVICE_AUTO_START) + state |= Service.STATE_AUTOMATIC; + if (lpServiceConfig.dwStartType == Advapi32.SERVICE_DEMAND_START) + state |= Service.STATE_MANUAL; + if ((lpServiceConfig.dwServiceType & Advapi32.SERVICE_INTERACTIVE_PROCESS) != 0) + state |= Service.STATE_INTERACTIVE; + result.setAccount(lpServiceConfig.lpServiceStartName); + result.setCommand(lpServiceConfig.lpBinaryPathName); + result.setDependencies(lpServiceConfig.getDependencies()); + result.setDisplayName(lpServiceConfig.lpDisplayName); + + } + else + { + state |= Service.STATE_UNKNOWN; + System.out.println("Error in QueryServiceConfig: " + Native.getLastError()); + } + } + else + { + state |= Service.STATE_UNKNOWN; + System.out.println("Error in QueryServiceConfig: " + Native.getLastError()); + } + if (!advapi32.QueryServiceStatusEx(service, (byte) advapi32.SC_STATUS_PROCESS_INFO, null, 0, pcbBytesNeeded)) + { + // now get the data + int cbBufSize = pcbBytesNeeded.getValue(); + Memory buffer = new Memory(cbBufSize); + buffer.clear(); + if (advapi32.QueryServiceStatusEx(service, (byte) advapi32.SC_STATUS_PROCESS_INFO, buffer, cbBufSize, pcbBytesNeeded)) + { + SERVICE_STATUS_PROCESS lpBuffer = new SERVICE_STATUS_PROCESS(); + lpBuffer.init(buffer); + if (lpBuffer.dwCurrentState == advapi32.SERVICE_RUNNING) + state |= Service.STATE_RUNNING; + if (lpBuffer.dwCurrentState == advapi32.SERVICE_PAUSED) + state |= Service.STATE_PAUSED; + if (lpBuffer.dwCurrentState == advapi32.SERVICE_START_PENDING) + state |= Service.STATE_STARTING; + if (lpBuffer.dwCurrentState == advapi32.SERVICE_STOP_PENDING) + state |= Service.STATE_STOPPING; + result.setPid(lpBuffer.dwProcessId); + } + else + { + state |= Service.STATE_UNKNOWN; + System.out.println("Error in QueryServiceStatusEx: " + Native.getLastError()); + } + } + if (!advapi32.QueryServiceConfig2(service, (byte) advapi32.SERVICE_CONFIG_DESCRIPTION, null, 0, pcbBytesNeeded)) + { + // now get the data + int cbBufSize = pcbBytesNeeded.getValue(); + Memory buffer = new Memory(cbBufSize); + buffer.clear(); + if (advapi32.QueryServiceConfig2(service, (byte) advapi32.SERVICE_CONFIG_DESCRIPTION, buffer, cbBufSize, pcbBytesNeeded)) + { + SERVICE_DESCRIPTION lpBuffer = new SERVICE_DESCRIPTION(); + lpBuffer.init(buffer); + result.setDescription(lpBuffer.lpDescription); + } + else + { + state |= Service.STATE_UNKNOWN; + System.out.println("Error in QueryServiceStatusEx: " + Native.getLastError()); + } + } + + else + { + state |= Service.STATE_UNKNOWN; + System.out.println("Error in QueryServiceStatusEx: " + Native.getLastError()); + } + + advapi32.CloseServiceHandle(service); + } + advapi32.CloseServiceHandle(serviceManager); + } + result.setState(state); + + return result; + + } + + public int state() + { + Advapi32 advapi32; + Pointer serviceManager, service; + int result = Service.STATE_UNKNOWN; + + advapi32 = Advapi32.INSTANCE; + + serviceManager = openServiceControlManager(null, WINNT.GENERIC_READ); + // System.out.println("Win32Service.state() serviceManager "+serviceManager); + + if (serviceManager != null) + { + result = 0; + service = advapi32.OpenService(serviceManager, serviceName, WINNT.GENERIC_READ); + // System.out.println("Win32Service.state() service "+service); + + if (service != null) + { + IntByReference pcbBytesNeeded = new IntByReference(); + result |= Service.STATE_INSTALLED; + // get size required + if (!advapi32.QueryServiceConfig(service, null, 0, pcbBytesNeeded)) + { + // now get the data + int cbBufSize = pcbBytesNeeded.getValue(); + if (cbBufSize > 8192) + cbBufSize = 8192; + Memory buffer = new Memory(cbBufSize); + buffer.clear(); + if (advapi32.QueryServiceConfig(service, buffer, cbBufSize, pcbBytesNeeded)) + { + QUERY_SERVICE_CONFIG lpServiceConfig = new QUERY_SERVICE_CONFIG(); + lpServiceConfig.init(buffer); + if (lpServiceConfig.dwStartType == Advapi32.SERVICE_DISABLED) + result |= Service.STATE_DISABLED; + if (lpServiceConfig.dwStartType == Advapi32.SERVICE_BOOT_START | lpServiceConfig.dwStartType == Advapi32.SERVICE_SYSTEM_START + | lpServiceConfig.dwStartType == Advapi32.SERVICE_AUTO_START) + result |= Service.STATE_AUTOMATIC; + if (lpServiceConfig.dwStartType == Advapi32.SERVICE_DEMAND_START) + result |= Service.STATE_MANUAL; + if ((lpServiceConfig.dwServiceType & Advapi32.SERVICE_INTERACTIVE_PROCESS) != 0) + result |= Service.STATE_INTERACTIVE; + + } + else + { + result |= Service.STATE_UNKNOWN; + int error = Native.getLastError(); + System.out.println("Error getting buffer size in QueryServiceConfig: " + error + " " + Kernel32Util.formatMessageFromLastErrorCode(error)); + } + } + else + { + result |= Service.STATE_UNKNOWN; + int error = Native.getLastError(); + System.out.println("Error in QueryServiceConfig: " + error + " " + Kernel32Util.formatMessageFromLastErrorCode(error)); + } + if (!advapi32.QueryServiceStatusEx(service, (byte) advapi32.SC_STATUS_PROCESS_INFO, null, 0, pcbBytesNeeded)) + { + // now get the data + int cbBufSize = pcbBytesNeeded.getValue(); + Memory buffer = new Memory(cbBufSize); + buffer.clear(); + if (advapi32.QueryServiceStatusEx(service, (byte) advapi32.SC_STATUS_PROCESS_INFO, buffer, cbBufSize, pcbBytesNeeded)) + { + SERVICE_STATUS_PROCESS lpBuffer = new SERVICE_STATUS_PROCESS(); + lpBuffer.init(buffer); + if (lpBuffer.dwCurrentState == advapi32.SERVICE_RUNNING) + result |= Service.STATE_RUNNING; + if (lpBuffer.dwCurrentState == advapi32.SERVICE_PAUSED) + result |= Service.STATE_PAUSED; + // System.out.println("Win32Service.state() dwCurrentState "+lpBuffer.dwCurrentState); + + } + else + { + result |= Service.STATE_UNKNOWN; + int error = Native.getLastError(); + System.out.println("Error getting buffer size in QueryServiceStatusEx: " + error + " " + Kernel32Util.formatMessageFromLastErrorCode(error)); + } + } + else + { + result |= Service.STATE_UNKNOWN; + int error = Native.getLastError(); + System.out.println("Error in QueryServiceStatusEx: " + error + " " + Kernel32Util.formatMessageFromLastErrorCode(error)); + } + + advapi32.CloseServiceHandle(service); + } + advapi32.CloseServiceHandle(serviceManager); + } + + return result; + + } + + /** + * Ask the ServiceControlManager to start the service. + * + * @return true on success + */ + public boolean start() + { + Advapi32 advapi32; + Pointer serviceManager, service; + boolean success = false; + + advapi32 = Advapi32.INSTANCE; + + serviceManager = openServiceControlManager(null, WINNT.GENERIC_EXECUTE); + // System.out.println("service.start() serviceManager "+serviceManager); + + if (serviceManager != null) + { + service = advapi32.OpenService(serviceManager, serviceName, WINNT.GENERIC_EXECUTE); + // System.out.println("service.start() service "+service); + + if (service != null) + { + success = advapi32.StartService(service, 0, null); + // System.out.println("service.start() StartService "+success); + advapi32.CloseServiceHandle(service); + } + advapi32.CloseServiceHandle(serviceManager); + } + + return (success); + } + + /** + * Ask the ServiceControlManager to stop the service. + * + * @return true on success + */ + public boolean stop() throws Exception + { + Advapi32 advapi32; + Pointer serviceManager, service; + Advapi32.SERVICE_STATUS serviceStatus; + boolean success = false; + + advapi32 = Advapi32.INSTANCE; + + serviceManager = openServiceControlManager(null, WINNT.GENERIC_EXECUTE); + + if (serviceManager != null) + { + service = advapi32.OpenService(serviceManager, serviceName, WINNT.GENERIC_EXECUTE); + + if (service != null) + { + serviceStatus = new Advapi32.SERVICE_STATUS(); + success = advapi32.ControlService(service, WINSVC.SERVICE_CONTROL_STOP, serviceStatus); + advapi32.CloseServiceHandle(service); + } + advapi32.CloseServiceHandle(serviceManager); + } + + return (success); + } + + /** + * Initialize the service, connect to the ServiceControlManager. + */ + public void init() + { + Advapi32 advapi32; + // Advapi32.SERVICE_TABLE_ENTRY[] entries = new + // Advapi32.SERVICE_TABLE_ENTRY[2]; + Advapi32.SERVICE_TABLE_ENTRY entry; + + serviceMain = new ServiceMain(); + advapi32 = Advapi32.INSTANCE; + entry = new Advapi32.SERVICE_TABLE_ENTRY(); + entry.size(); + entry.lpServiceName = serviceName; + entry.lpServiceProc = serviceMain; + entry.write(); + + if (!advapi32.StartServiceCtrlDispatcher(entry.toArray(2))) + { + log("error in StartServiceCtrlDispatcher"); + int err = Native.getLastError(); + lastWinError = err; + log(err + ":" + Kernel32Util.formatMessageFromLastErrorCode(err)); + } + } + + public void setStopTimeout(int t) + { + stopTimeout = t; + } + + public int getStopTimeout() + { + return stopTimeout; + } + + public void reportStartup() + { + reportStatus(WINSVC.SERVICE_RUNNING, WINERROR.NO_ERROR, 0); + + onStart(); + + try + { + synchronized (waitObject) + { + waitObject.wait(); + } + } + catch (InterruptedException ex) + { + } + reportStatus(WINSVC.SERVICE_STOPPED, WINERROR.NO_ERROR, 0); + + // Avoid returning from ServiceMain, which will cause a crash + // See http://support.microsoft.com/kb/201349, which recommends + // having init() wait for this thread. + // Waiting on this thread in init() won't fix the crash, though. + // System.exit(0); + + } + + /** + * Get a handle to the ServiceControlManager. + * + * @param machine + * name of the machine or null for localhost + * @param access + * access flags + * @return handle to ServiceControlManager or null when failed + */ + static private Pointer openServiceControlManager(String machine, int access) + { + Pointer handle = null; + Advapi32 advapi32; + + advapi32 = Advapi32.INSTANCE; + handle = advapi32.OpenSCManager(machine, null, access); + if (handle == null) + { + int err = Native.getLastError(); + lastWinError = err; + System.out.println("Error in OpenSCManager: " + Integer.toHexString(err)); + if (err == 5) + System.out.println("Access denied: please check the user credentials"); + } + + return (handle); + } + + static public Map enumerateServices(String machine) + { + Map result = new HashMap(); + // Open the Service Control Manager + Pointer sc = openServiceControlManager(machine, WINSVC.SC_MANAGER_ENUMERATE_SERVICE); + + // Check if OpenSCManager returns NULL. Otherwise proceed + if (sc != null && !sc.equals(null)) + { + Memory service_data = null; + int service_data_size = 0; + int infoLevel = WINSVC.SC_ENUM_PROCESS_INFO; + boolean retVal; + IntByReference bytesNeeded = new IntByReference(0); + IntByReference srvCount = new IntByReference(0); + IntByReference resumeHandle = new IntByReference(0); + int srvType = WINSVC.SERVICE_WIN32; + int srvState = WINSVC.SERVICE_STATE_ALL; + + // Call EnumServicesStatus with null data and data_size == 0, so we + // get the required memory size + retVal = Advapi32.INSTANCE.EnumServicesStatusExW(sc, infoLevel, srvType, srvState, service_data, service_data_size, bytesNeeded, + srvCount, resumeHandle, null); + + int err = Native.getLastError(); + // EnumServicesStatus should need more memory space + if ((!retVal) || err == WINERROR.ERROR_MORE_DATA) + { + int bytesCount = bytesNeeded.getValue(); + service_data = new Memory(bytesCount); + service_data.clear(); + service_data_size = bytesCount; + // System.out.println(resumeHandle.getValue()); + resumeHandle.setValue(0); + retVal = Advapi32.INSTANCE.EnumServicesStatusExW(sc, infoLevel, srvType, srvState, service_data, service_data_size, bytesNeeded, + srvCount, resumeHandle, null); + if (!retVal) + { + err = Native.getLastError(); + System.out.println("Error in EnumServicesStatusExA " + Integer.toHexString(err)); + return null; + } + } + else + return null; + + ENUM_SERVICE_STATUS_PROCESS serviceStatus = new ENUM_SERVICE_STATUS_PROCESS(); + serviceStatus.init(service_data); + for (int i = 0; i < srvCount.getValue(); i++) + { + result.put(serviceStatus.getServiceName().toLowerCase(), serviceStatus); + serviceStatus = serviceStatus.next(); + } + } + + // Close the SC_HANLDE returned by OpenSCManager + Advapi32.INSTANCE.CloseServiceHandle(sc); + + return result; + } + + /** + * Report service status to the ServiceControlManager. + * + * @param status + * status + * @param win32ExitCode + * exit code + * @param waitHint + * time to wait + */ + protected void reportStatus(int status, int win32ExitCode, int waitHint) + { + Advapi32 advapi32; + Advapi32.SERVICE_STATUS serviceStatus; + + advapi32 = Advapi32.INSTANCE; + serviceStatus = new Advapi32.SERVICE_STATUS(); + serviceStatus.dwServiceType = WINNT.SERVICE_WIN32_OWN_PROCESS; + serviceStatus.dwControlsAccepted = WINSVC.SERVICE_ACCEPT_STOP | WINSVC.SERVICE_ACCEPT_SHUTDOWN; + serviceStatus.dwWin32ExitCode = win32ExitCode; + serviceStatus.dwWaitHint = waitHint; + serviceStatus.dwCurrentState = status; + serviceStatus.dwCheckPoint = checkPoint; + log("reporting status " + checkPoint); + + advapi32.SetServiceStatus(serviceStatusHandle, serviceStatus); + } + + /** + * Called when service is starting. + */ + public abstract void onStart(); + + /* + * Called when service should stop. + */ + public abstract void onStop(); + + public abstract void log(String txt); + + /** + * Implementation of the service main function. + */ + private class ServiceMain implements Advapi32.SERVICE_MAIN_FUNCTION + { + + /** + * Called when the service is starting. + * + * @param dwArgc + * number of arguments + * @param lpszArgv + * pointer to arguments + */ + public void callback(int dwArgc, Pointer lpszArgv) + { + Advapi32 advapi32; + + advapi32 = Advapi32.INSTANCE; + + log("+ ServiceMain callback"); + serviceControl = new ServiceControl(); + serviceStatusHandle = advapi32.RegisterServiceCtrlHandlerEx(serviceName, serviceControl, null); + + // if we are waiting for application to report startup + if (!autoReportStartup) + try + { + // report the startup time + reportStatus(WINSVC.SERVICE_START_PENDING, WINERROR.NO_ERROR, startupTimeout); + // wait for application to send startup notification + startupLock.lock(); + if (!startupCondition.await(startupTimeout, TimeUnit.MILLISECONDS)) + { + log("service startup timeout -> aborting"); + System.exit(999); + } + } + catch (InterruptedException e) + { + e.printStackTrace(); + } + finally + { + startupLock.unlock(); + } + else + // if we are not waiting for the application, give us at most 5 + // seconds to report startup + reportStatus(WINSVC.SERVICE_START_PENDING, WINERROR.NO_ERROR, 5000); + + // this method will hang until the service terminates + reportStartup(); + + } + } + + /** + * Implementation of the service control function. + */ + private class ServiceControl implements Advapi32.HandlerEx + { + + /** + * Called when the service get a control code. + * + * @param dwControl + * @param dwEventType + * @param lpEventData + * @param lpContext + */ + public int callback(int dwControl, int dwEventType, Pointer lpEventData, Pointer lpContext) + { + log("received service control " + dwControl); + switch (dwControl) + { + case WINSVC.SERVICE_CONTROL_STOP: + case WINSVC.SERVICE_CONTROL_SHUTDOWN: + checkPoint = 1; + reportStatus(WINSVC.SERVICE_STOP_PENDING, WINERROR.NO_ERROR, stopTimeout); + _stopping = true; + if (dwControl == WINSVC.SERVICE_CONTROL_STOP) + _stopReason = "SERVICE"; + if (dwControl == WINSVC.SERVICE_CONTROL_SHUTDOWN) + _stopReason = "COMPUTER"; + onStop(); + } + return WINERROR.NO_ERROR; + } + } + + public int getStartupTimeout() + { + return startupTimeout; + } + + public boolean isAutoReportStartup() + { + return autoReportStartup; + } + + public void setStartupTimeout(int startupTimeout) + { + this.startupTimeout = startupTimeout; + } + + public void setAutoReportStartup(boolean autoReportStartup) + { + this.autoReportStartup = autoReportStartup; + } + + public void notifyStartup() + { + try + { + startupLock.lock(); + startupCondition.signal(); + } + finally + { + startupLock.unlock(); + } + } + + public void signalStopping(long waitHint) + { + if (_stopping) + reportStatus(WINSVC.SERVICE_STOP_PENDING, WINERROR.NO_ERROR, (int)waitHint); + } + + public boolean requestElevation() + { + return lastWinError == 5; + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/apache/commons/configuration/AbstractConfiguration.java b/javaUtilities/yajsw/src/main/java/org/apache/commons/configuration/AbstractConfiguration.java new file mode 100644 index 0000000000..ac2ac82354 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/apache/commons/configuration/AbstractConfiguration.java @@ -0,0 +1,1331 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.configuration; + +import java.lang.reflect.Array; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.NoSuchElementException; +import java.util.Properties; + +import org.apache.commons.configuration.event.ConfigurationErrorEvent; +import org.apache.commons.configuration.event.ConfigurationErrorListener; +import org.apache.commons.configuration.event.EventSource; +import org.apache.commons.configuration.interpol.ConfigurationInterpolator; +import org.apache.commons.lang.BooleanUtils; +import org.apache.commons.lang.ClassUtils; +import org.apache.commons.lang.ObjectUtils; +import org.apache.commons.lang.text.StrLookup; +import org.apache.commons.lang.text.StrSubstitutor; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.impl.NoOpLog; + +/** + *

    Abstract configuration class. Provides basic functionality but does not + * store any data.

    + *

    If you want to write your own Configuration class then you should + * implement only abstract methods from this class. A lot of functionality + * needed by typical implementations of the {@code Configuration} + * interface is already provided by this base class. Following is a list of + * features implemented here: + *

    • Data conversion support. The various data types required by the + * {@code Configuration} interface are already handled by this base class. + * A concrete sub class only needs to provide a generic {@code getProperty()} + * method.
    • + *
    • Support for variable interpolation. Property values containing special + * variable tokens (like ${var}) will be replaced by their + * corresponding values.
    • + *
    • Support for string lists. The values of properties to be added to this + * configuration are checked whether they contain a list delimiter character. If + * this is the case and if list splitting is enabled, the string is split and + * multiple values are added for this property. (With the + * {@code setListDelimiter()} method the delimiter character can be + * specified; per default a comma is used. The + * {@code setDelimiterParsingDisabled()} method can be used to disable + * list splitting completely.)
    • + *
    • Allows to specify how missing properties are treated. Per default the + * get methods returning an object will return null if the searched + * property key is not found (and no default value is provided). With the + * {@code setThrowExceptionOnMissing()} method this behavior can be + * changed to throw an exception when a requested property cannot be found.
    • + *
    • Basic event support. Whenever this configuration is modified registered + * event listeners are notified. Refer to the various {@code EVENT_XXX} + * constants to get an impression about which event types are supported.
    • + *

    + * + * @author Konstantin Shaposhnikov + * @author Henning P. Schmiedehausen + * @version $Id: AbstractConfiguration.java 1234985 2012-01-23 21:09:09Z oheger $ + */ +public abstract class AbstractConfiguration extends EventSource implements Configuration +{ + /** + * Constant for the add property event type. + * @since 1.3 + */ + public static final int EVENT_ADD_PROPERTY = 1; + + /** + * Constant for the clear property event type. + * @since 1.3 + */ + public static final int EVENT_CLEAR_PROPERTY = 2; + + /** + * Constant for the set property event type. + * @since 1.3 + */ + public static final int EVENT_SET_PROPERTY = 3; + + /** + * Constant for the clear configuration event type. + * @since 1.3 + */ + public static final int EVENT_CLEAR = 4; + + /** + * Constant for the get property event type. This event type is used for + * error events. + * @since 1.4 + */ + public static final int EVENT_READ_PROPERTY = 5; + + /** start token */ + protected static final String START_TOKEN = "${"; + + /** end token */ + protected static final String END_TOKEN = "}"; + + /** + * Constant for the disabled list delimiter. This character is passed to the + * list parsing methods if delimiter parsing is disabled. So this character + * should not occur in string property values. + */ + private static final char DISABLED_DELIMITER = '\0'; + + /** The default value for listDelimiter */ + private static char defaultListDelimiter = ','; + + /** Delimiter used to convert single values to lists */ + private char listDelimiter = defaultListDelimiter; + + /** + * When set to true the given configuration delimiter will not be used + * while parsing for this configuration. + */ + private boolean delimiterParsingDisabled; + + /** + * Whether the configuration should throw NoSuchElementExceptions or simply + * return null when a property does not exist. Defaults to return null. + */ + private boolean throwExceptionOnMissing; + + /** Stores a reference to the object that handles variable interpolation.*/ + private StrSubstitutor substitutor; + + /** Stores the logger.*/ + private Log log; + + /** + * Creates a new instance of {@code AbstractConfiguration}. + */ + public AbstractConfiguration() + { + setLogger(null); + } + + /** + * For configurations extending AbstractConfiguration, allow them to change + * the listDelimiter from the default comma (","). This value will be used + * only when creating new configurations. Those already created will not be + * affected by this change + * + * @param delimiter The new listDelimiter + */ + public static void setDefaultListDelimiter(char delimiter) + { + AbstractConfiguration.defaultListDelimiter = delimiter; + } + + /** + * Sets the default list delimiter. + * + * @param delimiter the delimiter character + * @deprecated Use AbstractConfiguration.setDefaultListDelimiter(char) + * instead + */ + @Deprecated + public static void setDelimiter(char delimiter) + { + setDefaultListDelimiter(delimiter); + } + + /** + * Retrieve the current delimiter. By default this is a comma (","). + * + * @return The delimiter in use + */ + public static char getDefaultListDelimiter() + { + return AbstractConfiguration.defaultListDelimiter; + } + + /** + * Returns the default list delimiter. + * + * @return the default list delimiter + * @deprecated Use AbstractConfiguration.getDefaultListDelimiter() instead + */ + @Deprecated + public static char getDelimiter() + { + return getDefaultListDelimiter(); + } + + /** + * Change the list delimiter for this configuration. + * + * Note: this change will only be effective for new parsings. If you + * want it to take effect for all loaded properties use the no arg constructor + * and call this method before setting the source. + * + * @param listDelimiter The new listDelimiter + */ + public void setListDelimiter(char listDelimiter) + { + this.listDelimiter = listDelimiter; + } + + /** + * Retrieve the delimiter for this configuration. The default + * is the value of defaultListDelimiter. + * + * @return The listDelimiter in use + */ + public char getListDelimiter() + { + return listDelimiter; + } + + /** + * Determine if this configuration is using delimiters when parsing + * property values to convert them to lists of values. Defaults to false + * @return true if delimiters are not being used + */ + public boolean isDelimiterParsingDisabled() + { + return delimiterParsingDisabled; + } + + /** + * Set whether this configuration should use delimiters when parsing + * property values to convert them to lists of values. By default delimiter + * parsing is enabled + * + * Note: this change will only be effective for new parsings. If you + * want it to take effect for all loaded properties use the no arg constructor + * and call this method before setting source. + * @param delimiterParsingDisabled a flag whether delimiter parsing should + * be disabled + */ + public void setDelimiterParsingDisabled(boolean delimiterParsingDisabled) + { + this.delimiterParsingDisabled = delimiterParsingDisabled; + } + + /** + * Allows to set the {@code throwExceptionOnMissing} flag. This + * flag controls the behavior of property getter methods that return + * objects if the requested property is missing. If the flag is set to + * false (which is the default value), these methods will return + * null. If set to true, they will throw a + * {@code NoSuchElementException} exception. Note that getter methods + * for primitive data types are not affected by this flag. + * + * @param throwExceptionOnMissing The new value for the property + */ + public void setThrowExceptionOnMissing(boolean throwExceptionOnMissing) + { + this.throwExceptionOnMissing = throwExceptionOnMissing; + } + + /** + * Returns true if missing values throw Exceptions. + * + * @return true if missing values throw Exceptions + */ + public boolean isThrowExceptionOnMissing() + { + return throwExceptionOnMissing; + } + + /** + * Returns the object that is responsible for variable interpolation. + * + * @return the object responsible for variable interpolation + * @since 1.4 + */ + public synchronized StrSubstitutor getSubstitutor() + { + if (substitutor == null) + { + substitutor = new StrSubstitutor(createInterpolator()); + } + return substitutor; + } + + /** + * Returns the {@code ConfigurationInterpolator} object that manages + * the lookup objects for resolving variables. Note: If this + * object is manipulated (e.g. new lookup objects added), synchronization + * has to be manually ensured. Because + * {@code ConfigurationInterpolator} is not thread-safe concurrent + * access to properties of this configuration instance (which causes the + * interpolator to be invoked) may cause race conditions. + * + * @return the {@code ConfigurationInterpolator} associated with this + * configuration + * @since 1.4 + */ + public ConfigurationInterpolator getInterpolator() + { + return (ConfigurationInterpolator) getSubstitutor() + .getVariableResolver(); + } + + /** + * Creates the interpolator object that is responsible for variable + * interpolation. This method is invoked on first access of the + * interpolation features. It creates a new instance of + * {@code ConfigurationInterpolator} and sets the default lookup + * object to an implementation that queries this configuration. + * + * @return the newly created interpolator object + * @since 1.4 + */ + protected ConfigurationInterpolator createInterpolator() + { + ConfigurationInterpolator interpol = new ConfigurationInterpolator(); + interpol.setDefaultLookup(new StrLookup() + { + @Override + public String lookup(String var) + { + Object prop = resolveContainerStore(var); + return (prop != null) ? prop.toString() : null; + } + }); + return interpol; + } + + /** + * Returns the logger used by this configuration object. + * + * @return the logger + * @since 1.4 + */ + public Log getLogger() + { + return log; + } + + /** + * Allows to set the logger to be used by this configuration object. This + * method makes it possible for clients to exactly control logging behavior. + * Per default a logger is set that will ignore all log messages. Derived + * classes that want to enable logging should call this method during their + * initialization with the logger to be used. + * + * @param log the new logger + * @since 1.4 + */ + public void setLogger(Log log) + { + this.log = (log != null) ? log : new NoOpLog(); + } + + /** + * Adds a special + * {@link org.apache.commons.configuration.event.ConfigurationErrorListener} + * object to this configuration that will log all internal errors. This + * method is intended to be used by certain derived classes, for which it is + * known that they can fail on property access (e.g. + * {@code DatabaseConfiguration}). + * + * @since 1.4 + */ + public void addErrorLogListener() + { + addErrorListener(new ConfigurationErrorListener() + { + public void configurationError(ConfigurationErrorEvent event) + { + getLogger().warn("Internal error", event.getCause()); + } + }); + } + + public void addProperty(String key, Object value) + { + fireEvent(EVENT_ADD_PROPERTY, key, value, true); + addPropertyValues(key, value, + isDelimiterParsingDisabled() ? DISABLED_DELIMITER + : getListDelimiter()); + fireEvent(EVENT_ADD_PROPERTY, key, value, false); + } + + /** + * Adds a key/value pair to the Configuration. Override this method to + * provide write access to underlying Configuration store. + * + * @param key key to use for mapping + * @param value object to store + */ + protected abstract void addPropertyDirect(String key, Object value); + + /** + * Adds the specified value for the given property. This method supports + * single values and containers (e.g. collections or arrays) as well. In the + * latter case, {@code addPropertyDirect()} will be called for each + * element. + * + * @param key the property key + * @param value the value object + * @param delimiter the list delimiter character + */ + private void addPropertyValues(String key, Object value, char delimiter) + { + Iterator it = PropertyConverter.toIterator(value, delimiter); + while (it.hasNext()) + { + addPropertyDirect(key, it.next()); + } + } + + /** + * interpolate key names to handle ${key} stuff + * + * @param base string to interpolate + * + * @return returns the key name with the ${key} substituted + */ + protected String interpolate(String base) + { + Object result = interpolate((Object) base); + return (result == null) ? null : result.toString(); + } + + /** + * Returns the interpolated value. Non String values are returned without + * change. + * + * @param value + * the value to interpolate + * + * @return returns the value with variables substituted + */ + protected Object interpolate(Object value) + { + if (interpolator == null) + return PropertyConverter.interpolate(value, this); + else + { + try + { + return interpolator.interpolate(value); + } + catch (Exception e) + { + e.printStackTrace(); + return PropertyConverter.interpolate(value, this); + } + } + } + + /** + * Recursive handler for multple levels of interpolation. + * + * When called the first time, priorVariables should be null. + * + * @param base string with the ${key} variables + * @param priorVariables serves two purposes: to allow checking for loops, + * and creating a meaningful exception message should a loop occur. It's + * 0'th element will be set to the value of base from the first call. All + * subsequent interpolated variables are added afterward. + * + * @return the string with the interpolation taken care of + * @deprecated Interpolation is now handled by + * {@link PropertyConverter}; this method will no longer be + * called + */ + @Deprecated + protected String interpolateHelper(String base, List priorVariables) + { + return base; // just a dummy implementation + } + + public Configuration subset(String prefix) + { + return new SubsetConfiguration(this, prefix, "."); + } + + public void setProperty(String key, Object value) + { + fireEvent(EVENT_SET_PROPERTY, key, value, true); + setDetailEvents(false); + try + { + clearProperty(key); + addProperty(key, value); + } + finally + { + setDetailEvents(true); + } + fireEvent(EVENT_SET_PROPERTY, key, value, false); + } + + /** + * Removes the specified property from this configuration. This + * implementation performs some preparations and then delegates to + * {@code clearPropertyDirect()}, which will do the real work. + * + * @param key the key to be removed + */ + public void clearProperty(String key) + { + fireEvent(EVENT_CLEAR_PROPERTY, key, null, true); + clearPropertyDirect(key); + fireEvent(EVENT_CLEAR_PROPERTY, key, null, false); + } + + /** + * Removes the specified property from this configuration. This method is + * called by {@code clearProperty()} after it has done some + * preparations. It should be overridden in sub classes. This base + * implementation is just left empty. + * + * @param key the key to be removed + */ + protected void clearPropertyDirect(String key) + { + // override in sub classes + } + + public void clear() + { + fireEvent(EVENT_CLEAR, null, null, true); + setDetailEvents(false); + boolean useIterator = true; + try + { + Iterator it = getKeys(); + while (it.hasNext()) + { + String key = it.next(); + if (useIterator) + { + try + { + it.remove(); + } + catch (UnsupportedOperationException usoex) + { + useIterator = false; + } + } + + if (useIterator && containsKey(key)) + { + useIterator = false; + } + + if (!useIterator) + { + // workaround for Iterators that do not remove the property + // on calling remove() or do not support remove() at all + clearProperty(key); + } + } + } + finally + { + setDetailEvents(true); + } + fireEvent(EVENT_CLEAR, null, null, false); + } + + /** + * {@inheritDoc} This implementation returns keys that either match the + * prefix or start with the prefix followed by a dot ('.'). So the call + * {@code getKeys("db");} will find the keys {@code db}, + * {@code db.user}, or {@code db.password}, but not the key + * {@code dbdriver}. + */ + public Iterator getKeys(String prefix) + { + return new PrefixedKeysIterator(getKeys(), prefix); + } + + public Properties getProperties(String key) + { + return getProperties(key, null); + } + + /** + * Get a list of properties associated with the given configuration key. + * + * @param key The configuration key. + * @param defaults Any default values for the returned + * {@code Properties} object. Ignored if {@code null}. + * + * @return The associated properties if key is found. + * + * @throws ConversionException is thrown if the key maps to an object that + * is not a String/List of Strings. + * + * @throws IllegalArgumentException if one of the tokens is malformed (does + * not contain an equals sign). + */ + public Properties getProperties(String key, Properties defaults) + { + /* + * Grab an array of the tokens for this key. + */ + String[] tokens = getStringArray(key); + + /* + * Each token is of the form 'key=value'. + */ + Properties props = defaults == null ? new Properties() : new Properties(defaults); + for (String token : tokens) + { + int equalSign = token.indexOf('='); + if (equalSign > 0) + { + String pkey = token.substring(0, equalSign).trim(); + String pvalue = token.substring(equalSign + 1).trim(); + props.put(pkey, pvalue); + } + else if (tokens.length == 1 && "".equals(token)) + { + // Semantically equivalent to an empty Properties + // object. + break; + } + else + { + throw new IllegalArgumentException('\'' + token + "' does not contain an equals sign"); + } + } + return props; + } + + /** + * {@inheritDoc} + * @see PropertyConverter#toBoolean(Object) + */ + public boolean getBoolean(String key) + { + Boolean b = getBoolean(key, null); + if (b != null) + { + return b.booleanValue(); + } + else + { + throw new NoSuchElementException('\'' + key + "' doesn't map to an existing object"); + } + } + + /** + * {@inheritDoc} + * @see PropertyConverter#toBoolean(Object) + */ + public boolean getBoolean(String key, boolean defaultValue) + { + return getBoolean(key, BooleanUtils.toBooleanObject(defaultValue)).booleanValue(); + } + + /** + * Obtains the value of the specified key and tries to convert it into a + * {@code Boolean} object. If the property has no value, the passed + * in default value will be used. + * + * @param key the key of the property + * @param defaultValue the default value + * @return the value of this key converted to a {@code Boolean} + * @throws ConversionException if the value cannot be converted to a + * {@code Boolean} + * @see PropertyConverter#toBoolean(Object) + */ + public Boolean getBoolean(String key, Boolean defaultValue) + { + Object value = resolveContainerStore(key); + + if (value == null) + { + return defaultValue; + } + else + { + try + { + return PropertyConverter.toBoolean(interpolate(value)); + } + catch (ConversionException e) + { + throw new ConversionException('\'' + key + "' doesn't map to a Boolean object", e); + } + } + } + + public byte getByte(String key) + { + Byte b = getByte(key, null); + if (b != null) + { + return b.byteValue(); + } + else + { + throw new NoSuchElementException('\'' + key + " doesn't map to an existing object"); + } + } + + public byte getByte(String key, byte defaultValue) + { + return getByte(key, new Byte(defaultValue)).byteValue(); + } + + public Byte getByte(String key, Byte defaultValue) + { + Object value = resolveContainerStore(key); + + if (value == null) + { + return defaultValue; + } + else + { + try + { + return PropertyConverter.toByte(interpolate(value)); + } + catch (ConversionException e) + { + throw new ConversionException('\'' + key + "' doesn't map to a Byte object", e); + } + } + } + + public double getDouble(String key) + { + Double d = getDouble(key, null); + if (d != null) + { + return d.doubleValue(); + } + else + { + throw new NoSuchElementException('\'' + key + "' doesn't map to an existing object"); + } + } + + public double getDouble(String key, double defaultValue) + { + return getDouble(key, new Double(defaultValue)).doubleValue(); + } + + public Double getDouble(String key, Double defaultValue) + { + Object value = resolveContainerStore(key); + + if (value == null) + { + return defaultValue; + } + else + { + try + { + return PropertyConverter.toDouble(interpolate(value)); + } + catch (ConversionException e) + { + throw new ConversionException('\'' + key + "' doesn't map to a Double object", e); + } + } + } + + public float getFloat(String key) + { + Float f = getFloat(key, null); + if (f != null) + { + return f.floatValue(); + } + else + { + throw new NoSuchElementException('\'' + key + "' doesn't map to an existing object"); + } + } + + public float getFloat(String key, float defaultValue) + { + return getFloat(key, new Float(defaultValue)).floatValue(); + } + + public Float getFloat(String key, Float defaultValue) + { + Object value = resolveContainerStore(key); + + if (value == null) + { + return defaultValue; + } + else + { + try + { + return PropertyConverter.toFloat(interpolate(value)); + } + catch (ConversionException e) + { + throw new ConversionException('\'' + key + "' doesn't map to a Float object", e); + } + } + } + + public int getInt(String key) + { + Integer i = getInteger(key, null); + if (i != null) + { + return i.intValue(); + } + else + { + throw new NoSuchElementException('\'' + key + "' doesn't map to an existing object"); + } + } + + public int getInt(String key, int defaultValue) + { + Integer i = getInteger(key, null); + + if (i == null) + { + return defaultValue; + } + + return i.intValue(); + } + + public Integer getInteger(String key, Integer defaultValue) + { + Object value = resolveContainerStore(key); + + if (value == null) + { + return defaultValue; + } + else + { + try + { + return PropertyConverter.toInteger(interpolate(value)); + } + catch (ConversionException e) + { + throw new ConversionException('\'' + key + "' doesn't map to an Integer object", e); + } + } + } + + public long getLong(String key) + { + Long l = getLong(key, null); + if (l != null) + { + return l.longValue(); + } + else + { + throw new NoSuchElementException('\'' + key + "' doesn't map to an existing object"); + } + } + + public long getLong(String key, long defaultValue) + { + return getLong(key, new Long(defaultValue)).longValue(); + } + + public Long getLong(String key, Long defaultValue) + { + Object value = resolveContainerStore(key); + + if (value == null) + { + return defaultValue; + } + else + { + try + { + return PropertyConverter.toLong(interpolate(value)); + } + catch (ConversionException e) + { + throw new ConversionException('\'' + key + "' doesn't map to a Long object", e); + } + } + } + + public short getShort(String key) + { + Short s = getShort(key, null); + if (s != null) + { + return s.shortValue(); + } + else + { + throw new NoSuchElementException('\'' + key + "' doesn't map to an existing object"); + } + } + + public short getShort(String key, short defaultValue) + { + return getShort(key, new Short(defaultValue)).shortValue(); + } + + public Short getShort(String key, Short defaultValue) + { + Object value = resolveContainerStore(key); + + if (value == null) + { + return defaultValue; + } + else + { + try + { + return PropertyConverter.toShort(interpolate(value)); + } + catch (ConversionException e) + { + throw new ConversionException('\'' + key + "' doesn't map to a Short object", e); + } + } + } + + /** + * {@inheritDoc} + * @see #setThrowExceptionOnMissing(boolean) + */ + public BigDecimal getBigDecimal(String key) + { + BigDecimal number = getBigDecimal(key, null); + if (number != null) + { + return number; + } + else if (isThrowExceptionOnMissing()) + { + throw new NoSuchElementException('\'' + key + "' doesn't map to an existing object"); + } + else + { + return null; + } + } + + public BigDecimal getBigDecimal(String key, BigDecimal defaultValue) + { + Object value = resolveContainerStore(key); + + if (value == null) + { + return defaultValue; + } + else + { + try + { + return PropertyConverter.toBigDecimal(interpolate(value)); + } + catch (ConversionException e) + { + throw new ConversionException('\'' + key + "' doesn't map to a BigDecimal object", e); + } + } + } + + /** + * {@inheritDoc} + * @see #setThrowExceptionOnMissing(boolean) + */ + public BigInteger getBigInteger(String key) + { + BigInteger number = getBigInteger(key, null); + if (number != null) + { + return number; + } + else if (isThrowExceptionOnMissing()) + { + throw new NoSuchElementException('\'' + key + "' doesn't map to an existing object"); + } + else + { + return null; + } + } + + public BigInteger getBigInteger(String key, BigInteger defaultValue) + { + Object value = resolveContainerStore(key); + + if (value == null) + { + return defaultValue; + } + else + { + try + { + return PropertyConverter.toBigInteger(interpolate(value)); + } + catch (ConversionException e) + { + throw new ConversionException('\'' + key + "' doesn't map to a BigInteger object", e); + } + } + } + + /** + * {@inheritDoc} + * @see #setThrowExceptionOnMissing(boolean) + */ + public String getString(String key) + { + String s = getString(key, null); + if (s != null) + { + return s; + } + else if (isThrowExceptionOnMissing()) + { + throw new NoSuchElementException('\'' + key + "' doesn't map to an existing object"); + } + else + { + return null; + } + } + + public String getString(String key, String defaultValue) + { + Object value = resolveContainerStore(key); + + if (value instanceof String) + { + return interpolate((String) value); + } + else if (value == null) + { + return interpolate(defaultValue); + } + else + { + throw new ConversionException('\'' + key + "' doesn't map to a String object"); + } + } + + /** + * Get an array of strings associated with the given configuration key. + * If the key doesn't map to an existing object, an empty array is returned. + * If a property is added to a configuration, it is checked whether it + * contains multiple values. This is obvious if the added object is a list + * or an array. For strings it is checked whether the string contains the + * list delimiter character that can be specified using the + * {@code setListDelimiter()} method. If this is the case, the string + * is split at these positions resulting in a property with multiple + * values. + * + * @param key The configuration key. + * @return The associated string array if key is found. + * + * @throws ConversionException is thrown if the key maps to an + * object that is not a String/List of Strings. + * @see #setListDelimiter(char) + * @see #setDelimiterParsingDisabled(boolean) + */ + public String[] getStringArray(String key) + { + Object value = getProperty(key); + + String[] array; + + if (value instanceof String) + { + array = new String[1]; + + array[0] = interpolate((String) value); + } + else if (value instanceof List) + { + List list = (List) value; + array = new String[list.size()]; + + for (int i = 0; i < array.length; i++) + { + array[i] = interpolate(ObjectUtils.toString(list.get(i), null)); + } + } + else if (value == null) + { + array = new String[0]; + } + else if (isScalarValue(value)) + { + array = new String[1]; + array[0] = value.toString(); + } + else + { + throw new ConversionException('\'' + key + "' doesn't map to a String/List object"); + } + return array; + } + + /** + * {@inheritDoc} + * @see #getStringArray(String) + */ + public List getList(String key) + { + return getList(key, new ArrayList()); + } + + public List getList(String key, List defaultValue) + { + Object value = getProperty(key); + List list; + + if (value instanceof String) + { + list = new ArrayList(1); + list.add(interpolate((String) value)); + } + else if (value instanceof List) + { + list = new ArrayList(); + List l = (List) value; + + // add the interpolated elements in the new list + for (Object elem : l) + { + list.add(interpolate(elem)); + } + } + else if (value == null) + { + list = defaultValue; + } + else if (value.getClass().isArray()) + { + return Arrays.asList((Object[]) value); + } + else if (isScalarValue(value)) + { + return Collections.singletonList((Object) value.toString()); + } + else + { + throw new ConversionException('\'' + key + "' doesn't map to a List object: " + value + ", a " + + value.getClass().getName()); + } + return list; + } + + /** + * Returns an object from the store described by the key. If the value is a + * Collection object, replace it with the first object in the collection. + * + * @param key The property key. + * + * @return value Value, transparently resolving a possible collection dependency. + */ + protected Object resolveContainerStore(String key) + { + Object value = getProperty(key); + if (value != null) + { + if (value instanceof Collection) + { + Collection collection = (Collection) value; + value = collection.isEmpty() ? null : collection.iterator().next(); + } + else if (value.getClass().isArray() && Array.getLength(value) > 0) + { + value = Array.get(value, 0); + } + } + + return value; + } + + /** + * Checks whether the specified object is a scalar value. This method is + * called by {@code getList()} and {@code getStringArray()} if the + * property requested is not a string, a list, or an array. If it returns + * true, the calling method transforms the value to a string and + * returns a list or an array with this single element. This implementation + * returns true if the value is of a wrapper type for a primitive + * type. + * + * @param value the value to be checked + * @return a flag whether the value is a scalar + * @since 1.7 + */ + protected boolean isScalarValue(Object value) + { + return ClassUtils.wrapperToPrimitive(value.getClass()) != null; + } + + /** + * Copies the content of the specified configuration into this + * configuration. If the specified configuration contains a key that is also + * present in this configuration, the value of this key will be replaced by + * the new value. Note: This method won't work well when copying + * hierarchical configurations because it is not able to copy information + * about the properties' structure (i.e. the parent-child-relationships will + * get lost). So when dealing with hierarchical configuration objects their + * {@link HierarchicalConfiguration#clone() clone()} methods + * should be used. + * + * @param c the configuration to copy (can be null, then this + * operation will have no effect) + * @since 1.5 + */ + public void copy(Configuration c) + { + if (c != null) + { + for (Iterator it = c.getKeys(); it.hasNext();) + { + String key = it.next(); + Object value = c.getProperty(key); + fireEvent(EVENT_SET_PROPERTY, key, value, true); + setDetailEvents(false); + try + { + clearProperty(key); + addPropertyValues(key, value, DISABLED_DELIMITER); + } + finally + { + setDetailEvents(true); + } + fireEvent(EVENT_SET_PROPERTY, key, value, false); + } + } + } + + /** + * Appends the content of the specified configuration to this configuration. + * The values of all properties contained in the specified configuration + * will be appended to this configuration. So if a property is already + * present in this configuration, its new value will be a union of the + * values in both configurations. Note: This method won't work + * well when appending hierarchical configurations because it is not able to + * copy information about the properties' structure (i.e. the + * parent-child-relationships will get lost). So when dealing with + * hierarchical configuration objects their + * {@link HierarchicalConfiguration#clone() clone()} methods + * should be used. + * + * @param c the configuration to be appended (can be null, then this + * operation will have no effect) + * @since 1.5 + */ + public void append(Configuration c) + { + if (c != null) + { + for (Iterator it = c.getKeys(); it.hasNext();) + { + String key = it.next(); + Object value = c.getProperty(key); + fireEvent(EVENT_ADD_PROPERTY, key, value, true); + addPropertyValues(key, value, DISABLED_DELIMITER); + fireEvent(EVENT_ADD_PROPERTY, key, value, false); + } + } + } + + /** + * Returns a configuration with the same content as this configuration, but + * with all variables replaced by their actual values. This method tries to + * clone the configuration and then perform interpolation on all properties. + * So property values of the form ${var} will be resolved as + * far as possible (if a variable cannot be resolved, it remains unchanged). + * This operation is useful if the content of a configuration is to be + * exported or processed by an external component that does not support + * variable interpolation. + * + * @return a configuration with all variables interpolated + * @throws ConfigurationRuntimeException if this configuration cannot be + * cloned + * @since 1.5 + */ + public Configuration interpolatedConfiguration() + { + // first clone this configuration + AbstractConfiguration c = (AbstractConfiguration) ConfigurationUtils + .cloneConfiguration(this); + + // now perform interpolation + c.setDelimiterParsingDisabled(true); + for (Iterator it = getKeys(); it.hasNext();) + { + String key = it.next(); + c.setProperty(key, getList(key)); + } + + c.setDelimiterParsingDisabled(isDelimiterParsingDisabled()); + return c; + } + + private Interpolator interpolator = null; + + public void setInterpolator(Interpolator interpolator) throws SecurityException, NoSuchMethodException + { + this.interpolator = interpolator; + } +} diff --git a/javaUtilities/yajsw/src/main/java/org/apache/commons/configuration/AbstractConfiguration.javax b/javaUtilities/yajsw/src/main/java/org/apache/commons/configuration/AbstractConfiguration.javax new file mode 100644 index 0000000000..208f82d50e --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/apache/commons/configuration/AbstractConfiguration.javax @@ -0,0 +1,1341 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.configuration; + +import java.lang.reflect.Array; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.NoSuchElementException; +import java.util.Properties; + +import org.apache.commons.collections.Predicate; +import org.apache.commons.collections.iterators.FilterIterator; +import org.apache.commons.configuration.event.ConfigurationErrorEvent; +import org.apache.commons.configuration.event.ConfigurationErrorListener; +import org.apache.commons.configuration.event.EventSource; +import org.apache.commons.configuration.interpol.ConfigurationInterpolator; +import org.apache.commons.lang.BooleanUtils; +import org.apache.commons.lang.text.StrLookup; +import org.apache.commons.lang.text.StrSubstitutor; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.impl.NoOpLog; + +/** + *

    + * Abstract configuration class. Provides basic functionality but does not store + * any data. + *

    + *

    + * If you want to write your own Configuration class then you should implement + * only abstract methods from this class. A lot of functionality needed by + * typical implementations of the Configuration interface is + * already provided by this base class. Following is a list of features + * implemented here: + *

      + *
    • Data conversion support. The various data types required by the + * Configuration interface are already handled by this base class. + * A concrete sub class only needs to provide a generic + * getProperty() method.
    • + *
    • Support for variable interpolation. Property values containing special + * variable tokens (like ${var}) will be replaced by their + * corresponding values.
    • + *
    • Support for string lists. The values of properties to be added to this + * configuration are checked whether they contain a list delimiter character. If + * this is the case and if list splitting is enabled, the string is split and + * multiple values are added for this property. (With the + * setListDelimiter() method the delimiter character can be + * specified; per default a comma is used. The + * setDelimiterParsingDisabled() method can be used to disable list + * splitting completely.)
    • + *
    • Allows to specify how missing properties are treated. Per default the get + * methods returning an object will return null if the searched property + * key is not found (and no default value is provided). With the + * setThrowExceptionOnMissing() method this behavior can be changed + * to throw an exception when a requested property cannot be found.
    • + *
    • Basic event support. Whenever this configuration is modified registered + * event listeners are notified. Refer to the various EVENT_XXX + * constants to get an impression about which event types are supported.
    • + *
    + *

    + * + * @author Konstantin Shaposhnikov + * @author Oliver Heger + * @author Henning P. Schmiedehausen + * @version $Id: AbstractConfiguration.java 730776 2009-01-02 16:37:12Z oheger $ + */ +public abstract class AbstractConfiguration extends EventSource implements Configuration +{ + /** + * Constant for the add property event type. + * + * @since 1.3 + */ + public static final int EVENT_ADD_PROPERTY = 1; + + /** + * Constant for the clear property event type. + * + * @since 1.3 + */ + public static final int EVENT_CLEAR_PROPERTY = 2; + + /** + * Constant for the set property event type. + * + * @since 1.3 + */ + public static final int EVENT_SET_PROPERTY = 3; + + /** + * Constant for the clear configuration event type. + * + * @since 1.3 + */ + public static final int EVENT_CLEAR = 4; + + /** + * Constant for the get property event type. This event type is used for + * error events. + * + * @since 1.4 + */ + public static final int EVENT_READ_PROPERTY = 5; + + /** start token */ + protected static final String START_TOKEN = "${"; + + /** end token */ + protected static final String END_TOKEN = "}"; + + /** + * Constant for the disabled list delimiter. This character is passed to the + * list parsing methods if delimiter parsing is disabled. So this character + * should not occur in string property values. + */ + private static final char DISABLED_DELIMITER = '\0'; + + /** The default value for listDelimiter */ + private static char defaultListDelimiter = ','; + + /** Delimiter used to convert single values to lists */ + private char listDelimiter = defaultListDelimiter; + + /** + * When set to true the given configuration delimiter will not be used while + * parsing for this configuration. + */ + private boolean delimiterParsingDisabled; + + /** + * Whether the configuration should throw NoSuchElementExceptions or simply + * return null when a property does not exist. Defaults to return null. + */ + private boolean throwExceptionOnMissing; + + /** Stores a reference to the object that handles variable interpolation. */ + private StrSubstitutor substitutor; + + /** Stores the logger. */ + private Log log; + + /** + * Creates a new instance of AbstractConfiguration. + */ + public AbstractConfiguration() + { + setLogger(null); + } + + /** + * For configurations extending AbstractConfiguration, allow them to change + * the listDelimiter from the default comma (","). This value will be used + * only when creating new configurations. Those already created will not be + * affected by this change + * + * @param delimiter + * The new listDelimiter + */ + public static void setDefaultListDelimiter(char delimiter) + { + AbstractConfiguration.defaultListDelimiter = delimiter; + } + + /** + * Sets the default list delimiter. + * + * @param delimiter + * the delimiter character + * @deprecated Use AbstractConfiguration.setDefaultListDelimiter(char) + * instead + */ + public static void setDelimiter(char delimiter) + { + setDefaultListDelimiter(delimiter); + } + + /** + * Retrieve the current delimiter. By default this is a comma (","). + * + * @return The delimiter in use + */ + public static char getDefaultListDelimiter() + { + return AbstractConfiguration.defaultListDelimiter; + } + + /** + * Returns the default list delimiter. + * + * @return the default list delimiter + * @deprecated Use AbstractConfiguration.getDefaultListDelimiter() instead + */ + public static char getDelimiter() + { + return getDefaultListDelimiter(); + } + + /** + * Change the list delimiter for this configuration. + * + * Note: this change will only be effective for new parsings. If you want it + * to take effect for all loaded properties use the no arg constructor and + * call this method before setting the source. + * + * @param listDelimiter + * The new listDelimiter + */ + public void setListDelimiter(char listDelimiter) + { + this.listDelimiter = listDelimiter; + } + + /** + * Retrieve the delimiter for this configuration. The default is the value + * of defaultListDelimiter. + * + * @return The listDelimiter in use + */ + public char getListDelimiter() + { + return listDelimiter; + } + + /** + * Determine if this configuration is using delimiters when parsing property + * values to convert them to lists of values. Defaults to false + * + * @return true if delimiters are not being used + */ + public boolean isDelimiterParsingDisabled() + { + return delimiterParsingDisabled; + } + + /** + * Set whether this configuration should use delimiters when parsing + * property values to convert them to lists of values. By default delimiter + * parsing is enabled + * + * Note: this change will only be effective for new parsings. If you want it + * to take effect for all loaded properties use the no arg constructor and + * call this method before setting source. + * + * @param delimiterParsingDisabled + * a flag whether delimiter parsing should be disabled + */ + public void setDelimiterParsingDisabled(boolean delimiterParsingDisabled) + { + this.delimiterParsingDisabled = delimiterParsingDisabled; + } + + /** + * Allows to set the throwExceptionOnMissing flag. This flag + * controls the behavior of property getter methods that return objects if + * the requested property is missing. If the flag is set to false + * (which is the default value), these methods will return null. If + * set to true, they will throw a NoSuchElementException + * exception. Note that getter methods for primitive data types are not + * affected by this flag. + * + * @param throwExceptionOnMissing + * The new value for the property + */ + public void setThrowExceptionOnMissing(boolean throwExceptionOnMissing) + { + this.throwExceptionOnMissing = throwExceptionOnMissing; + } + + /** + * Returns true if missing values throw Exceptions. + * + * @return true if missing values throw Exceptions + */ + public boolean isThrowExceptionOnMissing() + { + return throwExceptionOnMissing; + } + + /** + * Returns the object that is responsible for variable interpolation. + * + * @return the object responsible for variable interpolation + * @since 1.4 + */ + public synchronized StrSubstitutor getSubstitutor() + { + if (substitutor == null) + { + substitutor = new StrSubstitutor(createInterpolator()); + } + return substitutor; + } + + /** + * Returns the ConfigurationInterpolator object that manages + * the lookup objects for resolving variables. Note: If this object + * is manipulated (e.g. new lookup objects added), synchronisation has to be + * manually ensured. Because ConfigurationInterpolator is not + * thread-safe concurrent access to properties of this configuration + * instance (which causes the interpolator to be invoked) may cause race + * conditions. + * + * @return the ConfigurationInterpolator associated with this + * configuration + * @since 1.4 + */ + public ConfigurationInterpolator getInterpolator() + { + return (ConfigurationInterpolator) getSubstitutor().getVariableResolver(); + } + + /** + * Creates the interpolator object that is responsible for variable + * interpolation. This method is invoked on first access of the + * interpolation features. It creates a new instance of + * ConfigurationInterpolator and sets the default lookup object + * to an implementation that queries this configuration. + * + * @return the newly created interpolator object + * @since 1.4 + */ + protected ConfigurationInterpolator createInterpolator() + { + ConfigurationInterpolator interpol = new ConfigurationInterpolator(); + interpol.setDefaultLookup(new StrLookup() + { + public String lookup(String var) + { + Object prop = resolveContainerStore(var); + return (prop != null) ? prop.toString() : null; + } + }); + return interpol; + } + + /** + * Returns the logger used by this configuration object. + * + * @return the logger + * @since 1.4 + */ + public Log getLogger() + { + return log; + } + + /** + * Allows to set the logger to be used by this configuration object. This + * method makes it possible for clients to exactly control logging behavior. + * Per default a logger is set that will ignore all log messages. Derived + * classes that want to enable logging should call this method during their + * initialization with the logger to be used. + * + * @param log + * the new logger + * @since 1.4 + */ + public void setLogger(Log log) + { + this.log = (log != null) ? log : new NoOpLog(); + } + + /** + * Adds a special + * {@link org.apache.commons.configuration.event.ConfigurationErrorListener} + * object to this configuration that will log all internal errors. This + * method is intended to be used by certain derived classes, for which it is + * known that they can fail on property access (e.g. + * DatabaseConfiguration). + * + * @since 1.4 + */ + public void addErrorLogListener() + { + addErrorListener(new ConfigurationErrorListener() + { + public void configurationError(ConfigurationErrorEvent event) + { + getLogger().warn("Internal error", event.getCause()); + } + }); + } + + public void addProperty(String key, Object value) + { + fireEvent(EVENT_ADD_PROPERTY, key, value, true); + addPropertyValues(key, value, isDelimiterParsingDisabled() ? DISABLED_DELIMITER : getListDelimiter()); + fireEvent(EVENT_ADD_PROPERTY, key, value, false); + } + + /** + * Adds a key/value pair to the Configuration. Override this method to + * provide write access to underlying Configuration store. + * + * @param key + * key to use for mapping + * @param value + * object to store + */ + protected abstract void addPropertyDirect(String key, Object value); + + /** + * Adds the specified value for the given property. This method supports + * single values and containers (e.g. collections or arrays) as well. In the + * latter case, addPropertyDirect() will be called for each + * element. + * + * @param key + * the property key + * @param value + * the value object + * @param delimiter + * the list delimiter character + */ + private void addPropertyValues(String key, Object value, char delimiter) + { + Iterator it = PropertyConverter.toIterator(value, delimiter); + while (it.hasNext()) + { + addPropertyDirect(key, it.next()); + } + } + + /** + * interpolate key names to handle ${key} stuff + * + * @param base + * string to interpolate + * + * @return returns the key name with the ${key} substituted + */ + protected String interpolate(String base) + { + Object result = interpolate((Object) base); + return (result == null) ? null : result.toString(); + } + + /** + * Returns the interpolated value. Non String values are returned without + * change. + * + * @param value + * the value to interpolate + * + * @return returns the value with variables substituted + */ + protected Object interpolate(Object value) + { + if (interpolator == null) + return PropertyConverter.interpolate(value, this); + else + { + try + { + return interpolator.interpolate(value); + } + catch (Exception e) + { + e.printStackTrace(); + return PropertyConverter.interpolate(value, this); + } + } + } + + /** + * Recursive handler for multple levels of interpolation. + * + * When called the first time, priorVariables should be null. + * + * @param base + * string with the ${key} variables + * @param priorVariables + * serves two purposes: to allow checking for loops, and creating + * a meaningful exception message should a loop occur. It's 0'th + * element will be set to the value of base from the first call. + * All subsequent interpolated variables are added afterward. + * + * @return the string with the interpolation taken care of + * @deprecated Interpolation is now handled by + * {@link PropertyConverter}; this method will no + * longer be called + */ + protected String interpolateHelper(String base, List priorVariables) + { + return base; // just a dummy implementation + } + + public Configuration subset(String prefix) + { + return new SubsetConfiguration(this, prefix, "."); + } + + public void setProperty(String key, Object value) + { + fireEvent(EVENT_SET_PROPERTY, key, value, true); + setDetailEvents(false); + try + { + clearProperty(key); + addProperty(key, value); + } + finally + { + setDetailEvents(true); + } + fireEvent(EVENT_SET_PROPERTY, key, value, false); + } + + /** + * Removes the specified property from this configuration. This + * implementation performs some preparations and then delegates to + * clearPropertyDirect(), which will do the real work. + * + * @param key + * the key to be removed + */ + public void clearProperty(String key) + { + fireEvent(EVENT_CLEAR_PROPERTY, key, null, true); + clearPropertyDirect(key); + fireEvent(EVENT_CLEAR_PROPERTY, key, null, false); + } + + /** + * Removes the specified property from this configuration. This method is + * called by clearProperty() after it has done some + * preparations. It should be overriden in sub classes. This base + * implementation is just left empty. + * + * @param key + * the key to be removed + */ + protected void clearPropertyDirect(String key) + { + // override in sub classes + } + + public void clear() + { + fireEvent(EVENT_CLEAR, null, null, true); + setDetailEvents(false); + boolean useIterator = true; + try + { + Iterator it = getKeys(); + while (it.hasNext()) + { + String key = (String) it.next(); + if (useIterator) + { + try + { + it.remove(); + } + catch (UnsupportedOperationException usoex) + { + useIterator = false; + } + } + + if (useIterator && containsKey(key)) + { + useIterator = false; + } + + if (!useIterator) + { + // workaround for Iterators that do not remove the property + // on calling remove() or do not support remove() at all + clearProperty(key); + } + } + } + finally + { + setDetailEvents(true); + } + fireEvent(EVENT_CLEAR, null, null, false); + } + + public Iterator getKeys(final String prefix) + { + return new FilterIterator(getKeys(), new Predicate() + { + public boolean evaluate(Object obj) + { + String key = (String) obj; + return key.startsWith(prefix + ".") || key.equals(prefix); + } + }); + } + + public Properties getProperties(String key) + { + return getProperties(key, null); + } + + /** + * Get a list of properties associated with the given configuration key. + * + * @param key + * The configuration key. + * @param defaults + * Any default values for the returned Properties + * object. Ignored if null. + * + * @return The associated properties if key is found. + * + * @throws ConversionException + * is thrown if the key maps to an object that is not a + * String/List of Strings. + * + * @throws IllegalArgumentException + * if one of the tokens is malformed (does not contain an equals + * sign). + */ + public Properties getProperties(String key, Properties defaults) + { + /* + * Grab an array of the tokens for this key. + */ + String[] tokens = getStringArray(key); + + /* + * Each token is of the form 'key=value'. + */ + Properties props = defaults == null ? new Properties() : new Properties(defaults); + for (int i = 0; i < tokens.length; i++) + { + String token = tokens[i]; + int equalSign = token.indexOf('='); + if (equalSign > 0) + { + String pkey = token.substring(0, equalSign).trim(); + String pvalue = token.substring(equalSign + 1).trim(); + props.put(pkey, pvalue); + } + else if (tokens.length == 1 && "".equals(token)) + { + // Semantically equivalent to an empty Properties + // object. + break; + } + else + { + throw new IllegalArgumentException('\'' + token + "' does not contain an equals sign"); + } + } + return props; + } + + /** + * {@inheritDoc} + * + * @see PropertyConverter#toBoolean(Object) + */ + public boolean getBoolean(String key) + { + Boolean b = getBoolean(key, null); + if (b != null) + { + return b.booleanValue(); + } + else + { + throw new NoSuchElementException('\'' + key + "' doesn't map to an existing object"); + } + } + + /** + * {@inheritDoc} + * + * @see PropertyConverter#toBoolean(Object) + */ + public boolean getBoolean(String key, boolean defaultValue) + { + return getBoolean(key, BooleanUtils.toBooleanObject(defaultValue)).booleanValue(); + } + + /** + * Obtains the value of the specified key and tries to convert it into a + * Boolean object. If the property has no value, the passed in + * default value will be used. + * + * @param key + * the key of the property + * @param defaultValue + * the default value + * @return the value of this key converted to a Boolean + * @throws ConversionException + * if the value cannot be converted to a Boolean + * @see PropertyConverter#toBoolean(Object) + */ + public Boolean getBoolean(String key, Boolean defaultValue) + { + Object value = resolveContainerStore(key); + + if (value == null) + { + return defaultValue; + } + else + { + try + { + return PropertyConverter.toBoolean(interpolate(value)); + } + catch (ConversionException e) + { + throw new ConversionException('\'' + key + "' doesn't map to a Boolean object", e); + } + } + } + + public byte getByte(String key) + { + Byte b = getByte(key, null); + if (b != null) + { + return b.byteValue(); + } + else + { + throw new NoSuchElementException('\'' + key + " doesn't map to an existing object"); + } + } + + public byte getByte(String key, byte defaultValue) + { + return getByte(key, new Byte(defaultValue)).byteValue(); + } + + public Byte getByte(String key, Byte defaultValue) + { + Object value = resolveContainerStore(key); + + if (value == null) + { + return defaultValue; + } + else + { + try + { + return PropertyConverter.toByte(interpolate(value)); + } + catch (ConversionException e) + { + throw new ConversionException('\'' + key + "' doesn't map to a Byte object", e); + } + } + } + + public double getDouble(String key) + { + Double d = getDouble(key, null); + if (d != null) + { + return d.doubleValue(); + } + else + { + throw new NoSuchElementException('\'' + key + "' doesn't map to an existing object"); + } + } + + public double getDouble(String key, double defaultValue) + { + return getDouble(key, new Double(defaultValue)).doubleValue(); + } + + public Double getDouble(String key, Double defaultValue) + { + Object value = resolveContainerStore(key); + + if (value == null) + { + return defaultValue; + } + else + { + try + { + return PropertyConverter.toDouble(interpolate(value)); + } + catch (ConversionException e) + { + throw new ConversionException('\'' + key + "' doesn't map to a Double object", e); + } + } + } + + public float getFloat(String key) + { + Float f = getFloat(key, null); + if (f != null) + { + return f.floatValue(); + } + else + { + throw new NoSuchElementException('\'' + key + "' doesn't map to an existing object"); + } + } + + public float getFloat(String key, float defaultValue) + { + return getFloat(key, new Float(defaultValue)).floatValue(); + } + + public Float getFloat(String key, Float defaultValue) + { + Object value = resolveContainerStore(key); + + if (value == null) + { + return defaultValue; + } + else + { + try + { + return PropertyConverter.toFloat(interpolate(value)); + } + catch (ConversionException e) + { + throw new ConversionException('\'' + key + "' doesn't map to a Float object", e); + } + } + } + + public int getInt(String key) + { + Integer i = getInteger(key, null); + if (i != null) + { + return i.intValue(); + } + else + { + throw new NoSuchElementException('\'' + key + "' doesn't map to an existing object"); + } + } + + public int getInt(String key, int defaultValue) + { + Integer i = getInteger(key, null); + + if (i == null) + { + return defaultValue; + } + + return i.intValue(); + } + + public Integer getInteger(String key, Integer defaultValue) + { + Object value = resolveContainerStore(key); + + if (value == null) + { + return defaultValue; + } + else + { + try + { + return PropertyConverter.toInteger(interpolate(value)); + } + catch (ConversionException e) + { + throw new ConversionException('\'' + key + "' doesn't map to an Integer object", e); + } + } + } + + public long getLong(String key) + { + Long l = getLong(key, null); + if (l != null) + { + return l.longValue(); + } + else + { + throw new NoSuchElementException('\'' + key + "' doesn't map to an existing object"); + } + } + + public long getLong(String key, long defaultValue) + { + return getLong(key, new Long(defaultValue)).longValue(); + } + + public Long getLong(String key, Long defaultValue) + { + Object value = resolveContainerStore(key); + + if (value == null) + { + return defaultValue; + } + else + { + try + { + return PropertyConverter.toLong(interpolate(value)); + } + catch (ConversionException e) + { + throw new ConversionException('\'' + key + "' doesn't map to a Long object", e); + } + } + } + + public short getShort(String key) + { + Short s = getShort(key, null); + if (s != null) + { + return s.shortValue(); + } + else + { + throw new NoSuchElementException('\'' + key + "' doesn't map to an existing object"); + } + } + + public short getShort(String key, short defaultValue) + { + return getShort(key, new Short(defaultValue)).shortValue(); + } + + public Short getShort(String key, Short defaultValue) + { + Object value = resolveContainerStore(key); + + if (value == null) + { + return defaultValue; + } + else + { + try + { + return PropertyConverter.toShort(interpolate(value)); + } + catch (ConversionException e) + { + throw new ConversionException('\'' + key + "' doesn't map to a Short object", e); + } + } + } + + /** + * {@inheritDoc} + * + * @see #setThrowExceptionOnMissing(boolean) + */ + public BigDecimal getBigDecimal(String key) + { + BigDecimal number = getBigDecimal(key, null); + if (number != null) + { + return number; + } + else if (isThrowExceptionOnMissing()) + { + throw new NoSuchElementException('\'' + key + "' doesn't map to an existing object"); + } + else + { + return null; + } + } + + public BigDecimal getBigDecimal(String key, BigDecimal defaultValue) + { + Object value = resolveContainerStore(key); + + if (value == null) + { + return defaultValue; + } + else + { + try + { + return PropertyConverter.toBigDecimal(interpolate(value)); + } + catch (ConversionException e) + { + throw new ConversionException('\'' + key + "' doesn't map to a BigDecimal object", e); + } + } + } + + /** + * {@inheritDoc} + * + * @see #setThrowExceptionOnMissing(boolean) + */ + public BigInteger getBigInteger(String key) + { + BigInteger number = getBigInteger(key, null); + if (number != null) + { + return number; + } + else if (isThrowExceptionOnMissing()) + { + throw new NoSuchElementException('\'' + key + "' doesn't map to an existing object"); + } + else + { + return null; + } + } + + public BigInteger getBigInteger(String key, BigInteger defaultValue) + { + Object value = resolveContainerStore(key); + + if (value == null) + { + return defaultValue; + } + else + { + try + { + return PropertyConverter.toBigInteger(interpolate(value)); + } + catch (ConversionException e) + { + throw new ConversionException('\'' + key + "' doesn't map to a BigInteger object", e); + } + } + } + + /** + * {@inheritDoc} + * + * @see #setThrowExceptionOnMissing(boolean) + */ + public String getString(String key) + { + String s = getString(key, null); + if (s != null) + { + return s; + } + else if (isThrowExceptionOnMissing()) + { + throw new NoSuchElementException('\'' + key + "' doesn't map to an existing object"); + } + else + { + return null; + } + } + + public String getString(String key, String defaultValue) + { + Object value = resolveContainerStore(key); + + if (value instanceof String) + { + return interpolate((String) value); + } + else if (value == null) + { + return interpolate(defaultValue); + } + else + { + throw new ConversionException('\'' + key + "' doesn't map to a String object"); + } + } + + /** + * Get an array of strings associated with the given configuration key. If + * the key doesn't map to an existing object, an empty array is returned. If + * a property is added to a configuration, it is checked whether it contains + * multiple values. This is obvious if the added object is a list or an + * array. For strings it is checked whether the string contains the list + * delimiter character that can be specified using the + * setListDelimiter() method. If this is the case, the string + * is splitted at these positions resulting in a property with multiple + * values. + * + * @param key + * The configuration key. + * @return The associated string array if key is found. + * + * @throws ConversionException + * is thrown if the key maps to an object that is not a + * String/List of Strings. + * @see #setListDelimiter(char) + * @see #setDelimiterParsingDisabled(boolean) + */ + public String[] getStringArray(String key) + { + Object value = getProperty(key); + + String[] array; + + if (value instanceof String) + { + array = new String[1]; + + array[0] = interpolate((String) value); + } + else if (value instanceof List) + { + List list = (List) value; + array = new String[list.size()]; + + for (int i = 0; i < array.length; i++) + { + array[i] = interpolate((String) list.get(i)); + } + } + else if (value == null) + { + array = new String[0]; + } + else + { + throw new ConversionException('\'' + key + "' doesn't map to a String/List object"); + } + return array; + } + + /** + * {@inheritDoc} + * + * @see #getStringArray(String) + */ + public List getList(String key) + { + return getList(key, new ArrayList()); + } + + public List getList(String key, List defaultValue) + { + Object value = getProperty(key); + List list; + + if (value instanceof String) + { + list = new ArrayList(1); + list.add(interpolate((String) value)); + } + else if (value instanceof List) + { + list = new ArrayList(); + List l = (List) value; + + // add the interpolated elements in the new list + Iterator it = l.iterator(); + while (it.hasNext()) + { + list.add(interpolate(it.next())); + } + } + else if (value == null) + { + list = defaultValue; + } + else if (value.getClass().isArray()) + { + return Arrays.asList((Object[]) value); + } + else + { + throw new ConversionException('\'' + key + "' doesn't map to a List object: " + value + ", a " + value.getClass().getName()); + } + return list; + } + + /** + * Returns an object from the store described by the key. If the value is a + * Collection object, replace it with the first object in the collection. + * + * @param key + * The property key. + * + * @return value Value, transparently resolving a possible collection + * dependency. + */ + protected Object resolveContainerStore(String key) + { + Object value = getProperty(key); + if (value != null) + { + if (value instanceof Collection) + { + Collection collection = (Collection) value; + value = collection.isEmpty() ? null : collection.iterator().next(); + } + else if (value.getClass().isArray() && Array.getLength(value) > 0) + { + value = Array.get(value, 0); + } + } + return value; + } + + /** + * Copies the content of the specified configuration into this + * configuration. If the specified configuration contains a key that is also + * present in this configuration, the value of this key will be replaced by + * the new value. Note: This method won't work well when copying + * hierarchical configurations because it is not able to copy information + * about the properties' structure (i.e. the parent-child-relationships will + * get lost). So when dealing with hierarchical configuration objects their + * {@link HierarchicalConfiguration#clone() clone()} methods + * should be used. + * + * @param c + * the configuration to copy (can be null, then this + * operation will have no effect) + * @since 1.5 + */ + public void copy(Configuration c) + { + if (c != null) + { + for (Iterator it = c.getKeys(); it.hasNext();) + { + String key = (String) it.next(); + Object value = c.getProperty(key); + fireEvent(EVENT_SET_PROPERTY, key, value, true); + setDetailEvents(false); + try + { + clearProperty(key); + addPropertyValues(key, value, DISABLED_DELIMITER); + } + finally + { + setDetailEvents(true); + } + fireEvent(EVENT_SET_PROPERTY, key, value, false); + } + } + } + + /** + * Appends the content of the specified configuration to this configuration. + * The values of all properties contained in the specified configuration + * will be appended to this configuration. So if a property is already + * present in this configuration, its new value will be a union of the + * values in both configurations. Note: This method won't work well + * when appending hierarchical configurations because it is not able to copy + * information about the properties' structure (i.e. the + * parent-child-relationships will get lost). So when dealing with + * hierarchical configuration objects their + * {@link HierarchicalConfiguration#clone() clone()} methods + * should be used. + * + * @param c + * the configuration to be appended (can be null, then + * this operation will have no effect) + * @since 1.5 + */ + public void append(Configuration c) + { + if (c != null) + { + for (Iterator it = c.getKeys(); it.hasNext();) + { + String key = (String) it.next(); + Object value = c.getProperty(key); + fireEvent(EVENT_ADD_PROPERTY, key, value, true); + addPropertyValues(key, value, DISABLED_DELIMITER); + fireEvent(EVENT_ADD_PROPERTY, key, value, false); + } + } + } + + /** + * Returns a configuration with the same content as this configuration, but + * with all variables replaced by their actual values. This method tries to + * clone the configuration and then perform interpolation on all properties. + * So property values of the form ${var} will be resolved as + * far as possible (if a variable cannot be resolved, it remains unchanged). + * This operation is useful if the content of a configuration is to be + * exported or processed by an external component that does not support + * variable interpolation. + * + * @return a configuration with all variables interpolated + * @throws ConfigurationRuntimeException + * if this configuration cannot be cloned + * @since 1.5 + */ + public Configuration interpolatedConfiguration() + { + // first clone this configuration + AbstractConfiguration c = (AbstractConfiguration) ConfigurationUtils.cloneConfiguration(this); + + // now perform interpolation + c.setDelimiterParsingDisabled(true); + for (Iterator it = getKeys(); it.hasNext();) + { + String key = (String) it.next(); + c.setProperty(key, getList(key)); + } + + c.setDelimiterParsingDisabled(isDelimiterParsingDisabled()); + return c; + } + + private Interpolator interpolator = null; + + public void setInterpolator(Interpolator interpolator) throws SecurityException, NoSuchMethodException + { + this.interpolator = interpolator; + } +} diff --git a/javaUtilities/yajsw/src/main/java/org/apache/commons/configuration/ConfigurationBinding.java b/javaUtilities/yajsw/src/main/java/org/apache/commons/configuration/ConfigurationBinding.java new file mode 100644 index 0000000000..086be22d2c --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/apache/commons/configuration/ConfigurationBinding.java @@ -0,0 +1,55 @@ +package org.apache.commons.configuration; + +import groovy.lang.Binding; + +import java.util.HashMap; +import java.util.Map; + +public class ConfigurationBinding extends Binding +{ + Configuration _conf; + Map _utils; + Map _usedEnvVars = new HashMap(); + + public ConfigurationBinding(Configuration conf, Map utils) + { + _conf = conf; + _utils = utils; + } + + public Object getVariable(String name) + { + Object result = null; + if (_utils != null) + result = _utils.get(name); + if (result == null) + result = _conf.getProperty(name); + if (result == null) + result = super.getVariable(name); + if (result != null) + { + String r = System.getProperty(name); + if (result.equals(r)) + _usedEnvVars.put(name, result.toString()); + else + { + r = System.getenv(name); + if (result.equals(r)) + _usedEnvVars.put(name, result.toString()); + } + + } + return result; + } + + public Object getProperty(String name) + { + return getVariable(name); + } + + public Map getUsedEnvVars() + { + return _usedEnvVars; + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/apache/commons/configuration/GInterpolator.java b/javaUtilities/yajsw/src/main/java/org/apache/commons/configuration/GInterpolator.java new file mode 100644 index 0000000000..152c1a6239 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/apache/commons/configuration/GInterpolator.java @@ -0,0 +1,134 @@ +package org.apache.commons.configuration; + +import groovy.lang.Binding; +import groovy.lang.GroovyShell; + +import java.util.HashMap; +import java.util.Map; + +import org.jboss.netty.logging.InternalLogger; +import org.jboss.netty.logging.InternalLoggerFactory; + +public class GInterpolator implements Interpolator +{ + Binding _binding; + GroovyShell _shell; + Configuration _conf; + Map _cache = null; + String[] _imports = null; + InternalLogger log = InternalLoggerFactory.getInstance(this.getClass().getName()); + + public GInterpolator(Configuration conf, boolean cache, String[] imports, Map utils) + { + _conf = conf; + _binding = new ConfigurationBinding(conf, utils); + _shell = new GroovyShell(_binding); + setCache(cache); + _imports = imports; + } + + public GInterpolator(Configuration conf) + { + this(conf, false, null, null); + } + + public void setCache(boolean cache) + { + if (cache) + _cache = new HashMap(); + } + + public Object interpolate(Object value) + { + if (!(value instanceof String)) + return value; + if (_cache != null) + { + Object cachedResult = _cache.get(value); + if (cachedResult != null) + return cachedResult; + } + String result = (String) value; + int i = result.lastIndexOf("${"); + while (i != -1) + { + int r = getExpression(result, i + 2); + String expression = result.substring(i + 2, r); + String eval = evaluate(expression); + String p1 = result.substring(0, i); + String p2 = result.substring(r + 1); + result = p1 + eval + p2; + i = result.lastIndexOf("${"); + } + if (_cache != null) + _cache.put(value, result); + return result; + + } + + private int getExpression(String value, int i) + { + int i1 = value.indexOf('{', i); + int i2 = value.indexOf('}', i); + while (i1 != -1 && i2 > i1) + { + i2 = value.indexOf('}', i2 + 1); + i1 = value.indexOf('{', i1 + 1); + } + + return i2; + } + + private String evaluate(String value) + { + String result = null; + Exception caught = null; + try + { + result = (String)_binding.getVariable(value); + } + catch (Exception ex) + { + caught = ex; + try + { + result = _conf.getString(value); + } + catch (Exception ex1) + { + caught = ex1; + } + } + if (result == null) + try + { + if (_imports != null) + for (String im : _imports) + { + value = "import " + im + "\n" + value; + } + result = _shell.evaluate(value).toString(); + } + catch (Exception ex) + { + caught = ex; + } + if (result == null) + { + result = "?unresolved?"; + if (caught != null) + //log.warn("error evaluating "+value, caught); + log.warn("error evaluating "+value+" : "+caught.getMessage()); + else + log.warn("error evaluating "+value); + } + return result; + + } + + public Binding getBinding() + { + return _binding; + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/apache/commons/configuration/Interpolator.java b/javaUtilities/yajsw/src/main/java/org/apache/commons/configuration/Interpolator.java new file mode 100644 index 0000000000..fc1c768ea8 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/apache/commons/configuration/Interpolator.java @@ -0,0 +1,7 @@ +package org.apache.commons.configuration; + +public interface Interpolator +{ + public Object interpolate(Object name); + +} diff --git a/javaUtilities/yajsw/src/main/java/org/apache/commons/vfs2/impl/StandardFileSystemManager.java b/javaUtilities/yajsw/src/main/java/org/apache/commons/vfs2/impl/StandardFileSystemManager.java new file mode 100644 index 0000000000..b4f70cf1a2 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/apache/commons/vfs2/impl/StandardFileSystemManager.java @@ -0,0 +1,645 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.vfs2.impl; + +import java.io.IOException; +import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Enumeration; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; + +import org.apache.commons.vfs2.FileSystemException; +import org.apache.commons.vfs2.VfsLog; +import org.apache.commons.vfs2.operations.FileOperationProvider; +import org.apache.commons.vfs2.provider.FileProvider; +import org.apache.commons.vfs2.util.Messages; +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; + +/** + * A {@link org.apache.commons.vfs2.FileSystemManager} that configures itself + * from an XML (Default: providers.xml) configuration file.
    + * Certain providers are only loaded and available if the dependend library is in your + * classpath. You have to configure your debugging facility to log "debug" messages to see + * if a provider was skipped due to "unresolved externals". + * + * @author Commons VFS team + */ +public class StandardFileSystemManager + extends DefaultFileSystemManager +{ + private static final String CONFIG_RESOURCE = "providers.xml"; + private static final String PLUGIN_CONFIG_RESOURCE = "META-INF/vfs-providers.xml"; + + private URL configUri; + private ClassLoader classLoader; + + /** + * Sets the configuration file for this manager. + * @param configUri The URI for this manager. + */ + public void setConfiguration(final String configUri) + { + try + { + setConfiguration(new URL(configUri)); + } + catch (MalformedURLException e) + { + getLogger().warn(e.getLocalizedMessage(), e); + } + } + + /** + * Sets the configuration file for this manager. + * @param configUri The URI forthis manager. + */ + public void setConfiguration(final URL configUri) + { + this.configUri = configUri; + } + + /** + * Sets the ClassLoader to use to load the providers. Default is to + * use the ClassLoader that loaded this class. + * @param classLoader The ClassLoader. + */ + public void setClassLoader(final ClassLoader classLoader) + { + this.classLoader = classLoader; + } + + /** + * Initializes this manager. Adds the providers and replicator. + * @throws FileSystemException if an error occurs. + */ + @Override + public void init() throws FileSystemException + { + // Set the replicator and temporary file store (use the same component) + final DefaultFileReplicator replicator = createDefaultFileReplicator(); + setReplicator(new PrivilegedFileReplicator(replicator)); + setTemporaryFileStore(replicator); + + /* replaced by findClassLoader + if (classLoader == null) + { + // Use default classloader + classLoader = getClass().getClassLoader(); + } + */ + + if (configUri == null) + { + // Use default config + final URL url = getClass().getResource(CONFIG_RESOURCE); + if (url == null) + { + throw new FileSystemException("vfs.impl/find-config-file.error", CONFIG_RESOURCE); + } + configUri = url; + } + + // Configure + configure(configUri); + + // Configure Plugins + configurePlugins(); + + // Initialise super-class + super.init(); + } + + /** + * Scans the classpath to find any droped plugin.
    + * The plugin-description has to be in /META-INF/vfs-providers.xml + * @throws FileSystemException if an error occurs. + */ + protected void configurePlugins() throws FileSystemException + { + ClassLoader cl = findClassLoader(); + + Enumeration enumResources; + try + { + enumResources = cl.getResources(PLUGIN_CONFIG_RESOURCE); + } + catch (IOException e) + { + throw new FileSystemException(e); + } + + while (enumResources.hasMoreElements()) + { + URL url = enumResources.nextElement(); + configure(url); + } + } + + private ClassLoader findClassLoader() + { + if (classLoader != null) + { + return classLoader; + } + + ClassLoader cl = Thread.currentThread().getContextClassLoader(); + if (cl == null) + { + cl = getClass().getClassLoader(); + } + + return cl; + } + + protected DefaultFileReplicator createDefaultFileReplicator() + { + return new DefaultFileReplicator(); + } + + /** + * Configures this manager from an XML configuration file. + * @param configUri The URI of the configuration. + * @throws FileSystemException if an error occus. + */ + private void configure(final URL configUri) throws FileSystemException + { + InputStream configStream = null; + try + { + // Load up the config + // TODO - validate + /* + final DocumentBuilder builder = createDocumentBuilder(); + configStream = configUri.openStream(); + final Element config = builder.parse(configStream).getDocumentElement(); + + configure(config); + */ + hardCodedConfiguration(); + } + catch (final Exception e) + { + throw new FileSystemException("vfs.impl/load-config.error", configUri.toString(), e); + } + finally + { + if (configStream != null) + { + try + { + configStream.close(); + } + catch (IOException e) + { + getLogger().warn(e.getLocalizedMessage(), e); + } + } + } + } + + private void hardCodedConfiguration() throws FileSystemException + { + //addProvider(providerClassName, schemas, ifAvailableClasses, ifAvailableSchemas, isDefault); + addProvider("org.apache.commons.vfs2.provider.url.UrlFileProvider", + null, + null, + null, + true); + addProvider("org.apache.commons.vfs2.provider.local.DefaultLocalFileProvider", + new String[]{"file"}, + null, + null, + false); + addProvider("org.apache.commons.vfs2.provider.zip.ZipFileProvider", + new String[]{"zip"}, + null, + null, + false); + addProvider("org.apache.commons.vfs2.provider.tar.TarFileProvider", + new String[]{"tar"}, + new String[] {"org.apache.commons.vfs2.provider.tar.TarInputStream"}, + null, + false); + addProvider("org.apache.commons.vfs2.provider.bzip2.Bzip2FileProvider", + new String[]{"bz2"}, + new String[] {"org.apache.commons.vfs2.provider.bzip2.CBZip2InputStream"}, + null, + false); + addProvider("org.apache.commons.vfs2.provider.gzip.GzipFileProvider", + new String[]{"gz"}, + null, + null, + false); + addProvider("org.apache.commons.vfs2.provider.jar.JarFileProvider", + new String[]{"jar", "sar", "ear", "par", "ejb3", "war"}, + null, + null, + false); + addProvider("org.apache.commons.vfs2.provider.temp.TemporaryFileProvider", + new String[]{"tmp"}, + null, + null, + false); + addProvider("org.apache.commons.vfs2.provider.ftp.FtpFileProvider", + new String[]{"ftp"}, + new String[] {"org.apache.commons.net.ftp.FTPFile"}, + null, + false); + addProvider("org.apache.commons.vfs2.provider.ftps.FtpsFileProvider", + new String[]{"ftps"}, + new String[] {"org.apache.commons.net.ftp.FTPFile"}, + null, + false); + addProvider("org.apache.commons.vfs2.provider.http.HttpFileProvider", + new String[]{"http"}, + new String[] {"org.apache.commons.httpclient.HttpClient"}, + null, + false); + addProvider("org.apache.commons.vfs2.provider.https.HttpsFileProvider", + new String[]{"https"}, + new String[] {"org.apache.commons.httpclient.HttpClient"}, + null, + false); + addProvider("org.apache.commons.vfs2.provider.sftp.SftpFileProvider", + new String[]{"sftp"}, + new String[] {"javax.crypto.Cipher","com.jcraft.jsch.JSch"}, + null, + false); + addProvider("org.apache.commons.vfs2.provider.res.ResourceFileProvider", + new String[]{"res"}, + null, + null, + false); + addProvider("org.apache.commons.vfs2.provider.webdav.WebdavFileProvider", + new String[]{"webdav"}, + new String[] {"org.apache.commons.httpclient.HttpClient", + "org.apache.jackrabbit.webdav.client.methods.DavMethod"}, + null, + false); + addProvider("org.apache.commons.vfs2.provider.tar.TarFileProvider", + new String[]{"tbz2"}, + null, + new String[] {"bz2", "tar"}, + false); + addProvider("org.apache.commons.vfs2.provider.ram.RamFileProvider", + new String[]{"ram"}, + null, + null, + false); + addMimeTypeMap("application/zip", "zip"); + addMimeTypeMap("application/x-tar", "tar"); + addMimeTypeMap("application/x-gzip", "gz"); + + addExtensionMap("zip", "zip"); + addExtensionMap("tar", "tar"); + addExtensionMap("jar", "jar"); + addExtensionMap("bz2", "bz2"); + addExtensionMap("gz", "gz"); + addExtensionMap("tgz", "tar"); + addExtensionMap("tbz2", "tar"); + addExtensionMap("jar", "jar"); + } + + private void addProvider(String providerClassName, String[] schemas, String[] requiredClasses, String[] requiredSchemes, boolean isDefault) throws FileSystemException + { + // Make sure all required schemes are available + if (requiredSchemes != null) + for (int i = 0; i < requiredSchemes.length; i++) + { + final String requiredScheme = requiredSchemes[i]; + if (!hasProvider(requiredScheme)) + { + final String msg = Messages.getString("vfs.impl/skipping-provider-scheme.debug", + new String[]{providerClassName, requiredScheme}); + VfsLog.debug(getLogger(), getLogger(), msg); + return; + } + } + + // Make sure all required classes are in classpath + if (requiredClasses != null) + for (int i = 0; i < requiredClasses.length; i++) + { + final String requiredClass = requiredClasses[i]; + if (!findClass(requiredClass)) + { + final String msg = Messages.getString("vfs.impl/skipping-provider.debug", + new String[]{providerClassName, requiredClass}); + VfsLog.debug(getLogger(), getLogger(), msg); + return; + } + } + + // Create and register the provider + final FileProvider provider = (FileProvider) createInstance(providerClassName); + if (schemas != null && schemas.length > 0) + { + addProvider(schemas, provider); + } + + // Set as default, if required + if (isDefault) + { + setDefaultProvider(provider); + } + + + } + + /** + * Configures this manager from an XML configuration file. + * @param configUri The URI of the configuration. + * @param configStream An InputStream containing the configuration. + * @throws FileSystemException if an error occurs. + */ + @SuppressWarnings("unused") + private void configure(final String configUri, final InputStream configStream) + throws FileSystemException + { + try + { + // Load up the config + // TODO - validate + final DocumentBuilder builder = createDocumentBuilder(); + final Element config = builder.parse(configStream).getDocumentElement(); + + configure(config); + + } + catch (final Exception e) + { + throw new FileSystemException("vfs.impl/load-config.error", configUri, e); + } + } + + /** + * Configure and create a DocumentBuilder + * @return A DocumentBuilder for the configuration. + * @throws ParserConfigurationException if an error occurs. + */ + private DocumentBuilder createDocumentBuilder() throws ParserConfigurationException + { + final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + factory.setIgnoringElementContentWhitespace(true); + factory.setIgnoringComments(true); + factory.setExpandEntityReferences(true); + return factory.newDocumentBuilder(); + } + + /** + * Configures this manager from an parsed XML configuration file + * @param config The configuration Element. + * @throws FileSystemException if an error occurs. + */ + private void configure(final Element config) throws FileSystemException + { + // Add the providers + final NodeList providers = config.getElementsByTagName("provider"); + final int count = providers.getLength(); + for (int i = 0; i < count; i++) + { + final Element provider = (Element) providers.item(i); + addProvider(provider, false); + } + + // Add the operation providers + final NodeList operationProviders = config.getElementsByTagName("operationProvider"); + for (int i = 0; i < operationProviders.getLength(); i++) + { + final Element operationProvider = (Element) operationProviders.item(i); + addOperationProvider(operationProvider); + } + + // Add the default provider + final NodeList defProviders = config.getElementsByTagName("default-provider"); + if (defProviders.getLength() > 0) + { + final Element provider = (Element) defProviders.item(0); + addProvider(provider, true); + } + + // Add the mime-type maps + final NodeList mimeTypes = config.getElementsByTagName("mime-type-map"); + for (int i = 0; i < mimeTypes.getLength(); i++) + { + final Element map = (Element) mimeTypes.item(i); + addMimeTypeMap(map); + } + + // Add the extension maps + final NodeList extensions = config.getElementsByTagName("extension-map"); + for (int i = 0; i < extensions.getLength(); i++) + { + final Element map = (Element) extensions.item(i); + addExtensionMap(map); + } + } + + /** + * Adds an extension map. + * @param map containing the Elements. + */ + private void addExtensionMap(final Element map) + { + final String extension = map.getAttribute("extension"); + final String scheme = map.getAttribute("scheme"); + if (scheme != null && scheme.length() > 0) + { + addExtensionMap(extension, scheme); + } + } + + /** + * Adds a mime-type map. + * @param map containing the Elements. + */ + private void addMimeTypeMap(final Element map) + { + final String mimeType = map.getAttribute("mime-type"); + final String scheme = map.getAttribute("scheme"); + addMimeTypeMap(mimeType, scheme); + } + + /** + * Adds a provider from a provider definition. + * @param providerDef the provider definition + * @param isDefault true if the default should be used. + * @throws FileSystemException if an error occurs. + */ + private void addProvider(final Element providerDef, final boolean isDefault) + throws FileSystemException + { + final String classname = providerDef.getAttribute("class-name"); + + // Make sure all required schemes are available + final String[] requiredSchemes = getRequiredSchemes(providerDef); + for (int i = 0; i < requiredSchemes.length; i++) + { + final String requiredScheme = requiredSchemes[i]; + if (!hasProvider(requiredScheme)) + { + final String msg = Messages.getString("vfs.impl/skipping-provider-scheme.debug", + new String[]{classname, requiredScheme}); + VfsLog.debug(getLogger(), getLogger(), msg); + return; + } + } + + // Make sure all required classes are in classpath + final String[] requiredClasses = getRequiredClasses(providerDef); + for (int i = 0; i < requiredClasses.length; i++) + { + final String requiredClass = requiredClasses[i]; + if (!findClass(requiredClass)) + { + final String msg = Messages.getString("vfs.impl/skipping-provider.debug", + new String[]{classname, requiredClass}); + VfsLog.debug(getLogger(), getLogger(), msg); + return; + } + } + + // Create and register the provider + final FileProvider provider = (FileProvider) createInstance(classname); + final String[] schemas = getSchemas(providerDef); + if (schemas.length > 0) + { + addProvider(schemas, provider); + } + + // Set as default, if required + if (isDefault) + { + setDefaultProvider(provider); + } + } + + /** + * Adds a operationProvider from a operationProvider definition. + */ + private void addOperationProvider(final Element providerDef) throws FileSystemException + { + final String classname = providerDef.getAttribute("class-name"); + + // Attach only to available schemas + final String[] schemas = getSchemas(providerDef); + for (int i = 0; i < schemas.length; i++) + { + final String schema = schemas[i]; + if (hasProvider(schema)) + { + final FileOperationProvider operationProvider = (FileOperationProvider) createInstance(classname); + addOperationProvider(schema, operationProvider); + } + } + } + + /** + * Tests if a class is available. + */ + private boolean findClass(final String className) + { + try + { + findClassLoader().loadClass(className); + return true; + } + catch (final ClassNotFoundException e) + { + return false; + } + } + + /** + * Extracts the required classes from a provider definition. + */ + private String[] getRequiredClasses(final Element providerDef) + { + final ArrayList classes = new ArrayList(); + final NodeList deps = providerDef.getElementsByTagName("if-available"); + final int count = deps.getLength(); + for (int i = 0; i < count; i++) + { + final Element dep = (Element) deps.item(i); + String className = dep.getAttribute("class-name"); + if (className != null && className.length() > 0) + { + classes.add(className); + } + } + return classes.toArray(new String[classes.size()]); + } + + /** + * Extracts the required schemes from a provider definition. + */ + private String[] getRequiredSchemes(final Element providerDef) + { + final ArrayList schemes = new ArrayList(); + final NodeList deps = providerDef.getElementsByTagName("if-available"); + final int count = deps.getLength(); + for (int i = 0; i < count; i++) + { + final Element dep = (Element) deps.item(i); + String scheme = dep.getAttribute("scheme"); + if (scheme != null && scheme.length() > 0) + { + schemes.add(scheme); + } + } + return schemes.toArray(new String[schemes.size()]); + } + + /** + * Extracts the schema names from a provider definition. + */ + private String[] getSchemas(final Element provider) + { + final ArrayList schemas = new ArrayList(); + final NodeList schemaElements = provider.getElementsByTagName("scheme"); + final int count = schemaElements.getLength(); + for (int i = 0; i < count; i++) + { + final Element scheme = (Element) schemaElements.item(i); + schemas.add(scheme.getAttribute("name")); + } + return schemas.toArray(new String[schemas.size()]); + } + + /** + * Creates a provider. + */ + private Object createInstance(final String className) + throws FileSystemException + { + try + { + final Class clazz = findClassLoader().loadClass(className); + return clazz.newInstance(); + } + catch (final Exception e) + { + throw new FileSystemException("vfs.impl/create-provider.error", className, e); + } + } +} diff --git a/javaUtilities/yajsw/src/main/java/org/codehaus/groovy/binding/PropertyBinding.java b/javaUtilities/yajsw/src/main/java/org/codehaus/groovy/binding/PropertyBinding.java new file mode 100644 index 0000000000..86f283bf99 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/codehaus/groovy/binding/PropertyBinding.java @@ -0,0 +1,372 @@ +/* + * Copyright 2007-2009 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.codehaus.groovy.binding; + +import groovy.beans.DefaultPropertyAccessor; +import groovy.beans.PropertyAccessor; +import groovy.lang.GroovyRuntimeException; +import groovy.lang.MissingMethodException; + +import java.awt.Component; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.beans.PropertyVetoException; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.net.URL; +import java.util.Enumeration; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +import javax.swing.SwingUtilities; + +import org.codehaus.groovy.runtime.DefaultGroovyMethods; +import org.codehaus.groovy.runtime.InvokerHelper; +import org.codehaus.groovy.runtime.InvokerInvocationException; +import org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation; +import org.jboss.netty.logging.InternalLogger; +import org.jboss.netty.logging.InternalLoggerFactory; + + +/** + * @author Danno Ferrin + * @author Andres Almiray + * @version $Revision$ + * @since Groovy 1.1 + */ +public class PropertyBinding implements SourceBinding, TargetBinding, TriggerBinding { + private static final ExecutorService DEFAULT_EXECUTOR_SERVICE = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()); + //private static final Logger LOG = Logger.getLogger(PropertyBinding.class.getName()); + private static final InternalLogger LOG = InternalLoggerFactory.getInstance(PropertyBinding.class.getName()); + + private static final Map> ACCESSORS = new LinkedHashMap>(); + + static { + Enumeration urls = fetchUrlsFor("META-INF/services/" + groovy.beans.PropertyAccessor.class.getName()); + while (urls.hasMoreElements()) { + try { + registerPropertyAccessors(DefaultGroovyMethods.readLines(urls.nextElement())); + } catch (IOException e) { + // ignore + // TODO should use a low priority logger + e.printStackTrace(); + } + } + } + + private static void registerPropertyAccessors(List lines) { + ClassLoader cl = Thread.currentThread().getContextClassLoader(); + for (String line : lines) { + line = line.trim(); + if (line.startsWith("#")) return; + String[] parts = line.split("="); + if (parts.length == 2) { + try { + ACCESSORS.put(cl.loadClass(parts[0].trim()), (Class) cl.loadClass(parts[1].trim())); + } catch (ClassNotFoundException e) { + // ignore + // TODO should use a low priority logger + e.printStackTrace(); + } + } + } + } + + private static Enumeration fetchUrlsFor(String path) { + try { + return Thread.currentThread().getContextClassLoader().getResources(path); + } catch (IOException e) { + return new Enumeration() { + public boolean hasMoreElements() { + return false; + } + + public URL nextElement() { + return null; + } + }; + } + } + + Object bean; + String propertyName; + boolean nonChangeCheck; + UpdateStrategy updateStrategy; + private final Object[] lock = new Object[0]; + private PropertyAccessor propertyAccessor; + + public PropertyBinding(Object bean, String propertyName) { + this(bean, propertyName, (UpdateStrategy) null); + } + + public PropertyBinding(Object bean, String propertyName, String updateStrategy) { + this(bean, propertyName, UpdateStrategy.of(updateStrategy)); + } + + public PropertyBinding(Object bean, String propertyName, UpdateStrategy updateStrategy) { + this.bean = bean; + this.propertyName = propertyName; + this.updateStrategy = pickUpdateStrategy(bean, updateStrategy); + if (LOG.isDebugEnabled()) { + LOG.debug("Updating with " + this.updateStrategy + " property '" + propertyName + "' of bean " + bean); + } + setupPropertyReaderAndWriter(); + } + + private void setupPropertyReaderAndWriter() { + synchronized (lock) { + propertyAccessor = fetchPropertyAccessor(bean != null ? bean.getClass() : null); + } + } + + private PropertyAccessor propertyAccessor() { + synchronized (lock) { + return propertyAccessor; + } + } + + private PropertyAccessor fetchPropertyAccessor(Class klass) { + if (klass == null) { + return DefaultPropertyAccessor.INSTANCE; + } + + Class accessorClass = ACCESSORS.get(klass); + if (accessorClass == null) { + for (Class c : klass.getInterfaces()) { + PropertyAccessor propertyAccessor = fetchPropertyAccessor(c); + if (propertyAccessor != DefaultPropertyAccessor.INSTANCE) { + return propertyAccessor; + } + } + return fetchPropertyAccessor(klass.getSuperclass()); + } + + try { + return accessorClass.newInstance(); + } catch (InstantiationException e) { + return DefaultPropertyAccessor.INSTANCE; + } catch (IllegalAccessException e) { + return DefaultPropertyAccessor.INSTANCE; + } + } + + public UpdateStrategy getUpdateStrategy() { + return updateStrategy; + } + + private UpdateStrategy pickUpdateStrategy(Object bean, UpdateStrategy updateStrategy) { + if (bean instanceof Component) { + return UpdateStrategy.MIXED; + } else if (updateStrategy != null) { + return updateStrategy; + } + return UpdateStrategy.SAME; + } + + public void updateTargetValue(final Object newValue) { + Runnable runnable = new Runnable() { + public void run() { + Object sourceValue = getSourceValue(); + // if (isNonChangeCheck()) { + if ((sourceValue == null && newValue == null) || + DefaultTypeTransformation.compareEqual(sourceValue, newValue)) { + // not a change, don't fire it + return; + } + // } + setBeanProperty(newValue); + } + }; + + switch (updateStrategy) { + case MIXED: + if (SwingUtilities.isEventDispatchThread()) { + runnable.run(); + } else { + SwingUtilities.invokeLater(runnable); + } + break; + case ASYNC: + SwingUtilities.invokeLater(runnable); + break; + case SYNC: + if (SwingUtilities.isEventDispatchThread()) { + runnable.run(); + } else { + try { + SwingUtilities.invokeAndWait(runnable); + } catch (InterruptedException e) { + LOG.warn("Error notifying propertyChangeListener", e); + throw new GroovyRuntimeException(e); + } catch (InvocationTargetException e) { + LOG.warn("Error notifying propertyChangeListener", e.getTargetException()); + throw new GroovyRuntimeException(e.getTargetException()); + } + } + break; + case SAME: + runnable.run(); + break; + case OUTSIDE: + if (SwingUtilities.isEventDispatchThread()) { + DEFAULT_EXECUTOR_SERVICE.submit(runnable); + } else { + runnable.run(); + } + break; + case DEFER: + DEFAULT_EXECUTOR_SERVICE.submit(runnable); + } + } + + private void setBeanProperty(Object newValue) { + try { + propertyAccessor().write(bean, propertyName, newValue); + } catch (InvokerInvocationException iie) { + if (!(iie.getCause() instanceof PropertyVetoException)) { + throw iie; + } + // ignore veto exceptions, just let the binding fail like a validation does + } + } + + public boolean isNonChangeCheck() { + synchronized (lock) { + return nonChangeCheck; + } + } + + public void setNonChangeCheck(boolean nonChangeCheck) { + synchronized (lock) { + this.nonChangeCheck = nonChangeCheck; + } + } + + public Object getSourceValue() { + return propertyAccessor().read(bean, propertyName); + } + + public FullBinding createBinding(SourceBinding source, TargetBinding target) { + return new PropertyFullBinding(source, target); + } + + class PropertyFullBinding extends AbstractFullBinding implements PropertyChangeListener { + + Object boundBean; + Object boundProperty; + boolean bound; + boolean boundToProperty; + + PropertyFullBinding(SourceBinding source, TargetBinding target) { + setSourceBinding(source); + setTargetBinding(target); + } + + public void propertyChange(PropertyChangeEvent event) { + if (boundToProperty || event.getPropertyName().equals(boundProperty)) { + update(); + } + } + + public void bind() { + if (!bound) { + bound = true; + boundBean = bean; + boundProperty = propertyName; + try { + InvokerHelper.invokeMethodSafe(boundBean, "addPropertyChangeListener", new Object[]{boundProperty, this}); + boundToProperty = true; + } catch (MissingMethodException mme) { + try { + boundToProperty = false; + InvokerHelper.invokeMethodSafe(boundBean, "addPropertyChangeListener", new Object[]{this}); + } catch (MissingMethodException mme2) { + throw new RuntimeException("Properties in beans of type " + bean.getClass().getName() + " are not observable in any capacity (no PropertyChangeListener support)."); + } + } + } + } + + public void unbind() { + if (bound) { + if (boundToProperty) { + try { + InvokerHelper.invokeMethodSafe(boundBean, "removePropertyChangeListener", new Object[]{boundProperty, this}); + } catch (MissingMethodException mme) { + // ignore, too bad so sad they don't follow conventions, we'll just leave the listener attached + } + } else { + try { + InvokerHelper.invokeMethodSafe(boundBean, "removePropertyChangeListener", new Object[]{this}); + } catch (MissingMethodException mme2) { + // ignore, too bad so sad they don't follow conventions, we'll just leave the listener attached + } + } + boundBean = null; + boundProperty = null; + bound = false; + } + } + + public void rebind() { + if (bound) { + unbind(); + bind(); + } + } + } + + public Object getBean() { + return bean; + } + + public void setBean(Object bean) { + this.bean = bean; + setupPropertyReaderAndWriter(); + } + + public String getPropertyName() { + return propertyName; + } + + public void setPropertyName(String propertyName) { + this.propertyName = propertyName; + } + + public enum UpdateStrategy { + MIXED, ASYNC, SYNC, SAME, OUTSIDE, DEFER; + + public static UpdateStrategy of(String str) { + if ("mixed".equalsIgnoreCase(str)) { + return MIXED; + } else if ("async".equalsIgnoreCase(str)) { + return ASYNC; + } else if ("sync".equalsIgnoreCase(str)) { + return SYNC; + } else if ("same".equalsIgnoreCase(str)) { + return SAME; + } else if ("outside".equalsIgnoreCase(str)) { + return OUTSIDE; + } else if ("defer".equalsIgnoreCase(str)) { + return DEFER; + } + return null; + } + } +} diff --git a/javaUtilities/yajsw/src/main/java/org/codehaus/groovy/runtime/DefaultGroovyMethods.java b/javaUtilities/yajsw/src/main/java/org/codehaus/groovy/runtime/DefaultGroovyMethods.java new file mode 100644 index 0000000000..9a39ab3a19 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/codehaus/groovy/runtime/DefaultGroovyMethods.java @@ -0,0 +1,18631 @@ +/* + * Copyright 2003-2012 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.codehaus.groovy.runtime; + +import groovy.io.EncodingAwareBufferedWriter; +import groovy.io.FileType; +import groovy.io.FileVisitResult; +import groovy.io.GroovyPrintWriter; +import groovy.lang.Closure; +import groovy.lang.DelegatingMetaClass; +import groovy.lang.EmptyRange; +import groovy.lang.ExpandoMetaClass; +import groovy.lang.GString; +import groovy.lang.GroovyObject; +import groovy.lang.GroovyRuntimeException; +import groovy.lang.GroovySystem; +import groovy.lang.IntRange; +import groovy.lang.MapWithDefault; +import groovy.lang.MetaClass; +import groovy.lang.MetaClassImpl; +import groovy.lang.MetaClassRegistry; +import groovy.lang.MetaMethod; +import groovy.lang.MetaProperty; +import groovy.lang.MissingPropertyException; +import groovy.lang.ObjectRange; +import groovy.lang.PropertyValue; +import groovy.lang.Range; +import groovy.lang.SpreadMap; +import groovy.lang.StringWriterIOException; +import groovy.lang.Writable; +import groovy.util.CharsetToolkit; +import groovy.util.ClosureComparator; +import groovy.util.GroovyCollections; +import groovy.util.MapEntry; +import groovy.util.OrderBy; +import groovy.util.PermutationGenerator; +import groovy.util.ProxyGenerator; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.EOFException; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.ObjectStreamClass; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.PrintStream; +import java.io.PrintWriter; +import java.io.Reader; +import java.io.StringReader; +import java.io.StringWriter; +import java.io.UnsupportedEncodingException; +import java.io.Writer; +import java.lang.reflect.Array; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.lang.reflect.Proxy; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.net.MalformedURLException; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.net.URLConnection; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.AbstractCollection; +import java.util.AbstractMap; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.BitSet; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.ListIterator; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Queue; +import java.util.Set; +import java.util.SortedMap; +import java.util.SortedSet; +import java.util.Stack; +import java.util.StringTokenizer; +import java.util.Timer; +import java.util.TimerTask; +import java.util.TreeMap; +import java.util.TreeSet; +import java.util.concurrent.BlockingQueue; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.codehaus.groovy.classgen.Verifier; +import org.codehaus.groovy.reflection.ClassInfo; +import org.codehaus.groovy.reflection.MixinInMetaClass; +import org.codehaus.groovy.reflection.ReflectionCache; +import org.codehaus.groovy.runtime.dgmimpl.NumberNumberDiv; +import org.codehaus.groovy.runtime.dgmimpl.NumberNumberMinus; +import org.codehaus.groovy.runtime.dgmimpl.NumberNumberMultiply; +import org.codehaus.groovy.runtime.dgmimpl.NumberNumberPlus; +import org.codehaus.groovy.runtime.dgmimpl.arrays.BooleanArrayGetAtMetaMethod; +import org.codehaus.groovy.runtime.dgmimpl.arrays.BooleanArrayPutAtMetaMethod; +import org.codehaus.groovy.runtime.dgmimpl.arrays.ByteArrayGetAtMetaMethod; +import org.codehaus.groovy.runtime.dgmimpl.arrays.ByteArrayPutAtMetaMethod; +import org.codehaus.groovy.runtime.dgmimpl.arrays.CharacterArrayGetAtMetaMethod; +import org.codehaus.groovy.runtime.dgmimpl.arrays.CharacterArrayPutAtMetaMethod; +import org.codehaus.groovy.runtime.dgmimpl.arrays.DoubleArrayGetAtMetaMethod; +import org.codehaus.groovy.runtime.dgmimpl.arrays.DoubleArrayPutAtMetaMethod; +import org.codehaus.groovy.runtime.dgmimpl.arrays.FloatArrayGetAtMetaMethod; +import org.codehaus.groovy.runtime.dgmimpl.arrays.FloatArrayPutAtMetaMethod; +import org.codehaus.groovy.runtime.dgmimpl.arrays.IntegerArrayGetAtMetaMethod; +import org.codehaus.groovy.runtime.dgmimpl.arrays.IntegerArrayPutAtMetaMethod; +import org.codehaus.groovy.runtime.dgmimpl.arrays.LongArrayGetAtMetaMethod; +import org.codehaus.groovy.runtime.dgmimpl.arrays.LongArrayPutAtMetaMethod; +import org.codehaus.groovy.runtime.dgmimpl.arrays.ObjectArrayGetAtMetaMethod; +import org.codehaus.groovy.runtime.dgmimpl.arrays.ObjectArrayPutAtMetaMethod; +import org.codehaus.groovy.runtime.dgmimpl.arrays.ShortArrayGetAtMetaMethod; +import org.codehaus.groovy.runtime.dgmimpl.arrays.ShortArrayPutAtMetaMethod; +import org.codehaus.groovy.runtime.metaclass.MetaClassRegistryImpl; +import org.codehaus.groovy.runtime.metaclass.MissingPropertyExceptionNoStack; +import org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation; +import org.codehaus.groovy.runtime.typehandling.GroovyCastException; +import org.codehaus.groovy.runtime.typehandling.NumberMath; +import org.codehaus.groovy.tools.RootLoader; +import org.jboss.netty.logging.InternalLogger; +import org.jboss.netty.logging.InternalLoggerFactory; + +/** + * This class defines new groovy methods which appear on normal JDK + * classes inside the Groovy environment. Static methods are used with the + * first parameter being the destination class, + * i.e. public static String reverse(String self) + * provides a reverse() method for String. + *

    + * NOTE: While this class contains many 'public' static methods, it is + * primarily regarded as an internal class (its internal package name + * suggests this also). We value backwards compatibility of these + * methods when used within Groovy but value less backwards compatibility + * at the Java method call level. I.e. future versions of Groovy may + * remove or move a method call in this file but would normally + * aim to keep the method available from within Groovy. + * + * @author James Strachan + * @author Jeremy Rayner + * @author Sam Pullara + * @author Rod Cope + * @author Guillaume Laforge + * @author John Wilson + * @author Hein Meling + * @author Dierk Koenig + * @author Pilho Kim + * @author Marc Guillemot + * @author Russel Winder + * @author bing ran + * @author Jochen Theodorou + * @author Paul King + * @author Michael Baehr + * @author Joachim Baumann + * @author Alex Tkachman + * @author Ted Naleid + * @author Brad Long + * @author Jim Jagielski + * @author Rodolfo Velasco + * @author jeremi Joslin + * @author Hamlet D'Arcy + * @author Cedric Champeau + * @author Tim Yates + * @author Dinko Srkoc + */ +public class DefaultGroovyMethods extends DefaultGroovyMethodsSupport { + + //private static final Logger LOG = Logger.getLogger(DefaultGroovyMethods.class.getName()); + private static final InternalLogger LOG = InternalLoggerFactory.getInstance(DefaultGroovyMethods.class.getName()); + + private static final Integer ONE = 1; + private static final BigInteger BI_INT_MAX = BigInteger.valueOf(Integer.MAX_VALUE); + private static final BigInteger BI_INT_MIN = BigInteger.valueOf(Integer.MIN_VALUE); + private static final BigInteger BI_LONG_MAX = BigInteger.valueOf(Long.MAX_VALUE); + private static final BigInteger BI_LONG_MIN = BigInteger.valueOf(Long.MIN_VALUE); + + public static final Class [] additionals = { + NumberNumberPlus.class, + NumberNumberMultiply.class, + NumberNumberMinus.class, + NumberNumberDiv.class, + ObjectArrayGetAtMetaMethod.class, + ObjectArrayPutAtMetaMethod.class, + BooleanArrayGetAtMetaMethod.class, + BooleanArrayPutAtMetaMethod.class, + ByteArrayGetAtMetaMethod.class, + ByteArrayPutAtMetaMethod.class, + CharacterArrayGetAtMetaMethod.class, + CharacterArrayPutAtMetaMethod.class, + ShortArrayGetAtMetaMethod.class, + ShortArrayPutAtMetaMethod.class, + IntegerArrayGetAtMetaMethod.class, + IntegerArrayPutAtMetaMethod.class, + LongArrayGetAtMetaMethod.class, + LongArrayPutAtMetaMethod.class, + FloatArrayGetAtMetaMethod.class, + FloatArrayPutAtMetaMethod.class, + DoubleArrayGetAtMetaMethod.class, + DoubleArrayPutAtMetaMethod.class, + }; + + /** + * Identity check. Since == is overridden in Groovy with the meaning of equality + * we need some fallback to check for object identity. Invoke using the + * 'is' method, like so: def same = this.is(that) + * + * @param self an object + * @param other an object to compare identity with + * @return true if self and other are both references to the same + * instance, false otherwise + * @since 1.0 + */ + public static boolean is(Object self, Object other) { + return self == other; + } + + /** + * Allows the closure to be called for the object reference self. + * Synonym for 'with()'. + * + * @param self the object to have a closure act upon + * @param closure the closure to call on the object + * @return result of calling the closure + * @since 1.0 + */ + public static T identity(Object self, Closure closure) { + return DefaultGroovyMethods.with(self, closure); + } + + /** + * Allows the closure to be called for the object reference self.

    + * Any method invoked inside the closure will first be invoked on the + * self reference. For instance, the following method calls to the append() + * method are invoked on the StringBuilder instance: + *

    +     * def b = new StringBuilder().with {
    +     *   append('foo')
    +     *   append('bar')
    +     *   return it
    +     * }
    +     * assert b.toString() == 'foobar' 
    +     * 
    + * This is commonly used to simplify object creation, such as this example: + *
    +     * def p = new Person().with {
    +     *   firstName = 'John'
    +     *   lastName = 'Doe'
    +     *   return it
    +     * }
    +     * 
    + * + * @param self the object to have a closure act upon + * @param closure the closure to call on the object + * @return result of calling the closure + * @since 1.5.0 + */ + public static T with(Object self, Closure closure) { + @SuppressWarnings("unchecked") + final Closure clonedClosure = (Closure) closure.clone(); + clonedClosure.setResolveStrategy(Closure.DELEGATE_FIRST); + clonedClosure.setDelegate(self); + return clonedClosure.call(self); + } + + /** + * Allows the subscript operator to be used to lookup dynamic property values. + * bean[somePropertyNameExpression]. The normal property notation + * of groovy is neater and more concise but only works with compile-time known + * property names. + * + * @param self the object to act upon + * @param property the property name of interest + * @return the property value + * @since 1.0 + */ + public static Object getAt(Object self, String property) { + return InvokerHelper.getProperty(self, property); + } + + /** + * Allows the subscript operator to be used to set dynamically named property values. + * bean[somePropertyNameExpression] = foo. The normal property notation + * of groovy is neater and more concise but only works with property names which + * are known at compile time. + * + * @param self the object to act upon + * @param property the name of the property to set + * @param newValue the value to set + * @since 1.0 + */ + public static void putAt(Object self, String property, Object newValue) { + InvokerHelper.setProperty(self, property, newValue); + } + + /** + * Generates a detailed dump string of an object showing its class, + * hashCode and fields. + * + * @param self an object + * @return the dump representation + * @since 1.0 + */ + public static String dump(Object self) { + if (self == null) { + return "null"; + } + StringBuilder buffer = new StringBuilder("<"); + Class klass = self.getClass(); + buffer.append(klass.getName()); + buffer.append("@"); + buffer.append(Integer.toHexString(self.hashCode())); + boolean groovyObject = self instanceof GroovyObject; + + /*jes this may be rewritten to use the new getProperties() stuff + * but the original pulls out private variables, whereas getProperties() + * does not. What's the real use of dump() here? + */ + while (klass != null) { + for (final Field field : klass.getDeclaredFields()) { + if ((field.getModifiers() & Modifier.STATIC) == 0) { + if (groovyObject && field.getName().equals("metaClass")) { + continue; + } + AccessController.doPrivileged(new PrivilegedAction() { + public Object run() { + field.setAccessible(true); + return null; + } + }); + buffer.append(" "); + buffer.append(field.getName()); + buffer.append("="); + try { + buffer.append(InvokerHelper.toString(field.get(self))); + } catch (Exception e) { + buffer.append(e); + } + } + } + + klass = klass.getSuperclass(); + } + + /* here is a different implementation that uses getProperties(). I have left + * it commented out because it returns a slightly different list of properties; + * i.e. it does not return privates. I don't know what dump() really should be doing, + * although IMO showing private fields is a no-no + */ + /* + List props = getProperties(self); + for(Iterator itr = props.keySet().iterator(); itr.hasNext(); ) { + String propName = itr.next().toString(); + + // the original skipped this, so I will too + if(pv.getName().equals("class")) continue; + if(pv.getName().equals("metaClass")) continue; + + buffer.append(" "); + buffer.append(propName); + buffer.append("="); + try { + buffer.append(InvokerHelper.toString(props.get(propName))); + } + catch (Exception e) { + buffer.append(e); + } + } + */ + + buffer.append(">"); + return buffer.toString(); + } + + /** + * Retrieves the list of {@link groovy.lang.MetaProperty} objects for 'self' and wraps it + * in a list of {@link groovy.lang.PropertyValue} objects that additionally provide + * the value for each property of 'self'. + * + * @param self the receiver object + * @return list of {@link groovy.lang.PropertyValue} objects + * @see groovy.util.Expando#getMetaPropertyValues() + * @since 1.0 + */ + public static List getMetaPropertyValues(Object self) { + MetaClass metaClass = InvokerHelper.getMetaClass(self); + List mps = metaClass.getProperties(); + List props = new ArrayList(mps.size()); + for (MetaProperty mp : mps) { + props.add(new PropertyValue(self, mp)); + } + return props; + } + + /** + * Convenience method that calls {@link #getMetaPropertyValues(java.lang.Object)}(self) + * and provides the data in form of simple key/value pairs, i.e.&nsbp;without + * type() information. + * + * @param self the receiver object + * @return meta properties as Map of key/value pairs + * @since 1.0 + */ + public static Map getProperties(Object self) { + List metaProps = getMetaPropertyValues(self); + Map props = new LinkedHashMap(metaProps.size()); + + for (PropertyValue mp : metaProps) { + try { + props.put(mp.getName(), mp.getValue()); + } catch (Exception e) { + LOG.error(self.getClass().getName()+ " " + "getProperty(" + mp.getName() + ")", e); + } + } + return props; + } + + /** + * Scoped use method + * + * @param self any Object + * @param categoryClass a category class to use + * @param closure the closure to invoke with the category in place + * @return the value returned from the closure + * @since 1.0 + */ + public static T use(Object self, Class categoryClass, Closure closure) { + return GroovyCategorySupport.use(categoryClass, closure); + } + + /** + * Extend object with category methods. + * All methods for given class and all super classes will be added to the object. + * + * @param self any Class + * @param categoryClasses a category classes to use + * @since 1.6.0 + */ + public static void mixin(MetaClass self, List categoryClasses) { + MixinInMetaClass.mixinClassesToMetaClass(self, categoryClasses); + } + + /** + * Extend class globally with category methods. + * All methods for given class and all super classes will be added to the class. + * + * @param self any Class + * @param categoryClasses a category classes to use + * @since 1.6.0 + */ + public static void mixin(Class self, List categoryClasses) { + mixin(getMetaClass(self), categoryClasses); + } + + /** + * Extend class globally with category methods. + * + * @param self any Class + * @param categoryClass a category class to use + * @since 1.6.0 + */ + public static void mixin(Class self, Class categoryClass) { + mixin(getMetaClass(self), Collections.singletonList(categoryClass)); + } + + /** + * Extend class globally with category methods. + * + * @param self any Class + * @param categoryClass a category class to use + * @since 1.6.0 + */ + public static void mixin(Class self, Class[] categoryClass) { + mixin(getMetaClass(self), Arrays.asList(categoryClass)); + } + + /** + * Extend class globally with category methods. + * + * @param self any Class + * @param categoryClass a category class to use + * @since 1.6.0 + */ + public static void mixin(MetaClass self, Class categoryClass) { + mixin(self, Collections.singletonList(categoryClass)); + } + + /** + * Extend class globally with category methods. + * + * @param self any Class + * @param categoryClass a category class to use + * @since 1.6.0 + */ + public static void mixin(MetaClass self, Class[] categoryClass) { + mixin(self, Arrays.asList(categoryClass)); + } + + /** + * Scoped use method with list of categories. + * + * @param self any Object + * @param categoryClassList a list of category classes + * @param closure the closure to invoke with the categories in place + * @return the value returned from the closure + * @since 1.0 + */ + public static T use(Object self, List categoryClassList, Closure closure) { + return GroovyCategorySupport.use(categoryClassList, closure); + } + + /** + * Allows the usage of addShutdownHook without getting the runtime first. + * + * @param self the object the method is called on (ignored) + * @param closure the shutdown hook action + * @since 1.5.0 + */ + public static void addShutdownHook(Object self, Closure closure) { + Runtime.getRuntime().addShutdownHook(new Thread(closure)); + } + + /** + * Allows you to use a list of categories, specifying the list as varargs. + * use(CategoryClass1, CategoryClass2) { ... } + * This method saves having to wrap the the category + * classes in a list. + * + * @param self any Object + * @param array a list of category classes and a Closure + * @return the value returned from the closure + * @since 1.0 + */ + public static Object use(Object self, Object[] array) { + if (array.length < 2) + throw new IllegalArgumentException( + "Expecting at least 2 arguments, a category class and a Closure"); + Closure closure; + try { + closure = (Closure) array[array.length - 1]; + } catch (ClassCastException e) { + throw new IllegalArgumentException("Expecting a Closure to be the last argument"); + } + List list = new ArrayList(array.length - 1); + for (int i = 0; i < array.length - 1; ++i) { + Class categoryClass; + try { + categoryClass = (Class) array[i]; + } catch (ClassCastException e) { + throw new IllegalArgumentException("Expecting a Category Class for argument " + i); + } + list.add(categoryClass); + } + return GroovyCategorySupport.use(list, closure); + } + + /** + * Print a value formatted Groovy style to self if it + * is a Writer, otherwise to the standard output stream. + * + * @param self any Object + * @param value the value to print + * @since 1.0 + */ + public static void print(Object self, Object value) { + // we won't get here if we are a PrintWriter + if (self instanceof Writer) { + try { + ((Writer) self).write(InvokerHelper.toString(value)); + } catch (IOException e) { + // TODO: Should we have some unified function like PrintWriter.checkError()? + } + } else { + System.out.print(InvokerHelper.toString(value)); + } + } + + /** + * Print a value formatted Groovy style to the print writer. + * + * @param self a PrintWriter + * @param value the value to print + * @since 1.0 + */ + public static void print(PrintWriter self, Object value) { + self.print(InvokerHelper.toString(value)); + } + + /** + * Print a value formatted Groovy style to the print stream. + * + * @param self a PrintStream + * @param value the value to print + * @since 1.6.0 + */ + public static void print(PrintStream self, Object value) { + self.print(InvokerHelper.toString(value)); + } + + /** + * Print a value to the standard output stream. + * This method delegates to the owner to execute the method. + * + * @param self a generated closure + * @param value the value to print + * @since 1.0 + */ + public static void print(Closure self, Object value) { + Object owner = getClosureOwner(self); + InvokerHelper.invokeMethod(owner, "print", new Object[]{value}); + } + + /** + * Print a linebreak to the standard output stream. + * + * @param self any Object + * @since 1.0 + */ + public static void println(Object self) { + // we won't get here if we are a PrintWriter + if (self instanceof Writer) { + PrintWriter pw = new GroovyPrintWriter((Writer) self); + pw.println(); + } else { + System.out.println(); + } + } + + /** + * Print a linebreak to the standard output stream. + * This method delegates to the owner to execute the method. + * + * @param self a closure + * @since 1.0 + */ + public static void println(Closure self) { + Object owner = getClosureOwner(self); + InvokerHelper.invokeMethod(owner, "println", new Object[0]); + } + + private static Object getClosureOwner(Closure cls) { + Object owner = cls.getOwner(); + while (owner instanceof GeneratedClosure) { + owner = ((Closure) owner).getOwner(); + } + return owner; + } + + /** + * Print a value formatted Groovy style (followed by a newline) to self + * if it is a Writer, otherwise to the standard output stream. + * + * @param self any Object + * @param value the value to print + * @since 1.0 + */ + public static void println(Object self, Object value) { + // we won't get here if we are a PrintWriter + if (self instanceof Writer) { + final PrintWriter pw = new GroovyPrintWriter((Writer) self); + pw.println(value); + } else { + System.out.println(InvokerHelper.toString(value)); + } + } + + /** + * Print a value formatted Groovy style (followed by a newline) to the print writer. + * + * @param self a PrintWriter + * @param value the value to print + * @since 1.0 + */ + public static void println(PrintWriter self, Object value) { + self.println(InvokerHelper.toString(value)); + } + + /** + * Print a value formatted Groovy style (followed by a newline) to the print stream. + * + * @param self any Object + * @param value the value to print + * @since 1.6.0 + */ + public static void println(PrintStream self, Object value) { + self.println(InvokerHelper.toString(value)); + } + + /** + * Print a value (followed by a newline) to the standard output stream. + * This method delegates to the owner to execute the method. + * + * @param self a closure + * @param value the value to print + * @since 1.0 + */ + public static void println(Closure self, Object value) { + Object owner = getClosureOwner(self); + InvokerHelper.invokeMethod(owner, "println", new Object[]{value}); + } + + /** + * Printf to a console (Only works with JDK1.5 or later). + * + * @param self any Object + * @param format a format string + * @param values values referenced by the format specifiers in the format string. + * @since 1.0 + */ + public static void printf(Object self, String format, Object[] values) { + if (self instanceof PrintStream) + ((PrintStream)self).printf(format, values); + else + System.out.printf(format, values); + } + + /** + * Sprintf to a string (Only works with JDK1.5 or later). + * + * @param self any Object + * @param format a format string + * @param values values referenced by the format specifiers in the format string. + * @return the resulting formatted string + * @since 1.5.0 + */ + public static String sprintf(Object self, String format, Object[] values) { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + PrintStream out = new PrintStream(outputStream); + out.printf(format, values); + return outputStream.toString(); + } + + /** + * Prints a formatted string using the specified format string and + * arguments. + *

    + * For examples,

    +     *     printf ( "Hello, %s!\n" , [ "world" ] as String[] )
    +     *     printf ( "Hello, %s!\n" , [ "Groovy" ])
    +     *     printf ( "%d + %d = %d\n" , [ 1 , 2 , 1+2 ] as Integer[] )
    +     *     printf ( "%d + %d = %d\n" , [ 3 , 3 , 3+3 ])
    +     * 

    + * ( 1..5 ).each { printf ( "-- %d\n" , [ it ] as Integer[] ) } + * ( 1..5 ).each { printf ( "-- %d\n" , [ it ] as int[] ) } + * ( 0x41..0x45 ).each { printf ( "-- %c\n" , [ it ] as char[] ) } + * ( 07..011 ).each { printf ( "-- %d\n" , [ it ] as byte[] ) } + * ( 7..11 ).each { printf ( "-- %d\n" , [ it ] as short[] ) } + * ( 7..11 ).each { printf ( "-- %d\n" , [ it ] as long[] ) } + * ( 7..11 ).each { printf ( "-- %5.2f\n" , [ it ] as float[] ) } + * ( 7..11 ).each { printf ( "-- %5.2g\n" , [ it ] as double[] ) } + *

    + *

    + * + * @param self any Object + * @param format A format string + * @param arg Argument which is referenced by the format specifiers in the format + * string. The type of arg should be one of Object[], List, + * int[], short[], byte[], char[], boolean[], long[], float[], or double[]. + * @since 1.0 + */ + public static void printf(Object self, String format, Object arg) { + if (self instanceof PrintStream) + printf((PrintStream) self, format, arg); + else if (self instanceof Writer) + printf((Writer) self, format, arg); + else + printf(System.out, format, arg); + } + + private static void printf(PrintStream self, String format, Object arg) { + self.print(sprintf(self, format, arg)); + } + + private static void printf(Writer self, String format, Object arg) { + try { + self.write(sprintf(self, format, arg)); + } catch (IOException e) { + printf(System.out, format, arg); + } + } + + /** + * Returns a formatted string using the specified format string and + * arguments. + *

    + * + * @param self any Object + * @param format A format string + * @param arg Argument which is referenced by the format specifiers in the format + * string. The type of arg should be one of Object[], List, + * int[], short[], byte[], char[], boolean[], long[], float[], or double[]. + * @return the resulting printf'd string + * @since 1.5.0 + */ + public static String sprintf(Object self, String format, Object arg) { + if (arg instanceof Object[]) { + return sprintf(self, format, (Object[]) arg); + } + if (arg instanceof List) { + return sprintf(self, format, ((List) arg).toArray()); + } + if (!arg.getClass().isArray()) { + Object[] o = (Object[]) java.lang.reflect.Array.newInstance(arg.getClass(), 1); + o[0] = arg; + return sprintf(self, format, o); + } + + Object[] ans; + String elemType = arg.getClass().getName(); + if (elemType.equals("[I")) { + int[] ia = (int[]) arg; + ans = new Integer[ia.length]; + for (int i = 0; i < ia.length; i++) { + ans[i] = ia[i]; + } + } else if (elemType.equals("[C")) { + char[] ca = (char[]) arg; + ans = new Character[ca.length]; + for (int i = 0; i < ca.length; i++) { + ans[i] = ca[i]; + } + } else if (elemType.equals("[Z")) { + boolean[] ba = (boolean[]) arg; + ans = new Boolean[ba.length]; + for (int i = 0; i < ba.length; i++) { + ans[i] = ba[i]; + } + } else if (elemType.equals("[B")) { + byte[] ba = (byte[]) arg; + ans = new Byte[ba.length]; + for (int i = 0; i < ba.length; i++) { + ans[i] = ba[i]; + } + } else if (elemType.equals("[S")) { + short[] sa = (short[]) arg; + ans = new Short[sa.length]; + for (int i = 0; i < sa.length; i++) { + ans[i] = sa[i]; + } + } else if (elemType.equals("[F")) { + float[] fa = (float[]) arg; + ans = new Float[fa.length]; + for (int i = 0; i < fa.length; i++) { + ans[i] = fa[i]; + } + } else if (elemType.equals("[J")) { + long[] la = (long[]) arg; + ans = new Long[la.length]; + for (int i = 0; i < la.length; i++) { + ans[i] = la[i]; + } + } else if (elemType.equals("[D")) { + double[] da = (double[]) arg; + ans = new Double[da.length]; + for (int i = 0; i < da.length; i++) { + ans[i] = da[i]; + } + } else { + throw new RuntimeException("sprintf(String," + arg + ")"); + } + return sprintf(self, format, ans); + } + + + /** + * Inspects returns the String that matches what would be typed into a + * terminal to create this object. + * + * @param self any Object + * @return a String that matches what would be typed into a terminal to + * create this object. e.g. [1, 'hello'].inspect() -> [1, "hello"] + * @since 1.0 + */ + public static String inspect(Object self) { + return InvokerHelper.inspect(self); + } + + /** + * Print to a console in interactive format. + * + * @param self any Object + * @param out the PrintWriter used for printing + * @since 1.0 + */ + public static void print(Object self, PrintWriter out) { + if (out == null) { + out = new PrintWriter(System.out); + } + out.print(InvokerHelper.toString(self)); + } + + /** + * Print to a console in interactive format. + * + * @param self any Object + * @param out the PrintWriter used for printing + * @since 1.0 + */ + public static void println(Object self, PrintWriter out) { + if (out == null) { + out = new PrintWriter(System.out); + } + out.println(InvokerHelper.toString(self)); + } + + /** + * Provide a dynamic method invocation method which can be overloaded in + * classes to implement dynamic proxies easily. + * + * @param object any Object + * @param method the name of the method to call + * @param arguments the arguments to use + * @return the result of the method call + * @since 1.0 + */ + public static Object invokeMethod(Object object, String method, Object arguments) { + return InvokerHelper.invokeMethod(object, method, arguments); + } + + // isCase methods + //------------------------------------------------------------------------- + + /** + * Method for overloading the behavior of the 'case' method in switch statements. + * The default implementation handles arrays types but otherwise simply delegates + * to Object#equals, but this may be overridden for other types. In this example: + *

     switch( a ) {
    +     *   case b: //some code
    +     * }
    + * "some code" is called when b.isCase( a ) returns + * true. + * + * @param caseValue the case value + * @param switchValue the switch value + * @return true if the switchValue is deemed to be equal to the caseValue + * @since 1.0 + */ + public static boolean isCase(Object caseValue, Object switchValue) { + if (caseValue.getClass().isArray()) { + return isCase(DefaultTypeTransformation.asCollection(caseValue), switchValue); + } + return caseValue.equals(switchValue); + } + + /** + * 'Case' implementation for a String, which uses String#equals(Object) + * in order to allow Strings to be used in switch statements. + * For example: + *
    switch( str ) {
    +     *   case 'one' :
    +     *   // etc...
    +     * }
    + * Note that this returns true for the case where both the + * 'switch' and 'case' operand is null. + * + * @param caseValue the case value + * @param switchValue the switch value + * @return true if the switchValue's toString() equals the caseValue + * @since 1.0 + */ + public static boolean isCase(String caseValue, Object switchValue) { + if (switchValue == null) { + return caseValue == null; + } + return caseValue.equals(switchValue.toString()); + } + + /** + * 'Case' implementation for a CharSequence, which simply calls the equivalent method for String. + * + * @param caseValue the case value + * @param switchValue the switch value + * @return true if the switchValue's toString() equals the caseValue + * @since 1.8.2 + */ + public static boolean isCase(CharSequence caseValue, Object switchValue) { + return isCase(caseValue.toString(), switchValue); + } + + /** + * 'Case' implementation for a GString, which simply calls the equivalent method for String. + * + * @param caseValue the case value + * @param switchValue the switch value + * @return true if the switchValue's toString() equals the caseValue + * @since 1.6.0 + */ + public static boolean isCase(GString caseValue, Object switchValue) { + return isCase(caseValue.toString(), switchValue); + } + + /** + * Special 'Case' implementation for Class, which allows testing + * for a certain class in a switch statement. + * For example: + *
    switch( obj ) {
    +     *   case List :
    +     *     // obj is a list
    +     *     break;
    +     *   case Set :
    +     *     // etc
    +     * }
    + * + * @param caseValue the case value + * @param switchValue the switch value + * @return true if the switchValue is deemed to be assignable from the given class + * @since 1.0 + */ + public static boolean isCase(Class caseValue, Object switchValue) { + if (switchValue instanceof Class) { + Class val = (Class) switchValue; + return caseValue.isAssignableFrom(val); + } + return caseValue.isInstance(switchValue); + } + + /** + * 'Case' implementation for collections which tests if the 'switch' + * operand is contained in any of the 'case' values. + * For example: + *
    switch( 3 ) {
    +     *   case [1,3,5]:
    +     *     assert true
    +     *     break
    +     *   default:
    +     *     assert false
    +     * }
    + * + * @param caseValue the case value + * @param switchValue the switch value + * @return true if the caseValue is deemed to contain the switchValue + * @see java.util.Collection#contains(java.lang.Object) + * @since 1.0 + */ + public static boolean isCase(Collection caseValue, Object switchValue) { + return caseValue.contains(switchValue); + } + + /** + * 'Case' implementation for maps which tests the groovy truth + * value obtained using the 'switch' operand as key. + * For example: + *
    switch( 'foo' ) {
    +     *   case [foo:true, bar:false]:
    +     *     assert true
    +     *     break
    +     *   default:
    +     *     assert false
    +     * }
    + * + * @param caseValue the case value + * @param switchValue the switch value + * @return the groovy truth value from caseValue corresponding to the switchValue key + * @since 1.7.6 + */ + public static boolean isCase(Map caseValue, Object switchValue) { + return DefaultTypeTransformation.castToBoolean(caseValue.get(switchValue)); + } + + /** + * 'Case' implementation for the {@link java.util.regex.Pattern} class, which allows + * testing a String against a number of regular expressions. + * For example: + *
    switch( str ) {
    +     *   case ~/one/ :
    +     *     // the regex 'one' matches the value of str
    +     * }
    +     * 
    + * Note that this returns true for the case where both the pattern and + * the 'switch' values are null. + * + * @param caseValue the case value + * @param switchValue the switch value + * @return true if the switchValue is deemed to match the caseValue + * @since 1.0 + */ + public static boolean isCase(Pattern caseValue, Object switchValue) { + if (switchValue == null) { + return caseValue == null; + } + final Matcher matcher = caseValue.matcher(switchValue.toString()); + if (matcher.matches()) { + RegexSupport.setLastMatcher(matcher); + return true; + } else { + return false; + } + } + + /** + * Special 'case' implementation for all numbers, which delegates to the + * compareTo() method for comparing numbers of different + * types. + * + * @param caseValue the case value + * @param switchValue the switch value + * @return true if the numbers are deemed equal + * @since 1.5.0 + */ + public static boolean isCase(Number caseValue, Number switchValue) { + return NumberMath.compareTo(caseValue, switchValue) == 0; + } + + /** + * Returns an iterator equivalent to this iterator all duplicated items removed + * by using the default comparator. The original iterator will become + * exhausted of elements after determining the unique values. A new iterator + * for the unique values will be returned. + * + * @param self an Iterator + * @return the modified Iterator + * @since 1.5.5 + */ + public static Iterator unique(Iterator self) { + return toList(unique(toList(self))).listIterator(); + } + + /** + * Modifies this collection to remove all duplicated items, using the + * default comparator. + *
    assert [1,3] == [1,3,3].unique()
    + * + * @param self a collection + * @return the now modified collection + * @see #unique(Collection, boolean) + * @since 1.0 + */ + public static Collection unique(Collection self) { + return unique(self, true); + } + + /** + * Remove all duplicates from a given Collection using the default comparator. + * If mutate is true, it works by modifying the original object (and also returning it). + * If mutate is false, a new collection is returned leaving the original unchanged. + *
    +     * assert [1,3] == [1,3,3].unique()
    +     * 
    + *
    +     * def orig = [1, 3, 2, 3]
    +     * def uniq = orig.unique(false)
    +     * assert orig == [1, 3, 2, 3]
    +     * assert uniq == [1, 3, 2]
    +     * 
    + * + * @param self a collection + * @param mutate false will cause a new list containing unique items from the collection to be created, true will mutate collections in place + * @return the now modified collection + * @since 1.8.1 + */ + public static Collection unique(Collection self, boolean mutate) { + List answer = new ArrayList(); + for (T t : self) { + boolean duplicated = false; + for (T t2 : answer) { + if (coercedEquals(t, t2)) { + duplicated = true; + break; + } + } + if (!duplicated) + answer.add(t); + } + if (mutate) { + self.clear(); + self.addAll(answer); + } + return mutate ? self : answer ; + } + + /** + * Provides a method that compares two comparables using Groovy's + * default number aware comparator. + * + * @param self a Comparable + * @param other another Comparable + * @return a -ve number, 0 or a +ve number according to Groovy's compareTo contract + * @since 1.6.0 + */ + public static int numberAwareCompareTo(Comparable self, Comparable other) { + NumberAwareComparator numberAwareComparator = new NumberAwareComparator(); + return numberAwareComparator.compare(self, other); + } + + /** + * Returns an iterator equivalent to this iterator but with all duplicated items + * removed by using a Closure to determine duplicate (equal) items. + * The original iterator will be fully processed after the call. + *

    + * If the closure takes a + * single parameter, the argument passed will be each element, and the + * closure should return a value used for comparison (either using + * {@link java.lang.Comparable#compareTo(java.lang.Object)} or {@link java.lang.Object#equals(java.lang.Object)}). + * If the closure takes two parameters, two items from the Iterator + * will be passed as arguments, and the closure should return an + * int value (with 0 indicating the items are not unique). + * + * @param self an Iterator + * @param closure a Closure used to determine unique items + * @return the modified Iterator + * @since 1.5.5 + */ + public static Iterator unique(Iterator self, Closure closure) { + return toList(unique(toList(self), closure)).listIterator(); + } + + /** + * A convenience method for making a collection unique using a Closure + * to determine duplicate (equal) items. + *

    + * If the closure takes a single parameter, the + * argument passed will be each element, and the closure + * should return a value used for comparison (either using + * {@link java.lang.Comparable#compareTo(java.lang.Object)} or {@link java.lang.Object#equals(java.lang.Object)}). + * If the closure takes two parameters, two items from the collection + * will be passed as arguments, and the closure should return an + * int value (with 0 indicating the items are not unique). + *
    assert [1,4] == [1,3,4,5].unique { it % 2 }
    + *
    assert [2,3,4] == [2,3,3,4].unique { a, b -> a <=> b }
    + * + * @param self a Collection + * @param closure a 1 or 2 arg Closure used to determine unique items + * @return self without any duplicates + * @see #unique(Collection, boolean, Closure) + * @since 1.0 + */ + public static Collection unique(Collection self, Closure closure) { + return unique(self, true, closure); + } + + /** + * A convenience method for making a collection unique using a Closure to determine duplicate (equal) items. + * If mutate is true, it works on the receiver object and returns it. If mutate is false, a new collection is returned. + *

    + * If the closure takes a single parameter, the + * argument passed will be each element, and the closure + * should return a value used for comparison (either using + * {@link java.lang.Comparable#compareTo(java.lang.Object)} or {@link java.lang.Object#equals(java.lang.Object)}). + * If the closure takes two parameters, two items from the collection + * will be passed as arguments, and the closure should return an + * int value (with 0 indicating the items are not unique). + *
    +     * def orig = [1, 3, 4, 5]
    +     * def uniq = orig.unique(false) { it % 2 }
    +     * assert orig == [1, 3, 4, 5]
    +     * assert uniq == [1, 4]
    +     * 
    + *
    +     * def orig = [2, 3, 3, 4]
    +     * def uniq = orig.unique(false) { a, b -> a <=> b }
    +     * assert orig == [2, 3, 3, 4]
    +     * assert uniq == [2, 3, 4]
    +     * 
    + * + * @param self a Collection + * @param mutate false will always cause a new list to be created, true will mutate lists in place + * @param closure a 1 or 2 arg Closure used to determine unique items + * @return self without any duplicates + * @since 1.8.1 + */ + public static Collection unique(Collection self, boolean mutate, Closure closure) { + // use a comparator of one item or two + int params = closure.getMaximumNumberOfParameters(); + if (params == 1) { + OrderBy by = new OrderBy(closure); + by.setEqualityCheck(true); + self = unique(self, mutate, by); + } else { + self = unique(self, mutate, new ClosureComparator(closure)); + } + return self; + } + + /** + * Returns an iterator equivalent to this iterator with all duplicated + * items removed by using the supplied comparator. + * + * @param self an Iterator + * @param comparator a Comparator + * @return the modified Iterator + * @since 1.5.5 + */ + public static Iterator unique(Iterator self, Comparator comparator) { + return toList(unique(toList(self), comparator)).listIterator(); + } + + /** + * Remove all duplicates from a given Collection. + * Works on the original object (and also returns it). + * The order of members in the Collection are compared by the given Comparator. + * For each duplicate, the first member which is returned + * by the given Collection's iterator is retained, but all other ones are removed. + * The given Collection's original order is preserved. + *

    + *

    +     *     class Person {
    +     *         def fname, lname
    +     *         String toString() {
    +     *             return fname + " " + lname
    +     *         }
    +     *     }
    +     *
    +     *     class PersonComparator implements Comparator {
    +     *         int compare(Object o1, Object o2) {
    +     *             Person p1 = (Person) o1
    +     *             Person p2 = (Person) o2
    +     *             if (p1.lname != p2.lname)
    +     *                 return p1.lname.compareTo(p2.lname)
    +     *             else
    +     *                 return p1.fname.compareTo(p2.fname)
    +     *         }
    +     *
    +     *         boolean equals(Object obj) {
    +     *             return this.equals(obj)
    +     *         }
    +     *     }
    +     *
    +     *     Person a = new Person(fname:"John", lname:"Taylor")
    +     *     Person b = new Person(fname:"Clark", lname:"Taylor")
    +     *     Person c = new Person(fname:"Tom", lname:"Cruz")
    +     *     Person d = new Person(fname:"Clark", lname:"Taylor")
    +     *
    +     *     def list = [a, b, c, d]
    +     *     List list2 = list.unique(new PersonComparator())
    +     *     assert( list2 == list && list == [a, b, c] )
    +     * 
    + * + * @param self a Collection + * @param comparator a Comparator + * @return self the now modified collection without duplicates + * @see #unique(java.util.Collection, boolean, java.util.Comparator) + * @since 1.0 + */ + public static Collection unique(Collection self, Comparator comparator) { + return unique(self, true, comparator) ; + } + + /** + * Remove all duplicates from a given Collection. + * If mutate is true, it works on the original object (and also returns it). If mutate is false, a new collection is returned. + * The order of members in the Collection are compared by the given Comparator. + * For each duplicate, the first member which is returned + * by the given Collection's iterator is retained, but all other ones are removed. + * The given Collection's original order is preserved. + *

    + *

    +     *     class Person {
    +     *         def fname, lname
    +     *         String toString() {
    +     *             return fname + " " + lname
    +     *         }
    +     *     }
    +     *
    +     *     class PersonComparator implements Comparator {
    +     *         int compare(Object o1, Object o2) {
    +     *             Person p1 = (Person) o1
    +     *             Person p2 = (Person) o2
    +     *             if (p1.lname != p2.lname)
    +     *                 return p1.lname.compareTo(p2.lname)
    +     *             else
    +     *                 return p1.fname.compareTo(p2.fname)
    +     *         }
    +     *
    +     *         boolean equals(Object obj) {
    +     *             return this.equals(obj)
    +     *         }
    +     *     }
    +     *
    +     *     Person a = new Person(fname:"John", lname:"Taylor")
    +     *     Person b = new Person(fname:"Clark", lname:"Taylor")
    +     *     Person c = new Person(fname:"Tom", lname:"Cruz")
    +     *     Person d = new Person(fname:"Clark", lname:"Taylor")
    +     *
    +     *     def list = [a, b, c, d]
    +     *     List list2 = list.unique(false, new PersonComparator())
    +     *     assert( list2 != list && list2 == [a, b, c] )
    +     * 
    + * + * + * @param self a Collection + * @param mutate false will always cause a new collection to be created, true will mutate collections in place + * @param comparator a Comparator + * @return self the collection without duplicates + * @since 1.8.1 + */ + public static Collection unique(Collection self, boolean mutate, Comparator comparator) { + List answer = new ArrayList(); + for (T t : self) { + boolean duplicated = false; + for (T t2 : answer) { + if (comparator.compare(t, t2) == 0) { + duplicated = true; + break; + } + } + if (!duplicated) + answer.add(t); + } + if (mutate) { + self.clear(); + self.addAll(answer); + } + return mutate ? self : answer; + } + + /** + * Iterates through an aggregate type or data structure, + * passing each item to the given closure. Custom types may utilize this + * method by simply providing an "iterator()" method. The items returned + * from the resulting iterator will be passed to the closure. + * + * @param self the object over which we iterate + * @param closure the closure applied on each element found + * @return the self Object + * @since 1.0 + */ + public static T each(T self, Closure closure) { + each(InvokerHelper.asIterator(self), closure); + return self; + } + + /** + * Iterates through an aggregate type or data structure, + * passing each item and the item's index (a counter starting at + * zero) to the given closure. + * + * @param self an Object + * @param closure a Closure to operate on each item + * @return the self Object + * @since 1.0 + */ + public static T eachWithIndex(T self, Closure closure) { + final Object[] args = new Object[2]; + int counter = 0; + for (Iterator iter = InvokerHelper.asIterator(self); iter.hasNext();) { + args[0] = iter.next(); + args[1] = counter++; + closure.call(args); + } + return self; + } + + private static Iterator each(Iterator iter, Closure closure) { + while (iter.hasNext()) { + Object arg = iter.next(); + closure.call(arg); + } + return iter; + } + + /** + * Allows a Map to be iterated through using a closure. If the + * closure takes one parameter then it will be passed the Map.Entry + * otherwise if the closure takes two parameters then it will be + * passed the key and the value. + *
    def result = ""
    +     * [a:1, b:3].each { key, value -> result += "$key$value" }
    +     * assert result == "a1b3"
    + *
    def result = ""
    +     * [a:1, b:3].each { entry -> result += entry }
    +     * assert result == "a=1b=3"
    + * + * In general, the order in which the map contents are processed + * cannot be guaranteed. In practise, specialized forms of Map, + * e.g. a TreeMap will have its contents processed according to + * the natural ordering of the map. + * + * @param self the map over which we iterate + * @param closure the 1 or 2 arg closure applied on each entry of the map + * @return returns the self parameter + * @since 1.5.0 + */ + public static Map each(Map self, Closure closure) { + for (Map.Entry entry : self.entrySet()) { + callClosureForMapEntry(closure, entry); + } + return self; + } + + /** + * Allows a Map to be iterated through in reverse order using a closure. + * + * In general, the order in which the map contents are processed + * cannot be guaranteed. In practise, specialized forms of Map, + * e.g. a TreeMap will have its contents processed according to the + * reverse of the natural ordering of the map. + * + * @param self the map over which we iterate + * @param closure the 1 or 2 arg closure applied on each entry of the map + * @return returns the self parameter + * @see #each(Map, Closure) + * @since 1.7.2 + */ + public static Map reverseEach(Map self, Closure closure) { + final Iterator> entries = reverse(self.entrySet().iterator()); + while (entries.hasNext()) { + callClosureForMapEntry(closure, entries.next()); + } + return self; + } + + /** + * Allows a Map to be iterated through using a closure. If the + * closure takes two parameters then it will be passed the Map.Entry and + * the item's index (a counter starting at zero) otherwise if the closure + * takes three parameters then it will be passed the key, the value, and + * the index. + *
    def result = ""
    +     * [a:1, b:3].eachWithIndex { key, value, index -> result += "$index($key$value)" }
    +     * assert result == "0(a1)1(b3)"
    + *
    def result = ""
    +     * [a:1, b:3].eachWithIndex { entry, index -> result += "$index($entry)" }
    +     * assert result == "0(a=1)1(b=3)"
    + * + * @param self the map over which we iterate + * @param closure a 2 or 3 arg Closure to operate on each item + * @return the self Object + * @since 1.5.0 + */ + public static Map eachWithIndex(Map self, Closure closure) { + int counter = 0; + for (Map.Entry entry : self.entrySet()) { + callClosureForMapEntryAndCounter(closure, entry, counter++); + } + return self; + } + + /** + * Iterate over each element of the list in the reverse order. + *
    def result = []
    +     * [1,2,3].reverseEach { result << it }
    +     * assert result == [3,2,1]
    + * + * @param self a List + * @param closure a closure to which each item is passed. + * @return the original list + * @since 1.5.0 + */ + public static List reverseEach(List self, Closure closure) { + each(new ReverseListIterator(self), closure); + return self; + } + + /** + * Iterate over each element of the array in the reverse order. + * + * @param self an Object array + * @param closure a closure to which each item is passed + * @return the original array + * @since 1.5.2 + */ + public static T[] reverseEach(T[] self, Closure closure) { + each(new ReverseListIterator(Arrays.asList(self)), closure); + return self; + } + + /** + * Used to determine if the given predicate closure is valid (i.e.&nsbp;returns + * true for all items in this data structure). + * A simple example for a list: + *
    def list = [3,4,5]
    +     * def greaterThanTwo = list.every { it > 2 }
    +     * 
    + * + * @param self the object over which we iterate + * @param closure the closure predicate used for matching + * @return true if every iteration of the object matches the closure predicate + * @since 1.0 + */ + public static boolean every(Object self, Closure closure) { + for (Iterator iter = InvokerHelper.asIterator(self); iter.hasNext();) { + if (!DefaultTypeTransformation.castToBoolean(closure.call(iter.next()))) { + return false; + } + } + return true; + } + + /** + * Iterates over the entries of a map, and checks whether a predicate is + * valid for all entries. If the + * closure takes one parameter then it will be passed the Map.Entry + * otherwise if the closure takes two parameters then it will be + * passed the key and the value. + *
    def map = [a:1, b:2.0, c:2L]
    +     * assert !map.every { key, value -> value instanceof Integer }
    +     * assert map.every { entry -> entry.value instanceof Number }
    + * + * @param self the map over which we iterate + * @param closure the 1 or 2 arg Closure predicate used for matching + * @return true if every entry of the map matches the closure predicate + * @since 1.5.0 + */ + public static boolean every(Map self, Closure closure) { + for (Map.Entry entry : self.entrySet()) { + if (!DefaultTypeTransformation.castToBoolean(callClosureForMapEntry(closure, entry))) { + return false; + } + } + return true; + } + + /** + * Iterates over every element of a collection, and checks whether all + * elements are true according to the Groovy Truth. + * Equivalent to self.every({element -> element}) + * + * @param self the object over which we iterate + * @return true if every item in the collection matches the closure + * predicate + * @since 1.5.0 + */ + public static boolean every(Object self) { + for (Iterator iter = InvokerHelper.asIterator(self); iter.hasNext();) { + if (!DefaultTypeTransformation.castToBoolean(iter.next())) { + return false; + } + } + return true; + } + + /** + * Iterates over the contents of an object or collection, and checks whether a + * predicate is valid for at least one element. + * + * @param self the object over which we iterate + * @param closure the closure predicate used for matching + * @return true if any iteration for the object matches the closure predicate + * @since 1.0 + */ + public static boolean any(Object self, Closure closure) { + for (Iterator iter = InvokerHelper.asIterator(self); iter.hasNext();) { + if (DefaultTypeTransformation.castToBoolean(closure.call(iter.next()))) { + return true; + } + } + return false; + } + + /** + * Iterates over the entries of a map, and checks whether a predicate is + * valid for at least one entry. If the + * closure takes one parameter then it will be passed the Map.Entry + * otherwise if the closure takes two parameters then it will be + * passed the key and the value. + *
    +     * assert [2:3, 4:5, 5:10].any { key, value -> key * 2 == value }
    +     * assert ![2:3, 4:5, 5:10].any { entry -> entry.key == entry.value * 2 }
    +     * 
    + * + * @param self the map over which we iterate + * @param closure the 1 or 2 arg closure predicate used for matching + * @return true if any entry in the map matches the closure predicate + * @since 1.5.0 + */ + public static boolean any(Map self, Closure closure) { + for (Map.Entry entry : self.entrySet()) { + if (DefaultTypeTransformation.castToBoolean(callClosureForMapEntry(closure, entry))) { + return true; + } + } + return false; + } + + /** + * Iterates over the elements of a collection, and checks whether at least + * one element is true according to the Groovy Truth. + * Equivalent to self.any({element -> element}) + * + * @param self the object over which we iterate + * @return true if any item in the collection matches the closure predicate + * @since 1.5.0 + */ + public static boolean any(Object self) { + for (Iterator iter = InvokerHelper.asIterator(self); iter.hasNext();) { + if (DefaultTypeTransformation.castToBoolean(iter.next())) { + return true; + } + } + return false; + } + + /** + * Iterates over the collection of items which this Object represents and returns each item that matches + * the given filter - calling the {@link #isCase(java.lang.Object, java.lang.Object)} + * method used by switch statements. This method can be used with different + * kinds of filters like regular expressions, classes, ranges etc. + * Example: + *
    +     * def list = ['a', 'b', 'aa', 'bc', 3, 4.5]
    +     * assert list.grep( ~/a+/ )  == ['a', 'aa']
    +     * assert list.grep( ~/../ )  == ['aa', 'bc']
    +     * assert list.grep( Number ) == [ 3, 4.5 ]
    +     * assert list.grep{ it.toString().size() == 1 } == [ 'a', 'b', 3 ]
    +     * 
    + * + * @param self the object over which we iterate + * @param filter the filter to perform on the object (using the {@link #isCase(java.lang.Object, java.lang.Object)} method) + * @return a collection of objects which match the filter + * @since 1.5.6 + */ + public static Collection grep(Object self, Object filter) { + Collection answer = createSimilarOrDefaultCollection(self); + MetaClass metaClass = InvokerHelper.getMetaClass(filter); + for (Iterator iter = InvokerHelper.asIterator(self); iter.hasNext();) { + Object object = iter.next(); + if (DefaultTypeTransformation.castToBoolean(metaClass.invokeMethod(filter, "isCase", object))) { + answer.add(object); + } + } + return answer; + } + + /** + * Iterates over the collection of items which this Object represents and returns each item that matches + * using the IDENTITY Closure as a filter - effectively returning all elements which satisfy Groovy truth. + *

    + * Example: + *

    +     * def items = [1, 2, 0, false, true, '', 'foo', [], [4, 5], null]
    +     * assert items.grep() == [1, 2, true, 'foo', [4, 5]]
    +     * 
    + * + * @param self the object over which we iterate + * @return a collection of objects which match the filter + * @since 1.8.1 + * @see Closure#IDENTITY + */ + public static Collection grep(Object self) { + return grep(self, Closure.IDENTITY); + } + + /** + * Counts the number of occurrences of the given value from the + * items within this Iterator. + * Comparison is done using Groovy's == operator (using + * compareTo(value) == 0 or equals(value) ). + * The iterator will become exhausted of elements after determining the count value. + * + * @param self the Iterator from which we count the number of matching occurrences + * @param value the value being searched for + * @return the number of occurrences + * @since 1.5.0 + */ + public static Number count(Iterator self, Object value) { + long answer = 0; + while (self.hasNext()) { + if (DefaultTypeTransformation.compareEqual(self.next(), value)) { + ++answer; + } + } + // for b/c with Java return an int if we can + if (answer <= Integer.MAX_VALUE) return (int) answer; + return answer; + } + + /** + * Counts the number of occurrences which satisfy the given closure from the + * items within this Iterator. + * The iterator will become exhausted of elements after determining the count value. + *

    + * Example usage: + *

    assert [2,4,2,1,3,5,2,4,3].toSet().iterator().count{ it % 2 == 0 } == 2
    + * + * @param self the Iterator from which we count the number of matching occurrences + * @param closure a closure condition + * @return the number of occurrences + * @since 1.8.0 + */ + public static Number count(Iterator self, Closure closure) { + long answer = 0; + while (self.hasNext()) { + if (DefaultTypeTransformation.castToBoolean(closure.call(self.next()))) { + ++answer; + } + } + // for b/c with Java return an int if we can + if (answer <= Integer.MAX_VALUE) return (int) answer; + return answer; + } + + /** + * Counts the number of occurrences of the given value inside this collection. + * Comparison is done using Groovy's == operator (using + * compareTo(value) == 0 or equals(value) ). + *

    + * Example usage: + *

    assert [2,4,2,1,3,5,2,4,3].count(4) == 2
    + * + * @param self the collection within which we count the number of occurrences + * @param value the value being searched for + * @return the number of occurrences + * @since 1.0 + */ + public static Number count(Collection self, Object value) { + return count(self.iterator(), value); + } + + /** + * Counts the number of occurrences which satisfy the given closure from inside this collection. + *

    + * Example usage: + *

    assert [2,4,2,1,3,5,2,4,3].count{ it % 2 == 0 } == 5
    + * + * @param self the collection within which we count the number of occurrences + * @param closure a closure condition + * @return the number of occurrences + * @since 1.8.0 + */ + public static Number count(Collection self, Closure closure) { + return count(self.iterator(), closure); + } + + /** + * Counts the number of occurrences which satisfy the given closure from inside this map. + * If the closure takes one parameter then it will be passed the Map.Entry. + * Otherwise, the closure should take two parameters and will be passed the key and value. + *

    + * Example usage: + *

    assert [a:1, b:1, c:2, d:2].count{ k,v -> k == 'a' || v == 2 } == 3
    + * + * @param self the map within which we count the number of occurrences + * @param closure a 1 or 2 arg Closure condition applying on the entries + * @return the number of occurrences + * @since 1.8.0 + */ + public static Number count(Map self, Closure closure) { + long answer = 0; + for (Object entry : self.entrySet()) { + if (DefaultTypeTransformation.castToBoolean(callClosureForMapEntry(closure, (Map.Entry) entry))) { + ++answer; + } + } + // for b/c with Java return an int if we can + if (answer <= Integer.MAX_VALUE) return (int) answer; + return answer; + } + + + /** + * Counts the number of occurrences of the given value inside this array. + * Comparison is done using Groovy's == operator (using + * compareTo(value) == 0 or equals(value) ). + * + * @param self the array within which we count the number of occurrences + * @param value the value being searched for + * @return the number of occurrences + * @since 1.6.4 + */ + public static Number count(Object[] self, Object value) { + return count(Arrays.asList(self), value); + } + + /** + * Counts the number of occurrences which satisfy the given closure from inside this array. + * + * @param self the array within which we count the number of occurrences + * @param closure a closure condition + * @return the number of occurrences + * @since 1.8.0 + */ + public static Number count(Object[] self, Closure closure) { + return count(Arrays.asList(self), closure); + } + + /** + * Counts the number of occurrences of the given value inside this array. + * Comparison is done using Groovy's == operator (using + * compareTo(value) == 0 or equals(value) ). + * + * @param self the array within which we count the number of occurrences + * @param value the value being searched for + * @return the number of occurrences + * @since 1.6.4 + */ + public static Number count(int[] self, Object value) { + return count(InvokerHelper.asIterator(self), value); + } + + /** + * Counts the number of occurrences of the given value inside this array. + * Comparison is done using Groovy's == operator (using + * compareTo(value) == 0 or equals(value) ). + * + * @param self the array within which we count the number of occurrences + * @param value the value being searched for + * @return the number of occurrences + * @since 1.6.4 + */ + public static Number count(long[] self, Object value) { + return count(InvokerHelper.asIterator(self), value); + } + + /** + * Counts the number of occurrences of the given value inside this array. + * Comparison is done using Groovy's == operator (using + * compareTo(value) == 0 or equals(value) ). + * + * @param self the array within which we count the number of occurrences + * @param value the value being searched for + * @return the number of occurrences + * @since 1.6.4 + */ + public static Number count(short[] self, Object value) { + return count(InvokerHelper.asIterator(self), value); + } + + /** + * Counts the number of occurrences of the given value inside this array. + * Comparison is done using Groovy's == operator (using + * compareTo(value) == 0 or equals(value) ). + * + * @param self the array within which we count the number of occurrences + * @param value the value being searched for + * @return the number of occurrences + * @since 1.6.4 + */ + public static Number count(char[] self, Object value) { + return count(InvokerHelper.asIterator(self), value); + } + + /** + * Counts the number of occurrences of the given value inside this array. + * Comparison is done using Groovy's == operator (using + * compareTo(value) == 0 or equals(value) ). + * + * @param self the array within which we count the number of occurrences + * @param value the value being searched for + * @return the number of occurrences + * @since 1.6.4 + */ + public static Number count(boolean[] self, Object value) { + return count(InvokerHelper.asIterator(self), value); + } + + /** + * Counts the number of occurrences of the given value inside this array. + * Comparison is done using Groovy's == operator (using + * compareTo(value) == 0 or equals(value) ). + * + * @param self the array within which we count the number of occurrences + * @param value the value being searched for + * @return the number of occurrences + * @since 1.6.4 + */ + public static Number count(double[] self, Object value) { + return count(InvokerHelper.asIterator(self), value); + } + + /** + * Counts the number of occurrences of the given value inside this array. + * Comparison is done using Groovy's == operator (using + * compareTo(value) == 0 or equals(value) ). + * + * @param self the array within which we count the number of occurrences + * @param value the value being searched for + * @return the number of occurrences + * @since 1.6.4 + */ + public static Number count(float[] self, Object value) { + return count(InvokerHelper.asIterator(self), value); + } + + /** + * Counts the number of occurrences of the given value inside this array. + * Comparison is done using Groovy's == operator (using + * compareTo(value) == 0 or equals(value) ). + * + * @param self the array within which we count the number of occurrences + * @param value the value being searched for + * @return the number of occurrences + * @since 1.6.4 + */ + public static Number count(byte[] self, Object value) { + return count(InvokerHelper.asIterator(self), value); + } + + /** + * Convert a Collection to a List. Always returns a new List + * even if the Collection is already a List. + *

    + * Example usage: + *

    def x = [1,2,3] as HashSet
    +     * assert x.class == HashSet
    +     * assert x.toList() instanceof List
    + * + * @param self a collection + * @return a List + * @since 1.0 + */ + public static List toList(Collection self) { + List answer = new ArrayList(self.size()); + answer.addAll(self); + return answer; + } + + /** + * Convert an iterator to a List. The iterator will become + * exhausted of elements after making this conversion. + * + * @param self an iterator + * @return a List + * @since 1.5.0 + */ + public static List toList(Iterator self) { + List answer = new ArrayList(); + while (self.hasNext()) { + answer.add(self.next()); + } + return answer; + } + + /** + * Convert an enumeration to a List. + * + * @param self an enumeration + * @return a List + * @since 1.5.0 + */ + public static List toList(Enumeration self) { + List answer = new ArrayList(); + while (self.hasMoreElements()) { + answer.add(self.nextElement()); + } + return answer; + } + + /** + * Collates this list into sub-lists of length size. + * Example: + *
    def list = [ 1, 2, 3, 4, 5, 6, 7 ]
    +     * def coll = list.collate( 3 )
    +     * assert coll == [ [ 1, 2, 3 ], [ 4, 5, 6 ], [ 7 ] ]
    + * + * @param self a List + * @param size the length of each sub-list in the returned list + * @return a List containing the data collated into sub-lists + * @since 1.8.6 + */ + public static List> collate( List self, int size ) { + return collate( self, size, size, true ) ; + } + + /** + * Collates this list into sub-lists of length size stepping through the code step + * elements for each subList. + * Example: + *
    def list = [ 1, 2, 3, 4 ]
    +     * def coll = list.collate( 3, 1 )
    +     * assert coll == [ [ 1, 2, 3 ], [ 2, 3, 4 ], [ 3, 4 ], [ 4 ] ]
    + * + * @param self a List + * @param size the length of each sub-list in the returned list + * @param step the number of elements to step through for each sub-list + * @return a List containing the data collated into sub-lists + * @since 1.8.6 + */ + public static List> collate( List self, int size, int step ) { + return collate( self, size, step, true ) ; + } + + /** + * Collates this list into sub-lists of length size. Any remaining elements in + * the list after the subdivision will be dropped if keepRemainder is false. + * Example: + *
    def list = [ 1, 2, 3, 4, 5, 6, 7 ]
    +     * def coll = list.collate( 3, false )
    +     * assert coll == [ [ 1, 2, 3 ], [ 4, 5, 6 ] ]
    + * + * @param self a List + * @param size the length of each sub-list in the returned list + * @param keepRemainder if true, any rmeaining elements are returned as sub-lists. Otherwise they are discarded + * @return a List containing the data collated into sub-lists + * @since 1.8.6 + */ + public static List> collate( List self, int size, boolean keepRemainder ) { + return collate( self, size, size, keepRemainder ) ; + } + + /** + * Collates this list into sub-lists of length size stepping through the code step + * elements for each sub-list. Any remaining elements in the list after the subdivision will be dropped if + * keepRemainder is false. + * Example: + *
    def list = [ 1, 2, 3, 4 ]
    +     * assert list.collate( 3, 1, true  ) == [ [ 1, 2, 3 ], [ 2, 3, 4 ], [ 3, 4 ], [ 4 ] ]
    +     * assert list.collate( 3, 1, false ) == [ [ 1, 2, 3 ], [ 2, 3, 4 ] ]
    + * + * @param self a List + * @param size the length of each sub-list in the returned list + * @param step the number of elements to step through for each sub-list + * @param keepRemainder if true, any rmeaining elements are returned as sub-lists. Otherwise they are discarded + * @return a List containing the data collated into sub-lists + * @since 1.8.6 + */ + public static List> collate( List self, int size, int step, boolean keepRemainder ) { + List> answer = new ArrayList>(); + if( size <= 0 || self.size() == 0 ) { + answer.add( self ) ; + } + else { + for( int pos = 0 ; pos < self.size() && pos > -1 ; pos += step ) { + if( !keepRemainder && pos > self.size() - size ) { + break ; + } + List element = new ArrayList() ; + for( int offs = pos ; offs < pos + size && offs < self.size() ; offs++ ) { + element.add( self.get( offs ) ) ; + } + answer.add( element ) ; + } + } + return answer ; + } + + /** + * Iterates through this aggregate Object transforming each item into a new value using the + * transform closure, returning a list of transformed values. + * Example: + *
    def list = [1, 'a', 1.23, true ]
    +     * def types = list.collect { it.class }
    +     * assert types == [Integer, String, BigDecimal, Boolean]
    + * + * @param self an aggregate Object with an Iterator returning its items + * @param transform the closure used to transform each item of the aggregate object + * @return a List of the transformed values + * @since 1.0 + */ + public static List collect(Object self, Closure transform) { + return (List) collect(self, new ArrayList(), transform); + } + + /** + * Iterates through this aggregate Object transforming each item into a new value using Closure.IDENTITY + * as a transformer, basically returning a list of items copied from the original object. + *
    assert [1,2,3] == [1,2,3].iterator().collect()
    + * + * @param self an aggregate Object with an Iterator returning its items + * @return a List of the transformed values + * @see Closure#IDENTITY + * @since 1.8.5 + */ + public static Collection collect(Object self) { + return collect(self, Closure.IDENTITY); + } + + /** + * Iterates through this aggregate Object transforming each item into a new value using the transform closure + * and adding it to the supplied collector. + * + * @param self an aggregate Object with an Iterator returning its items + * @param collector the Collection to which the transformed values are added + * @param transform the closure used to transform each item of the aggregate object + * @return the collector with all transformed values added to it + * @since 1.0 + */ + public static Collection collect(Object self, Collection collector, Closure transform) { + for (Iterator iter = InvokerHelper.asIterator(self); iter.hasNext(); ) { + collector.add(transform.call(iter.next())); + } + return collector; + } + + /** + * Iterates through this collection transforming each entry into a new value using the transform closure + * returning a list of transformed values. + *
    assert [2,4,6] == [1,2,3].collect { it * 2 }
    + * + * @param self a collection + * @param transform the closure used to transform each item of the collection + * @return a List of the transformed values + * @since 1.0 + */ + public static List collect(Collection self, Closure transform) { + return (List) collect(self, new ArrayList(self.size()), transform); + } + + /** + * Iterates through this collection transforming each entry into a new value using Closure.IDENTITY + * as a transformer, basically returning a list of items copied from the original collection. + *
    assert [1,2,3] == [1,2,3].collect()
    + * + * @param self a collection + * @return a List of the transformed values + * @since 1.8.5 + * @see Closure#IDENTITY + */ + public static List collect(Collection self) { + return (List) collect(self, Closure.IDENTITY); + } + + /** + * Iterates through this collection transforming each value into a new value using the transform closure + * and adding it to the supplied collector. + *
    assert [1,2,3] as HashSet == [2,4,5,6].collect(new HashSet()) { (int)(it / 2) }
    + * + * @param self a collection + * @param collector the Collection to which the transformed values are added + * @param transform the closure used to transform each item of the collection + * @return the collector with all transformed values added to it + * @since 1.0 + */ + public static Collection collect(Collection self, Collection collector, Closure transform) { + for (Object item : self) { + collector.add(transform.call(item)); + if (transform.getDirective() == Closure.DONE) { + break; + } + } + return collector; + } + + /** + * Deprecated alias for collectNested + * + * @deprecated Use collectNested instead + * @see #collectNested(Collection, Closure) + */ + public static List collectAll(Collection self, Closure transform) { + return collectNested(self, transform); + } + + /** + * Recursively iterates through this collection transforming each non-Collection value + * into a new value using the closure as a transformer. Returns a potentially nested + * list of transformed values. + *
    +     * assert [2,[4,6],[8],[]] == [1,[2,3],[4],[]].collectNested { it * 2 }
    +     * 
    + * + * @param self a collection + * @param transform the closure used to transform each item of the collection + * @return the resultant collection + * @since 1.8.1 + */ + public static List collectNested(Collection self, Closure transform) { + return (List) collectNested(self, new ArrayList(self.size()), transform); + } + + /** + * Deprecated alias for collectNested + * + * @deprecated Use collectNested instead + * @see #collectNested(Collection, Collection, Closure) + */ + public static Collection collectAll(Collection self, Collection collector, Closure transform) { + return collectNested(self, collector, transform); + } + + /** + * Recursively iterates through this collection transforming each non-Collection value + * into a new value using the transform closure. Returns a potentially nested + * collection of transformed values. + *
    def x = [1,[2,3],[4],[]].collectNested(new Vector()) { it * 2 }
    +     * assert x == [2,[4,6],[8],[]]
    +     * assert x instanceof Vector
    + * + * @param self a collection + * @param collector an initial Collection to which the transformed values are added + * @param transform the closure used to transform each element of the collection + * @return the collector with all transformed values added to it + * @since 1.8.1 + */ + public static Collection collectNested(Collection self, Collection collector, Closure transform) { + for (Object item : self) { + if (item instanceof Collection) { + Collection c = (Collection) item; + collector.add(collectNested(c, createSimilarCollection(collector, c.size()), transform)); + } else { + collector.add(transform.call(item)); + } + if (transform.getDirective() == Closure.DONE) { + break; + } + } + return collector; + } + + /** + * Projects each item from a source collection to a collection and concatenates (flattens) the resulting collections into a single list. + *

    + *

    +     * def nums = 1..10
    +     * def squaresAndCubesOfEvens = nums.collectMany{ it % 2 ? [] : [it**2, it**3] }
    +     * assert squaresAndCubesOfEvens == [4, 8, 16, 64, 36, 216, 64, 512, 100, 1000]
    +     *
    +     * def animals = ['CAT', 'DOG', 'ELEPHANT'] as Set
    +     * def smallAnimals = animals.collectMany{ it.size() > 3 ? [] : [it.toLowerCase()] }
    +     * assert smallAnimals == ['cat', 'dog']
    +     *
    +     * def orig = nums as Set
    +     * def origPlusIncrements = orig.collectMany{ [it, it+1] }
    +     * assert origPlusIncrements.size() == orig.size() * 2
    +     * assert origPlusIncrements.unique().size() == orig.size() + 1
    +     * 
    + * + * @param self a collection + * @param projection a projecting Closure returning a collection of items + * @return a list created from the projected collections concatenated (flattened) together + * @see #sum(java.util.Collection, groovy.lang.Closure) + * @since 1.8.1 + */ + public static List collectMany(Collection self, Closure> projection) { + return (List) collectMany(self, new ArrayList(), projection); + } + + /** + * Projects each item from a source collection to a result collection and concatenates (flattens) the resulting + * collections adding them into the collector. + *

    + *

    +     * def animals = ['CAT', 'DOG', 'ELEPHANT'] as Set
    +     * def smallAnimals = animals.collectMany(['ant', 'bee']){ it.size() > 3 ? [] : [it.toLowerCase()] }
    +     * assert smallAnimals == ['ant', 'bee', 'cat', 'dog']
    +     *
    +     * def nums = 1..5
    +     * def origPlusIncrements = nums.collectMany([] as Set){ [it, it+1] }
    +     * assert origPlusIncrements.size() == nums.size() + 1
    +     * 
    + * + * @param self a collection + * @param collector an initial collection to add the projected items to + * @param projection a projecting Closure returning a collection of items + * @return the collector with the projected collections concatenated (flattened) to it + * @since 1.8.5 + */ + public static Collection collectMany(Collection self, Collection collector, Closure> projection) { + for (Object next : self) { + collector.addAll(projection.call(next)); + } + return collector; + } + + /** + * Projects each item from a source array to a collection and concatenates (flattens) the resulting collections into a single list. + *

    + *

    +     * def nums = [1, 2, 3, 4, 5, 6] as Object[]
    +     * def squaresAndCubesOfEvens = nums.collectMany{ it % 2 ? [] : [it**2, it**3] }
    +     * assert squaresAndCubesOfEvens == [4, 8, 16, 64, 36, 216]
    +     * 
    + * + * @param self an object array + * @param projection a projecting Closure returning a collection of items + * @return a list created from the projected collections concatenated (flattened) together + * @see #sum(Object[], groovy.lang.Closure) + * @since 1.8.1 + */ + public static List collectMany(Object[] self, Closure> projection) { + return collectMany(toList(self), projection); + } + + /** + * Projects each item from a source iterator to a collection and concatenates (flattens) the resulting collections into a single list. + *

    + *

    +     * def numsIter = [1, 2, 3, 4, 5, 6].iterator()
    +     * def squaresAndCubesOfEvens = numsIter.collectMany{ it % 2 ? [] : [it**2, it**3] }
    +     * assert squaresAndCubesOfEvens == [4, 8, 16, 64, 36, 216]
    +     * 
    + * + * @param self an iterator + * @param projection a projecting Closure returning a collection of items + * @return a list created from the projected collections concatenated (flattened) together + * @see #sum(Iterator, groovy.lang.Closure) + * @since 1.8.1 + */ + public static List collectMany(Iterator self, Closure> projection) { + return collectMany(toList(self), projection); + } + + /** + * Iterates through this Map transforming each map entry into a new value using the transform closure + * returning the collector with all transformed vakues added to it. + *
    assert [a:1, b:2].collect( [] as HashSet ) { key, value -> key*value } == ["a", "bb"] as Set
    +     * assert [3:20, 2:30].collect( [] as HashSet ) { entry -> entry.key * entry.value } == [60] as Set
    + * + * @param self a Map + * @param collector the Collection to which transformed values are added + * @param transform the transformation closure which can take one (Map.Entry) or two (key, value) parameters + * @return the collector with all transformed values added to it + * @since 1.0 + */ + public static Collection collect(Map self, Collection collector, Closure transform) { + for (Map.Entry entry : self.entrySet()) { + collector.add(callClosureForMapEntry(transform, entry)); + } + return collector; + } + + /** + * Iterates through this Map transforming each map entry into a new value using the transform closure + * returning a list of transformed values. + *
    assert [a:1, b:2].collect { key, value -> key*value } == ["a", "bb"]
    +     * assert [3:20, 2:30].collect { entry -> entry.key * entry.value } == [60, 60]
    + * + * @param self a Map + * @param transform the transformation closure which can take one (Map.Entry) or two (key, value) parameters + * @return the resultant list of transformed values + * @since 1.0 + */ + public static List collect(Map self, Closure transform) { + return (List) collect(self, new ArrayList(self.size()), transform); + } + + /** + * Iterates through this Map transforming each map entry using the transform closure + * returning a map of the transformed entries. + *
    +     * assert [a:1, b:2].collectEntries( [:] ) { k, v -> [v, k] } == [1:'a', 2:'b']
    +     * assert [a:1, b:2].collectEntries( [30:'C'] ) { key, value ->
    +     *     [(value*10): key.toUpperCase()] } == [10:'A', 20:'B', 30:'C']
    +     * 
    + * + * @param self a Map + * @param collector the Map into which the transformed entries are put + * @param transform the closure used for transforming, which can take one (Map.Entry) or two (key, value) parameters and + * should return a Map.Entry, a Map or a two-element list containing the resulting key and value + * @return the collector with all transformed values added to it + * @see #collect(Map, Collection, Closure) + * @since 1.7.9 + */ + public static Map collectEntries(Map self, Map collector, Closure transform) { + for (Map.Entry entry : self.entrySet()) { + addEntry(collector, callClosureForMapEntry(transform, entry)); + } + return collector; + } + + /** + * Iterates through this Map transforming each entry using the transform closure + * and returning a map of the transformed entries. + *
    +     * assert [a:1, b:2].collectEntries { key, value -> [value, key] } == [1:'a', 2:'b']
    +     * assert [a:1, b:2].collectEntries { key, value ->
    +     *     [(value*10): key.toUpperCase()] } == [10:'A', 20:'B']
    +     * 
    + * + * @param self a Map + * @param transform the closure used for transforming, which can take one (Map.Entry) or two (key, value) parameters and + * should return a Map.Entry, a Map or a two-element list containing the resulting key and value + * @return a Map of the transformed entries + * @see #collect(Map, Collection, Closure) + * @since 1.7.9 + */ + public static Map collectEntries(Map self, Closure transform) { + return collectEntries(self, createSimilarMap(self), transform); + } + + /** + * Iterates through this Collection transforming each item using the transform closure + * and returning a map of the resulting transformed entries. + *
    +     * def letters = "abc"
    +     * // collect letters with index using list style
    +     * assert (0..2).collectEntries { index -> [index, letters[index]] } == [0:'a', 1:'b', 2:'c']
    +     * // collect letters with index using map style
    +     * assert (0..2).collectEntries { index -> [(index): letters[index]] } == [0:'a', 1:'b', 2:'c']
    +     * 
    + * + * @param self a Collection + * @param transform the closure used for transforming, which has an item from self as the parameter and + * should return a Map.Entry, a Map or a two-element list containing the resulting key and value + * @return a Map of the transformed entries + * @see #collectEntries(Collection, Map, Closure) + * @since 1.7.9 + */ + public static Map collectEntries(Collection self, Closure transform) { + return collectEntries(self, new LinkedHashMap(), transform); + } + + /** + * A variant of collectEntries using the identity closure as the transform. + * The source collection should be a list of [key, value] tuples or a Map.Entry. + *
    +     * def nums = [1, 10, 100, 1000]
    +     * def tuples = nums.collect{ [it, it.toString().size()] }
    +     * assert tuples == [[1, 1], [10, 2], [100, 3], [1000, 4]]
    +     * def map = tuples.collectEntries()
    +     * assert map == [1:1, 10:2, 100:3, 1000:4]
    +     * 
    + * + * @param self a Collection + * @return a Map of the transformed entries + * @see #collectEntries(Collection, Closure) + * @since 1.8.5 + */ + public static Map collectEntries(Collection self) { + return collectEntries(self, new LinkedHashMap(), Closure.IDENTITY); + } + + /** + * Iterates through this Collection transforming each item using the closure + * as a transformer into a map entry, returning a map of the transformed entries. + *
    +     * def letters = "abc"
    +     * // collect letters with index
    +     * assert (0..2).collectEntries( [:] ) { index -> [index, letters[index]] } == [0:'a', 1:'b', 2:'c']
    +     * assert (0..2).collectEntries( [4:'d'] ) { index ->
    +     *     [(index+1): letters[index]] } == [1:'a', 2:'b', 3:'c', 4:'d']
    +     * 
    + * + * @param self a Collection + * @param collector the Map into which the transformed entries are put + * @param transform the closure used for transforming, which has an item from self as the parameter and + * should return a Map.Entry, a Map or a two-element list containing the resulting key and value + * @return the collector with all transformed values added to it + * @see #collect(Map, Collection, Closure) + * @since 1.7.9 + */ + public static Map collectEntries(Collection self, Map collector, Closure transform) { + for (Object next : self) { + addEntry(collector, transform.call(next)); + } + return collector; + } + + /** + * A variant of collectEntries using the identity closure as the transform. + * + * @param self a Collection + * @param collector the Map into which the transformed entries are put + * @return the collector with all transformed values added to it + * @see #collectEntries(Collection, Map, Closure) + * @since 1.8.5 + */ + public static Map collectEntries(Collection self, Map collector) { + return collectEntries(self, collector, Closure.IDENTITY); + } + + /** + * Iterates through this array transforming each item using the transform closure + * and returning a map of the resulting transformed entries. + *
    +     * def letters = "abc"
    +     * def nums = [0, 1, 2] as Integer[]
    +     * // collect letters with index
    +     * assert nums.collectEntries( [:] ) { index -> [index, letters[index]] } == [0:'a', 1:'b', 2:'c']
    +     * assert nums.collectEntries( [4:'d'] ) { index ->
    +     *     [(index+1): letters[index]] } == [1:'a', 2:'b', 3:'c', 4:'d']
    +     * 
    + * + * @param self an Object array + * @param collector the Map into which the transformed entries are put + * @param transform the closure used for transforming, which has an item from self as the parameter and + * should return a Map.Entry, a Map or a two-element list containing the resulting key and value + * @return the collector with all transformed values added to it + * @see #collect(Map, Collection, Closure) + * @since 1.7.9 + */ + public static Map collectEntries(Object[] self, Map collector, Closure transform) { + return collectEntries(toList(self), collector, transform); + } + + /** + * A variant of collectEntries using the identity closure as the transform. + * + * @param self an Object array + * @param collector the Map into which the transformed entries are put + * @return the collector with all transformed values added to it + * @see #collectEntries(Object[], Map, Closure) + * @since 1.8.5 + */ + public static Map collectEntries(Object[] self, Map collector) { + return collectEntries(self, collector, Closure.IDENTITY); + } + + /** + * Iterates through this array transforming each item using the transform closure + * and returning a map of the resulting transformed entries. + *
    +     * def letters = "abc"
    +     * def nums = [0, 1, 2] as Integer[]
    +     * // collect letters with index using list style
    +     * assert nums.collectEntries { index -> [index, letters[index]] } == [0:'a', 1:'b', 2:'c']
    +     * // collect letters with index using map style
    +     * assert nums.collectEntries { index -> [(index): letters[index]] } == [0:'a', 1:'b', 2:'c']
    +     * 
    + * + * @param self a Collection + * @param transform the closure used for transforming, which has an item from self as the parameter and + * should return a Map.Entry, a Map or a two-element list containing the resulting key and value + * @return a Map of the transformed entries + * @see #collectEntries(Collection, Map, Closure) + * @since 1.7.9 + */ + public static Map collectEntries(Object[] self, Closure transform) { + return collectEntries(toList(self), new LinkedHashMap(), transform); + } + + /** + * A variant of collectEntries using the identity closure as the transform. + * + * @param self an Object array + * @return the collector with all transformed values added to it + * @see #collectEntries(Object[], Closure) + * @since 1.8.5 + */ + public static Map collectEntries(Object[] self) { + return collectEntries(self, Closure.IDENTITY); + } + + private static void addEntry(Map result, Object newEntry) { + if (newEntry instanceof Map) { + leftShift(result, (Map)newEntry); + } else if (newEntry instanceof List && ((List)newEntry).size() == 2) { + List list = (List) newEntry; + leftShift(result, new MapEntry(list.get(0), list.get(1))); + } else { + // TODO: enforce stricter behavior? + // given Map.Entry is an interface, we get a proxy which gives us lots + // of flexibility but sometimes the error messages might be unexpected + leftShift(result, asType(newEntry, Map.Entry.class)); + } + } + + /** + * Finds the first value matching the closure condition + * + * @param self an Object with an iterator returning its values + * @param closure a closure condition + * @return the first Object found or null if none was found + * @since 1.0 + */ + public static Object find(Object self, Closure closure) { + for (Iterator iter = InvokerHelper.asIterator(self); iter.hasNext();) { + Object value = iter.next(); + if (DefaultTypeTransformation.castToBoolean(closure.call(value))) { + return value; + } + } + return null; + } + + /** + * Finds the first item matching the IDENTITY Closure (i.e. matching Groovy truth). + *

    + * Example: + *

    +     * def items = [null, 0, 0.0, false, '', [], 42, 43]
    +     * assert items.find() == 42
    +     * 
    + * + * @param self an Object with an Iterator returning its values + * @return the first Object found or null if none was found + * @since 1.8.1 + * @see Closure#IDENTITY + */ + public static Object find(Object self) { + return find(self, Closure.IDENTITY); + } + + /** + * Treats the object as iterable, iterating through the values it represents and returns the first non-null result obtained from calling the closure, otherwise returns the defaultResult. + * + * @param self an Object with an iterator returning its values + * @param defaultResult an Object that should be returned if all closure results are null + * @param closure a closure that returns a non-null value when processing should stop + * @return the first non-null result of the closure, otherwise the default value + * @since 1.7.5 + */ + public static Object findResult(Object self, Object defaultResult, Closure closure) { + Object result = findResult(self, closure); + if (result == null) return defaultResult; + return result; + } + + /** + * Treats the object as iterable, iterating through the values it represents and returns the first non-null result obtained from calling the closure, otherwise returns null. + * + * @param self an Object with an iterator returning its values + * @param closure a closure that returns a non-null value when processing should stop + * @return the first non-null result of the closure + * @since 1.7.5 + */ + public static Object findResult(Object self, Closure closure) { + for (Iterator iter = InvokerHelper.asIterator(self); iter.hasNext();) { + Object value = iter.next(); + Object result = closure.call(value); + if (result != null) { + return result; + } + } + return null; + } + + /** + * Finds the first value matching the closure condition. Example: + *
    def list = [1,2,3]
    +     * assert 2 == list.find { it > 1 }
    +     * 
    + * + * @param self a Collection + * @param closure a closure condition + * @return the first Object found + * @since 1.0 + */ + public static T find(Collection self, Closure closure) { + for (T value : self) { + if (DefaultTypeTransformation.castToBoolean(closure.call(value))) { + return value; + } + } + return null; + } + + /** + * Finds the first item matching the IDENTITY Closure (i.e. matching Groovy truth). + *

    + * Example: + *

    +     * def items = [null, 0, 0.0, false, '', [], 42, 43]
    +     * assert items.find() == 42
    +     * 
    + * + * @param self a Collection + * @return the first Object found or null if none was found + * @since 1.8.1 + * @see Closure#IDENTITY + */ + public static T find(Collection self) { + return find(self, Closure.IDENTITY); + } + + /** + * Iterates through the collection calling the given closure for each item but stopping once the first non-null + * result is found and returning that result. If all are null, the defaultResult is returned. + *

    + * Examples: + *

    +     * def list = [1,2,3]
    +     * assert "Found 2" == list.findResult("default") { it > 1 ? "Found $it" : null }
    +     * assert "default" == list.findResult("default") { it > 3 ? "Found $it" : null }
    +     * 
    + * + * @param self a Collection + * @param defaultResult an Object that should be returned if all closure results are null + * @param closure a closure that returns a non-null value when processing should stop and a value should be returned + * @return the first non-null result from calling the closure, or the defaultValue + * @since 1.7.5 + */ + public static T findResult(Collection self, U defaultResult, Closure closure) { + T result = findResult(self, closure); + if (result == null) return defaultResult; + return result; + } + + /** + * Iterates through the collection calling the given closure for each item but stopping once the first non-null + * result is found and returning that result. If all results are null, null is returned. + *

    + * Example: + *

    +     * def list = [1,2,3]
    +     * assert "Found 2" == list.findResult { it > 1 ? "Found $it" : null }
    +     * 
    + * + * @param self a Collection + * @param closure a closure that returns a non-null value when processing should stop and a value should be returned + * @return the first non-null result from calling the closure, or null + * @since 1.7.5 + */ + public static T findResult(Collection self, Closure closure) { + for (Object value : self) { + T result = closure.call(value); + if (result != null) { + return result; + } + } + return null; + } + + /** + * Iterates through the collection transforming items using the supplied closure + * and collecting any non-null results. + *

    + * Example: + *

    +     * def list = [1,2,3]
    +     * def result = list.findResults { it > 1 ? "Found $it" : null }
    +     * assert result == ["Found 2", "Found 3"]
    +     * 
    + * + * @param self a Collection + * @param filteringTransform a Closure that should return either a non-null transformed value or null for items which should be discarded + * @return the list of non-null transformed values + * @since 1.8.1 + */ + public static Collection findResults(Collection self, Closure filteringTransform) { + List result = new ArrayList(); + for (Object value : self) { + T transformed = filteringTransform.call(value); + if (transformed != null) { + result.add(transformed); + } + } + return result; + } + + /** + * Iterates through the map transforming items using the supplied closure + * and collecting any non-null results. + * If the closure takes two parameters, the entry key and value are passed. + * If the closure takes one parameter, the Map.Entry object is passed. + *

    + * Example: + *

    +     * def map = [a:1, b:2, hi:2, cat:3, dog:2]
    +     * def result = map.findResults { k, v -> k.size() == v ? "Found $k:$v" : null }
    +     * assert result == ["Found a:1", "Found hi:2", "Found cat:3"]
    +     * 
    + * + * @param self a Map + * @param filteringTransform a 1 or 2 arg Closure that should return either a non-null transformed value or null for items which should be discarded + * @return the list of non-null transformed values + * @since 1.8.1 + */ + public static Collection findResults(Map self, Closure filteringTransform) { + List result = new ArrayList(); + for (Map.Entry entry : self.entrySet()) { + T transformed = callClosureForMapEntry(filteringTransform, entry); + if (transformed != null) { + result.add(transformed); + } + } + return result; + } + + /** + * Finds the first entry matching the closure condition. + * If the closure takes two parameters, the entry key and value are passed. + * If the closure takes one parameter, the Map.Entry object is passed. + *
    assert [a:1, b:3].find { it.value == 3 }.key == "b"
    + * + * @param self a Map + * @param closure a 1 or 2 arg Closure condition + * @return the first Object found + * @since 1.0 + */ + public static Map.Entry find(Map self, Closure closure) { + for (Map.Entry entry : self.entrySet()) { + if (DefaultTypeTransformation.castToBoolean(callClosureForMapEntry(closure, entry))) { + return entry; + } + } + return null; + } + + /** + * Returns the first non-null closure result found by passing each map entry to the closure, otherwise the defaultResult is returned. + * If the closure takes two parameters, the entry key and value are passed. + * If the closure takes one parameter, the Map.Entry object is passed. + *
    +     * assert "Found b:3" == [a:1, b:3].findResult("default") { if (it.value == 3) return "Found ${it.key}:${it.value}" }
    +     * assert "default" == [a:1, b:3].findResult("default") { if (it.value == 9) return "Found ${it.key}:${it.value}" }
    +     * assert "Found a:1" == [a:1, b:3].findResult("default") { k, v -> if (k.size() + v == 2) return "Found $k:$v" }
    +     * 
    + * + * @param self a Map + * @param defaultResult an Object that should be returned if all closure results are null + * @param closure a 1 or 2 arg Closure that returns a non-null value when processing should stop and a value should be returned + * @return the first non-null result collected by calling the closure, or the defaultResult if no such result was found + * @since 1.7.5 + */ + public static T findResult(Map self, U defaultResult, Closure closure) { + T result = findResult(self, closure); + if (result == null) return defaultResult; + return result; + } + + /** + * Returns the first non-null closure result found by passing each map entry to the closure, otherwise null is returned. + * If the closure takes two parameters, the entry key and value are passed. + * If the closure takes one parameter, the Map.Entry object is passed. + *
    +     * assert "Found b:3" == [a:1, b:3].findResult { if (it.value == 3) return "Found ${it.key}:${it.value}" }
    +     * assert null == [a:1, b:3].findResult { if (it.value == 9) return "Found ${it.key}:${it.value}" }
    +     * assert "Found a:1" == [a:1, b:3].findResult { k, v -> if (k.size() + v == 2) return "Found $k:$v" }
    +     * 
    + * + * @param self a Map + * @param closure a 1 or 2 arg Closure that returns a non-null value when processing should stop and a value should be returned + * @return the first non-null result collected by calling the closure, or null if no such result was found + * @since 1.7.5 + */ + public static T findResult(Map self, Closure closure) { + for (Map.Entry entry : self.entrySet()) { + T result = callClosureForMapEntry(closure, entry); + if (result != null) { + return result; + } + } + return null; + } + + /** + * Finds all values matching the closure condition. + *
    assert [2,4] == [1,2,3,4].findAll { it % 2 == 0 }
    + * + * @param self a Collection + * @param closure a closure condition + * @return a Collection of matching values + * @since 1.5.6 + */ + public static Collection findAll(Collection self, Closure closure) { + Collection answer = createSimilarCollection(self); + Iterator iter = self.iterator(); + return findAll(closure, answer, iter); + } + + /** + * Finds the items matching the IDENTITY Closure (i.e. matching Groovy truth). + *

    + * Example: + *

    +     * def items = [1, 2, 0, false, true, '', 'foo', [], [4, 5], null]
    +     * assert items.findAll() == [1, 2, true, 'foo', [4, 5]]
    +     * 
    + * + * @param self a Collection + * @return a List of the values found + * @since 1.8.1 + * @see Closure#IDENTITY + */ + public static Collection findAll(Collection self) { + return findAll(self, Closure.IDENTITY); + } + + /** + * Finds all items matching the closure condition. + * + * @param self an Object with an Iterator returning its values + * @param closure a closure condition + * @return a List of the values found + * @since 1.6.0 + */ + public static Collection findAll(Object self, Closure closure) { + List answer = new ArrayList(); + Iterator iter = InvokerHelper.asIterator(self); + return findAll(closure, answer, iter); + } + + /** + * Finds all items matching the IDENTITY Closure (i.e. matching Groovy truth). + *

    + * Example: + *

    +     * def items = [1, 2, 0, false, true, '', 'foo', [], [4, 5], null]
    +     * assert items.findAll() == [1, 2, true, 'foo', [4, 5]]
    +     * 
    + * + * @param self an Object with an Iterator returning its values + * @return a List of the values found + * @since 1.8.1 + * @see Closure#IDENTITY + */ + public static Collection findAll(Object self) { + return findAll(self, Closure.IDENTITY); + } + + private static Collection findAll(Closure closure, Collection answer, Iterator iter) { + while (iter.hasNext()) { + T value = iter.next(); + if (DefaultTypeTransformation.castToBoolean(closure.call(value))) { + answer.add(value); + } + } + return answer; + } + + /** + * Returns true if this collection contains all of the elements + * in the specified array. + * + * @param self a Collection to be checked for containment + * @param items array to be checked for containment in this collection + * @return true if this collection contains all of the elements + * in the specified array + * @see Collection#containsAll(Collection) + * @since 1.7.2 + */ + public static boolean containsAll(Collection self, Object[] items) { + return self.containsAll(Arrays.asList(items)); + } + + /** + * Modifies this collection by removing its elements that are contained + * within the specified object array. + * + * See also findAll and grep when wanting to produce a new list + * containing items which don't match some criteria while leaving the original collection unchanged. + * + * @param self a Collection to be modified + * @param items array containing elements to be removed from this collection + * @return true if this collection changed as a result of the call + * @see Collection#removeAll(Collection) + * @since 1.7.2 + */ + public static boolean removeAll(Collection self, Object[] items) { + return self.removeAll(Arrays.asList(items)); + } + + /** + * Modifies this collection so that it retains only its elements that are contained + * in the specified array. In other words, removes from this collection all of + * its elements that are not contained in the specified array. + * + * See also grep and findAll when wanting to produce a new list + * containing items which match some specified items but leaving the original collection unchanged. + * + * @param self a Collection to be modified + * @param items array containing elements to be retained from this collection + * @return true if this collection changed as a result of the call + * @see Collection#retainAll(Collection) + * @since 1.7.2 + */ + public static boolean retainAll(Collection self, Object[] items) { + return self.retainAll(Arrays.asList(items)); + } + + /** + * Modifies this collection so that it retains only its elements + * that are matched according to the specified closure condition. In other words, + * removes from this collection all of its elements that don't match. + * + * See also findAll and grep when wanting to produce a new list + * containing items which match some criteria but leaving the original collection unchanged. + * + * @param self a Collection to be modified + * @param condition a closure condition + * @return true if this collection changed as a result of the call + * @see Iterator#remove() + * @since 1.7.2 + */ + public static boolean retainAll(Collection self, Closure condition) { + Iterator iter = InvokerHelper.asIterator(self); + boolean result = false; + while (iter.hasNext()) { + Object value = iter.next(); + if (!DefaultTypeTransformation.castToBoolean(condition.call(value))) { + iter.remove(); + result = true; + } + } + return result; + } + + /** + * Modifies this collection by removing the elements that are matched according + * to the specified closure condition. + * + * See also findAll and grep when wanting to produce a new list + * containing items which don't match some criteria while leaving the original collection unchanged. + * + * @param self a Collection to be modified + * @param condition a closure condition + * @return true if this collection changed as a result of the call + * @see Iterator#remove() + * @since 1.7.2 + */ + public static boolean removeAll(Collection self, Closure condition) { + Iterator iter = InvokerHelper.asIterator(self); + boolean result = false; + while (iter.hasNext()) { + Object value = iter.next(); + if (DefaultTypeTransformation.castToBoolean(condition.call(value))) { + iter.remove(); + result = true; + } + } + return result; + } + + /** + * Modifies the collection by adding all of the elements in the specified array to the collection. + * The behavior of this operation is undefined if + * the specified array is modified while the operation is in progress. + * + * See also plus or the '+' operator if wanting to produce a new collection + * containing additional items but while leaving the original collection unchanged. + * + * @param self a Collection to be modified + * @param items array containing elements to be added to this collection + * @return true if this collection changed as a result of the call + * @see Collection#addAll(Collection) + * @since 1.7.2 + */ + public static boolean addAll(Collection self, T[] items) { + return self.addAll(Arrays.asList(items)); + } + + /** + * Modifies this list by inserting all of the elements in the specified array into the + * list at the specified position. Shifts the + * element currently at that position (if any) and any subsequent + * elements to the right (increases their indices). The new elements + * will appear in this list in the order that they occur in the array. + * The behavior of this operation is undefined if the specified array + * is modified while the operation is in progress. + * + * See also plus for similar functionality with copy semantics, i.e. which produces a new + * list after adding the additional items at the specified position but leaves the original list unchanged. + * + * @param self a list to be modified + * @param items array containing elements to be added to this collection + * @param index index at which to insert the first element from the + * specified array + * @return true if this collection changed as a result of the call + * @see List#addAll(int, Collection) + * @since 1.7.2 + */ + public static boolean addAll(List self, int index, T[] items) { + return self.addAll(index, Arrays.asList(items)); + } + + /** + * Splits all items into two lists based on the closure condition. + * The first list contains all items matching the closure expression. + * The second list all those that don't. + * + * @param self an Object with an Iterator returning its values + * @param closure a closure condition + * @return a List whose first item is the accepted values and whose second item is the rejected values + * @since 1.6.0 + */ + public static Collection split(Object self, Closure closure) { + List accept = new ArrayList(); + List reject = new ArrayList(); + return split(closure, accept, reject, InvokerHelper.asIterator(self)); + } + + /** + * Splits all items into two collections based on the closure condition. + * The first list contains all items which match the closure expression. + * The second list all those that don't. + *

    + * Example usage: + *

    assert [[2,4],[1,3]] == [1,2,3,4].split { it % 2 == 0 }
    + * + * @param self a Collection of values + * @param closure a closure condition + * @return a List whose first item is the accepted values and whose second item is the rejected values + * @since 1.6.0 + */ + public static Collection> split(Collection self, Closure closure) { + Collection accept = createSimilarCollection(self); + Collection reject = createSimilarCollection(self); + Iterator iter = self.iterator(); + return split(closure, accept, reject, iter); + } + + private static Collection> split(Closure closure, Collection accept, Collection reject, Iterator iter) { + List> answer = new ArrayList>(); + while (iter.hasNext()) { + T value = iter.next(); + if (DefaultTypeTransformation.castToBoolean(closure.call(value))) { + accept.add(value); + } else { + reject.add(value); + } + } + answer.add(accept); + answer.add(reject); + return answer; + } + + /** + * Adds GroovyCollections#combinations(Collection) as a method on collections. + *

    + * Example usage: + *

    assert [['a', 'b'],[1, 2, 3]].combinations() == [['a', 1], ['b', 1], ['a', 2], ['b', 2], ['a', 3], ['b', 3]]
    + * + * @param self a Collection of lists + * @return a List of the combinations found + * @see groovy.util.GroovyCollections#combinations(java.util.Collection) + * @since 1.5.0 + */ + public static List combinations(Collection self) { + return GroovyCollections.combinations(self); + } + + /** + * Finds all non-null subsequences of a list. + *

    + * Example usage: + *

    def result = [1, 2, 3].subsequences()
    +     * assert result == [[1, 2, 3], [1, 3], [2, 3], [1, 2], [1], [2], [3]] as Set
    + * + * @param self the List of items + * @return the subsequences from the list + * @since 1.7.0 + */ + public static Set> subsequences(List self) { + return GroovyCollections.subsequences(self); + } + + /** + * Finds all permutations of a collection. + *

    + * Example usage: + *

    def result = [1, 2, 3].permutations()
    +     * assert result == [[3, 2, 1], [3, 1, 2], [1, 3, 2], [2, 3, 1], [2, 1, 3], [1, 2, 3]] as Set
    + * + * @param self the Collection of items + * @return the permutations from the list + * @since 1.7.0 + */ + public static Set> permutations(List self) { + Set> ans = new HashSet>(); + PermutationGenerator generator = new PermutationGenerator(self); + while (generator.hasNext()) { + ans.add(generator.next()); + } + return ans; + } + + /** + * Iterates over all permutations of a collection, running a closure for each iteration. + *

    + * Example usage: + *

    def permutations = []
    +     * [1, 2, 3].eachPermutation{ permutations << it }
    +     * assert permutations == [[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]]
    + * + * @param self the Collection of items + * @param closure the closure to call for each permutation + * @return the permutations from the list + * @since 1.7.0 + */ + public static Iterator> eachPermutation(Collection self, Closure closure) { + Iterator> generator = new PermutationGenerator(self); + while (generator.hasNext()) { + closure.call(generator.next()); + } + return generator; + } + + /** + * Adds GroovyCollections#transpose(List) as a method on lists.
    + * A TransposeFunction takes a collection of columns and returns a collection of + * rows. The first row consists of the first element from each column. Successive + * rows are constructed similarly. + *

    + * Example usage: + *

    def result = [['a', 'b'], [1, 2]].transpose()
    +     * assert result == [['a', 1], ['b', 2]]
    + *
    def result = [['a', 'b'], [1, 2], [3, 4]].transpose()
    +     * assert result == [['a', 1, 3], ['b', 2, 4]]
    + * + * @param self a List of lists + * @return a List of the transposed lists + * @see groovy.util.GroovyCollections#transpose(java.util.List) + * @since 1.5.0 + */ + public static List transpose(List self) { + return GroovyCollections.transpose(self); + } + + /** + * Finds all entries matching the closure condition. If the + * closure takes one parameter then it will be passed the Map.Entry. + * Otherwise if the closure should take two parameters, which will be + * the key and the value. + *

    + * If the self map is one of TreeMap, LinkedHashMap, Hashtable + * or Properties, the returned Map will preserve that type, otherwise a HashMap will + * be returned. + *

    + * Example usage: + *

    def result = [a:1, b:2, c:4, d:5].findAll { it.value % 2 == 0 }
    +     * assert result.every { it instanceof Map.Entry }
    +     * assert result*.key == ["b", "c"]
    +     * assert result*.value == [2, 4]
    + * + * @param self a Map + * @param closure a 1 or 2 arg Closure condition applying on the entries + * @return a new subMap + * @since 1.0 + */ + public static Map findAll(Map self, Closure closure) { + Map answer = createSimilarMap(self); + for (Map.Entry entry : self.entrySet()) { + if (DefaultTypeTransformation.castToBoolean(callClosureForMapEntry(closure, entry))) { + answer.put(entry.getKey(), entry.getValue()); + } + } + return answer; + } + + /** + * Sorts all collection members into groups determined by the + * supplied mapping closure. The closure should return the key that this + * item should be grouped by. The returned LinkedHashMap will have an entry for each + * distinct key returned from the closure, with each value being a list of + * items for that group. + *

    + * Example usage: + *

    assert [0:[2,4,6], 1:[1,3,5]] == [1,2,3,4,5,6].groupBy { it % 2 }
    + * + * @param self a collection to group + * @param closure a closure mapping entries on keys + * @return a new Map grouped by keys + * @since 1.0 + */ + public static Map> groupBy(Collection self, Closure closure) { + Map> answer = new LinkedHashMap>(); + for (T element : self) { + K value = closure.call(element); + groupAnswer(answer, element, value); + } + return answer; + } + + /** + * Sorts all collection members into (sub)groups determined by the supplied + * mapping closures. Each closure should return the key that this item + * should be grouped by. The returned LinkedHashMap will have an entry for each + * distinct 'key path' returned from the closures, with each value being a list + * of items for that 'group path'.

    + * + * Example usage: + *

    def result = [1,2,3,4,5,6].groupBy({ it % 2 }, { it < 4 })
    +     * assert result == [1:[(true):[1, 3], (false):[5]], 0:[(true):[2], (false):[4, 6]]]
    + * + * Another example: + *
    def sql = groovy.sql.Sql.newInstance(/* ... */)
    +     * def data = sql.rows("SELECT * FROM a_table").groupBy({ it.column1 }, { it.column2 }, { it.column3 })
    +     * if (data.val1.val2.val3) {
    +     *     // there exists a record where:
    +     *     //   a_table.column1 == val1
    +     *     //   a_table.column2 == val2, and
    +     *     //   a_table.column3 == val3
    +     * } else {
    +     *     // there is no such record
    +     * }
    + * If an empty array of closures is supplied the IDENTITY Closure will be used. + * + * @param self a collection to group + * @param closures an array of closures, each mapping entries on keys + * @return a new Map grouped by keys on each criterion + * @since 1.8.1 + * @see Closure#IDENTITY + */ + public static Map groupBy(Collection self, Object... closures) { + final Closure head = closures.length == 0 ? Closure.IDENTITY : (Closure) closures[0]; + + @SuppressWarnings("unchecked") + Map first = groupBy(self, head); + if (closures.length < 2) + return first; + + final Object[] tail = new Object[closures.length - 1]; + System.arraycopy(closures, 1, tail, 0, closures.length - 1); // Arrays.copyOfRange only since JDK 1.6 + + // inject([:]) { a,e -> a << [(e.key): e.value.groupBy(tail)] } + Map acc = new LinkedHashMap(); + for (Map.Entry item : first.entrySet()) { + acc.put(item.getKey(), groupBy(item.getValue(), tail)); + } + + return acc; + } + + /** + * Sorts all collection members into (sub)groups determined by the supplied + * mapping closures. Each closure should return the key that this item + * should be grouped by. The returned LinkedHashMap will have an entry for each + * distinct 'key path' returned from the closures, with each value being a list + * of items for that 'group path'.

    + * + * Example usage: + *

    def result = [1,2,3,4,5,6].groupBy([{ it % 2 }, { it < 4 }])
    +     * assert result == [1:[(true):[1, 3], (false):[5]], 0:[(true):[2], (false):[4, 6]]]
    + * + * Another example: + *
    def sql = groovy.sql.Sql.newInstance(/* ... */)
    +     * def data = sql.rows("SELECT * FROM a_table").groupBy([{ it.column1 }, { it.column2 }, { it.column3 }])
    +     * if (data.val1.val2.val3) {
    +     *     // there exists a record where:
    +     *     //   a_table.column1 == val1
    +     *     //   a_table.column2 == val2, and
    +     *     //   a_table.column3 == val3
    +     * } else {
    +     *     // there is no such record
    +     * }
    + * If an empty list of closures is supplied the IDENTITY Closure will be used. + * + * @param self a collection to group + * @param closures a list of closures, each mapping entries on keys + * @return a new Map grouped by keys on each criterion + * @since 1.8.1 + * @see Closure#IDENTITY + */ + public static Map groupBy(Collection self, List closures) { + return groupBy(self, closures.toArray()); + } + + + /** + * Sorts all collection members into groups determined by the supplied mapping + * closure and counts the group size. The closure should return the key that each + * item should be grouped by. The returned Map will have an entry for each + * distinct key returned from the closure, with each value being the frequency of + * items occurring for that group. + *

    + * Example usage: + *

    assert [0:2, 1:3] == [1,2,3,4,5].countBy { it % 2 }
    + * + * @param self a collection to group and count + * @param closure a closure mapping items to the frequency keys + * @return a new Map grouped by keys with frequency counts + * @since 1.8.0 + */ + public static Map countBy(Collection self, Closure closure) { + return countBy(self.iterator(), closure); + } + + /** + * Sorts all array members into groups determined by the supplied mapping + * closure and counts the group size. The closure should return the key that each + * item should be grouped by. The returned Map will have an entry for each + * distinct key returned from the closure, with each value being the frequency of + * items occurring for that group. + *

    + * Example usage: + *

    assert ([1,2,2,2,3] as Object[]).countBy{ it % 2 } == [1:2, 0:3]
    + * + * @param self an object array to group and count + * @param closure a closure mapping items to the frequency keys + * @return a new Map grouped by keys with frequency counts + * @see #countBy(Collection, Closure) + * @since 1.8.0 + */ + public static Map countBy(Object[] self, Closure closure) { + return countBy(Arrays.asList(self), closure); + } + + /** + * Sorts all iterator items into groups determined by the supplied mapping + * closure and counts the group size. The closure should return the key that each + * item should be grouped by. The returned Map will have an entry for each + * distinct key returned from the closure, with each value being the frequency of + * items occurring for that group. + *

    + * Example usage: + *

    assert [1,2,2,2,3].toSet().iterator().countBy{ it % 2 } == [1:2, 0:1]
    + * + * @param self an iterator to group and count + * @param closure a closure mapping items to the frequency keys + * @return a new Map grouped by keys with frequency counts + * @see #countBy(Collection, Closure) + * @since 1.8.0 + */ + public static Map countBy(Iterator self, Closure closure) { + Map answer = new LinkedHashMap(); + while (self.hasNext()) { + K value = closure.call(self.next()); + countAnswer(answer, value); + } + return answer; + } + + /** + * Groups all map entries into groups determined by the + * supplied mapping closure. The closure will be passed a Map.Entry or + * key and value (depending on the number of parameters the closure accepts) + * and should return the key that each item should be grouped under. The + * resulting map will have an entry for each 'group' key returned by the + * closure, with values being the list of map entries that belong to each + * group. (If instead of a list of map entries, you want an actual map + * use {code}groupBy{code}.) + *
    def result = [a:1,b:2,c:3,d:4,e:5,f:6].groupEntriesBy { it.value % 2 }
    +     * assert result[0]*.key == ["b", "d", "f"]
    +     * assert result[1]*.value == [1, 3, 5]
    + * + * @param self a map to group + * @param closure a 1 or 2 arg Closure mapping entries on keys + * @return a new Map grouped by keys + * @since 1.5.2 + */ + public static Map>> groupEntriesBy(Map self, Closure closure) { + final Map>> answer = new LinkedHashMap>>(); + for (Map.Entry entry : self.entrySet()) { + G value = callClosureForMapEntry(closure, entry); + groupAnswer(answer, entry, value); + } + return answer; + } + + /** + * Groups the members of a map into sub maps determined by the + * supplied mapping closure. The closure will be passed a Map.Entry or + * key and value (depending on the number of parameters the closure accepts) + * and should return the key that each item should be grouped under. The + * resulting map will have an entry for each 'group' key returned by the + * closure, with values being the map members from the original map that + * belong to each group. (If instead of a map, you want a list of map entries + * use {code}groupEntriesBy{code}.) + *

    + * If the self map is one of TreeMap, Hashtable or Properties, + * the returned Map will preserve that type, otherwise a LinkedHashMap will + * be returned. + *

    def result = [a:1,b:2,c:3,d:4,e:5,f:6].groupBy { it.value % 2 }
    +     * assert result == [0:[b:2, d:4, f:6], 1:[a:1, c:3, e:5]]
    + * + * @param self a map to group + * @param closure a closure mapping entries on keys + * @return a new Map grouped by keys + * @since 1.0 + */ + public static Map> groupBy(Map self, Closure closure) { + final Map>> initial = groupEntriesBy(self, closure); + final Map> answer = new LinkedHashMap>(); + for (Map.Entry>> outer : initial.entrySet()) { + G key = outer.getKey(); + List> entries = outer.getValue(); + Map target = createSimilarMap(self); + putAll(target, entries); + answer.put(key, target); + } + return answer; + } + + /** + * Groups the members of a map into sub maps determined by the supplied + * mapping closures. Each closure will be passed a Map.Entry or key and + * value (depending on the number of parameters the closure accepts) and + * should return the key that each item should be grouped under. The + * resulting map will have an entry for each 'group path' returned by all + * closures, with values being the map members from the original map that + * belong to each such 'group path'.

    + * + * If the self map is one of TreeMap, Hashtable, or Properties, + * the returned Map will preserve that type, otherwise a LinkedHashMap will + * be returned. + * + *

    def result = [a:1,b:2,c:3,d:4,e:5,f:6].groupBy({ it.value % 2 }, { it.key.next() })
    +     * assert result == [1:[b:[a:1], d:[c:3], f:[e:5]], 0:[c:[b:2], e:[d:4], g:[f:6]]]
    + * If an empty array of closures is supplied the IDENTITY Closure will be used. + * + * @param self a map to group + * @param closures an array of closures that map entries on keys + * @return a new map grouped by keys on each criterion + * @since 1.8.1 + * @see Closure#IDENTITY + */ + public static Map groupBy(Map self, Object... closures) { + @SuppressWarnings("unchecked") + final Closure head = closures.length == 0 ? Closure.IDENTITY : (Closure) closures[0]; + + @SuppressWarnings("unchecked") + Map first = groupBy(self, head); + if (closures.length < 2) + return first; + + final Object[] tail = new Object[closures.length - 1]; + System.arraycopy(closures, 1, tail, 0, closures.length - 1); // Arrays.copyOfRange only since JDK 1.6 + + Map acc = new LinkedHashMap(); + for (Map.Entry item: first.entrySet()) { + acc.put(item.getKey(), groupBy(item.getValue(), tail)); + } + + return acc; + } + + /** + * Groups the members of a map into sub maps determined by the supplied + * mapping closures. Each closure will be passed a Map.Entry or key and + * value (depending on the number of parameters the closure accepts) and + * should return the key that each item should be grouped under. The + * resulting map will have an entry for each 'group path' returned by all + * closures, with values being the map members from the original map that + * belong to each such 'group path'.

    + * + * If the self map is one of TreeMap, Hashtable, or Properties, + * the returned Map will preserve that type, otherwise a LinkedHashMap will + * be returned. + * + *

    def result = [a:1,b:2,c:3,d:4,e:5,f:6].groupBy([{ it.value % 2 }, { it.key.next() }])
    +     * assert result == [1:[b:[a:1], d:[c:3], f:[e:5]], 0:[c:[b:2], e:[d:4], g:[f:6]]]
    + * If an empty list of closures is supplied the IDENTITY Closure will be used. + * + * @param self a map to group + * @param closures a list of closures that map entries on keys + * @return a new map grouped by keys on each criterion + * @since 1.8.1 + * @see Closure#IDENTITY + */ + public static Map groupBy(Map self, List closures) { + return groupBy(self, closures.toArray()); + } + + /** + * Groups the members of a map into groups determined by the + * supplied mapping closure and counts the frequency of the created groups. + * The closure will be passed a Map.Entry or + * key and value (depending on the number of parameters the closure accepts) + * and should return the key that each item should be grouped under. The + * resulting map will have an entry for each 'group' key returned by the + * closure, with values being the frequency counts for that 'group'. + *

    + *

    def result = [a:1,b:2,c:3,d:4,e:5].countBy { it.value % 2 }
    +     * assert result == [0:2, 1:3]
    + * + * @param self a map to group and count + * @param closure a closure mapping entries to frequency count keys + * @return a new Map grouped by keys with frequency counts + * @since 1.8.0 + */ + public static Map countBy(Map self, Closure closure) { + Map answer = new LinkedHashMap(); + for (Object entry : self.entrySet()) { + countAnswer(answer, callClosureForMapEntry(closure, (Map.Entry) entry)); + } + return answer; + } + + /** + * Groups the current element according to the value + * + * @param answer the map containing the results + * @param element the element to be placed + * @param value the value according to which the element will be placed + * @since 1.5.0 + */ + protected static void groupAnswer(final Map> answer, T element, K value) { + if (answer.containsKey(value)) { + answer.get(value).add(element); + } else { + List groupedElements = new ArrayList(); + groupedElements.add(element); + answer.put(value, groupedElements); + } + } + + private static void countAnswer(final Map answer, T mappedKey) { + if (!answer.containsKey(mappedKey)) { + answer.put(mappedKey, 0); + } + int current = answer.get(mappedKey); + answer.put(mappedKey, current + 1); + } + + // internal helper method + protected static T callClosureForMapEntry(Closure closure, Map.Entry entry) { + if (closure.getMaximumNumberOfParameters() == 2) { + return closure.call(new Object[]{entry.getKey(), entry.getValue()}); + } + return closure.call(entry); + } + + // internal helper method + protected static T callClosureForLine(Closure closure, String line, int counter) { + if (closure.getMaximumNumberOfParameters() == 2) { + return closure.call(new Object[]{line, counter}); + } + return closure.call(line); + } + + // internal helper method + protected static T callClosureForMapEntryAndCounter(Closure closure, Map.Entry entry, int counter) { + if (closure.getMaximumNumberOfParameters() == 3) { + return closure.call(new Object[]{entry.getKey(), entry.getValue(), counter}); + } + if (closure.getMaximumNumberOfParameters() == 2) { + return closure.call(new Object[]{entry, counter}); + } + return closure.call(entry); + } + + + /** + * Iterates through the given Collection, passing in the initial value to + * the 2-arg closure along with the first item. The result is passed back (injected) into + * the closure along with the second item. The new result is injected back into + * the closure along with the third item and so on until the entire collection + * has been used. Also known as foldLeft or reduce in functional parlance. + * + * Examples: + *
    +     * assert 1*1*2*3*4 == [1,2,3,4].inject(1) { acc, val -> acc * val }
    +     *
    +     * assert 0+1+2+3+4 == [1,2,3,4].inject(0) { acc, val -> acc + val }
    +     *
    +     * assert 'The quick brown fox' ==
    +     *     ['quick', 'brown', 'fox'].inject('The') { acc, val -> acc + ' ' + val }
    +     *
    +     * assert 'bat' ==
    +     *     ['rat', 'bat', 'cat'].inject('zzz') { min, next -> next < min ? next : min }
    +     *
    +     * def max = { a, b -> [a, b].max() }
    +     * def animals = ['bat', 'rat', 'cat']
    +     * assert 'rat' == animals.inject('aaa', max)
    +     * 
    + * Visual representation of the last example above: + *
    +     *    initVal  animals[0]
    +     *       v        v
    +     * max('aaa',   'bat')  =>  'bat'  animals[1]
    +     *                            v       v
    +     *                      max('bat',  'rat')  =>  'rat'  animals[2]
    +     *                                                v       v
    +     *                                          max('rat',  'cat')  =>  'rat'
    +     * 
    + * + * @param self a Collection + * @param initialValue some initial value + * @param closure a closure + * @return the result of the last closure call + * @since 1.0 + */ + public static T inject(Collection self, U initialValue, Closure closure) { + return (T) inject(self.iterator(), initialValue, closure); + } + + /** + * Iterates through the given Map, passing in the initial value to + * the 2-arg Closure along with the first item (or 3-arg Closure along with the first key and value). + * The result is passed back (injected) into + * the closure along with the second item. The new result is injected back into + * the closure along with the third item and so on until the entire collection + * has been used. Also known as foldLeft or reduce in functional parlance. + * + * Examples: + *
    +     * def map = [a:1, b:2, c:3]
    +     * assert map.inject([]) { list, k, v ->
    +     *   list + [k] * v
    +     * } == ['a', 'b', 'b', 'c', 'c', 'c']
    +     * 
    + * + * @param self a Map + * @param initialValue some initial value + * @param closure a 2 or 3 arg Closure + * @return the result of the last closure call + * @since 1.8.1 + */ + public static T inject(Map self, U initialValue, Closure closure) { + T value = initialValue; + for (Map.Entry entry : self.entrySet()) { + if (closure.getMaximumNumberOfParameters() == 3) { + value = closure.call(value, entry.getKey(), entry.getValue()); + } else { + value = closure.call(value, entry); + } + } + return value; + } + + + /** + * Iterates through the given Iterator, passing in the initial value to + * the closure along with the first item. The result is passed back (injected) into + * the closure along with the second item. The new result is injected back into + * the closure along with the third item and so on until the Iterator has been + * expired of values. Also known as foldLeft in functional parlance. + * + * @param self an Iterator + * @param initialValue some initial value + * @param closure a closure + * @return the result of the last closure call + * @see #inject(Collection, Object, Closure) + * @since 1.5.0 + */ + public static T inject(Iterator self, U initialValue, Closure closure) { + T value = initialValue; + Object[] params = new Object[2]; + while (self.hasNext()) { + Object item = self.next(); + params[0] = value; + params[1] = item; + value = closure.call(params); + } + return value; + } + + /** + * Iterates through the given Object, passing in the initial value to + * the closure along with the first item. The result is passed back (injected) into + * the closure along with the second item. The new result is injected back into + * the closure along with the third item and so on until further iteration of + * the object is not possible. Also known as foldLeft in functional parlance. + * + * @param self an Object + * @param initialValue some initial value + * @param closure a closure + * @return the result of the last closure call + * @see #inject(Collection, Object, Closure) + * @since 1.5.0 + */ + public static T inject(Object self, U initialValue, Closure closure) { + Iterator iter = InvokerHelper.asIterator(self); + return (T) inject(iter, initialValue, closure); + } + + /** + * Iterates through the given array, passing in the initial value to + * the closure along with the first item. The result is passed back (injected) into + * the closure along with the second item. The new result is injected back into + * the closure along with the third item and so on until all elements of the array + * have been used. Also known as foldLeft in functional parlance. + * + * @param self an Object[] + * @param initialValue some initial value + * @param closure a closure + * @return the result of the last closure call + * @see #inject(Collection, Object, Closure) + * @since 1.5.0 + */ + public static T inject(Object[] self, U initialValue, Closure closure) { + Object[] params = new Object[2]; + T value = initialValue; + for (Object next : self) { + params[0] = value; + params[1] = next; + value = closure.call(params); + } + return value; + } + + /** + * Sums the items in a collection. This is equivalent to invoking the + * "plus" method on all items in the collection. + *
    assert 1+2+3+4 == [1,2,3,4].sum()
    + * + * @param self Collection of values to add together + * @return The sum of all of the items + * @since 1.0 + */ + public static Object sum(Collection self) { + return sum(self, null, true); + } + + /** + * Sums the items in an array. This is equivalent to invoking the + * "plus" method on all items in the array. + * + * @param self The array of values to add together + * @return The sum of all of the items + * @see #sum(java.util.Collection) + * @since 1.7.1 + */ + public static Object sum(Object[] self) { + return sum(toList(self), null, true); + } + + /** + * Sums the items from an Iterator. This is equivalent to invoking the + * "plus" method on all items from the Iterator. The iterator will become + * exhausted of elements after determining the sum value. + * + * @param self an Iterator for the values to add together + * @return The sum of all of the items + * @since 1.5.5 + */ + public static Object sum(Iterator self) { + return sum(toList(self), null, true); + } + + /** + * Sums the items in a collection, adding the result to some initial value. + *
    assert 5+1+2+3+4 == [1,2,3,4].sum(5)
    + * + * @param self a collection of values to sum + * @param initialValue the items in the collection will be summed to this initial value + * @return The sum of all of the items. + * @since 1.5.0 + */ + public static Object sum(Collection self, Object initialValue) { + return sum(self, initialValue, false); + } + + /** + * Sums the items in an array, adding the result to some initial value. + * + * @param self an array of values to sum + * @param initialValue the items in the array will be summed to this initial value + * @return The sum of all of the items. + * @since 1.7.1 + */ + public static Object sum(Object[] self, Object initialValue) { + return sum(toList(self), initialValue, false); + } + + /** + * Sums the items from an Iterator, adding the result to some initial value. This is + * equivalent to invoking the "plus" method on all items from the Iterator. The iterator + * will become exhausted of elements after determining the sum value. + * + * @param self an Iterator for the values to add together + * @param initialValue the items in the collection will be summed to this initial value + * @return The sum of all of the items + * @since 1.5.5 + */ + public static Object sum(Iterator self, Object initialValue) { + return sum(toList(self), initialValue, false); + } + + private static Object sum(Collection self, Object initialValue, boolean first) { + Object result = initialValue; + Object[] param = new Object[1]; + for (Object next : self) { + param[0] = next; + if (first) { + result = param[0]; + first = false; + continue; + } + MetaClass metaClass = InvokerHelper.getMetaClass(result); + result = metaClass.invokeMethod(result, "plus", param); + } + return result; + } + + /** + * Sums the result of apply a closure to each item of a collection. + * coll.sum(closure) is equivalent to: + * coll.collect(closure).sum(). + *
    assert 4+6+10+12 == [2,3,5,6].sum() { it * 2 }
    + * + * @param self a Collection + * @param closure a single parameter closure that returns a numeric value. + * @return The sum of the values returned by applying the closure to each + * item of the collection. + * @since 1.0 + */ + public static Object sum(Collection self, Closure closure) { + return sum(self, null, closure, true); + } + + /** + * Sums the result of apply a closure to each item of an array. + * array.sum(closure) is equivalent to: + * array.collect(closure).sum(). + * + * @param self An array + * @param closure a single parameter closure that returns a numeric value. + * @return The sum of the values returned by applying the closure to each + * item of the array. + * @since 1.7.1 + */ + public static Object sum(Object[] self, Closure closure) { + return sum(toList(self), null, closure, true); + } + + /** + * Sums the result of apply a closure to each item returned from an iterator. + * iter.sum(closure) is equivalent to: + * iter.collect(closure).sum(). The iterator will become + * exhausted of elements after determining the sum value. + * + * @param self An Iterator + * @param closure a single parameter closure that returns a numeric value. + * @return The sum of the values returned by applying the closure to each + * item from the Iterator. + * @since 1.7.1 + */ + public static Object sum(Iterator self, Closure closure) { + return sum(toList(self), null, closure, true); + } + + /** + * Sums the result of applying a closure to each item of a collection to some initial value. + * coll.sum(initVal, closure) is equivalent to: + * coll.collect(closure).sum(initVal). + *
    assert 50+4+6+10+12 == [2,3,5,6].sum(50) { it * 2 }
    + * + * @param self a Collection + * @param closure a single parameter closure that returns a numeric value. + * @param initialValue the closure results will be summed to this initial value + * @return The sum of the values returned by applying the closure to each + * item of the collection. + * @since 1.5.0 + */ + public static Object sum(Collection self, Object initialValue, Closure closure) { + return sum(self, initialValue, closure, false); + } + + /** + * Sums the result of applying a closure to each item of an array to some initial value. + * array.sum(initVal, closure) is equivalent to: + * array.collect(closure).sum(initVal). + * + * @param self an array + * @param closure a single parameter closure that returns a numeric value. + * @param initialValue the closure results will be summed to this initial value + * @return The sum of the values returned by applying the closure to each + * item of the array. + * @since 1.7.1 + */ + public static Object sum(Object[] self, Object initialValue, Closure closure) { + return sum(toList(self), initialValue, closure, false); + } + + /** + * Sums the result of applying a closure to each item of an Iterator to some initial value. + * iter.sum(initVal, closure) is equivalent to: + * iter.collect(closure).sum(initVal). The iterator will become + * exhausted of elements after determining the sum value. + * + * @param self an Iterator + * @param closure a single parameter closure that returns a numeric value. + * @param initialValue the closure results will be summed to this initial value + * @return The sum of the values returned by applying the closure to each + * item from the Iterator. + * @since 1.7.1 + */ + public static Object sum(Iterator self, Object initialValue, Closure closure) { + return sum(toList(self), initialValue, closure, false); + } + + private static Object sum(Collection self, Object initialValue, Closure closure, boolean first) { + Object result = initialValue; + Object[] closureParam = new Object[1]; + Object[] plusParam = new Object[1]; + for (Object next : self) { + closureParam[0] = next; + plusParam[0] = closure.call(closureParam); + if (first) { + result = plusParam[0]; + first = false; + continue; + } + MetaClass metaClass = InvokerHelper.getMetaClass(result); + result = metaClass.invokeMethod(result, "plus", plusParam); + } + return result; + } + + /** + * Concatenates the toString() representation of each + * item from the iterator, with the given String as a separator between + * each item. The iterator will become exhausted of elements after + * determining the resulting conjoined value. + * + * @param self an Iterator of items + * @param separator a String separator + * @return the joined String + * @since 1.5.5 + */ + public static String join(Iterator self, String separator) { + return join(toList(self), separator); + } + + /** + * Concatenates the toString() representation of each + * item in this collection, with the given String as a separator between + * each item. + *
    assert "1, 2, 3" == [1,2,3].join(", ")
    + * + * @param self a Collection of objects + * @param separator a String separator + * @return the joined String + * @since 1.0 + */ + public static String join(Collection self, String separator) { + StringBuilder buffer = new StringBuilder(); + boolean first = true; + + if (separator == null) separator = ""; + + for (Object value : self) { + if (first) { + first = false; + } else { + buffer.append(separator); + } + buffer.append(InvokerHelper.toString(value)); + } + return buffer.toString(); + } + + /** + * Concatenates the toString() representation of each + * items in this array, with the given String as a separator between each + * item. + * + * @param self an array of Object + * @param separator a String separator + * @return the joined String + * @since 1.0 + */ + public static String join(Object[] self, String separator) { + StringBuilder buffer = new StringBuilder(); + boolean first = true; + + if (separator == null) separator = ""; + + for (Object next : self) { + String value = InvokerHelper.toString(next); + if (first) { + first = false; + } else { + buffer.append(separator); + } + buffer.append(value); + } + return buffer.toString(); + } + + /** + * Adds min() method to Collection objects. + *
    assert 2 == [4,2,5].min()
    + * + * @param self a Collection + * @return the minimum value + * @see groovy.util.GroovyCollections#min(java.util.Collection) + * @since 1.0 + */ + public static T min(Collection self) { + return GroovyCollections.min(self); + } + + /** + * Adds min() method to Iterator objects. The iterator will become + * exhausted of elements after determining the minimum value. + * + * @param self an Iterator + * @return the minimum value + * @see #min(java.util.Collection) + * @since 1.5.5 + */ + public static T min(Iterator self) { + return min(toList(self)); + } + + /** + * Adds min() method to Object arrays. + * + * @param self an Object array + * @return the minimum value + * @see #min(java.util.Collection) + * @since 1.5.5 + */ + public static T min(T[] self) { + return min(toList(self)); + } + + /** + * Selects the minimum value found in the collection using the given comparator. + *
    assert "hi" == ["hello","hi","hey"].min( { a, b -> a.length() <=> b.length() } as Comparator )
    + * + * @param self a Collection + * @param comparator a Comparator + * @return the minimum value + * @since 1.0 + */ + public static T min(Collection self, Comparator comparator) { + T answer = null; + for (T value : self) { + if (answer == null || comparator.compare(value, answer) < 0) { + answer = value; + } + } + return answer; + } + + /** + * Selects the minimum value found from the Iterator using the given comparator. + * + * @param self an Iterator + * @param comparator a Comparator + * @return the minimum value + * @see #min(java.util.Collection, java.util.Comparator) + * @since 1.5.5 + */ + public static T min(Iterator self, Comparator comparator) { + return min(toList(self), comparator); + } + + /** + * Selects the minimum value found from the Object array using the given comparator. + * + * @param self an Object array + * @param comparator a Comparator + * @return the minimum value + * @see #min(java.util.Collection, java.util.Comparator) + * @since 1.5.5 + */ + public static T min(T[] self, Comparator comparator) { + return min(toList(self), comparator); + } + + /** + * Selects an item in the collection having the minimum + * value as determined by the supplied closure. + * If more than one item has the minimum value, + * an arbitrary choice is made between the items having the minimum value. + *

    + * If the closure has two parameters + * it is used like a traditional Comparator. I.e. it should compare + * its two parameters for order, returning a negative integer, + * zero, or a positive integer when the first parameter is less than, + * equal to, or greater than the second respectively. Otherwise, + * the Closure is assumed to take a single parameter and return a + * Comparable (typically an Integer) which is then used for + * further comparison. + *
    +     * assert "hi" == ["hello","hi","hey"].min { it.length() }
    +     * 
    + *
    +     * def lastDigit = { a, b -> a % 10 <=> b % 10 }
    +     * assert [19, 55, 91].min(lastDigit) == 91
    +     * 
    + *
    +     * def pets = ['dog', 'cat', 'anaconda']
    +     * def shortestName = pets.min{ it.size() } // one of 'dog' or 'cat'
    +     * assert shortestName.size() == 3
    +     * 
    + * + * @param self a Collection + * @param closure a 1 or 2 arg Closure used to determine the correct ordering + * @return the minimum value + * @since 1.0 + */ + public static T min(Collection self, Closure closure) { + int params = closure.getMaximumNumberOfParameters(); + if (params != 1) { + return min(self, new ClosureComparator(closure)); + } + T answer = null; + Object answer_value = null; + for (T item : self) { + Object value = closure.call(item); + if (answer == null || ScriptBytecodeAdapter.compareLessThan(value, answer_value)) { + answer = item; + answer_value = value; + } + } + return answer; + } + + /** + * Selects an entry in the map having the minimum + * calculated value as determined by the supplied closure. + * If more than one entry has the minimum value, + * an arbitrary choice is made between the entries having the minimum value. + *

    + * If the closure has two parameters + * it is used like a traditional Comparator. I.e. it should compare + * its two parameters for order, returning a negative integer, + * zero, or a positive integer when the first parameter is less than, + * equal to, or greater than the second respectively. Otherwise, + * the Closure is assumed to take a single parameter and return a + * Comparable (typically an Integer) which is then used for + * further comparison. + *
    +     * def zoo = [monkeys:6, lions:5, tigers:7]
    +     * def leastCommonEntry = zoo.min{ it.value }
    +     * assert leastCommonEntry.value == 5
    +     * def mostCommonEntry = zoo.min{ a, b -> b.value <=> a.value } // double negative!
    +     * assert mostCommonEntry.value == 7
    +     * 
    + * Edge case for multiple min values: + *
    +     * def zoo = [monkeys:6, lions:5, tigers:7]
    +     * def lastCharOfName = { e -> e.key[-1] }
    +     * def ans = zoo.min(lastCharOfName) // some random entry
    +     * assert lastCharOfName(ans) == 's'
    +     * 
    + * + * @param self a Map + * @param closure a 1 or 2 arg Closure used to determine the correct ordering + * @return the Map.Entry having the minimum value as determined by the closure + * @since 1.7.6 + */ + public static Map.Entry min(Map self, Closure closure) { + return min(self.entrySet(), closure); + } + + /** + * Selects an entry in the map having the maximum + * calculated value as determined by the supplied closure. + * If more than one entry has the maximum value, + * an arbitrary choice is made between the entries having the maximum value. + *

    + * If the closure has two parameters + * it is used like a traditional Comparator. I.e. it should compare + * its two parameters for order, returning a negative integer, + * zero, or a positive integer when the first parameter is less than, + * equal to, or greater than the second respectively. Otherwise, + * the Closure is assumed to take a single parameter and return a + * Comparable (typically an Integer) which is then used for + * further comparison. An example: + *
    +     * def zoo = [monkeys:6, lions:5, tigers:7]
    +     * def mostCommonEntry = zoo.max{ it.value }
    +     * assert mostCommonEntry.value == 7
    +     * def leastCommonEntry = zoo.max{ a, b -> b.value <=> a.value } // double negative!
    +     * assert leastCommonEntry.value == 5
    +     * 
    + * Edge case for multiple max values: + *
    +     * def zoo = [monkeys:6, lions:5, tigers:7]
    +     * def lengthOfNamePlusNumber = { e -> e.key.size() + e.value }
    +     * def ans = zoo.max(lengthOfNamePlusNumber) // one of [monkeys:6, tigers:7]
    +     * assert lengthOfNamePlusNumber(ans) == 13
    +     * 
    + * + * @param self a Map + * @param closure a 1 or 2 arg Closure used to determine the correct ordering + * @return the Map.Entry having the maximum value as determined by the closure + * @since 1.7.6 + */ + public static Map.Entry max(Map self, Closure closure) { + return max(self.entrySet(), closure); + } + + /** + * Selects the minimum value found from the Iterator + * using the closure to determine the correct ordering. + * The iterator will become + * exhausted of elements after this operation. + *

    + * If the closure has two parameters + * it is used like a traditional Comparator. I.e. it should compare + * its two parameters for order, returning a negative integer, + * zero, or a positive integer when the first parameter is less than, + * equal to, or greater than the second respectively. Otherwise, + * the Closure is assumed to take a single parameter and return a + * Comparable (typically an Integer) which is then used for + * further comparison. + * + * @param self an Iterator + * @param closure a Closure used to determine the correct ordering + * @return the minimum value + * @see #min(java.util.Collection, groovy.lang.Closure) + * @since 1.5.5 + */ + public static T min(Iterator self, Closure closure) { + return min(toList(self), closure); + } + + /** + * Selects the minimum value found from the Object array + * using the closure to determine the correct ordering. + *

    + * If the closure has two parameters + * it is used like a traditional Comparator. I.e. it should compare + * its two parameters for order, returning a negative integer, + * zero, or a positive integer when the first parameter is less than, + * equal to, or greater than the second respectively. Otherwise, + * the Closure is assumed to take a single parameter and return a + * Comparable (typically an Integer) which is then used for + * further comparison. + * + * @param self an Object array + * @param closure a Closure used to determine the correct ordering + * @return the minimum value + * @see #min(java.util.Collection, groovy.lang.Closure) + * @since 1.5.5 + */ + public static T min(T[] self, Closure closure) { + return min(toList(self), closure); + } + + /** + * Adds max() method to Collection objects. + *
    assert 5 == [2,3,1,5,4].max()
    + * + * @param self a Collection + * @return the maximum value + * @see groovy.util.GroovyCollections#max(java.util.Collection) + * @since 1.0 + */ + public static T max(Collection self) { + return GroovyCollections.max(self); + } + + /** + * Adds max() method to Iterator objects. The iterator will become + * exhausted of elements after determining the maximum value. + * + * @param self an Iterator + * @return the maximum value + * @see groovy.util.GroovyCollections#max(java.util.Collection) + * @since 1.5.5 + */ + public static T max(Iterator self) { + return max(toList(self)); + } + + /** + * Adds max() method to Object arrays. + * + * @param self an Object array + * @return the maximum value + * @see #max(java.util.Collection) + * @since 1.5.5 + */ + public static T max(T[] self) { + return max(toList(self)); + } + + /** + * Selects an item in the collection having the maximum + * value as determined by the supplied closure. + * If more than one item has the maximum value, + * an arbitrary choice is made between the items having the maximum value. + *

    + * If the closure has two parameters + * it is used like a traditional Comparator. I.e. it should compare + * its two parameters for order, returning a negative integer, + * zero, or a positive integer when the first parameter is less than, + * equal to, or greater than the second respectively. Otherwise, + * the Closure is assumed to take a single parameter and return a + * Comparable (typically an Integer) which is then used for + * further comparison. + *
    assert "hello" == ["hello","hi","hey"].max { it.length() }
    + *
    assert "hello" == ["hello","hi","hey"].max { a, b -> a.length() <=> b.length() }
    + *
    +     * def pets = ['dog', 'elephant', 'anaconda']
    +     * def longestName = pets.max{ it.size() } // one of 'elephant' or 'anaconda'
    +     * assert longestName.size() == 8
    +     * 
    + * + * @param self a Collection + * @param closure a 1 or 2 arg Closure used to determine the correct ordering + * @return the maximum value + * @since 1.0 + */ + public static T max(Collection self, Closure closure) { + int params = closure.getMaximumNumberOfParameters(); + if (params != 1) { + return max(self, new ClosureComparator(closure)); + } + T answer = null; + Object answerValue = null; + for (T item : self) { + Object value = closure.call(item); + if (answer == null || ScriptBytecodeAdapter.compareLessThan(answerValue, value)) { + answer = item; + answerValue = value; + } + } + return answer; + } + + /** + * Selects the maximum value found from the Iterator + * using the closure to determine the correct ordering. + * The iterator will become exhausted of elements after this operation. + *

    + * If the closure has two parameters + * it is used like a traditional Comparator. I.e. it should compare + * its two parameters for order, returning a negative integer, + * zero, or a positive integer when the first parameter is less than, + * equal to, or greater than the second respectively. Otherwise, + * the Closure is assumed to take a single parameter and return a + * Comparable (typically an Integer) which is then used for + * further comparison. + * + * @param self an Iterator + * @param closure a Closure used to determine the correct ordering + * @return the maximum value + * @see #max(java.util.Collection, groovy.lang.Closure) + * @since 1.5.5 + */ + public static T max(Iterator self, Closure closure) { + return max(toList(self), closure); + } + + /** + * Selects the maximum value found from the Object array + * using the closure to determine the correct ordering. + *

    + * If the closure has two parameters + * it is used like a traditional Comparator. I.e. it should compare + * its two parameters for order, returning a negative integer, + * zero, or a positive integer when the first parameter is less than, + * equal to, or greater than the second respectively. Otherwise, + * the Closure is assumed to take a single parameter and return a + * Comparable (typically an Integer) which is then used for + * further comparison. + * + * @param self an Object array + * @param closure a Closure used to determine the correct ordering + * @return the maximum value + * @see #max(java.util.Collection, groovy.lang.Closure) + * @since 1.5.5 + */ + public static T max(T[] self, Closure closure) { + return max(toList(self), closure); + } + + /** + * Selects the maximum value found in the collection using the given comparator. + *
    assert "hello" == ["hello","hi","hey"].max( { a, b -> a.length() <=> b.length() } as Comparator )
    + * + * @param self a Collection + * @param comparator a Comparator + * @return the maximum value + * @since 1.0 + */ + public static T max(Collection self, Comparator comparator) { + T answer = null; + for (T value : self) { + if (answer == null || comparator.compare(value, answer) > 0) { + answer = value; + } + } + return answer; + } + + /** + * Selects the maximum value found from the Iterator using the given comparator. + * + * @param self an Iterator + * @param comparator a Comparator + * @return the maximum value + * @since 1.5.5 + */ + public static T max(Iterator self, Comparator comparator) { + return max(toList(self), comparator); + } + + /** + * Selects the maximum value found from the Object array using the given comparator. + * + * @param self an Object array + * @param comparator a Comparator + * @return the maximum value + * @since 1.5.5 + */ + public static T max(T[] self, Comparator comparator) { + return max(toList(self), comparator); + } + + /** + * Provide the standard Groovy size() method for Iterator. + * The iterator will become exhausted of elements after determining the size value. + * + * @param self an Iterator + * @return the length of the Iterator + * @since 1.5.5 + */ + public static int size(Iterator self) { + int count = 0; + while (self.hasNext()) { + self.next(); + count++; + } + return count; + } + + /** + * Provide the standard Groovy size() method for String. + * + * @param text a String + * @return the length of the String + * @since 1.0 + */ + public static int size(String text) { + return text.length(); + } + + /** + * Provide the standard Groovy size() method for CharSequence. + * + * @param text a CharSequence + * @return the length of the CharSequence + * @since 1.8.2 + */ + public static int size(CharSequence text) { + return text.length(); + } + + /** + * Provide the standard Groovy size() method for StringBuffer. + * + * @param buffer a StringBuffer + * @return the length of the StringBuffer + * @since 1.0 + */ + public static int size(StringBuffer buffer) { + return buffer.length(); + } + + /** + * Provide the standard Groovy size() method for File. + * + * @param self a file object + * @return the file's size (length) + * @since 1.5.0 + */ + public static long size(File self) { + return self.length(); + } + + + /** + * Provide the standard Groovy size() method for Matcher. + * + * @param self a matcher object + * @return the matcher's size (count) + * @since 1.5.0 + */ + public static long size(Matcher self) { + return getCount(self); + } + + /** + * Provide the standard Groovy size() method for an array. + * + * @param self an Array of objects + * @return the size (length) of the Array + * @since 1.0 + */ + public static int size(Object[] self) { + return self.length; + } + + /** + * Support the subscript operator for CharSequence. + * + * @param text a CharSequence + * @param index the index of the Character to get + * @return the Character at the given index + * @since 1.0 + */ + public static CharSequence getAt(CharSequence text, int index) { + index = normaliseIndex(index, text.length()); + return text.subSequence(index, index + 1); + } + + /** + * Support the subscript operator for String. + * + * @param text a String + * @param index the index of the Character to get + * @return the Character at the given index + * @since 1.0 + */ + public static String getAt(String text, int index) { + index = normaliseIndex(index, text.length()); + return text.substring(index, index + 1); + } + + /** + * Support the range subscript operator for CharSequence + * + * @param text a CharSequence + * @param range a Range + * @return the subsequence CharSequence + * @since 1.0 + */ + public static CharSequence getAt(CharSequence text, Range range) { + int from = normaliseIndex(DefaultTypeTransformation.intUnbox(range.getFrom()), text.length()); + int to = normaliseIndex(DefaultTypeTransformation.intUnbox(range.getTo()), text.length()); + + boolean reverse = range.isReverse(); + // If this is a backwards range, reverse the arguments to substring. + if (from > to) { + int tmp = from; + from = to; + to = tmp; + reverse = !reverse; + } + + CharSequence sequence = text.subSequence(from, to + 1); + return reverse ? reverse((String) sequence) : sequence; + } + + /** + * Support the range subscript operator for CharSequence or StringBuffer with IntRange + * + * @param text a CharSequence + * @param range an IntRange + * @return the subsequence CharSequence + * @since 1.0 + */ + public static CharSequence getAt(CharSequence text, IntRange range) { + return getAt(text, (Range) range); + } + + /** + * Support the range subscript operator for CharSequence or StringBuffer with EmptyRange + * + * @param text a CharSequence + * @param range an EmptyRange + * @return the subsequence CharSequence + * @since 1.5.0 + */ + public static CharSequence getAt(CharSequence text, EmptyRange range) { + return ""; + } + + /** + * Support the range subscript operator for String with IntRange + * + * @param text a String + * @param range an IntRange + * @return the resulting String + * @since 1.0 + */ + public static String getAt(String text, IntRange range) { + return getAt(text, (Range) range); + } + + /** + * Support the range subscript operator for String with EmptyRange + * + * @param text a String + * @param range an EmptyRange + * @return the resulting String + * @since 1.5.0 + */ + public static String getAt(String text, EmptyRange range) { + return ""; + } + + /** + * Support the range subscript operator for String + * + * @param text a String + * @param range a Range + * @return a substring corresponding to the Range + * @since 1.0 + */ + public static String getAt(String text, Range range) { + int from = normaliseIndex(DefaultTypeTransformation.intUnbox(range.getFrom()), text.length()); + int to = normaliseIndex(DefaultTypeTransformation.intUnbox(range.getTo()), text.length()); + + // If this is a backwards range, reverse the arguments to substring. + boolean reverse = range.isReverse(); + if (from > to) { + int tmp = to; + to = from; + from = tmp; + reverse = !reverse; + } + + String answer = text.substring(from, to + 1); + if (reverse) { + answer = reverse(answer); + } + return answer; + } + + /** + * Creates a new string which is the reverse (backwards) of this string + * + * @param self a String + * @return a new string with all the characters reversed. + * @since 1.0 + * @see java.lang.StringBuilder#reverse() + */ + public static String reverse(String self) { + return new StringBuilder(self).reverse().toString(); + } + + /** + * Creates a new CharSequence which is the reverse (backwards) of this string + * + * @param self a CharSequence + * @return a new CharSequence with all the characters reversed. + * @see #reverse(String) + * @since 1.8.2 + */ + public static CharSequence reverse(CharSequence self) { + return new StringBuilder(self).reverse().toString(); + } + + /** + *

    Strip leading whitespace/control characters followed by '|' from + * every line in a String.

    + *
    +     * assert 'ABC\n123\n456' == '''ABC
    +     *                             |123
    +     *                             |456'''.stripMargin()
    +     * 
    + * + * @param self The String to strip the margin from + * @return the stripped String + * @see #stripMargin(String, char) + * @since 1.7.3 + */ + public static String stripMargin(String self) { + return stripMargin(self, '|'); + } + + /** + *

    Strip leading whitespace/control characters followed by '|' from + * every line in a CharSequence.

    + * + * @param self The CharSequence to strip the margin from + * @return the stripped CharSequence + * @see #stripMargin(CharSequence, char) + * @since 1.8.2 + */ + public static CharSequence stripMargin(CharSequence self) { + return stripMargin(self, '|'); + } + + /** + *

    Strip leading whitespace/control characters followed by marginChar from + * every line in a String.

    + * + * @param self The String to strip the margin from + * @param marginChar Any character that serves as margin delimiter + * @return the stripped String + * @see #stripMargin(String, char) + * @since 1.7.3 + */ + public static String stripMargin(String self, String marginChar) { + if (marginChar == null || marginChar.length() == 0) return stripMargin(self, '|'); + // TODO IllegalArgumentException for marginChar.length() > 1 ? Or support String as marker? + return stripMargin(self, marginChar.charAt(0)); + } + + /** + *

    Strip leading whitespace/control characters followed by marginChar from + * every line in a CharSequence.

    + * + * @param self The CharSequence to strip the margin from + * @param marginChar Any character that serves as margin delimiter + * @return the stripped CharSequence + * @see #stripMargin(String, String) + * @since 1.8.2 + */ + public static String stripMargin(CharSequence self, CharSequence marginChar) { + return stripMargin(self.toString(), marginChar.toString()); + } + + /** + *

    Strip leading whitespace/control characters followed by marginChar from + * every line in a String.

    + *
    +     * assert 'ABC\n123\n456' == '''ABC
    +     *                             *123
    +     *                             *456'''.stripMargin('*')
    +     * 
    + * + * @param self The String to strip the margin from + * @param marginChar Any character that serves as margin delimiter + * @return the stripped String + * @since 1.7.3 + */ + public static String stripMargin(String self, char marginChar) { + if (self.length() == 0) return self; + try { + StringBuilder builder = new StringBuilder(); + for (String line : readLines(self)) { + builder.append(stripMarginFromLine(line, marginChar)); + builder.append("\n"); + } + // remove the normalized ending line ending if it was not present + if (!self.endsWith("\n")) { + builder.deleteCharAt(builder.length() - 1); + } + return builder.toString(); + } catch (IOException e) { + /* ignore */ + } + return self; + } + + /** + *

    Strip leading whitespace/control characters followed by marginChar from + * every line in a String.

    + * + * @param self The CharSequence to strip the margin from + * @param marginChar Any character that serves as margin delimiter + * @return the stripped CharSequence + * @see #stripMargin(String, char) + * @since 1.8.2 + */ + public static CharSequence stripMargin(CharSequence self, char marginChar) { + return stripMargin(self.toString(), marginChar); + } + + // TODO expose this for stream based stripping? + private static String stripMarginFromLine(String line, char marginChar) { + int length = line.length(); + int index = 0; + while (index < length && line.charAt(index) <= ' ') index++; + return (index < length && line.charAt(index) == marginChar) ? line.substring(index + 1) : line; + } + + /** + *

    Strip leading spaces from every line in a String. The + * line with the least number of leading spaces determines + * the number to remove. Lines only containing whitespace are + * ignored when calculating the number of leading spaces to strip.

    + *
    +     * assert '  A\n B\nC' == '   A\n  B\n C'.stripIndent()
    +     * 
    + * + * @param self The String to strip the leading spaces from + * @return the stripped String + * @see #stripIndent(String, int) + * @since 1.7.3 + */ + public static String stripIndent(String self) { + if (self.length() == 0) return self; + int runningCount = -1; + try { + for (String line : readLines(self)) { + // don't take blank lines into account for calculating the indent + if (isAllWhitespace(line)) continue; + if (runningCount == -1) runningCount = line.length(); + runningCount = findMinimumLeadingSpaces(line, runningCount); + if (runningCount == 0) break; + } + } catch (IOException e) { + /* ignore */ + } + return stripIndent(self, runningCount == -1 ? 0 : runningCount); + } + + /** + *

    Strip leading spaces from every line in a CharSequence. The + * line with the least number of leading spaces determines + * the number to remove. Lines only containing whitespace are + * ignored when calculating the number of leading spaces to strip.

    + * + * @param self The CharSequence to strip the leading spaces from + * @return the stripped CharSequence + * @see #stripIndent(String) + * @since 1.8.2 + */ + public static CharSequence stripIndent(CharSequence self) { + return stripIndent(self.toString()); + } + + /** + * True if a String only contains whitespace characters. + * + * @param self The String to check the characters in + * @return true If all characters are whitespace characters + * @see Character#isWhitespace(char) + * @since 1.6 + */ + public static boolean isAllWhitespace(String self) { + for (int i = 0; i < self.length(); i++) { + if (!Character.isWhitespace(self.charAt(i))) + return false; + } + return true; + } + + /** + * True if a CharSequence only contains whitespace characters. + * + * @param self The CharSequence to check the characters in + * @return true If all characters are whitespace characters + * @see #isAllWhitespace(String) + * @since 1.8.2 + */ + public static boolean isAllWhitespace(CharSequence self) { + return isAllWhitespace(self.toString()); + } + + // TODO expose this for stream based scenarios? + private static int findMinimumLeadingSpaces(String line, int count) { + int length = line.length(); + int index = 0; + while (index < length && index < count && Character.isWhitespace(line.charAt(index))) index++; + return index; + } + + /** + *

    Strip numChar leading characters from + * every line in a String.

    + *
    +     * assert 'DEF\n456' == '''ABCDEF\n123456'''.stripIndent(3)
    +     * 
    + * + * @param self The String to strip the characters from + * @param numChars The number of characters to strip + * @return the stripped String + * @since 1.7.3 + */ + public static String stripIndent(String self, int numChars) { + if (self.length() == 0 || numChars <= 0) return self; + try { + StringBuilder builder = new StringBuilder(); + for (String line : readLines(self)) { + // normalize an empty or whitespace line to \n + // or strip the indent for lines containing non-space characters + if (!isAllWhitespace(line)) { + builder.append(stripIndentFromLine(line, numChars)); + } + builder.append("\n"); + } + // remove the normalized ending line ending if it was not present + if (!self.endsWith("\n")) { + builder.deleteCharAt(builder.length() - 1); + } + return builder.toString(); + } catch (IOException e) { + /* ignore */ + } + return self; + } + + /** + *

    Strip numChar leading characters from + * every line in a CharSequence.

    + * + * @param self The CharSequence to strip the characters from + * @param numChars The number of characters to strip + * @return the stripped CharSequence + * @since 1.8.2 + */ + public static CharSequence stripIndent(CharSequence self, int numChars) { + return stripIndent(self); + } + + // TODO expose this for stream based stripping? + private static String stripIndentFromLine(String line, int numChars) { + int length = line.length(); + return numChars <= length ? line.substring(numChars) : ""; + } + + /** + * Transforms a String representing a URL into a URL object. + * + * @param self the String representing a URL + * @return a URL + * @throws MalformedURLException is thrown if the URL is not well formed. + * @since 1.0 + */ + public static URL toURL(String self) throws MalformedURLException { + return new URL(self); + } + + /** + * Transforms a CharSequence representing a URL into a URL object. + * + * @param self the CharSequence representing a URL + * @return a URL + * @throws MalformedURLException is thrown if the URL is not well formed. + * @since 1.8.2 + */ + public static URL toURL(CharSequence self) throws MalformedURLException { + return new URL(self.toString()); + } + + /** + * Transforms a String representing a URI into a URI object. + * + * @param self the String representing a URI + * @return a URI + * @throws URISyntaxException is thrown if the URI is not well formed. + * @since 1.0 + */ + public static URI toURI(String self) throws URISyntaxException { + return new URI(self); + } + + /** + * Transforms a CharSequence representing a URI into a URI object. + * + * @param self the CharSequence representing a URI + * @return a URI + * @throws URISyntaxException is thrown if the URI is not well formed. + * @since 1.8.2 + */ + public static URI toURI(CharSequence self) throws URISyntaxException { + return new URI(self.toString()); + } + + /** + * Turns a String into a regular expression Pattern + * + * @param self a String to convert into a regular expression + * @return the regular expression pattern + * @since 1.5.0 + */ + public static Pattern bitwiseNegate(String self) { + return Pattern.compile(self); + } + + /** + * Turns a CharSequence into a regular expression Pattern + * + * @param self a String to convert into a regular expression + * @return the regular expression pattern + * @since 1.8.2 + */ + public static Pattern bitwiseNegate(CharSequence self) { + return Pattern.compile(self.toString()); + } + + /** + * Replaces the first substring of a String that matches the given + * compiled regular expression with the given replacement. + *

    + * Note that backslashes (\) and dollar signs ($) in the + * replacement string may cause the results to be different than if it were + * being treated as a literal replacement string; see + * {@link java.util.regex.Matcher#replaceFirst}. + * Use {@link java.util.regex.Matcher#quoteReplacement} to suppress the special + * meaning of these characters, if desired. + *

    + *

    +     * assert "foo".replaceFirst('o', 'X') == 'fXo'
    +     * 
    + * + * @param self the string that is to be matched + * @param pattern the regex Pattern to which the string of interest is to be matched + * @param replacement the string to be substituted for the first match + * @return The resulting String + * @see java.lang.String#replaceFirst(java.lang.String, java.lang.String) + * @since 1.6.1 + */ + public static String replaceFirst(String self, Pattern pattern, String replacement) { + return pattern.matcher(self).replaceFirst(replacement); + } + + /** + * Replaces the first substring of a CharSequence that matches the given + * compiled regular expression with the given replacement. + * + * @param self the CharSequence that is to be matched + * @param pattern the regex Pattern to which the CharSequence of interest is to be matched + * @param replacement the CharSequence to be substituted for the first match + * @return The resulting CharSequence + * @see #replaceFirst(String, Pattern, String) + * @since 1.8.2 + */ + public static CharSequence replaceFirst(CharSequence self, Pattern pattern, CharSequence replacement) { + return pattern.matcher(self).replaceFirst(replacement.toString()); + } + + /** + * Replaces all substrings of a String that match the given + * compiled regular expression with the given replacement. + *

    + * Note that backslashes (\) and dollar signs ($) in the + * replacement string may cause the results to be different than if it were + * being treated as a literal replacement string; see + * {@link java.util.regex.Matcher#replaceAll}. + * Use {@link java.util.regex.Matcher#quoteReplacement} to suppress the special + * meaning of these characters, if desired. + *

    + *

    +     * assert "foo".replaceAll('o', 'X') == 'fXX'
    +     * 
    + * + * @param self the string that is to be matched + * @param pattern the regex Pattern to which the string of interest is to be matched + * @param replacement the string to be substituted for the first match + * @return The resulting String + * @see java.lang.String#replaceAll(java.lang.String, java.lang.String) + * @since 1.6.1 + */ + public static String replaceAll(String self, Pattern pattern, String replacement) { + return pattern.matcher(self).replaceAll(replacement); + } + + /** + * Replaces all substrings of a CharSequence that match the given + * compiled regular expression with the given replacement. + * + * @param self the CharSequence that is to be matched + * @param pattern the regex Pattern to which the CharSequence of interest is to be matched + * @param replacement the CharSequence to be substituted for the first match + * @return The resulting CharSequence + * @see #replaceAll(String, Pattern, String) + * @since 1.8.2 + */ + public static CharSequence replaceAll(CharSequence self, Pattern pattern, CharSequence replacement) { + return pattern.matcher(self).replaceAll(replacement.toString()); + } + + /** + * Translates a string by replacing characters from the sourceSet with characters from replacementSet. + * If the first character from sourceSet appears in the string, it will be replaced with the first character from replacementSet. + * If the second character from sourceSet appears in the string, it will be replaced with the second character from replacementSet. + * and so on for all provided replacement characters. + *

    + * Here is an example which converts the vowels in a word from lower to uppercase: + *

    +     * assert 'hello'.tr('aeiou', 'AEIOU') == 'hEllO'
    +     * 
    + * A character range using regex-style syntax can also be used, e.g. here is an example which converts a word from lower to uppercase: + *
    +     * assert 'hello'.tr('a-z', 'A-Z') == 'HELLO'
    +     * 
    + * Hyphens at the start or end of sourceSet or replacementSet are treated as normal hyphens and are not + * considered to be part of a range specification. Similarly, a hyphen immediately after an earlier range + * is treated as a normal hyphen. So, '-x', 'x-' have no ranges while 'a-c-e' has the range 'a-c' plus + * the '-' character plus the 'e' character. + *

    + * Unlike the unix tr command, Groovy's tr command supports reverse ranges, e.g.: + *

    +     * assert 'hello'.tr('z-a', 'Z-A') == 'HELLO'
    +     * 
    + * If replacementSet is smaller than sourceSet, then the last character from replacementSet is used as the replacement for all remaining source characters as shown here: + *
    +     * assert 'Hello World!'.tr('a-z', 'A') == 'HAAAA WAAAA!'
    +     * 
    + * If sourceSet contains repeated characters, the last specified replacement is used as shown here: + *
    +     * assert 'Hello World!'.tr('lloo', '1234') == 'He224 W4r2d!'
    +     * 
    + * The functionality provided by tr can be achieved using regular expressions but tr provides a much more compact + * notation and efficient implementation for certain scenarios. + * + * @param self the string that is to be translated + * @param sourceSet the set of characters to translate from + * @param replacementSet the set of replacement characters + * @return The resulting translated String + * @see org.codehaus.groovy.util.StringUtil#tr(String, String, String) + * @since 1.7.3 + */ + public static String tr(final String self, String sourceSet, String replacementSet) throws ClassNotFoundException { + return (String) InvokerHelper.invokeStaticMethod("org.codehaus.groovy.util.StringUtil", "tr", new Object[]{self, sourceSet, replacementSet}); + } + + /** + * Translates a string by replacing characters from the sourceSet with characters from replacementSet. + * + * @param self the CharSequence that is to be translated + * @param sourceSet the set of characters to translate from + * @param replacementSet the set of replacement characters + * @return The resulting translated CharSequence + * @see #tr(String, String, String) + * @since 1.8.2 + */ + public static CharSequence tr(final CharSequence self, CharSequence sourceSet, CharSequence replacementSet) throws ClassNotFoundException { + return tr(self.toString(), sourceSet.toString(), replacementSet.toString()); + } + + /** + * Tells whether or not self matches the given + * compiled regular expression Pattern. + * + * @param self the string that is to be matched + * @param pattern the regex Pattern to which the string of interest is to be matched + * @return true if the string matches + * @see java.lang.String#matches(java.lang.String) + * @since 1.6.1 + */ + public static boolean matches(String self, Pattern pattern) { + return pattern.matcher(self).matches(); + } + + /** + * Tells whether or not a CharSequence matches the given + * compiled regular expression Pattern. + * + * @param self the CharSequence that is to be matched + * @param pattern the regex Pattern to which the string of interest is to be matched + * @return true if the CharSequence matches + * @see java.lang.String#matches(java.lang.String) + * @since 1.8.2 + */ + public static boolean matches(CharSequence self, Pattern pattern) { + return pattern.matcher(self).matches(); + } + + /** + * Finds the first occurrence of a regular expression String within a String. + * If the regex doesn't match, null will be returned. + *

    + *

    For example, if the regex doesn't match the result is null: + *

    +     *     assert null == "New York, NY".find(/\d{5}/)
    +     * 
    + *

    + *

    If it does match, we get the matching string back: + *

    +     *      assert "10292" == "New York, NY 10292-0098".find(/\d{5}/)
    +     * 
    + *

    + *

    If we have capture groups in our expression, we still get back the full match + *

    +     *      assert "10292-0098" == "New York, NY 10292-0098".find(/(\d{5})-?(\d{4})/)
    +     * 
    + *

    + * + * @param self a String + * @param regex the capturing regex + * @return a String containing the matched portion, or null if the regex doesn't match + * @since 1.6.1 + */ + public static String find(String self, String regex) { + return find(self, Pattern.compile(regex)); + } + + /** + * Finds the first occurrence of a regular expression CharSequence within a CharSequence. + * + * @param self a CharSequence + * @param regex the capturing regex + * @return a CharSequence containing the matched portion, or null if the regex doesn't match + * @see #find(String, Pattern) + * @since 1.8.2 + */ + public static CharSequence find(CharSequence self, CharSequence regex) { + return find(self.toString(), Pattern.compile(regex.toString())); + } + + /** + * Finds the first occurrence of a compiled regular expression Pattern within a String. + * If the pattern doesn't match, null will be returned. + *

    + *

    For example, if the pattern doesn't match the result is null: + *

    +     *     assert null == "New York, NY".find(~/\d{5}/)
    +     * 
    + *

    + *

    If it does match, we get the matching string back: + *

    +     *      assert "10292" == "New York, NY 10292-0098".find(~/\d{5}/)
    +     * 
    + *

    + *

    If we have capture groups in our expression, the groups are ignored and + * we get back the full match: + *

    +     *      assert "10292-0098" == "New York, NY 10292-0098".find(~/(\d{5})-?(\d{4})/)
    +     * 
    + * If you need to work with capture groups, then use the closure version + * of this method or use Groovy's matcher operators or use eachMatch. + *

    + * + * @param self a String + * @param pattern the compiled regex Pattern + * @return a String containing the matched portion, or null if the regex pattern doesn't match + * @since 1.6.1 + */ + public static String find(String self, Pattern pattern) { + Matcher matcher = pattern.matcher(self); + if (matcher.find()) { + return matcher.group(0); + } + return null; + } + + /** + * Finds the first occurrence of a compiled regular expression Pattern within a CharSequence. + * + * @param self a CharSequence + * @param pattern the compiled regex Pattern + * @return a CharSequence containing the matched portion, or null if the regex pattern doesn't match + * @see #find(String, Pattern) + * @since 1.8.2 + */ + public static CharSequence find(CharSequence self, Pattern pattern) { + return find(self.toString(), pattern); + } + + /** + * Returns the result of calling a closure with the first occurrence of a regular expression found within a String. + * If the regex doesn't match, the closure will not be called and find will return null. + *

    + *

    For example, if the regex doesn't match, the result is null: + *

    +     *     assert null == "New York, NY".find(~/\d{5}/) { match -> return "-$match-"}
    +     * 
    + *

    + *

    If it does match and we don't have any capture groups in our regex, there is a single parameter + * on the closure that the match gets passed to: + *

    +     *      assert "-10292-" == "New York, NY 10292-0098".find(~/\d{5}/) { match -> return "-$match-"}
    +     * 
    + *

    + *

    If we have capture groups in our expression, our closure has one parameter for the match, followed by + * one for each of the capture groups: + *

    +     *      assert "10292" == "New York, NY 10292-0098".find(~/(\d{5})-?(\d{4})/) { match, zip, plusFour ->
    +     *          assert match == "10292-0098"
    +     *          assert zip == "10292"
    +     *          assert plusFour == "0098"
    +     *          return zip
    +     *      }
    +     * 
    + *

    If we have capture groups in our expression, and our closure has one parameter, + * the closure will be passed an array with the first element corresponding to the whole match, + * followed by an element for each of the capture groups: + *

    +     *      assert "10292" == "New York, NY 10292-0098".find(~/(\d{5})-?(\d{4})/) { match, zip, plusFour ->
    +     *          assert array[0] == "10292-0098"
    +     *          assert array[1] == "10292"
    +     *          assert array[2] == "0098"
    +     *          return array[1]
    +     *      }
    +     * 
    + *

    If a capture group is optional, and doesn't match, then the corresponding value + * for that capture group passed to the closure will be null as illustrated here: + *

    +     *      assert "2339999" == "adsf 233-9999 adsf".find(~/(\d{3})?-?(\d{3})-(\d{4})/) { match, areaCode, exchange, stationNumber ->
    +     *          assert "233-9999" == match
    +     *          assert null == areaCode
    +     *          assert "233" == exchange
    +     *          assert "9999" == stationNumber
    +     *          return "$exchange$stationNumber"
    +     *      }
    +     * 
    + *

    + * + * @param self a String + * @param regex the capturing regex string + * @param closure the closure that will be passed the full match, plus each of the capturing groups + * @return a String containing the result of the closure, or null if the regex pattern doesn't match + * @since 1.6.1 + */ + public static String find(String self, String regex, Closure closure) { + return find(self, Pattern.compile(regex), closure); + } + + /** + * Returns the result of calling a closure with the first occurrence of a regular expression found within a CharSequence. + * If the regex doesn't match, the closure will not be called and find will return null. + * + * @param self a CharSequence + * @param regex the capturing regex CharSequence + * @param closure the closure that will be passed the full match, plus each of the capturing groups + * @return a CharSequence containing the result of the closure, or null if the regex pattern doesn't match + * @see #find(String, Pattern, Closure) + * @since 1.8.2 + */ + public static CharSequence find(CharSequence self, CharSequence regex, Closure closure) { + return find(self.toString(), Pattern.compile(regex.toString()), closure); + } + + /** + * Returns the result of calling a closure with the first occurrence of a compiled regular expression found within a String. + * If the regex doesn't match, the closure will not be called and find will return null. + *

    + *

    For example, if the pattern doesn't match, the result is null: + *

    +     *     assert null == "New York, NY".find(~/\d{5}/) { match -> return "-$match-"}
    +     * 
    + *

    + *

    If it does match and we don't have any capture groups in our regex, there is a single parameter + * on the closure that the match gets passed to: + *

    +     *      assert "-10292-" == "New York, NY 10292-0098".find(~/\d{5}/) { match -> return "-$match-"}
    +     * 
    + *

    + *

    If we have capture groups in our expression, our closure has one parameter for the match, followed by + * one for each of the capture groups: + *

    +     *      assert "10292" == "New York, NY 10292-0098".find(~/(\d{5})-?(\d{4})/) { match, zip, plusFour ->
    +     *          assert match == "10292-0098"
    +     *          assert zip == "10292"
    +     *          assert plusFour == "0098"
    +     *          return zip
    +     *      }
    +     * 
    + *

    If we have capture groups in our expression, and our closure has one parameter, + * the closure will be passed an array with the first element corresponding to the whole match, + * followed by an element for each of the capture groups: + *

    +     *      assert "10292" == "New York, NY 10292-0098".find(~/(\d{5})-?(\d{4})/) { match, zip, plusFour ->
    +     *          assert array[0] == "10292-0098"
    +     *          assert array[1] == "10292"
    +     *          assert array[2] == "0098"
    +     *          return array[1]
    +     *      }
    +     * 
    + *

    If a capture group is optional, and doesn't match, then the corresponding value + * for that capture group passed to the closure will be null as illustrated here: + *

    +     *      assert "2339999" == "adsf 233-9999 adsf".find(~/(\d{3})?-?(\d{3})-(\d{4})/) { match, areaCode, exchange, stationNumber ->
    +     *          assert "233-9999" == match
    +     *          assert null == areaCode
    +     *          assert "233" == exchange
    +     *          assert "9999" == stationNumber
    +     *          return "$exchange$stationNumber"
    +     *      }
    +     * 
    + *

    + * + * @param self a String + * @param pattern the compiled regex Pattern + * @param closure the closure that will be passed the full match, plus each of the capturing groups + * @return a String containing the result of the closure, or null if the regex pattern doesn't match + * @since 1.6.1 + */ + public static String find(String self, Pattern pattern, Closure closure) { + Matcher matcher = pattern.matcher(self); + if (matcher.find()) { + if (hasGroup(matcher)) { + int count = matcher.groupCount(); + List groups = new ArrayList(count); + for (int i = 0; i <= count; i++) { + groups.add(matcher.group(i)); + } + return InvokerHelper.toString(closure.call(groups)); + } else { + return InvokerHelper.toString(closure.call(matcher.group(0))); + } + } + return null; + } + + /** + * Returns the result of calling a closure with the first occurrence of a regular expression found within a + * CharSequence. If the regex doesn't match, the closure will not be called and find will return null. + * + * @param self a CharSequence + * @param pattern the compiled regex Pattern + * @param closure the closure that will be passed the full match, plus each of the capturing groups + * @return a CharSequence containing the result of the closure, or null if the regex pattern doesn't match + * @see #find(String, java.util.regex.Pattern, groovy.lang.Closure) + * @since 1.8.2 + */ + public static CharSequence find(CharSequence self, Pattern pattern, Closure closure) { + return find(self.toString(), pattern, closure); + } + + /** + * Returns a (possibly empty) list of all occurrences of a regular expression (in String format) found within a String. + *

    + *

    For example, if the regex doesn't match, it returns an empty list: + *

    +     * assert [] == "foo".findAll(/(\w*) Fish/)
    +     * 
    + *

    Any regular expression matches are returned in a list, and all regex capture groupings are ignored, only the full match is returned: + *

    +     * def expected = ["One Fish", "Two Fish", "Red Fish", "Blue Fish"]
    +     * assert expected == "One Fish, Two Fish, Red Fish, Blue Fish".findAll(/(\w*) Fish/)
    +     * 
    + * If you need to work with capture groups, then use the closure version + * of this method or use Groovy's matcher operators or use eachMatch. + *

    + * + * @param self a String + * @param regex the capturing regex String + * @return a List containing all full matches of the regex within the string, an empty list will be returned if there are no matches + * @since 1.6.1 + */ + public static List findAll(String self, String regex) { + return findAll(self, Pattern.compile(regex)); + } + + /** + * Returns a (possibly empty) list of all occurrences of a regular expression (in CharSequence format) found within a CharSequence. + * + * @param self a CharSequence + * @param regex the capturing regex CharSequence + * @return a List containing all full matches of the regex within the CharSequence, an empty list will be returned if there are no matches + * @see #findAll(String, String) + * @since 1.8.2 + */ + public static List findAll(CharSequence self, CharSequence regex) { + return new ArrayList(findAll(self.toString(), regex.toString())); + } + + /** + * Returns a (possibly empty) list of all occurrences of a regular expression (in Pattern format) found within a String. + *

    + *

    For example, if the pattern doesn't match, it returns an empty list: + *

    +     * assert [] == "foo".findAll(~/(\w*) Fish/)
    +     * 
    + *

    Any regular expression matches are returned in a list, and all regex capture groupings are ignored, only the full match is returned: + *

    +     * def expected = ["One Fish", "Two Fish", "Red Fish", "Blue Fish"]
    +     * assert expected == "One Fish, Two Fish, Red Fish, Blue Fish".findAll(~/(\w*) Fish/)
    +     * 
    + * + * @param self a String + * @param pattern the compiled regex Pattern + * @return a List containing all full matches of the Pattern within the string, an empty list will be returned if there are no matches + * @since 1.6.1 + */ + public static List findAll(String self, Pattern pattern) { + Matcher matcher = pattern.matcher(self); + List list = new ArrayList(); + for (Iterator iter = iterator(matcher); iter.hasNext();) { + if (hasGroup(matcher)) { + list.add((String) ((List) iter.next()).get(0)); + } else { + list.add((String) iter.next()); + } + } + return list; + } + + /** + * Returns a (possibly empty) list of all occurrences of a regular expression (in Pattern format) found within a CharSequence. + * + * @param self a CharSequence + * @param pattern the compiled regex Pattern + * @return a List containing all full matches of the Pattern within the CharSequence, an empty list will be returned if there are no matches + * @see #findAll(String, Pattern) + * @since 1.8.2 + */ + public static List findAll(CharSequence self, Pattern pattern) { + return new ArrayList(findAll(self.toString(), pattern)); + } + + /** + * Finds all occurrences of a regular expression string within a String. Any matches are passed to the specified closure. The closure + * is expected to have the full match in the first parameter. If there are any capture groups, they will be placed in subsequent parameters. + *

    + * If there are no matches, the closure will not be called, and an empty List will be returned. + *

    + *

    For example, if the regex doesn't match, it returns an empty list: + *

    +     * assert [] == "foo".findAll(/(\w*) Fish/) { match, firstWord -> return firstWord }
    +     * 
    + *

    Any regular expression matches are passed to the closure, if there are no capture groups, there will be one parameter for the match: + *

    +     * assert ["couldn't", "wouldn't"] == "I could not, would not, with a fox.".findAll(/.ould/) { match -> "${match}n't"}
    +     * 
    + *

    If there are capture groups, the first parameter will be the match followed by one parameter for each capture group: + *

    +     * def orig = "There's a Wocket in my Pocket"
    +     * assert ["W > Wocket", "P > Pocket"] == orig.findAll(/(.)ocket/) { match, firstLetter -> "$firstLetter > $match" }
    +     * 
    + * + * @param self a String + * @param regex the capturing regex String + * @param closure will be passed the full match plus each of the capturing groups + * @return a List containing all full matches of the regex within the string, an empty list will be returned if there are no matches + * @since 1.6.1 + */ + public static List findAll(String self, String regex, Closure closure) { + return findAll(self, Pattern.compile(regex), closure); + } + + /** + * Finds all occurrences of a capturing regular expression CharSequence within a CharSequence. + * + * @param self a CharSequence + * @param regex the capturing regex CharSequence + * @param closure will be passed the full match plus each of the capturing groups + * @return a List containing all full matches of the regex within the CharSequence, an empty list will be returned if there are no matches + * @see #findAll(String, String, Closure) + * @since 1.8.2 + */ + public static List findAll(CharSequence self, CharSequence regex, Closure closure) { + return findAll(self.toString(), regex.toString(), closure); + } + + /** + * Finds all occurrences of a compiled regular expression Pattern within a String. Any matches are passed to the specified closure. The closure + * is expected to have the full match in the first parameter. If there are any capture groups, they will be placed in subsequent parameters. + *

    + * If there are no matches, the closure will not be called, and an empty List will be returned. + *

    + *

    For example, if the pattern doesn't match, it returns an empty list: + *

    +     * assert [] == "foo".findAll(~/(\w*) Fish/) { match, firstWord -> return firstWord }
    +     * 
    + *

    Any regular expression matches are passed to the closure, if there are no capture groups, there will be one parameter for the match: + *

    +     * assert ["couldn't", "wouldn't"] == "I could not, would not, with a fox.".findAll(~/.ould/) { match -> "${match}n't"}
    +     * 
    + *

    If there are capture groups, the first parameter will be the match followed by one parameter for each capture group: + *

    +     * def orig = "There's a Wocket in my Pocket"
    +     * assert ["W > Wocket", "P > Pocket"] == orig.findAll(~/(.)ocket/) { match, firstLetter -> "$firstLetter > $match" }
    +     * 
    + * + * @param self a String + * @param pattern the compiled regex Pattern + * @param closure will be passed the full match plus each of the capturing groups + * @return a List containing all full matches of the regex Pattern within the string, an empty list will be returned if there are no matches + * @since 1.6.1 + */ + public static List findAll(String self, Pattern pattern, Closure closure) { + Matcher matcher = pattern.matcher(self); + return collect(matcher, closure); + } + + /** + * Finds all occurrences of a compiled regular expression Pattern within a CharSequence. + * + * @param self a CharSequence + * @param pattern the compiled regex Pattern + * @param closure will be passed the full match plus each of the capturing groups + * @return a List containing all full matches of the regex Pattern within the CharSequence, an empty list will be returned if there are no matches + * @see #findAll(String, Pattern, Closure) + * @since 1.8.2 + */ + public static List findAll(CharSequence self, Pattern pattern, Closure closure) { + return findAll(self.toString(), pattern, closure); + } + + /** + * Replaces all occurrences of a captured group by the result of a closure on that text. + *

    + *

    For examples, + *

    +     *     assert "hellO wOrld" == "hello world".replaceAll("(o)") { it[0].toUpperCase() }
    +     * 

    + * assert "FOOBAR-FOOBAR-" == "foobar-FooBar-".replaceAll("(([fF][oO]{2})[bB]ar)", { Object[] it -> it[0].toUpperCase() }) + *

    + * Here, + * it[0] is the global string of the matched group + * it[1] is the first string in the matched group + * it[2] is the second string in the matched group + *

    + *

    + * assert "FOO-FOO-" == "foobar-FooBar-".replaceAll("(([fF][oO]{2})[bB]ar)", { x, y, z -> z.toUpperCase() }) + *

    + * Here, + * x is the global string of the matched group + * y is the first string in the matched group + * z is the second string in the matched group + *

    + *

    Note that unlike String.replaceAll(String regex, String replacement), where the replacement string + * treats '$' and '\' specially (for group substitution), the result of the closure is converted to a string + * and that value is used literally for the replacement.

    + * + * @param self a String + * @param regex the capturing regex + * @param closure the closure to apply on each captured group + * @return a String with replaced content + * @throws java.util.regex.PatternSyntaxException if the regular expression's syntax is invalid + * @since 1.0 + * @see java.util.regex.Matcher#quoteReplacement(java.lang.String) + * @see #replaceAll(String, Pattern, Closure) + */ + public static String replaceAll(final String self, final String regex, final Closure closure) { + return replaceAll(self, Pattern.compile(regex), closure); + } + + /** + * Replaces all occurrences of a captured group by the result of a closure on that text. + * + * @param self a CharSequence + * @param regex the capturing regex + * @param closure the closure to apply on each captured group + * @return a CharSequence with replaced content + * @throws java.util.regex.PatternSyntaxException if the regular expression's syntax is invalid + * @since 1.8.2 + * @see #replaceAll(String, Pattern, Closure) + */ + public static CharSequence replaceAll(final CharSequence self, final CharSequence regex, final Closure closure) { + return replaceAll(self.toString(), Pattern.compile(regex.toString()), closure); + } + + /** + * Replaces each substring of this CharSequence that matches the given + * regular expression with the given replacement. + * + * @param self a CharSequence + * @param regex the capturing regex + * @param replacement the capturing regex + * @return a CharSequence with replaced content + * @throws java.util.regex.PatternSyntaxException if the regular expression's syntax is invalid + * @see String#replaceAll(String, String) + * @since 1.8.2 + */ + public static CharSequence replaceAll(final CharSequence self, final CharSequence regex, final CharSequence replacement) { + return self.toString().replaceAll(regex.toString(), replacement.toString()); + } + + /** + * Replaces the first occurrence of a captured group by the result of a closure call on that text. + *

    + *

    For example (with some replaceAll variants thrown in for comparison purposes), + *

    +     * assert "hellO world" == "hello world".replaceFirst("(o)") { it[0].toUpperCase() } // first match
    +     * assert "hellO wOrld" == "hello world".replaceAll("(o)") { it[0].toUpperCase() }   // all matches
    +     * 

    + * assert '1-FISH, two fish' == "one fish, two fish".replaceFirst(/([a-z]{3})\s([a-z]{4})/) { [one:1, two:2][it[1]] + '-' + it[2].toUpperCase() } + * assert '1-FISH, 2-FISH' == "one fish, two fish".replaceAll(/([a-z]{3})\s([a-z]{4})/) { [one:1, two:2][it[1]] + '-' + it[2].toUpperCase() } + *

    + * + * @param self a String + * @param regex the capturing regex + * @param closure the closure to apply on the first captured group + * @return a String with replaced content + * @throws java.util.regex.PatternSyntaxException if the regular expression's syntax is invalid + * @since 1.7.7 + * @see java.util.regex.Matcher#quoteReplacement(java.lang.String) + * @see #replaceFirst(String, Pattern, Closure) + */ + public static String replaceFirst(final String self, final String regex, final Closure closure) { + return replaceFirst(self, Pattern.compile(regex), closure); + } + + /** + * Replaces the first substring of this CharSequence that matches the given + * regular expression with the given replacement. + * + * @param self a CharSequence + * @param regex the capturing regex + * @param replacement the capturing regex + * @return a CharSequence with replaced content + * @throws java.util.regex.PatternSyntaxException if the regular expression's syntax is invalid + * @see String#replaceAll(String, String) + * @since 1.8.2 + */ + public static String replaceFirst(final CharSequence self, final CharSequence regex, final CharSequence replacement) { + return self.toString().replaceFirst(regex.toString(), replacement.toString()); + } + + /** + * Replaces the first occurrence of a captured group by the result of a closure call on that text. + * + * @param self a CharSequence + * @param regex the capturing regex + * @param closure the closure to apply on the first captured group + * @return a CharSequence with replaced content + * @throws java.util.regex.PatternSyntaxException if the regular expression's syntax is invalid + * @see #replaceFirst(String, String, Closure) + * @since 1.8.2 + */ + public static String replaceFirst(final CharSequence self, final CharSequence regex, final Closure closure) { + return replaceFirst(self.toString(), regex.toString(), closure); + } + + /** + * Get a replacement corresponding to the matched pattern for {@link org.codehaus.groovy.runtime.DefaultGroovyMethods#replaceAll(String, Pattern, Closure)}. + * The closure take parameter: + *
      + *
    • Whole of match if the pattern include no capturing group
    • + *
    • Object[] of capturing groups if the closure takes Object[] as parameter
    • + *
    • List of capturing groups
    • + *
    + * + * @param matcher the matcher object used for matching + * @param closure specified with replaceAll() to get replacement + * @return replacement correspond replacement for a match + */ + private static String getReplacement(Matcher matcher, Closure closure) { + if (!hasGroup(matcher)) { + return InvokerHelper.toString(closure.call(matcher.group())); + } + + int count = matcher.groupCount(); + List groups = new ArrayList(); + for (int i = 0; i <= count; i++) { + groups.add(matcher.group(i)); + } + + if (closure.getParameterTypes().length == 1 + && closure.getParameterTypes()[0] == Object[].class) { + return InvokerHelper.toString(closure.call(groups.toArray())); + } + return InvokerHelper.toString(closure.call(groups)); + } + + /** + * Replaces all occurrences of a captured group by the result of a closure call on that text. + *

    + *

    For examples, + *

    +     *     assert "hellO wOrld" == "hello world".replaceAll(~"(o)") { it[0].toUpperCase() }
    +     * 

    + * assert "FOOBAR-FOOBAR-" == "foobar-FooBar-".replaceAll(~"(([fF][oO]{2})[bB]ar)", { it[0].toUpperCase() }) + *

    + * Here, + * it[0] is the global string of the matched group + * it[1] is the first string in the matched group + * it[2] is the second string in the matched group + *

    + *

    + * assert "FOOBAR-FOOBAR-" == "foobar-FooBar-".replaceAll(~"(([fF][oO]{2})[bB]ar)", { Object[] it -> it[0].toUpperCase() }) + *

    + * Here, + * it[0] is the global string of the matched group + * it[1] is the first string in the matched group + * it[2] is the second string in the matched group + *

    + *

    + * assert "FOO-FOO-" == "foobar-FooBar-".replaceAll("(([fF][oO]{2})[bB]ar)", { x, y, z -> z.toUpperCase() }) + *

    + * Here, + * x is the global string of the matched group + * y is the first string in the matched group + * z is the second string in the matched group + *

    + *

    Note that unlike String.replaceAll(String regex, String replacement), where the replacement string + * treats '$' and '\' specially (for group substitution), the result of the closure is converted to a string + * and that value is used literally for the replacement.

    + * + * @param self a String + * @param pattern the capturing regex Pattern + * @param closure the closure to apply on each captured group + * @return a String with replaced content + * @since 1.6.8 + * @see java.util.regex.Matcher#quoteReplacement(java.lang.String) + */ + public static String replaceAll(final String self, final Pattern pattern, final Closure closure) { + final Matcher matcher = pattern.matcher(self); + if (matcher.find()) { + final StringBuffer sb = new StringBuffer(self.length() + 16); + do { + String replacement = getReplacement(matcher, closure); + matcher.appendReplacement(sb, Matcher.quoteReplacement(replacement)); + } while (matcher.find()); + matcher.appendTail(sb); + return sb.toString(); + } else { + return self; + } + } + + /** + * Replaces all occurrences of a captured group by the result of a closure call on that text. + * + * @param self a CharSequence + * @param pattern the capturing regex Pattern + * @param closure the closure to apply on each captured group + * @return a CharSequence with replaced content + * @since 1.8.2 + * @see #replaceAll(String, Pattern, Closure) + */ + public static String replaceAll(final CharSequence self, final Pattern pattern, final Closure closure) { + return replaceAll(self.toString(), pattern, closure); + } + + /** + * Replaces the first occurrence of a captured group by the result of a closure call on that text. + *

    + *

    For example (with some replaceAll variants thrown in for comparison purposes), + *

    +     * assert "hellO world" == "hello world".replaceFirst(~"(o)") { it[0].toUpperCase() } // first match
    +     * assert "hellO wOrld" == "hello world".replaceAll(~"(o)") { it[0].toUpperCase() }   // all matches
    +     * 

    + * assert '1-FISH, two fish' == "one fish, two fish".replaceFirst(~/([a-z]{3})\s([a-z]{4})/) { [one:1, two:2][it[1]] + '-' + it[2].toUpperCase() } + * assert '1-FISH, 2-FISH' == "one fish, two fish".replaceAll(~/([a-z]{3})\s([a-z]{4})/) { [one:1, two:2][it[1]] + '-' + it[2].toUpperCase() } + *

    + * + * @param self a String + * @param pattern the capturing regex Pattern + * @param closure the closure to apply on the first captured group + * @return a String with replaced content + * @since 1.7.7 + * @see #replaceAll(String, Pattern, Closure) + */ + public static String replaceFirst(final String self, final Pattern pattern, final Closure closure) { + final Matcher matcher = pattern.matcher(self); + if (matcher.find()) { + final StringBuffer sb = new StringBuffer(self.length() + 16); + String replacement = getReplacement(matcher, closure); + matcher.appendReplacement(sb, Matcher.quoteReplacement(replacement)); + matcher.appendTail(sb); + return sb.toString(); + } else { + return self; + } + } + + /** + * Replaces the first occurrence of a captured group by the result of a closure call on that text. + * + * @param self a CharSequence + * @param pattern the capturing regex Pattern + * @param closure the closure to apply on the first captured group + * @return a CharSequence with replaced content + * @see #replaceFirst(String, Pattern, Closure) + * @since 1.8.2 + */ + public static String replaceFirst(final CharSequence self, final Pattern pattern, final Closure closure) { + return replaceFirst(self.toString(), pattern, closure); + } + + private static String getPadding(String padding, int length) { + if (padding.length() < length) { + return multiply(padding, length / padding.length() + 1).substring(0, length); + } else { + return padding.substring(0, length); + } + } + + /** + * Pad a String to a minimum length specified by numberOfChars, adding the supplied padding String as many times as needed to the left. + * + * If the String is already the same size or bigger than the target numberOfChars, then the original String is returned. An example: + *
    +     * println 'Numbers:'
    +     * [1, 10, 100, 1000].each{ println it.toString().padLeft(5, '*') }
    +     * [2, 20, 200, 2000].each{ println it.toString().padLeft(5, '*_') }
    +     * 
    + * will produce output like: + *
    +     * Numbers:
    +     * ****1
    +     * ***10
    +     * **100
    +     * *1000
    +     * *_*_2
    +     * *_*20
    +     * *_200
    +     * *2000
    +     * 
    + * + * @param self a String object + * @param numberOfChars the total minimum number of characters of the resulting string + * @param padding the characters used for padding + * @return the String padded to the left + * @since 1.0 + */ + public static String padLeft(String self, Number numberOfChars, String padding) { + int numChars = numberOfChars.intValue(); + if (numChars <= self.length()) { + return self; + } else { + return getPadding(padding, numChars - self.length()) + self; + } + } + + /** + * Pad a CharSequence to a minimum length specified by numberOfChars, adding the supplied padding CharSequence as many times as needed to the left. + * + * @param self a CharSequence object + * @param numberOfChars the total minimum number of characters of the resulting CharSequence + * @param padding the characters used for padding + * @return the CharSequence padded to the left + * @see #padLeft(String, Number, String) + * @since 1.8.2 + */ + public static CharSequence padLeft(CharSequence self, Number numberOfChars, CharSequence padding) { + return padLeft(self.toString(), numberOfChars, padding.toString()); + } + + /** + * Pad a String to a minimum length specified by numberOfChars by adding the space character to the left as many times as needed. + * + * If the String is already the same size or bigger than the target numberOfChars, then the original String is returned. An example: + *
    +     * println 'Numbers:'
    +     * [1, 10, 100, 1000].each{ println it.toString().padLeft(5) }
    +     * 
    + * will produce output like: + *
    +     * Numbers:
    +     *     1
    +     *    10
    +     *   100
    +     *  1000
    +     * 
    + * + * @param self a String object + * @param numberOfChars the total minimum number of characters of the resulting string + * @return the String padded to the left + * @see #padLeft(String, Number, String) + * @since 1.0 + */ + public static String padLeft(String self, Number numberOfChars) { + return padLeft(self, numberOfChars, " "); + } + + /** + * Pad a CharSequence to a minimum length specified by numberOfChars by adding the space character to the left as many times as needed. + * + * @param self a CharSequence object + * @param numberOfChars the total minimum number of characters of the resulting CharSequence + * @return the CharSequence padded to the left + * @see #padLeft(CharSequence, Number, CharSequence) + * @since 1.8.2 + */ + public static CharSequence padLeft(CharSequence self, Number numberOfChars) { + return padLeft(self, numberOfChars, " "); + } + + /** + * Pad a String to a minimum length specified by numberOfChars, adding the supplied padding String as many times as needed to the right. + * + * If the String is already the same size or bigger than the target numberOfChars, then the original String is returned. An example: + *
    +     * ['A', 'BB', 'CCC', 'DDDD'].each{ println it.padRight(5, '#') + it.size() }
    +     * 
    + * will produce output like: + *
    +     * A####1
    +     * BB###2
    +     * CCC##3
    +     * DDDD#4
    +     * 
    + * + * @param self a String object + * @param numberOfChars the total minimum number of characters of the resulting string + * @param padding the characters used for padding + * @return the String padded to the right + * @since 1.0 + */ + public static String padRight(String self, Number numberOfChars, String padding) { + int numChars = numberOfChars.intValue(); + if (numChars <= self.length()) { + return self; + } else { + return self + getPadding(padding, numChars - self.length()); + } + } + + /** + * Pad a CharSequence to a minimum length specified by numberOfChars, adding the supplied padding CharSequence as many times as needed to the right. + * + * @param self a CharSequence object + * @param numberOfChars the total minimum number of characters of the resulting CharSequence + * @param padding the characters used for padding + * @return the CharSequence padded to the right + * @see #padRight(String, Number, String) + * @since 1.8.2 + */ + public static CharSequence padRight(CharSequence self, Number numberOfChars, CharSequence padding) { + return padRight(self.toString(), numberOfChars, padding.toString()); + } + + /** + * Pad a String to a minimum length specified by numberOfChars by adding the space character to the right as many times as needed. + * + * If the String is already the same size or bigger than the target numberOfChars, then the original String is returned. An example: + *
    +     * ['A', 'BB', 'CCC', 'DDDD'].each{ println it.padRight(5) + it.size() }
    +     * 
    + * will produce output like: + *
    +     * A    1
    +     * BB   2
    +     * CCC  3
    +     * DDDD 4
    +     * 
    + * + * @param self a String object + * @param numberOfChars the total minimum number of characters of the resulting string + * @return the String padded to the right + * @since 1.0 + */ + public static String padRight(String self, Number numberOfChars) { + return padRight(self, numberOfChars, " "); + } + + /** + * Pad a CharSequence to a minimum length specified by numberOfChars by adding the space character to the right as many times as needed. + * + * @param self a CharSequence object + * @param numberOfChars the total minimum number of characters of the resulting string + * @return the CharSequence padded to the right + * @see #padRight(String, Number) + * @since 1.8.2 + */ + public static CharSequence padRight(CharSequence self, Number numberOfChars) { + return padRight(self.toString(), numberOfChars); + } + + /** + * Pad a String to a minimum length specified by numberOfChars, appending the supplied padding String around the original as many times as needed keeping it centered. + * + * If the String is already the same size or bigger than the target numberOfChars, then the original String is returned. An example: + *
    +     * ['A', 'BB', 'CCC', 'DDDD'].each{ println '|' + it.center(6, '+') + '|' }
    +     * 
    + * will produce output like: + *
    +     * |++A+++|
    +     * |++BB++|
    +     * |+CCC++|
    +     * |+DDDD+|
    +     * 
    + * + * @param self a String object + * @param numberOfChars the total minimum number of characters of the resulting string + * @param padding the characters used for padding + * @return the String centered with padded characters around it + * @since 1.0 + */ + public static String center(String self, Number numberOfChars, String padding) { + int numChars = numberOfChars.intValue(); + if (numChars <= self.length()) { + return self; + } else { + int charsToAdd = numChars - self.length(); + String semiPad = charsToAdd % 2 == 1 ? + getPadding(padding, charsToAdd / 2 + 1) : + getPadding(padding, charsToAdd / 2); + if (charsToAdd % 2 == 0) + return semiPad + self + semiPad; + else + return semiPad.substring(0, charsToAdd / 2) + self + semiPad; + } + } + + /** + * Pad a CharSequence to a minimum length specified by numberOfChars, appending the supplied padding CharSequence around the original as many times as needed keeping it centered. + * + * @param self a CharSequence object + * @param numberOfChars the total minimum number of characters of the resulting CharSequence + * @param padding the characters used for padding + * @return the CharSequence centered with padded characters around it + * @see #center(String, Number, String) + * @since 1.8.2 + */ + public static CharSequence center(CharSequence self, Number numberOfChars, CharSequence padding) { + return center(self.toString(), numberOfChars, padding.toString()); + } + + /** + * Pad a String to a minimum length specified by numberOfChars by adding the space character around it as many times as needed so that it remains centered. + * + * If the String is already the same size or bigger than the target numberOfChars, then the original String is returned. An example: + *
    +     * ['A', 'BB', 'CCC', 'DDDD'].each{ println '|' + it.center(6) + '|' }
    +     * 
    + * will produce output like: + *
    +     * |  A   |
    +     * |  BB  |
    +     * | CCC  |
    +     * | DDDD |
    +     * 
    + * + * @param self a String object + * @param numberOfChars the total minimum number of characters of the resulting string + * @return the String centered with padded characters around it + * @see #center(String, Number, String) + * @since 1.0 + */ + public static String center(String self, Number numberOfChars) { + return center(self, numberOfChars, " "); + } + + /** + * Pad a CharSequence to a minimum length specified by numberOfChars by adding the space character around it as many times as needed so that it remains centered. + * + * @param self a CharSequence object + * @param numberOfChars the total minimum number of characters of the resulting CharSequence + * @return the CharSequence centered with padded characters around it + * @see #center(String, Number) + * @since 1.8.2 + */ + public static CharSequence center(CharSequence self, Number numberOfChars) { + return center(self.toString(), numberOfChars); + } + + /** + * Support the subscript operator, e.g. matcher[index], for a regex Matcher. + *

    + * For an example using no group match, + *

    +     *    def p = /ab[d|f]/
    +     *    def m = "abcabdabeabf" =~ p
    +     *    assert 2 == m.count
    +     *    assert 2 == m.size() // synonym for m.getCount()
    +     *    assert ! m.hasGroup()
    +     *    assert 0 == m.groupCount()
    +     *    def matches = ["abd", "abf"]
    +     *    for (i in 0..<m.count) {
    +     *      assert m[i] == matches[i]
    +     *    }
    +     * 
    + *

    + * For an example using group matches, + *

    +     *    def p = /(?:ab([c|d|e|f]))/
    +     *    def m = "abcabdabeabf" =~ p
    +     *    assert 4 == m.count
    +     *    assert m.hasGroup()
    +     *    assert 1 == m.groupCount()
    +     *    def matches = [["abc", "c"], ["abd", "d"], ["abe", "e"], ["abf", "f"]]
    +     *    for (i in 0..<m.count) {
    +     *      assert m[i] == matches[i]
    +     *    }
    +     * 
    + *

    + * For another example using group matches, + *

    +     *    def m = "abcabdabeabfabxyzabx" =~ /(?:ab([d|x-z]+))/
    +     *    assert 3 == m.count
    +     *    assert m.hasGroup()
    +     *    assert 1 == m.groupCount()
    +     *    def matches = [["abd", "d"], ["abxyz", "xyz"], ["abx", "x"]]
    +     *    for (i in 0..<m.count) {
    +     *      assert m[i] == matches[i]
    +     *    }
    +     * 
    + * + * @param matcher a Matcher + * @param idx an index + * @return object a matched String if no groups matched, list of matched groups otherwise. + * @since 1.0 + */ + public static Object getAt(Matcher matcher, int idx) { + try { + int count = getCount(matcher); + if (idx < -count || idx >= count) { + throw new IndexOutOfBoundsException("index is out of range " + (-count) + ".." + (count - 1) + " (index = " + idx + ")"); + } + idx = normaliseIndex(idx, count); + + Iterator iter = iterator(matcher); + Object result = null; + for (int i = 0; i <= idx; i++) { + result = iter.next(); + } + return result; + } + catch (IllegalStateException ex) { + return null; + } + } + + /** + * Set the position of the given Matcher to the given index. + * + * @param matcher a Matcher + * @param idx the index number + * @since 1.0 + */ + public static void setIndex(Matcher matcher, int idx) { + int count = getCount(matcher); + if (idx < -count || idx >= count) { + throw new IndexOutOfBoundsException("index is out of range " + (-count) + ".." + (count - 1) + " (index = " + idx + ")"); + } + if (idx == 0) { + matcher.reset(); + } else if (idx > 0) { + matcher.reset(); + for (int i = 0; i < idx; i++) { + matcher.find(); + } + } else if (idx < 0) { + matcher.reset(); + idx += getCount(matcher); + for (int i = 0; i < idx; i++) { + matcher.find(); + } + } + } + + /** + * Find the number of Strings matched to the given Matcher. + * + * @param matcher a Matcher + * @return int the number of Strings matched to the given matcher. + * @since 1.0 + */ + public static int getCount(Matcher matcher) { + int counter = 0; + matcher.reset(); + while (matcher.find()) { + counter++; + } + return counter; + } + + /** + * Check whether a Matcher contains a group or not. + * + * @param matcher a Matcher + * @return boolean true if matcher contains at least one group. + * @since 1.0 + */ + public static boolean hasGroup(Matcher matcher) { + return matcher.groupCount() > 0; + } + + /** + * Support the range subscript operator for a List. + *
    def list = [1, "a", 4.5, true]
    +     * assert list[1..2] == ["a", 4.5]
    + * + * @param self a List + * @param range a Range indicating the items to get + * @return a sublist based on range borders or a new list if range is reversed + * @see java.util.List#subList(int,int) + * @since 1.0 + */ + public static List getAt(List self, Range range) { + RangeInfo info = subListBorders(self.size(), range); + List answer = self.subList(info.from, info.to); // sublist is always exclusive, but Ranges are not + if (info.reverse) { + answer = reverse(answer); + } + return answer; + } + + /** + * Support the range subscript operator for a List. + *
    def list = [true, 1, 3.4]
    +     * assert list[0..<0] == []
    + * + * @param self a List + * @param range a Range indicating the items to get + * @return a sublist based on range borders or a new list if range is reversed + * @see java.util.List#subList(int,int) + * @since 1.0 + */ + public static List getAt(List self, EmptyRange range) { + return new ArrayList (); + } + + /** + * Select a List of items from a List using a Collection to + * identify the indices to be selected. + *
    def list = [true, 1, 3.4, false]
    +     * assert list[1,0,2] == [1, true, 3.4]
    + * + * @param self a List + * @param indices a Collection of indices + * @return a new list of the values at the given indices + * @since 1.0 + */ + public static List getAt(List self, Collection indices) { + List answer = new ArrayList(indices.size()); + for (Object value : indices) { + if (value instanceof Range) { + answer.addAll(getAt(self, (Range) value)); + } else if (value instanceof List) { + answer.addAll(getAt(self, (List) value)); + } else { + int idx = DefaultTypeTransformation.intUnbox(value); + answer.add(getAt(self, idx)); + } + } + return answer; + } + + /** + * Select a List of items from an Object array using a Collection to + * identify the indices to be selected. + * + * @param self an Array of Objects + * @param indices a Collection of indices + * @return a new list of the values at the given indices + * @since 1.0 + */ + public static List getAt(T[] self, Collection indices) { + List answer = new ArrayList(indices.size()); + for (Object value : indices) { + if (value instanceof Range) { + answer.addAll(getAt(self, (Range) value)); + } else if (value instanceof Collection) { + answer.addAll(getAt(self, (Collection) value)); + } else { + int idx = DefaultTypeTransformation.intUnbox(value); + answer.add(getAtImpl(self, idx)); + } + } + return answer; + } + + /** + * Select a List of characters from a CharSequence using a Collection + * to identify the indices to be selected. + * + * @param self a CharSequence + * @param indices a Collection of indices + * @return a CharSequence consisting of the characters at the given indices + * @since 1.0 + */ + public static CharSequence getAt(CharSequence self, Collection indices) { + StringBuilder answer = new StringBuilder(); + for (Object value : indices) { + if (value instanceof Range) { + answer.append(getAt(self, (Range) value)); + } else if (value instanceof Collection) { + answer.append(getAt(self, (Collection) value)); + } else { + int idx = DefaultTypeTransformation.intUnbox(value); + answer.append(getAt(self, idx)); + } + } + return answer.toString(); + } + + /** + * Select a List of characters from a String using a Collection + * to identify the indices to be selected. + * + * @param self a String + * @param indices a Collection of indices + * @return a String consisting of the characters at the given indices + * @since 1.0 + */ + public static String getAt(String self, Collection indices) { + return (String) getAt((CharSequence) self, indices); + } + + /** + * Select a List of values from a Matcher using a Collection + * to identify the indices to be selected. + * + * @param self a Matcher + * @param indices a Collection of indices + * @return a String of the values at the given indices + * @since 1.6.0 + */ + public static List getAt(Matcher self, Collection indices) { + List result = new ArrayList(); + for (Object value : indices) { + if (value instanceof Range) { + result.addAll(getAt(self, (Range) value)); + } else { + int idx = DefaultTypeTransformation.intUnbox(value); + result.add(getAt(self, idx)); + } + } + return result; + } + + /** + * Creates a sub-Map containing the given keys. This method is similar to + * List.subList() but uses keys rather than index ranges. + *
    assert [1:10, 2:20, 4:40].subMap( [2, 4] ) == [2:20, 4:40]
    + * + * @param map a Map + * @param keys a Collection of keys + * @return a new Map containing the given keys + * @since 1.0 + */ + public static Map subMap(Map map, Collection keys) { + Map answer = new LinkedHashMap(keys.size()); + for (K key : keys) { + answer.put(key, map.get(key)); + } + return answer; + } + + /** + * Looks up an item in a Map for the given key and returns the value - unless + * there is no entry for the given key in which case add the default value + * to the map and return that. + *
    def map=[:]
    +     * map.get("a", []) << 5
    +     * assert map == [a:[5]]
    + * + * @param map a Map + * @param key the key to lookup the value of + * @param defaultValue the value to return and add to the map for this key if + * there is no entry for the given key + * @return the value of the given key or the default value, added to the map if the + * key did not exist + * @since 1.0 + */ + public static V get(Map map, K key, V defaultValue) { + if (!map.containsKey(key)) { + map.put(key, defaultValue); + } + return map.get(key); + } + + /** + * Support the range subscript operator for an Array + * + * @param array an Array of Objects + * @param range a Range + * @return a range of a list from the range's from index up to but not + * including the range's to value + * @since 1.0 + */ + public static List getAt(T[] array, Range range) { + List list = Arrays.asList(array); + return getAt(list, range); + } + + /** + * + * @param array an Array of Objects + * @param range an IntRange + * @return a range of a list from the range's from index up to but not + * including the range's to value + * @since 1.0 + */ + public static List getAt(T[] array, IntRange range) { + List list = Arrays.asList(array); + return getAt(list, range); + } + + /** + * + * @param array an Array of Objects + * @param range an EmptyRange + * @return an empty Range + * @since 1.5.0 + */ + public static List getAt(T[] array, EmptyRange range) { + return new ArrayList(); + } + + /** + * + * @param array an Array of Objects + * @param range an ObjectRange + * @return a range of a list from the range's from index up to but not + * including the range's to value + * @since 1.0 + */ + public static List getAt(T[] array, ObjectRange range) { + List list = Arrays.asList(array); + return getAt(list, range); + } + + private static T getAtImpl(T[] array, int idx) { + return array[normaliseIndex(idx, array.length)]; + } + + /** + * Allows conversion of arrays into a mutable List. + * + * @param array an Array of Objects + * @return the array as a List + * @since 1.0 + */ + public static List toList(T[] array) { + return new ArrayList(Arrays.asList(array)); + } + + /** + * Support the subscript operator for a List. + *
    def list = [2, "a", 5.3]
    +     * assert list[1] == "a"
    + * + * @param self a List + * @param idx an index + * @return the value at the given index + * @since 1.0 + */ + public static T getAt(List self, int idx) { + int size = self.size(); + int i = normaliseIndex(idx, size); + if (i < size) { + return self.get(i); + } else { + return null; + } + } + + /** + * Support the subscript operator for an Iterator. The iterator + * will be partially exhausted up until the idx entry after returning + * if a +ve or 0 idx is used, or fully exhausted if a -ve idx is used + * or no corresponding entry was found. Typical usage: + *
    +     * def iter = [2, "a", 5.3].iterator()
    +     * assert iter[1] == "a"
    +     * 
    + * A more elaborate example: + *
    +     * def items = [2, "a", 5.3]
    +     * def iter = items.iterator()
    +     * assert iter[-1] == 5.3
    +     * // iter exhausted, so reset
    +     * iter = items.iterator()
    +     * assert iter[1] == "a"
    +     * // iter partially exhausted so now idx starts after "a"
    +     * assert iter[0] == 5.3
    +     * 
    + * + * @param self an Iterator + * @param idx an index value (-self.size() <= idx < self.size()) + * @return the value at the given index (after normalisation) or null if no corresponding value was found + * @since 1.7.2 + */ + public static T getAt(Iterator self, int idx) { + if (idx < 0) { + // calculate whole list in this case + // recommend avoiding -ve's as this is not as efficient + List list = toList(self); + int adjustedIndex = idx + list.size(); + if (adjustedIndex < 0 || adjustedIndex >= list.size()) return null; + return list.get(adjustedIndex); + } + + int count = 0; + while (self.hasNext()) { + if (count == idx) { + return self.next(); + } else { + count++; + self.next(); + } + } + + return null; + } + /** + * A helper method to allow lists to work with subscript operators. + *
    def list = [2, 3]
    +     * list[0] = 1
    +     * assert list == [1, 3]
    + * + * @param self a List + * @param idx an index + * @param value the value to put at the given index + * @since 1.0 + */ + public static void putAt(List self, int idx, T value) { + int size = self.size(); + idx = normaliseIndex(idx, size); + if (idx < size) { + self.set(idx, value); + } else { + while (size < idx) { + self.add(size++, null); + } + self.add(idx, value); + } + } + + + /** + * Support the range subscript operator for StringBuffer. Index values are + * treated as characters within the buffer. + * + * @param self a StringBuffer + * @param range a Range + * @param value the object that's toString() will be inserted + * @since 1.0 + */ + public static void putAt(StringBuffer self, IntRange range, Object value) { + RangeInfo info = subListBorders(self.length(), range); + self.replace(info.from, info.to, value.toString()); + } + + /** + * Support the range subscript operator for StringBuffer. + * + * @param self a StringBuffer + * @param range a Range + * @param value the object that's toString() will be inserted + * @since 1.0 + */ + public static void putAt(StringBuffer self, EmptyRange range, Object value) { + RangeInfo info = subListBorders(self.length(), range); + self.replace(info.from, info.to, value.toString()); + } + + /** + * A helper method to allow lists to work with subscript operators. + *
    def list = ["a", true]
    +     * list[1..<1] = 5
    +     * assert list == ["a", 5, true]
    + * + * @param self a List + * @param range the (in this case empty) subset of the list to set + * @param value the values to put at the given sublist or a Collection of values + * @since 1.0 + */ + public static void putAt(List self, EmptyRange range, Object value) { + RangeInfo info = subListBorders(self.size(), range); + List sublist = self.subList(info.from, info.to); + sublist.clear(); + if (value instanceof Collection) { + Collection col = (Collection) value; + if (col.isEmpty()) return; + sublist.addAll(col); + } else { + sublist.add(value); + } + } + + /** + * A helper method to allow lists to work with subscript operators. + *
    def list = ["a", true]
    +     * list[1..<1] = [4, 3, 2]
    +     * assert list == ["a", 4, 3, 2, true]
    + * + * @param self a List + * @param range the (in this case empty) subset of the list to set + * @param value the Collection of values + * @since 1.0 + * @see #putAt(java.util.List, groovy.lang.EmptyRange, java.lang.Object) + */ + public static void putAt(List self, EmptyRange range, Collection value) { + putAt(self, range, (Object)value); + } + + private static List resizeListWithRangeAndGetSublist(List self, IntRange range) { + RangeInfo info = subListBorders(self.size(), range); + int size = self.size(); + if (info.to >= size) { + while (size < info.to) { + self.add(size++, null); + } + } + List sublist = self.subList(info.from, info.to); + sublist.clear(); + return sublist; + } + + /** + * List subscript assignment operator when given a range as the index and + * the assignment operand is a collection. + * Example:
    def myList = [4, 3, 5, 1, 2, 8, 10]
    +     * myList[3..5] = ["a", true]
    +     * assert myList == [4, 3, 5, "a", true, 10]
    + * + * Items in the given + * range are replaced with items from the collection. + * + * @param self a List + * @param range the subset of the list to set + * @param col the collection of values to put at the given sublist + * @since 1.5.0 + */ + public static void putAt(List self, IntRange range, Collection col) { + List sublist = resizeListWithRangeAndGetSublist(self, range); + if (col.isEmpty()) return; + sublist.addAll(col); + } + + /** + * List subscript assignment operator when given a range as the index. + * Example:
    def myList = [4, 3, 5, 1, 2, 8, 10]
    +     * myList[3..5] = "b"
    +     * assert myList == [4, 3, 5, "b", 10]
    + * + * Items in the given + * range are replaced with the operand. The value operand is + * always treated as a single value. + * + * @param self a List + * @param range the subset of the list to set + * @param value the value to put at the given sublist + * @since 1.0 + */ + public static void putAt(List self, IntRange range, Object value) { + List sublist = resizeListWithRangeAndGetSublist(self, range); + sublist.add(value); + } + + /** + * A helper method to allow lists to work with subscript operators. + *
    def list = ["a", true, 42, 9.4]
    +     * list[1, 4] = ["x", false]
    +     * assert list == ["a", "x", 42, 9.4, false]
    + * + * @param self a List + * @param splice the subset of the list to set + * @param values the value to put at the given sublist + * @since 1.0 + */ + public static void putAt(List self, List splice, List values) { + if (splice.isEmpty()) { + if ( ! values.isEmpty() ) + throw new IllegalArgumentException("Trying to replace 0 elements with "+values.size()+" elements"); + return; + } + Object first = splice.iterator().next(); + if (first instanceof Integer) { + if (values.size() != splice.size()) + throw new IllegalArgumentException("Trying to replace "+splice.size()+" elements with "+values.size()+" elements"); + Iterator valuesIter = values.iterator(); + for (Object index : splice) { + putAt(self, (Integer) index, valuesIter.next()); + } + } else { + throw new IllegalArgumentException("Can only index a List with another List of Integers, not a List of "+first.getClass().getName()); + } + } + + /** + * A helper method to allow lists to work with subscript operators. + *
    def list = ["a", true, 42, 9.4]
    +     * list[1, 3] = 5
    +     * assert list == ["a", 5, 42, 5]
    + * + * @param self a List + * @param splice the subset of the list to set + * @param value the value to put at the given sublist + * @since 1.0 + */ + public static void putAt(List self, List splice, Object value) { + if (splice.isEmpty()) { + return; + } + Object first = splice.get(0); + if (first instanceof Integer) { + for (Object index : splice) { + self.set((Integer) index, value); + } + } else { + throw new IllegalArgumentException("Can only index a List with another List of Integers, not a List of "+first.getClass().getName()); + } + } + + // helper method for putAt(Splice) + // todo: remove after putAt(Splice) gets deleted + protected static List getSubList(List self, List splice) { + int left /* = 0 */; + int right = 0; + boolean emptyRange = false; + if (splice.size() == 2) { + left = DefaultTypeTransformation.intUnbox(splice.get(0)); + right = DefaultTypeTransformation.intUnbox(splice.get(1)); + } else if (splice instanceof IntRange) { + IntRange range = (IntRange) splice; + left = range.getFromInt(); + right = range.getToInt(); + } else if (splice instanceof EmptyRange) { + RangeInfo info = subListBorders(self.size(), (EmptyRange) splice); + left = info.from; + emptyRange = true; + } else { + throw new IllegalArgumentException("You must specify a list of 2 indexes to create a sub-list"); + } + int size = self.size(); + left = normaliseIndex(left, size); + right = normaliseIndex(right, size); + List sublist /* = null */; + if (!emptyRange) { + sublist = self.subList(left, right + 1); + } else { + sublist = self.subList(left, left); + } + return sublist; + } + + /** + * Support the subscript operator for a Map. + *
    def map = [a:10]
    +     * assert map["a"] == 10
    + * + * @param self a Map + * @param key an Object as a key for the map + * @return the value corresponding to the given key + * @since 1.0 + */ + public static V getAt(Map self, K key) { + return self.get(key); + } + + /** + * Returns a new Map containing all entries from left and right, + * giving precedence to right. Any keys appearing in both Maps + * will appear in the resultant map with values from the right + * operand. If the left map is one of TreeMap, LinkedHashMap, Hashtable + * or Properties, the returned Map will preserve that type, otherwise a HashMap will + * be returned. + *

    + *

    + * Roughly equivalent to Map m = new HashMap(); m.putAll(left); m.putAll(right); return m; + * but with some additional logic to preserve the left Map type for common cases as + * described above. + *

    + *
    assert [a:10, b:20] + [a:5, c:7] == [a:5, b:20, c:7]
    + * + * @param left a Map + * @param right a Map + * @return a new Map containing all entries from left and right + * @since 1.5.0 + */ + public static Map plus(Map left, Map right) { + Map map = cloneSimilarMap(left); + map.putAll(right); + return map; + } + + /** + * A helper method to allow maps to work with subscript operators + * + * @param self a Map + * @param key an Object as a key for the map + * @param value the value to put into the map + * @return the value corresponding to the given key + * @since 1.0 + */ + public static V putAt(Map self, K key, V value) { + self.put(key, value); + return value; + } + + /** + * Support the subscript operator for Collection. + *
    assert [String, Long, Integer] == ["a",5L,2]["class"]
    + * + * @param coll a Collection + * @param property a String + * @return a List + * @since 1.0 + */ + public static List getAt(Collection coll, String property) { + List answer = new ArrayList(coll.size()); + for (Object item : coll) { + if (item == null) continue; + Object value; + try { + value = InvokerHelper.getProperty(item, property); + } catch (MissingPropertyExceptionNoStack mpe) { + String causeString = new MissingPropertyException(mpe.getProperty(), mpe.getType()).toString(); + throw new MissingPropertyException("Exception evaluating property '" + property + + "' for " + coll.getClass().getName() + ", Reason: " + causeString); + } + answer.add(value); + } + return answer; + } + + /** + * A convenience method for creating an immutable map. + * + * @param self a Map + * @return an immutable Map + * @see java.util.Collections#unmodifiableMap(java.util.Map) + * @since 1.0 + */ + public static Map asImmutable(Map self) { + return Collections.unmodifiableMap(self); + } + + /** + * A convenience method for creating an immutable sorted map. + * + * @param self a SortedMap + * @return an immutable SortedMap + * @see java.util.Collections#unmodifiableSortedMap(java.util.SortedMap) + * @since 1.0 + */ + public static SortedMap asImmutable(SortedMap self) { + return Collections.unmodifiableSortedMap(self); + } + + /** + * A convenience method for creating an immutable list + * + * @param self a List + * @return an immutable List + * @see java.util.Collections#unmodifiableList(java.util.List) + * @since 1.0 + */ + public static List asImmutable(List self) { + return Collections.unmodifiableList(self); + } + + /** + * A convenience method for creating an immutable list. + * + * @param self a Set + * @return an immutable Set + * @see java.util.Collections#unmodifiableSet(java.util.Set) + * @since 1.0 + */ + public static Set asImmutable(Set self) { + return Collections.unmodifiableSet(self); + } + + /** + * A convenience method for creating an immutable sorted set. + * + * @param self a SortedSet + * @return an immutable SortedSet + * @see java.util.Collections#unmodifiableSortedSet(java.util.SortedSet) + * @since 1.0 + */ + public static SortedSet asImmutable(SortedSet self) { + return Collections.unmodifiableSortedSet(self); + } + + /** + * A convenience method for creating an immutable Collection. + *
    def mutable = [1,2,3]
    +     * def immutable = mutable.asImmutable()
    +     * mutable << 4
    +     * try {
    +     *   immutable << 4
    +     *   assert false
    +     * } catch (UnsupportedOperationException) {
    +     *   assert true
    +     * }
    + * + * @param self a Collection + * @return an immutable Collection + * @see java.util.Collections#unmodifiableCollection(java.util.Collection) + * @since 1.5.0 + */ + public static Collection asImmutable(Collection self) { + return Collections.unmodifiableCollection(self); + } + + /** + * A convenience method for creating a synchronized Map. + * + * @param self a Map + * @return a synchronized Map + * @see java.util.Collections#synchronizedMap(java.util.Map) + * @since 1.0 + */ + public static Map asSynchronized(Map self) { + return Collections.synchronizedMap(self); + } + + /** + * A convenience method for creating a synchronized SortedMap. + * + * @param self a SortedMap + * @return a synchronized SortedMap + * @see java.util.Collections#synchronizedSortedMap(java.util.SortedMap) + * @since 1.0 + */ + public static SortedMap asSynchronized(SortedMap self) { + return Collections.synchronizedSortedMap(self); + } + + /** + * A convenience method for creating a synchronized Collection. + * + * @param self a Collection + * @return a synchronized Collection + * @see java.util.Collections#synchronizedCollection(java.util.Collection) + * @since 1.0 + */ + public static Collection asSynchronized(Collection self) { + return Collections.synchronizedCollection(self); + } + + /** + * A convenience method for creating a synchronized List. + * + * @param self a List + * @return a synchronized List + * @see java.util.Collections#synchronizedList(java.util.List) + * @since 1.0 + */ + public static List asSynchronized(List self) { + return Collections.synchronizedList(self); + } + + /** + * A convenience method for creating a synchronized Set. + * + * @param self a Set + * @return a synchronized Set + * @see java.util.Collections#synchronizedSet(java.util.Set) + * @since 1.0 + */ + public static Set asSynchronized(Set self) { + return Collections.synchronizedSet(self); + } + + /** + * A convenience method for creating a synchronized SortedSet. + * + * @param self a SortedSet + * @return a synchronized SortedSet + * @see java.util.Collections#synchronizedSortedSet(java.util.SortedSet) + * @since 1.0 + */ + public static SortedSet asSynchronized(SortedSet self) { + return Collections.synchronizedSortedSet(self); + } + + /** + * Synonym for {@link #toSpreadMap(java.util.Map)}. + * @param self a map + * @return a newly created SpreadMap + * @since 1.0 + */ + public static SpreadMap spread(Map self) { + return toSpreadMap(self); + } + + /** + * Returns a new SpreadMap from this map. + *

    + * The example below shows the various possible use cases: + *

    +     * def fn(Map m) { return m.a + m.b + m.c + m.d }
    +     *
    +     * assert fn(a:1, b:2, c:3, d:4) == 10
    +     * assert fn(a:1, *:[b:2, c:3], d:4) == 10
    +     * assert fn([a:1, b:2, c:3, d:4].toSpreadMap()) == 10
    +     * assert fn((['a', 1, 'b', 2, 'c', 3, 'd', 4] as Object[]).toSpreadMap()) == 10
    +     * assert fn(['a', 1, 'b', 2, 'c', 3, 'd', 4].toSpreadMap()) == 10
    +     * assert fn(['abcd'.toList(), 1..4].transpose().flatten().toSpreadMap()) == 10
    +     * 
    + * Note that toSpreadMap() is not normally used explicitly but under the covers by Groovy. + * + * @param self a map to be converted into a SpreadMap + * @return a newly created SpreadMap if this map is not null and its size is positive. + * @see groovy.lang.SpreadMap#SpreadMap(java.util.Map) + * @since 1.0 + */ + public static SpreadMap toSpreadMap(Map self) { + if (self == null) + throw new GroovyRuntimeException("Fail to convert Map to SpreadMap, because it is null."); + else + return new SpreadMap(self); + } + + /** + * Creates a spreadable map from this array. + *

    + * @param self an object array + * @return a newly created SpreadMap + * @see groovy.lang.SpreadMap#SpreadMap(java.lang.Object[]) + * @see #toSpreadMap(java.util.Map) + * @since 1.0 + */ + public static SpreadMap toSpreadMap(Object[] self) { + if (self == null) + throw new GroovyRuntimeException("Fail to convert Object[] to SpreadMap, because it is null."); + else if (self.length % 2 != 0) + throw new GroovyRuntimeException("Fail to convert Object[] to SpreadMap, because it's size is not even."); + else + return new SpreadMap(self); + } + + /** + * Creates a spreadable map from this list. + *

    + * @param self a list + * @return a newly created SpreadMap + * @see groovy.lang.SpreadMap#SpreadMap(java.util.List) + * @see #toSpreadMap(java.util.Map) + * @since 1.8.0 + */ + public static SpreadMap toSpreadMap(List self) { + if (self == null) + throw new GroovyRuntimeException("Fail to convert List to SpreadMap, because it is null."); + else if (self.size() % 2 != 0) + throw new GroovyRuntimeException("Fail to convert List to SpreadMap, because it's size is not even."); + else + return new SpreadMap(self); + } + + /** + * Wraps a map using the delegate pattern with a wrapper that intercepts all calls + * to get(key). If an unknown key is found, a default value will be + * stored into the Map before being returned. The default value stored will be the + * result of calling the supplied Closure with the key as the parameter to the Closure. + * Example usage: + *

    +     * def map = [a:1, b:2].withDefault{ k -> k.toCharacter().isLowerCase() ? 10 : -10 }
    +     * def expected = [a:1, b:2, c:10, D:-10]
    +     * assert expected.every{ e -> e.value == map[e.key] }
    +     *
    +     * def constMap = [:].withDefault{ 42 }
    +     * assert constMap.foo == 42
    +     * assert constMap.size() == 1
    +     * 
    + * + * @param self a Map + * @param init a Closure which is passed the unknown key + * @return the wrapped Map + * @since 1.7.1 + */ + public static Map withDefault(Map self, Closure init) { + return MapWithDefault.newInstance(self, init); + } + + /** + * Sorts the Collection. Assumes that the collection items are comparable + * and uses their natural ordering to determine the resulting order. + * If the Collection is a List, it is sorted in place and returned. + * Otherwise, the elements are first placed into a new list which is then + * sorted and returned - leaving the original Collection unchanged. + *
    assert [1,2,3] == [3,1,2].sort()
    + * + * @param self the collection to be sorted + * @return the sorted collection as a List + * @see #sort(Collection, boolean) + * @since 1.0 + */ + public static List sort(Collection self) { + return sort(self, true); + } + + /** + * Sorts the Collection. Assumes that the collection items are + * comparable and uses their natural ordering to determine the resulting order. + * If the Collection is a List and mutate is true, + * it is sorted in place and returned. Otherwise, the elements are first placed + * into a new list which is then sorted and returned - leaving the original Collection unchanged. + *
    assert [1,2,3] == [3,1,2].sort()
    + *
    +     * def orig = [1, 3, 2]
    +     * def sorted = orig.sort(false)
    +     * assert orig == [1, 3, 2]
    +     * assert sorted == [1, 2, 3]
    +     * 
    + * + * @param self the collection to be sorted + * @param mutate false will always cause a new list to be created, true will mutate lists in place + * @return the sorted collection as a List + * @since 1.8.1 + */ + public static List sort(Collection self, boolean mutate) { + List answer = mutate ? asList(self) : toList(self); + Collections.sort(answer, new NumberAwareComparator()); + return answer; + } + + /** + * Sorts the elements from the given map into a new ordered map using + * the closure as a comparator to determine the ordering. + * The original map is unchanged. + *
    def map = [a:5, b:3, c:6, d:4].sort { a, b -> a.value <=> b.value }
    +     * assert map == [b:3, d:4, a:5, c:6]
    + * + * @param self the original unsorted map + * @param closure a Closure used as a comparator + * @return the sorted map + * @since 1.6.0 + */ + public static Map sort(Map self, Closure closure) { + Map result = new LinkedHashMap(); + List> entries = asList(self.entrySet()); + sort(entries, closure); + for (Map.Entry entry : entries) { + result.put(entry.getKey(), entry.getValue()); + } + return result; + } + + /** + * Sorts the elements from the given map into a new ordered Map using + * the specified key comparator to determine the ordering. + * The original map is unchanged. + *
    def map = [ba:3, cz:6, ab:5].sort({ a, b -> a[-1] <=> b[-1] } as Comparator)
    +     * assert map*.value == [3, 5, 6]
    + * + * @param self the original unsorted map + * @param comparator a Comparator + * @return the sorted map + * @since 1.7.2 + */ + public static Map sort(Map self, Comparator comparator) { + Map result = new TreeMap(comparator); + result.putAll(self); + return result; + } + + /** + * Sorts the elements from the given map into a new ordered Map using + * the natural ordering of the keys to determine the ordering. + * The original map is unchanged. + *
    map = [ba:3, cz:6, ab:5].sort()
    +     * assert map*.value == [5, 3, 6]
    +     * 
    + * + * @param self the original unsorted map + * @return the sorted map + * @since 1.7.2 + */ + public static Map sort(Map self) { + return new TreeMap(self); + } + + /** + * Modifies this array so that its elements are in sorted order. + * The array items are assumed to be comparable. + * + * @param self the array to be sorted + * @return the sorted array + * @since 1.5.5 + */ + public static T[] sort(T[] self) { + Arrays.sort(self, new NumberAwareComparator()); + return self; + } + + /** + * Sorts the given array into sorted order. + * The array items are assumed to be comparable. + * If mutate is true, the array is sorted in place and returned. Otherwise, a new sorted + * array is returned and the original array remains unchanged. + *
    +     * def orig = ["hello","hi","Hey"] as String[]
    +     * def sorted = orig.sort(false)
    +     * assert orig == ["hello","hi","Hey"] as String[]
    +     * assert sorted == ["Hey","hello","hi"] as String[]
    +     * orig.sort(true)
    +     * assert orig == ["Hey","hello","hi"] as String[]
    +     * 
    + * + * @param self the array to be sorted + * @param mutate false will always cause a new array to be created, true will mutate the array in place + * @return the sorted array + * @since 1.8.1 + */ + public static T[] sort(T[] self, boolean mutate) { + T[] answer = mutate ? self : self.clone(); + Arrays.sort(answer, new NumberAwareComparator()); + return answer; + } + + /** + * Sorts the given iterator items into a sorted iterator. The items are + * assumed to be comparable. The original iterator will become + * exhausted of elements after completing this method call. + * A new iterator is produced that traverses the items in sorted order. + * + * @param self the Iterator to be sorted + * @return the sorted items as an Iterator + * @since 1.5.5 + */ + public static Iterator sort(Iterator self) { + return sort(toList(self)).listIterator(); + } + + /** + * Sorts the given iterator items into a sorted iterator using the comparator. The + * original iterator will become exhausted of elements after completing this method call. + * A new iterator is produced that traverses the items in sorted order. + * + * @param self the Iterator to be sorted + * @param comparator a Comparator used for comparing items + * @return the sorted items as an Iterator + * @since 1.5.5 + */ + public static Iterator sort(Iterator self, Comparator comparator) { + return sort(toList(self), comparator).listIterator(); + } + + /** + * Sorts the Collection using the given Comparator. If the Collection is a List, + * it is sorted in place and returned. Otherwise, the elements are first placed + * into a new list which is then sorted and returned - leaving the original Collection unchanged. + *
    +     * assert ["hi","hey","hello"] == ["hello","hi","hey"].sort( { a, b -> a.length() <=> b.length() } as Comparator )
    +     * 
    + *
    +     * assert ["hello","Hey","hi"] == ["hello","hi","Hey"].sort(String.CASE_INSENSITIVE_ORDER)
    +     * 
    + * + * @param self a collection to be sorted + * @param comparator a Comparator used for the comparison + * @return a sorted List + * @see #sort(Collection, boolean, Comparator) + * @since 1.0 + */ + public static List sort(Collection self, Comparator comparator) { + return sort(self, true, comparator); + } + + /** + * Sorts the Collection using the given Comparator. If the Collection is a List and mutate + * is true, it is sorted in place and returned. Otherwise, the elements are first placed + * into a new list which is then sorted and returned - leaving the original Collection unchanged. + *
    +     * assert ["hi","hey","hello"] == ["hello","hi","hey"].sort( { a, b -> a.length() <=> b.length() } as Comparator )
    +     * 
    + *
    +     * def orig = ["hello","hi","Hey"]
    +     * def sorted = orig.sort(false, String.CASE_INSENSITIVE_ORDER)
    +     * assert orig == ["hello","hi","Hey"]
    +     * assert sorted == ["hello","Hey","hi"]
    +     * 
    + * + * @param self a collection to be sorted + * @param mutate false will always cause a new list to be created, true will mutate lists in place + * @param comparator a Comparator used for the comparison + * @return a sorted List + * @since 1.8.1 + */ + public static List sort(Collection self, boolean mutate, Comparator comparator) { + List list = mutate ? asList(self) : toList(self); + Collections.sort(list, comparator); + return list; + } + + /** + * Sorts the given array into sorted order using the given comparator. + * + * @param self the array to be sorted + * @param comparator a Comparator used for the comparison + * @return the sorted array + * @since 1.5.5 + */ + public static T[] sort(T[] self, Comparator comparator) { + return sort(self, true, comparator); + } + + /** + * Modifies this array so that its elements are in sorted order as determined by the given comparator. + * If mutate is true, the array is sorted in place and returned. Otherwise, a new sorted + * array is returned and the original array remains unchanged. + *
    +     * def orig = ["hello","hi","Hey"] as String[]
    +     * def sorted = orig.sort(false, String.CASE_INSENSITIVE_ORDER)
    +     * assert orig == ["hello","hi","Hey"] as String[]
    +     * assert sorted == ["hello","Hey","hi"] as String[]
    +     * orig.sort(true, String.CASE_INSENSITIVE_ORDER)
    +     * assert orig == ["hello","Hey","hi"] as String[]
    +     * 
    + * + * @param self the array containing elements to be sorted + * @param mutate false will always cause a new array to be created, true will mutate arrays in place + * @param comparator a Comparator used for the comparison + * @return a sorted array + * @since 1.8.1 + */ + public static T[] sort(T[] self, boolean mutate, Comparator comparator) { + T[] answer = mutate ? self : self.clone(); + Arrays.sort(answer, comparator); + return answer; + } + + /** + * Sorts the given iterator items into a sorted iterator using the Closure to determine the correct ordering. + * The original iterator will be fully processed after the method call. + *

    + * If the closure has two parameters it is used like a traditional Comparator. + * I.e. it should compare its two parameters for order, returning a negative integer, + * zero, or a positive integer when the first parameter is less than, equal to, + * or greater than the second respectively. Otherwise, the Closure is assumed + * to take a single parameter and return a Comparable (typically an Integer) + * which is then used for further comparison. + * + * @param self the Iterator to be sorted + * @param closure a Closure used to determine the correct ordering + * @return the sorted items as an Iterator + * @since 1.5.5 + */ + public static Iterator sort(Iterator self, Closure closure) { + return sort(toList(self), closure).listIterator(); + } + + /** + * Sorts the elements from this array into a newly created array using + * the Closure to determine the correct ordering. + *

    + * If the closure has two parameters it is used like a traditional Comparator. I.e. it should compare + * its two parameters for order, returning a negative integer, zero, or a positive integer when the + * first parameter is less than, equal to, or greater than the second respectively. Otherwise, + * the Closure is assumed to take a single parameter and return a Comparable (typically an Integer) + * which is then used for further comparison. + * + * @param self the array containing the elements to be sorted + * @param closure a Closure used to determine the correct ordering + * @return the sorted array + * @since 1.5.5 + */ + @SuppressWarnings("unchecked") + public static T[] sort(T[] self, Closure closure) { + return sort(self, false, closure); + } + + /** + * Modifies this array so that its elements are in sorted order using the Closure to determine the correct ordering. + * If mutate is false, a new array is returned and the original array remains unchanged. + * Otherwise, the original array is sorted in place and returned. + *

    + * If the closure has two parameters it is used like a traditional Comparator. I.e. it should compare + * its two parameters for order, returning a negative integer, zero, or a positive integer when the + * first parameter is less than, equal to, or greater than the second respectively. Otherwise, + * the Closure is assumed to take a single parameter and return a Comparable (typically an Integer) + * which is then used for further comparison. + *
    +     * def orig = ["hello","hi","Hey"] as String[]
    +     * def sorted = orig.sort(false) { it.size() }
    +     * assert orig == ["hello","hi","Hey"] as String[]
    +     * assert sorted == ["hi","Hey","hello"] as String[]
    +     * orig.sort(true) { it.size() }
    +     * assert orig == ["hi","Hey","hello"] as String[]
    +     * 
    + * + * @param self the array to be sorted + * @param mutate false will always cause a new array to be created, true will mutate arrays in place + * @param closure a Closure used to determine the correct ordering + * @return the sorted array + * @since 1.8.1 + */ + @SuppressWarnings("unchecked") + public static T[] sort(T[] self, boolean mutate, Closure closure) { + T[] answer = (T[]) sort(toList(self), closure).toArray(); + if (mutate) { + System.arraycopy(answer, 0, self, 0, answer.length); + } + return mutate ? self : answer; + } + + /** + * Sorts this Collection using the given Closure to determine the correct ordering. If the Collection is a List, + * it is sorted in place and returned. Otherwise, the elements are first placed + * into a new list which is then sorted and returned - leaving the original Collection unchanged. + *

    + * If the Closure has two parameters + * it is used like a traditional Comparator. I.e. it should compare + * its two parameters for order, returning a negative integer, + * zero, or a positive integer when the first parameter is less than, + * equal to, or greater than the second respectively. Otherwise, + * the Closure is assumed to take a single parameter and return a + * Comparable (typically an Integer) which is then used for + * further comparison. + *
    assert ["hi","hey","hello"] == ["hello","hi","hey"].sort { it.length() }
    + *
    assert ["hi","hey","hello"] == ["hello","hi","hey"].sort { a, b -> a.length() <=> b.length() }
    + * + * @param self a Collection to be sorted + * @param closure a 1 or 2 arg Closure used to determine the correct ordering + * @return a newly created sorted List + * @see #sort(Collection, boolean, Closure) + * @since 1.0 + */ + public static List sort(Collection self, Closure closure) { + return sort(self, true, closure); + } + + /** + * Sorts this Collection using the given Closure to determine the correct ordering. If the Collection is a List + * and mutate is true, it is sorted in place and returned. Otherwise, the elements are first placed + * into a new list which is then sorted and returned - leaving the original Collection unchanged. + *

    + * If the closure has two parameters + * it is used like a traditional Comparator. I.e. it should compare + * its two parameters for order, returning a negative integer, + * zero, or a positive integer when the first parameter is less than, + * equal to, or greater than the second respectively. Otherwise, + * the Closure is assumed to take a single parameter and return a + * Comparable (typically an Integer) which is then used for + * further comparison. + *
    assert ["hi","hey","hello"] == ["hello","hi","hey"].sort { it.length() }
    + *
    assert ["hi","hey","hello"] == ["hello","hi","hey"].sort { a, b -> a.length() <=> b.length() }
    + *
    +     * def orig = ["hello","hi","Hey"]
    +     * def sorted = orig.sort(false) { it.toUpperCase() }
    +     * assert orig == ["hello","hi","Hey"]
    +     * assert sorted == ["hello","Hey","hi"]
    +     * 
    + * + * @param self a Collection to be sorted + * @param mutate false will always cause a new list to be created, true will mutate lists in place + * @param closure a 1 or 2 arg Closure used to determine the correct ordering + * @return a newly created sorted List + * @since 1.8.1 + */ + public static List sort(Collection self, boolean mutate, Closure closure) { + List list = mutate ? asList(self) : toList(self); + // use a comparator of one item or two + int params = closure.getMaximumNumberOfParameters(); + if (params == 1) { + Collections.sort(list, new OrderBy(closure)); + } else { + Collections.sort(list, new ClosureComparator(closure)); + } + return list; + } + + /** + * Avoids doing unnecessary work when sorting an already sorted set (i.e. an identity function for an already sorted set). + * + * @param self an already sorted set + * @return the set + * @since 1.0 + */ + public static SortedSet sort(SortedSet self) { + return self; + } + + /** + * Avoids doing unnecessary work when sorting an already sorted map (i.e. an identity function for an already sorted map). + * + * @param self an already sorted map + * @return the map + * @since 1.8.1 + */ + public static SortedMap sort(SortedMap self) { + return self; + } + + /** + * Removes the last item from the List. Using add() and pop() + * is similar to push and pop on a Stack. + *
    def list = ["a", false, 2]
    +     * assert list.pop() == 2
    +     * assert list == ["a", false]
    + * + * @param self a List + * @return the item removed from the List + * @throws NoSuchElementException if the list is empty and you try to pop() it. + * @since 1.0 + */ + public static T pop(List self) { + if (self.isEmpty()) { + throw new NoSuchElementException("Cannot pop() an empty List"); + } + return self.remove(self.size() - 1); + } + + /** + * Provides an easy way to append multiple Map.Entry values to a Map. + * + * @param self a Map + * @param entries a Collection of Map.Entry items to be added to the Map. + * @return the same map, after the items have been added to it. + * @since 1.6.1 + */ + public static Map putAll(Map self, Collection> entries) { + for (Map.Entry entry : entries) { + self.put(entry.getKey(), entry.getValue()); + } + return self; + } + + /** + * Returns a new Map containing all entries from self and entries, + * giving precedence to entries. Any keys appearing in both Maps + * will appear in the resultant map with values from the entries + * operand. If self map is one of TreeMap, LinkedHashMap, Hashtable + * or Properties, the returned Map will preserve that type, otherwise a HashMap will + * be returned. + *

    + * + * @param self a Map + * @param entries a Collection of Map.Entry items to be added to the Map. + * @return a new Map containing all key, value pairs from self and entries + * @since 1.6.1 + */ + public static Map plus(Map self, Collection> entries) { + Map map = cloneSimilarMap(self); + putAll(map, entries); + return map; + } + + /** + * Appends an item to the List. Synonym for add(). + *
    def list = [3, 4, 2]
    +     * list.push("x")
    +     * assert list == [3, 4, 2, "x"]
    + * + * @param self a List + * @param value element to be appended to this list. + * @return true (as per the general contract of the + * Collection.add method). + * @throws NoSuchElementException if the list is empty and you try to pop() it. + * @since 1.5.5 + */ + public static boolean push(List self, T value) { + return self.add(value); + } + + /** + * Returns the last item from the List. + *
    def list = [3, 4, 2]
    +     * assert list.last() == 2
    +     * assert list == [3, 4, 2]
    + * + * @param self a List + * @return the last item from the List + * @throws NoSuchElementException if the list is empty and you try to access the last() item. + * @since 1.5.5 + */ + public static T last(List self) { + if (self.isEmpty()) { + throw new NoSuchElementException("Cannot access last() element from an empty List"); + } + return self.get(self.size() - 1); + } + + /** + * Returns the last item from the Object array. + *
    def array = [3, 4, 2].toArray()
    +     * assert array.last() == 2
    + * + * @param self an ObjectArray + * @return the last item from the Object array + * @throws NoSuchElementException if the array is empty and you try to access the last() item. + * @since 1.7.3 + */ + public static T last(T[] self) { + if (self.length == 0) { + throw new NoSuchElementException("Cannot access last() element from an empty Array"); + } + return self[self.length - 1]; + } + + /** + * Returns the first item from the List. + *
    def list = [3, 4, 2]
    +     * assert list.first() == 3
    +     * assert list == [3, 4, 2]
    + * + * @param self a List + * @return the first item from the List + * @throws NoSuchElementException if the list is empty and you try to access the first() item. + * @since 1.5.5 + */ + public static T first(List self) { + if (self.isEmpty()) { + throw new NoSuchElementException("Cannot access first() element from an empty List"); + } + return self.get(0); + } + + /** + * Returns the first item from the Object array. + *
    def array = [3, 4, 2].toArray()
    +     * assert array.first() == 3
    + * + * @param self an Object array + * @return the first item from the Object array + * @throws NoSuchElementException if the array is empty and you try to access the first() item. + * @since 1.7.3 + */ + public static T first(T[] self) { + if (self.length == 0) { + throw new NoSuchElementException("Cannot access first() element from an empty List"); + } + return self[0]; + } + + /** + * Returns the first item from the List. + *
    def list = [3, 4, 2]
    +     * assert list.head() == 3
    +     * assert list == [3, 4, 2]
    + * + * @param self a List + * @return the first item from the List + * @throws NoSuchElementException if the list is empty and you try to access the head() item. + * @since 1.5.5 + */ + public static T head(List self) { + return first(self); + } + + /** + * Returns the first item from the Object array. + *
    def array = [3, 4, 2].toArray()
    +     * assert array.head() == 3
    + * + * @param self an Object array + * @return the first item from the Object array + * @throws NoSuchElementException if the array is empty and you try to access the head() item. + * @since 1.7.3 + */ + public static T head(T[] self) { + return first(self); + } + + /** + * Returns the items from the List excluding the first item. + *
    def list = [3, 4, 2]
    +     * assert list.tail() == [4, 2]
    +     * assert list == [3, 4, 2]
    + * + * @param self a List + * @return a list without its first element + * @throws NoSuchElementException if the list is empty and you try to access the tail() item. + * @since 1.5.6 + */ + public static List tail(List self) { + if (self.isEmpty()) { + throw new NoSuchElementException("Cannot access tail() for an empty List"); + } + List result = new ArrayList(self); + result.remove(0); + return result; + } + + /** + * Returns the items from the Object array excluding the first item. + *
    +     *     String[] strings = ["a", "b", "c"]
    +     *     def result = strings.tail()
    +     *     assert strings.class.componentType == String
    +     * 
    + * + * @param self an Object array + * @return an Object array without its first element + * @throws NoSuchElementException if the list is empty and you try to access the tail() item. + * @since 1.7.3 + */ + public static T[] tail(T[] self) { + if (self.length == 0) { + throw new NoSuchElementException("Cannot access tail() for an empty Object array"); + } + Class componentType = (Class) self.getClass().getComponentType(); + T[] result = (T[]) Array.newInstance(componentType, self.length - 1); + System.arraycopy(self, 1, result, 0, self.length - 1); + return result; + } + + /** + * Returns the first num elements from the head of this list. + *
    +     *     def strings = [ 'a', 'b', 'c' ]
    +     *     assert strings.take( 0 ) == []
    +     *     assert strings.take( 2 ) == [ 'a', 'b' ]
    +     *     assert strings.take( 5 ) == [ 'a', 'b', 'c' ]
    +     * 
    + * + * @param self the original list + * @param num the number of elements to take from this list + * @return a list consisting of the first num elements of this list, + * or else the whole list if it has less then num elements. + * @since 1.8.1 + */ + public static List take( List self, int num ) { + if( self.isEmpty() || num <= 0 ) { + return createSimilarList( self, 0 ) ; + } + if( self.size() <= num ) { + List ret = createSimilarList( self, self.size() ) ; + ret.addAll( self ) ; + return ret ; + } + List ret = createSimilarList( self, num ) ; + ret.addAll( self.subList( 0, num ) ) ; + return ret ; + } + + /** + * Returns the first num elements from the head of this array. + *
    +     *     String[] strings = [ 'a', 'b', 'c' ]
    +     *     assert strings.take( 0 ) == [] as String[]
    +     *     assert strings.take( 2 ) == [ 'a', 'b' ] as String[]
    +     *     assert strings.take( 5 ) == [ 'a', 'b', 'c' ] as String[]
    +     * 
    + * + * @param self the original array + * @param num the number of elements to take from this array + * @return an array consisting of the first num elements of this array, + * or else the whole array if it has less then num elements. + * @since 1.8.1 + */ + public static T[] take( T[] self, int num ) { + Class componentType = (Class) self.getClass().getComponentType(); + if( self.length == 0 || num <= 0 ) { + return (T[]) Array.newInstance(componentType, 0); + } + if( self.length <= num ) { + T[] ret = (T[]) Array.newInstance(componentType, self.length); + System.arraycopy(self, 0, ret, 0, self.length); + return ret; + } + + T[] ret = (T[]) Array.newInstance(componentType, num); + System.arraycopy(self, 0, ret, 0, num); + return ret; + } + + /** + * Returns a new map containing the first num elements from the head of this map. + * If the map instance does not have ordered keys, then this function could return a random num + * entries. Groovy by default used LinkedHashMap, so this shouldn't be an issue in the main. + *
    +     *     def strings = [ 'a':10, 'b':20, 'c':30 ]
    +     *     assert strings.take( 0 ) == [:]
    +     *     assert strings.take( 2 ) == [ 'a':10, 'b':20 ]
    +     *     assert strings.take( 5 ) == [ 'a':10, 'b':20, 'c':30 ]
    +     * 
    + * + * @param self the original map + * @param num the number of elements to take from this map + * @return a new map consisting of the first num elements of this map, + * or else the whole map if it has less then num elements. + * @since 1.8.1 + */ + public static Map take( Map self, int num ) { + if( self.isEmpty() || num <= 0 ) { + return createSimilarMap( self ) ; + } + Map ret = createSimilarMap( self ) ; + for( K key : self.keySet() ) { + ret.put( key, self.get( key ) ) ; + if( --num <= 0 ) { + break ; + } + } + return ret ; + } + + /** + * Returns an iterator to up to the first num elements from this iterator. + * The original iterator is stepped along by num elements. + *
    +     *     def a = 0
    +     *     def iter = [ hasNext:{ true }, next:{ a++ } ] as Iterator
    +     *
    +     *     def iteratorCompare( Iterator a, List b ) {
    +     *       a.collect { it } == b
    +     *     }
    +     *     assert iteratorCompare( iter.take( 0 ), [] )
    +     *     assert iteratorCompare( iter.take( 2 ), [ 0, 1 ] )
    +     *     assert iteratorCompare( iter.take( 5 ), [ 2, 3, 4, 5, 6 ] )
    +     * 
    + * + * @param self the Iterator + * @param num the number of elements to take from this iterator + * @return a list consisting of up to the first num elements of this iterator. + * @since 1.8.1 + */ + public static Iterator take( Iterator self, int num ) { + List ret = new ArrayList() ; + while( num-- > 0 && self.hasNext() ) { + ret.add( self.next() ) ; + } + return ret.listIterator() ; + } + + /** + * Returns the first num elements from this CharSequence. + *
    +     *     def text = "Groovy"
    +     *     assert text.take( 0 ) == ''
    +     *     assert text.take( 2 ) == 'Gr'
    +     *     assert text.take( 7 ) == 'Groovy'
    +     * 
    + * + * @param self the original CharSequence + * @param num the number of chars to take from this CharSequence + * @return a CharSequence consisting of the first num chars, + * or else the whole CharSequence if it has less then num elements. + * @since 1.8.1 + */ + public static CharSequence take( CharSequence self, int num ) { + if( num < 0 ) { + return self.subSequence( 0, 0 ) ; + } + if( self.length() <= num ) { + return self ; + } + return self.subSequence( 0, num ) ; + } + + /** + * Drops the given number of elements from the head of this list + * if they are available. + *
    +     *     def strings = [ 'a', 'b', 'c' ]
    +     *     assert strings.drop( 0 ) == [ 'a', 'b', 'c' ]
    +     *     assert strings.drop( 2 ) == [ 'c' ]
    +     *     assert strings.drop( 5 ) == []
    +     * 
    + * + * @param self the original list + * @param num the number of elements to drop from this list + * @return a list consisting of all elements of this list except the first + * num ones, or else the empty list, if this list has + * less than num elements. + * @since 1.8.1 + */ + public static List drop(List self, int num) { + if (self.size() <= num) { + return createSimilarList( self, 0 ) ; + } + if (num <= 0) { + List ret = createSimilarList( self, self.size() ) ; + ret.addAll( self ) ; + return ret ; + } + List ret = createSimilarList( self, self.size() - num ) ; + ret.addAll(self.subList(num, self.size())) ; + return ret ; + } + + /** + * Drops the given number of elements from the head of this array + * if they are available. + *
    +     *     String[] strings = [ 'a', 'b', 'c' ]
    +     *     assert strings.drop( 0 ) == [ 'a', 'b', 'c' ] as String[]
    +     *     assert strings.drop( 2 ) == [ 'c' ] as String[]
    +     *     assert strings.drop( 5 ) == [] as String[]
    +     * 
    + * + * @param self the original array + * @param num the number of elements to drop from this array + * @return an array consisting of all elements of this array except the + * first num ones, or else the empty array, if this + * array has less than num elements. + * @since 1.8.1 + */ + public static T[] drop(T[] self, int num) { + Class componentType = (Class) self.getClass().getComponentType(); + if (self.length <= num) { + return (T[]) Array.newInstance(componentType, 0); + } + if (num <= 0) { + T[] ret = (T[]) Array.newInstance(componentType, self.length); + System.arraycopy(self, 0, ret, 0, self.length); + return ret; + } + + T[] ret = (T[]) Array.newInstance(componentType, self.length - num); + System.arraycopy(self, num, ret, 0, self.length - num); + return ret; + } + + /** + * Drops the given number of key/value pairs from the head of this map if they are available. + * If the map instance does not have ordered keys, then this function could drop a random num + * entries. Groovy by default used LinkedHashMap, so this shouldn't be an issue in the main. + *
    +     *     def strings = [ 'a':10, 'b':20, 'c':30 ]
    +     *     assert strings.drop( 0 ) == [ 'a':10, 'b':20, 'c':30 ]
    +     *     assert strings.drop( 2 ) == [ 'c':30 ]
    +     *     assert strings.drop( 5 ) == [:]
    +     * 
    + * + * @param self the original map + * @param num the number of elements to drop from this map + * @return a map consisting of all key/value pairs of this map except the first + * num ones, or else the empty map, if this map has + * less than num elements. + * @since 1.8.1 + */ + public static Map drop( Map self, int num ) { + if( self.size() <= num ) { + return createSimilarMap( self ) ; + } + if( num == 0 ) { + return cloneSimilarMap( self ) ; + } + Map ret = createSimilarMap( self ) ; + for( K key : self.keySet() ) { + if( num-- <= 0 ) { + ret.put( key, self.get( key ) ) ; + } + } + return ret ; + } + + /** + * Drops the given number of elements from the head of this iterator if they are available. + * The original iterator is stepped along by num elements. + *
    +     *     def iteratorCompare( Iterator a, List b ) {
    +     *       a.collect { it } == b
    +     *     }
    +     *     def iter = [ 1, 2, 3, 4, 5 ].listIterator()
    +     *     assert iteratorCompare( iter.drop( 0 ), [ 1, 2, 3, 4, 5 ] )
    +     *     iter = [ 1, 2, 3, 4, 5 ].listIterator()
    +     *     assert iteratorCompare( iter.drop( 2 ), [ 3, 4, 5 ] )
    +     *     iter = [ 1, 2, 3, 4, 5 ].listIterator()
    +     *     assert iteratorCompare( iter.drop( 5 ), [] )
    +     * 
    + * + * @param self the original iterator + * @param num the number of elements to drop from this iterator + * @return The iterator stepped along by num elements if they exist. + * @since 1.8.1 + */ + public static Iterator drop(Iterator self, int num) { + while (num-- > 0 && self.hasNext()) { + self.next(); + } + return self ; + } + + /** + * Drops the given number of chars from the head of this CharSequence + * if they are available. + *
    +     *     def text = "Groovy"
    +     *     assert text.drop( 0 ) == 'Groovy'
    +     *     assert text.drop( 2 ) == 'oovy'
    +     *     assert text.drop( 7 ) == ''
    +     * 
    + * + * @param self the original CharSequence + * @param num the number of characters to drop from this iterator + * @return a CharSequence consisting of all characters except the first num ones, + * or else an empty String, if this CharSequence has less than num characters. + * @since 1.8.1 + */ + public static CharSequence drop(CharSequence self, int num) { + if( num <= 0 ) { + return self ; + } + if( self.length() <= num ) { + return self.subSequence( 0, 0 ) ; + } + return self.subSequence( num, self.length() ) ; + } + + /** + * Converts this Collection to a List. Returns the original Collection + * if it is already a List. + *

    + * Example usage: + *

    assert new HashSet().asList() instanceof List
    + * + * @param self a collection to be converted into a List + * @return a newly created List if this collection is not already a List + * @since 1.0 + */ + public static List asList(Collection self) { + if (self instanceof List) { + return (List) self; + } else { + return toList(self); + } + } + + /** + * Coerce an object instance to a boolean value. + * An object is coerced to true if it's not null, to false if it is null. + * + * @param object the object to coerce + * @return the boolean value + * @since 1.7.0 + */ + public static boolean asBoolean(Object object) { + return object != null; + } + + /** + * Coerce an Boolean instance to a boolean value. + * + * @param bool the Boolean + * @return the boolean value + * @since 1.7.0 + */ + public static boolean asBoolean(Boolean bool) { + return bool; + } + + /** + * Coerce a Matcher instance to a boolean value. + * + * @param matcher the matcher + * @return the boolean value + * @since 1.7.0 + */ + public static boolean asBoolean(Matcher matcher) { + RegexSupport.setLastMatcher(matcher); + return matcher.find(); + } + + /** + * Coerce a collection instance to a boolean value. + * A collection is coerced to false if it's empty, and to true otherwise. + *
    assert [1,2].asBoolean() == true
    + *
    assert [].asBoolean() == false
    + * + * @param collection the collection + * @return the boolean value + * @since 1.7.0 + */ + public static boolean asBoolean(Collection collection) { + return !collection.isEmpty(); + } + + /** + * Coerce a map instance to a boolean value. + * A map is coerced to false if it's empty, and to true otherwise. + *
    assert [:] as Boolean == false
    +     * assert [a:2] as Boolean == true
    + * + * @param map the map + * @return the boolean value + * @since 1.7.0 + */ + public static boolean asBoolean(Map map) { + return !map.isEmpty(); + } + + /** + * Coerce an iterator instance to a boolean value. + * An iterator is coerced to false if there are no more elements to iterate over, + * and to true otherwise. + * + * @param iterator the iterator + * @return the boolean value + * @since 1.7.0 + */ + public static boolean asBoolean(Iterator iterator) { + return iterator.hasNext(); + } + + /** + * Coerce an enumeration instance to a boolean value. + * An enumeration is coerced to false if there are no more elements to enumerate, + * and to true otherwise. + * + * @param enumeration the enumeration + * @return the boolean value + * @since 1.7.0 + */ + public static boolean asBoolean(Enumeration enumeration) { + return enumeration.hasMoreElements(); + } + + /** + * Coerce a string (an instance of CharSequence) to a boolean value. + * A string is coerced to false if it is of length 0, + * and to true otherwise. + * + * @param string the character sequence + * @return the boolean value + * @since 1.7.0 + */ + public static boolean asBoolean(CharSequence string) { + return string.length() > 0; + } + + /** + * Coerce an Object array to a boolean value. + * An Object array is false if the array is of length 0. + * and to true otherwise + * + * @param array the array + * @return the boolean value + * @since 1.7.0 + */ + public static boolean asBoolean(Object[] array) { + return array.length > 0; + } + + /** + * Coerces a byte array to a boolean value. + * A byte array is false if the array is of length 0, + * and true otherwise. + * + * @param array an array + * @return the array's boolean value + * @since 1.7.4 + */ + public static boolean asBoolean(byte[] array) { + return array.length > 0; + } + + /** + * Coerces a short array to a boolean value. + * A short array is false if the array is of length 0, + * and true otherwise. + * + * @param array an array + * @return the array's boolean value + * @since 1.7.4 + */ + public static boolean asBoolean(short[] array) { + return array.length > 0; + } + + /** + * Coerces an int array to a boolean value. + * An int array is false if the array is of length 0, + * and true otherwise. + * + * @param array an array + * @return the array's boolean value + * @since 1.7.4 + */ + public static boolean asBoolean(int[] array) { + return array.length > 0; + } + + /** + * Coerces a long array to a boolean value. + * A long array is false if the array is of length 0, + * and true otherwise. + * + * @param array an array + * @return the array's boolean value + * @since 1.7.4 + */ + public static boolean asBoolean(long[] array) { + return array.length > 0; + } + + /** + * Coerces a float array to a boolean value. + * A float array is false if the array is of length 0, + * and true otherwise. + * + * @param array an array + * @return the array's boolean value + * @since 1.7.4 + */ + public static boolean asBoolean(float[] array) { + return array.length > 0; + } + + /** + * Coerces a double array to a boolean value. + * A double array is false if the array is of length 0, + * and true otherwise. + * + * @param array an array + * @return the array's boolean value + * @since 1.7.4 + */ + public static boolean asBoolean(double[] array) { + return array.length > 0; + } + + /** + * Coerces a boolean array to a boolean value. + * A boolean array is false if the array is of length 0, + * and true otherwise. + * + * @param array an array + * @return the array's boolean value + * @since 1.7.4 + */ + public static boolean asBoolean(boolean[] array) { + return array.length > 0; + } + + /** + * Coerces a char array to a boolean value. + * A char array is false if the array is of length 0, + * and true otherwise. + * + * @param array an array + * @return the array's boolean value + * @since 1.7.4 + */ + public static boolean asBoolean(char[] array) { + return array.length > 0; + } + + /** + * Coerce a character to a boolean value. + * A character is coerced to false if it's character value is equal to 0, + * and to true otherwise. + * + * @param character the character + * @return the boolean value + * @since 1.7.0 + */ + + public static boolean asBoolean(Character character) { + return character != 0; + } + + /** + * Coerce a number to a boolean value. + * A number is coerced to false if its double value is equal to 0, and to true otherwise, + * and to true otherwise. + * + * @param number the number + * @return the boolean value + * @since 1.7.0 + */ + public static boolean asBoolean(Number number) { + return number.doubleValue() != 0; + } + + /** + * Converts the given collection to another type. A default concrete + * type is used for List, Set, or SortedSet. If the given type has + * a constructor taking a collection, that is used. Otherwise, the + * call is deferred to {link #asType(Object,Class)}. If this + * collection is already of the given type, the same instance is + * returned. + * + * @param col a collection + * @param clazz the desired class + * @return the object resulting from this type conversion + * @see #asType(java.lang.Object, java.lang.Class) + * @since 1.0 + */ + @SuppressWarnings("unchecked") + public static T asType(Collection col, Class clazz) { + if (col.getClass() == clazz) { + return (T) col; + } + if (clazz == List.class) { + return (T) asList(col); + } + if (clazz == Set.class) { + if (col instanceof Set) return (T) col; + return (T) new LinkedHashSet(col); + } + if (clazz == SortedSet.class) { + if (col instanceof SortedSet) return (T) col; + return (T) new TreeSet(col); + } + if (clazz == Queue.class) { + if (col instanceof Queue) return (T) col; + return (T) new LinkedList(col); + } + if (clazz == Stack.class) { + if (col instanceof Stack) return (T) col; + final Stack stack = new Stack(); + stack.addAll(col); + return (T) stack; + } + + if (clazz!=String[].class && ReflectionCache.isArray(clazz)) { + try { + return (T) asArrayType(col, clazz); + } catch (GroovyCastException e) { + /* ignore */ + } + } + + Object[] args = {col}; + try { + return (T) InvokerHelper.invokeConstructorOf(clazz, args); + } catch (Exception e) { + // ignore, the constructor that takes a Collection as an argument may not exist + } + if (Collection.class.isAssignableFrom(clazz)) { + try { + Collection result = (Collection) InvokerHelper.invokeConstructorOf(clazz, null); + result.addAll(col); + return (T)result; + } catch (Exception e) { + // ignore, the no arg constructor might not exist. + } + } + + return asType((Object) col, clazz); + } + + /** + * Converts the given array to either a List, Set, or + * SortedSet. If the given class is something else, the + * call is deferred to {link #asType(Object,Class)}. + * + * @param ary an array + * @param clazz the desired class + * @return the object resulting from this type conversion + * @see #asType(java.lang.Object, java.lang.Class) + * @since 1.5.1 + */ + @SuppressWarnings("unchecked") + public static T asType(Object[] ary, Class clazz) { + if (clazz == List.class) { + return (T) new ArrayList(Arrays.asList(ary)); + } + if (clazz == Set.class) { + return (T) new HashSet(Arrays.asList(ary)); + } + if (clazz == SortedSet.class) { + return (T) new TreeSet(Arrays.asList(ary)); + } + + return asType((Object) ary, clazz); + } + + /** + * Coerces the closure to an implementation of the given class. The class + * is assumed to be an interface or class with a single method definition. + * The closure is used as the implementation of that single method. + * + * @param cl the implementation of the single method + * @param clazz the target type + * @return a Proxy of the given type which wraps this closure. + * @since 1.0 + */ + @SuppressWarnings("unchecked") + public static T asType(Closure cl, Class clazz) { + if (clazz.isInterface() && !(clazz.isInstance(cl))) { + return (T) Proxy.newProxyInstance( + clazz.getClassLoader(), + new Class[]{clazz}, + new ConvertedClosure(cl)); + } + try { + return asType((Object) cl, clazz); + } catch (GroovyCastException ce) { + try { + return (T) ProxyGenerator.INSTANCE.instantiateAggregateFromBaseClass(cl, clazz); + } catch (GroovyRuntimeException cause) { + throw new GroovyCastException("Error casting closure to " + clazz.getName() + + ", Reason: " + cause.getMessage()); + } + } + } + + /** + * Coerces this map to the given type, using the map's keys as the public + * method names, and values as the implementation. Typically the value + * would be a closure which behaves like the method implementation. + * + * @param map this map + * @param clazz the target type + * @return a Proxy of the given type, which defers calls to this map's elements. + * @since 1.0 + */ + @SuppressWarnings("unchecked") + public static T asType(Map map, Class clazz) { + if (!(clazz.isInstance(map)) && clazz.isInterface()) { + return (T) Proxy.newProxyInstance( + clazz.getClassLoader(), + new Class[]{clazz}, + new ConvertedMap(map)); + } + try { + return asType((Object) map, clazz); + } catch (GroovyCastException ce) { + try { + return (T) ProxyGenerator.INSTANCE.instantiateAggregateFromBaseClass(map, clazz); + } catch (GroovyRuntimeException cause) { + throw new GroovyCastException("Error casting map to " + clazz.getName() + + ", Reason: " + cause.getMessage()); + } + } + } + + /** + * Creates a new List with the identical contents to this list + * but in reverse order. + *
    +     * def list = ["a", 4, false]
    +     * assert list.reverse() == [false, 4, "a"]
    +     * assert list == ["a", 4, false]
    +     * 
    + * + * @param self a List + * @return a reversed List + * @see #reverse(List, boolean) + * @since 1.0 + */ + public static List reverse(List self) { + return reverse(self, false); + } + + /** + * Reverses the elements in a list. If mutate is true, the original list is modified in place and returned. + * Otherwise, a new list containing the reversed items is produced. + *
    +     * def list = ["a", 4, false]
    +     * assert list.reverse(false) == [false, 4, "a"]
    +     * assert list == ["a", 4, false]
    +     * assert list.reverse(true) == [false, 4, "a"]
    +     * assert list == [false, 4, "a"]
    +     * 
    + * + * @param self a List + * @param mutate true if the list itself should be reversed in place and returned, false if a new list should be created + * @return a reversed List + * @since 1.8.1 + */ + public static List reverse(List self, boolean mutate) { + if (mutate) { + Collections.reverse(self); + return self; + } + int size = self.size(); + List answer = new ArrayList(size); + ListIterator iter = self.listIterator(size); + while (iter.hasPrevious()) { + answer.add(iter.previous()); + } + return answer; + } + + /** + * Creates a new array containing items which are the same as this array but in reverse order. + * + * @param self an array + * @return an array containing the reversed items + * @see #reverse(Object[], boolean) + * @since 1.5.5 + */ + @SuppressWarnings("unchecked") + public static T[] reverse(T[] self) { + return reverse(self, false); + } + + /** + * Reverse the items in an array. If mutate is true, the original array is modified in place and returned. + * Otherwise, a new array containing the reversed items is produced. + * + * @param self an array + * @param mutate true if the array itself should be reversed in place and returned, false if a new array should be created + * @return an array containing the reversed items + * @since 1.8.1 + */ + @SuppressWarnings("unchecked") + public static T[] reverse(T[] self, boolean mutate) { + if (!mutate) { + return (T[]) toList(new ReverseListIterator(Arrays.asList(self))).toArray(); + } + List items = Arrays.asList(self); + Collections.reverse(items); + System.arraycopy((T[])items.toArray(), 0, self, 0, items.size()); + return self; + } + + /** + * Reverses the iterator. The original iterator will become + * exhausted of elements after determining the reversed values. + * A new iterator for iterating through the reversed values is returned. + * + * @param self an Iterator + * @return a reversed Iterator + * @since 1.5.5 + */ + public static Iterator reverse(Iterator self) { + return new ReverseListIterator(toList(self)); + } + + /** + * Create a Collection as a union of two collections. If the left collection + * is a Set, then the returned collection will be a Set otherwise a List. + * This operation will always create a new object for the result, + * while the operands remain unchanged. + *
    assert [1,2,3,4] == [1,2] + [3,4]
    + * + * @param left the left Collection + * @param right the right Collection + * @return the merged Collection + * @since 1.5.0 + */ + public static Collection plus(Collection left, Collection right) { + final Collection answer = cloneSimilarCollection(left, left.size() + right.size()); + answer.addAll(right); + return answer; + } + + /** + * Creates a new list by adding all of the elements in the specified array to the elements from the original list at the specified index. + * Shifts the element currently at that index (if any) and any subsequent + * elements to the right (increasing their indices). The new elements + * will appear in this list in the order that they occur in the array. + * The behavior of this operation is undefined if the specified array + * is modified while the operation is in progress. The original list remains unchanged. + * + *
    +     * def items = [1, 2, 3]
    +     * def newItems = items.plus(2, 'a'..'c' as String[])
    +     * assert newItems == [1, 2, 'a', 'b', 'c', 3]
    +     * assert items == [1, 2, 3]
    +     * 
    + * + * See also addAll for similar functionality with modify semantics, i.e. which performs + * the changes on the original list itself. + * + * @param self an original list + * @param items array containing elements to be merged with elements from the original list + * @param index index at which to insert the first element from the specified array + * @return the new list + * @see #plus(List, int, List) + * @since 1.8.1 + */ + public static List plus(List self, int index, T[] items) { + return plus(self, index, Arrays.asList(items)); + } + + /** + * Creates a new list by adding all of the elements in the specified list + * to the elements from this list at the specified index. + * Shifts the element currently at that index (if any) and any subsequent + * elements to the right (increasing their indices). The new elements + * will appear in this list in the order that they occur in the array. + * The behavior of this operation is undefined if the specified array + * is modified while the operation is in progress. The original list remains unchanged. + * + *
    +     * def items = [1, 2, 3]
    +     * def newItems = items.plus(2, 'a'..'c')
    +     * assert newItems == [1, 2, 'a', 'b', 'c', 3]
    +     * assert items == [1, 2, 3]
    +     * 
    + * + * See also addAll for similar functionality with modify semantics, i.e. which performs + * the changes on the original list itself. + * + * @param self an original list + * @param additions array containing elements to be merged with elements from the original list + * @param index index at which to insert the first element from the specified list + * @return the new list + * @since 1.8.1 + */ + public static List plus(List self, int index, List additions) { + final List answer = new ArrayList(self); + answer.addAll(index, additions); + return answer; + } + + /** + * Create a collection as a union of a Collection and an Object. If the collection + * is a Set, then the returned collection will be a Set otherwise a List. + * This operation will always create a new object for the result, + * while the operands remain unchanged. + *
    assert [1,2,3] == [1,2] + 3
    + * + * @param left a Collection + * @param right an object to add/append + * @return the resulting Collection + * @since 1.5.0 + */ + public static Collection plus(Collection left, T right) { + final Collection answer = cloneSimilarCollection(left, left.size() + 1); + answer.add(right); + return answer; + } + + /** + * Create a List composed of the elements of this list, repeated + * a certain number of times. Note that for non-primitive + * elements, multiple references to the same instance will be added. + *
    assert [1,2,3,1,2,3] == [1,2,3] * 2
    + * + * @param self a Collection + * @param factor the number of times to append + * @return the multiplied list + * @since 1.0 + */ + public static List multiply(Collection self, Number factor) { + int size = factor.intValue(); + List answer = new ArrayList(self.size() * size); + for (int i = 0; i < size; i++) { + answer.addAll(self); + } + return answer; + } + + /** + * Create a Collection composed of the intersection of both collections. Any + * elements that exist in both collections are added to the resultant collection. + *
    assert [4,5] == [1,2,3,4,5].intersect([4,5,6,7,8])
    + * + * @param left a Collection + * @param right a Collection + * @return a Collection as an intersection of both collections + * @since 1.5.6 + */ + public static Collection intersect(Collection left, Collection right) { + if (left.isEmpty()) + return createSimilarCollection(left, 0); + + if (left.size() < right.size()) { + Collection swaptemp = left; + left = right; + right = swaptemp; + } + + // TODO optimise if same type? + // boolean nlgnSort = sameType(new Collection[]{left, right}); + + Collection result = createSimilarCollection(left, left.size()); + //creates the collection to look for values. + Collection pickFrom = new TreeSet(new NumberAwareComparator()); + pickFrom.addAll(left); + + for (final T t : right) { + if (pickFrom.contains(t)) + result.add(t); + } + return result; + } + + /** + * Create a Map composed of the intersection of both maps. + * Any entries that exist in both maps are added to the resultant map. + *
    assert [4:4,5:5] == [1:1,2:2,3:3,4:4,5:5].intersect([4:4,5:5,6:6,7:7,8:8])
    + *
    assert [1: 1, 2: 2, 3: 3, 4: 4].intersect( [1: 1.0, 2: 2, 5: 5] ) == [1:1, 2:2]
    + * + * @param left a map + * @param right a map + * @return a Map as an intersection of both maps + * @since 1.7.4 + */ + public static Map intersect(Map left, Map right) { + final Map ansMap = createSimilarMap(left); + if (right != null && right.size() > 0) { + final Iterator> it1 = left.entrySet().iterator(); + while (it1.hasNext()) { + final Map.Entry e1 = it1.next(); + final Iterator> it2 = right.entrySet().iterator(); + while (it2.hasNext()) { + final Map.Entry e2 = it2.next(); + if (DefaultTypeTransformation.compareEqual(e1, e2)) { + ansMap.put(e1.getKey(), e1.getValue()); + } + } + } + } + return ansMap; + } + + /** + * Returns true if the intersection of two collections is empty. + *
    assert [1,2,3].disjoint([3,4,5]) == false
    + *
    assert [1,2].disjoint([3,4]) == true
    + * + * @param left a Collection + * @param right a Collection + * @return boolean true if the intersection of two collections + * is empty, false otherwise. + * @since 1.0 + */ + public static boolean disjoint(Collection left, Collection right) { + + if (left.isEmpty() || right.isEmpty()) + return true; + + Collection pickFrom = new TreeSet(new NumberAwareComparator()); + pickFrom.addAll(right); + + for (final Object o : left) { + if (pickFrom.contains(o)) + return false; + } + return true; + } + + /** + * Compare the contents of this array to the contents of the given array. + * + * @param left an int array + * @param right the operand array. + * @return true if the contents of both arrays are equal. + * @since 1.5.0 + */ + public static boolean equals(int[] left, int[] right) { + if (left == null) { + return right == null; + } + if (right == null) { + return false; + } + if (left == right) { + return true; + } + if (left.length != right.length) { + return false; + } + for (int i = 0; i < left.length; i++) { + if (left[i] != right[i]) return false; + } + return true; + } + + /** + * Determines if the contents of this array are equal to the + * contents of the given list, in the same order. This returns + * false if either collection is null. + * + * @param left this array + * @param right the list being compared + * @return true if the contents of both collections are equal + * @since 1.5.0 + */ + public static boolean equals(Object[] left, List right) { + return coercedEquals(left, right); + } + + /** + * Determines if the contents of this list are equal to the + * contents of the given array in the same order. This returns + * false if either collection is null. + *
    assert [1, "a"].equals( [ 1, "a" ] as Object[] )
    + * + * @param left this List + * @param right this Object[] being compared to + * @return true if the contents of both collections are equal + * @since 1.5.0 + */ + public static boolean equals(List left, Object[] right) { + return coercedEquals(right, left); + } + + private static boolean coercedEquals(Object[] left, List right) { + if (left == null) { + return right == null; + } + if (right == null) { + return false; + } + if (left.length != right.size()) { + return false; + } + for (int i = left.length - 1; i >= 0; i--) { + final Object o1 = left[i]; + final Object o2 = right.get(i); + if (o1 == null) { + if (o2 != null) return false; + } else if (!coercedEquals(o1, o2)) { + return false; + } + } + return true; + } + + private static boolean coercedEquals(Object o1, Object o2) { + if (o1 instanceof Comparable) { + if (!(o2 instanceof Comparable && numberAwareCompareTo((Comparable) o1, (Comparable) o2) == 0)) { + return false; + } + } + return DefaultTypeTransformation.compareEqual(o1, o2); + } + + /** + * Compare the contents of two Lists. Order matters. + * If numbers exist in the Lists, then they are compared as numbers, + * for example 2 == 2L. If both lists are null, the result + * is true; otherwise if either list is null, the result + * is false. + *
    assert ["a", 2].equals(["a", 2])
    +     * assert ![2, "a"].equals("a", 2)
    +     * assert [2.0, "a"].equals(2L, "a") // number comparison at work
    + * + * @param left this List + * @param right the List being compared to. + * @return boolean true if the contents of both lists are identical, + * false otherwise. + * @since 1.0 + */ + public static boolean equals(List left, List right) { + if (left == null) { + return right == null; + } + if (right == null) { + return false; + } + if (left == right) { + return true; + } + if (left.size() != right.size()) { + return false; + } + final Iterator it1 = left.iterator(), it2 = right.iterator(); + while (it1.hasNext()) { + final Object o1 = it1.next(); + final Object o2 = it2.next(); + if (o1 == null) { + if (o2 != null) return false; + } else if (!coercedEquals(o1, o2)) { + return false; + } + } + return true; + } + + /** + * Compare the contents of two Sets for equality using Groovy's coercion rules. + *

    + * Returns true if the two sets have the same size, and every member + * of the specified set is contained in this set (or equivalently, every member + * of this set is contained in the specified set). + * If numbers exist in the sets, then they are compared as numbers, + * for example 2 == 2L. If both sets are null, the result + * is true; otherwise if either set is null, the result + * is false. Example usage: + *

    +     * Set s1 = ["a", 2]
    +     * def s2 = [2, 'a'] as Set
    +     * Set s3 = [3, 'a']
    +     * def s4 = [2.0, 'a'] as Set
    +     * def s5 = [2L, 'a'] as Set
    +     * assert s1.equals(s2)
    +     * assert !s1.equals(s3)
    +     * assert s1.equals(s4)
    +     * assert s1.equals(s5)
    + * + * @param self this Set + * @param other the Set being compared to + * @return true if the contents of both sets are identical + * @since 1.8.0 + */ + public static boolean equals(Set self, Set other) { + if (self == null) { + return other == null; + } + if (other == null) { + return false; + } + if (self == other) { + return true; + } + if (self.size() != other.size()) { + return false; + } + final Iterator it1 = self.iterator(); + Collection otherItems = new HashSet(other); + while (it1.hasNext()) { + final Object o1 = it1.next(); + final Iterator it2 = otherItems.iterator(); + T foundItem = null; + boolean found = false; + while (it2.hasNext() && foundItem == null) { + final T o2 = it2.next(); + if (coercedEquals(o1, o2)) { + foundItem = o2; + found = true; + } + } + if (!found) return false; + otherItems.remove(foundItem); + } + return otherItems.size() == 0; + } + + /** + * Compares two Maps treating coerced numerical values as identical. + *

    + * Example usage: + *

    assert [a:2, b:3] == [a:2L, b:3.0]
    + * + * @param self this Map + * @param other the Map being compared to + * @return true if the contents of both maps are identical + * @since 1.8.0 + */ + public static boolean equals(Map self, Map other) { + if (self == null) { + return other == null; + } + if (other == null) { + return false; + } + if (self == other) { + return true; + } + if (self.size() != other.size()) { + return false; + } + if (!self.keySet().equals(other.keySet())) { + return false; + } + for (Object key : self.keySet()) { + if (!coercedEquals(self.get(key), other.get(key))) { + return false; + } + } + return true; + } + + /** + * Create a Set composed of the elements of the first set minus the + * elements of the given collection. + *

    + * + * @param self a set object + * @param operands the items to remove from the set + * @return the resulting set + * @since 1.5.0 + */ + public static Set minus(Set self, Collection operands) { + Comparator comparator = (self instanceof SortedSet) ? ((SortedSet) self).comparator() : null; + final Set ansSet = createSimilarSet(self); + ansSet.addAll(self); + if (operands != null && operands.size() > 0) { + final Iterator it1 = self.iterator(); + while (it1.hasNext()) { + final Object o1 = it1.next(); + final Iterator it2 = operands.iterator(); + while (it2.hasNext()) { + final Object o2 = it2.next(); + boolean areEqual = (comparator != null)? (comparator.compare(o1, o2) == 0) : coercedEquals(o1, o2); + if (areEqual) { + ansSet.remove(o1); + } + } + } + } + return ansSet; + } + + /** + * Create a Set composed of the elements of the first set minus the operand. + * + * @param self a set object + * @param operand the operand to remove from the set + * @return the resulting set + * @since 1.5.0 + */ + public static Set minus(Set self, Object operand) { + Comparator comparator = (self instanceof SortedSet) ? ((SortedSet) self).comparator() : null; + final Set ansSet = createSimilarSet(self); + for (T t : self) { + boolean areEqual = (comparator != null)? (comparator.compare(t, operand) == 0) : coercedEquals(t, operand); + if (!areEqual) ansSet.add(t); + } + return ansSet; + } + + /** + * Create an array composed of the elements of the first array minus the + * elements of the given collection. + * + * @param self an object array + * @param removeMe a Collection of elements to remove + * @return an array with the supplied elements removed + * @since 1.5.5 + */ + @SuppressWarnings("unchecked") + public static T[] minus(T[] self, Collection removeMe) { + return (T[]) minus(toList(self), removeMe).toArray(); + } + + /** + * Create an array composed of the elements of the first array minus the + * elements of the given array. + * + * @param self an object array + * @param removeMe an array of elements to remove + * @return an array with the supplied elements removed + * @since 1.5.5 + */ + @SuppressWarnings("unchecked") + public static T[] minus(T[] self, T[] removeMe) { + return (T[]) minus(toList(self), toList(removeMe)).toArray(); + } + + /** + * Create a List composed of the elements of the first list minus + * every occurrence of elements of the given collection. + *

    assert [1, "a", true, true, false, 5.3] - [true, 5.3] == [1, "a", false]
    + * + * @param self a List + * @param removeMe a Collection of elements to remove + * @return a List with the supplied elements removed + * @since 1.0 + */ + public static List minus(List self, Collection removeMe) { + + if (self.size() == 0) + return new ArrayList(); + + boolean nlgnSort = sameType(new Collection[]{self, removeMe}); + + // We can't use the same tactic as for intersection + // since AbstractCollection only does a remove on the first + // element it encounters. + + Comparator numberComparator = new NumberAwareComparator(); + + if (nlgnSort && (self.get(0) instanceof Comparable)) { + //n*LOG(n) version + Set answer; + if (Number.class.isInstance(self.get(0))) { + answer = new TreeSet(numberComparator); + answer.addAll(self); + for (T t : self) { + if (Number.class.isInstance(t)) { + for (T t2 : removeMe) { + if (Number.class.isInstance(t2)) { + if (numberComparator.compare(t, t2) == 0) + answer.remove(t); + } + } + } else { + if (removeMe.contains(t)) + answer.remove(t); + } + } + } else { + answer = new TreeSet(numberComparator); + answer.addAll(self); + answer.removeAll(removeMe); + } + + List ansList = new ArrayList(); + for (T o : self) { + if (answer.contains(o)) + ansList.add(o); + } + return ansList; + } else { + //n*n version + List tmpAnswer = new LinkedList(self); + for (Iterator iter = tmpAnswer.iterator(); iter.hasNext();) { + T element = iter.next(); + boolean elementRemoved = false; + for (Iterator iterator = removeMe.iterator(); iterator.hasNext() && !elementRemoved;) { + T elt = iterator.next(); + if (numberComparator.compare(element, elt) == 0) { + iter.remove(); + elementRemoved = true; + } + } + } + + //remove duplicates + //can't use treeset since the base classes are different + return new ArrayList(tmpAnswer); + } + } + + /** + * Create a new List composed of the elements of the first list minus every occurrence of the + * operand. + *
    assert ["a", 5, 5, true] - 5 == ["a", true]
    + * + * @param self a List object + * @param operand an element to remove from the list + * @return the resulting List with the operand removed + * @since 1.0 + */ + public static List minus(List self, Object operand) { + List ansList = new ArrayList(); + for (T t : self) { + if (!coercedEquals(t, operand)) ansList.add(t); + } + return ansList; + } + + /** + * Create a new object array composed of the elements of the first array + * minus the operand. + * + * @param self an object array + * @param operand an element to remove from the array + * @return a new array with the operand removed + * @since 1.5.5 + */ + public static T[] minus(T[] self, Object operand) { + return (T[]) minus(toList(self), operand).toArray(); + } + + /** + * Create a Map composed of the entries of the first map minus the + * entries of the given map. + * + * @param self a map object + * @param operands the entries to remove from the map + * @return the resulting map + * @since 1.7.4 + */ + public static Map minus(Map self, Map operands) { + final Map ansMap = createSimilarMap(self); + ansMap.putAll(self); + if (operands != null && operands.size() > 0) { + final Iterator> it1 = self.entrySet().iterator(); + while (it1.hasNext()) { + final Map.Entry e1 = it1.next(); + final Iterator> it2 = operands.entrySet().iterator(); + while (it2.hasNext()) { + final Map.Entry e2 = it2.next(); + if (DefaultTypeTransformation.compareEqual(e1, e2)) { + ansMap.remove(e1.getKey()); + } + } + } + } + return ansMap; + } + + /** + * Flatten a collection. This collection and any nested arrays or + * collections have their contents (recursively) added to the new collection. + *
    assert [1,2,3,4,5] == [1,[2,3],[[4]],[],5].flatten()
    + * + * @param self a Collection to flatten + * @return a flattened Collection + * @since 1.6.0 + */ + public static Collection flatten(Collection self) { + return flatten(self, createSimilarCollection(self)); + } + + /** + * Flatten an array. This array and any nested arrays or + * collections have their contents (recursively) added to the new collection. + * + * @param self an Array to flatten + * @return a flattened Collection + * @since 1.6.0 + */ + public static Collection flatten(Object[] self) { + return flatten(toList(self), new ArrayList()); + } + + /** + * Flatten an array. This array and any nested arrays or + * collections have their contents (recursively) added to the new collection. + * + * @param self a boolean Array to flatten + * @return a flattened Collection + * @since 1.6.0 + */ + public static Collection flatten(boolean[] self) { + return flatten(toList(self), new ArrayList()); + } + + /** + * Flatten an array. This array and any nested arrays or + * collections have their contents (recursively) added to the new collection. + * + * @param self a byte Array to flatten + * @return a flattened Collection + * @since 1.6.0 + */ + public static Collection flatten(byte[] self) { + return flatten(toList(self), new ArrayList()); + } + + /** + * Flatten an array. This array and any nested arrays or + * collections have their contents (recursively) added to the new collection. + * + * @param self a char Array to flatten + * @return a flattened Collection + * @since 1.6.0 + */ + public static Collection flatten(char[] self) { + return flatten(toList(self), new ArrayList()); + } + + /** + * Flatten an array. This array and any nested arrays or + * collections have their contents (recursively) added to the new collection. + * + * @param self a short Array to flatten + * @return a flattened Collection + * @since 1.6.0 + */ + public static Collection flatten(short[] self) { + return flatten(toList(self), new ArrayList()); + } + + /** + * Flatten an array. This array and any nested arrays or + * collections have their contents (recursively) added to the new collection. + * + * @param self an int Array to flatten + * @return a flattened Collection + * @since 1.6.0 + */ + public static Collection flatten(int[] self) { + return flatten(toList(self), new ArrayList()); + } + + /** + * Flatten an array. This array and any nested arrays or + * collections have their contents (recursively) added to the new collection. + * + * @param self a long Array to flatten + * @return a flattened Collection + * @since 1.6.0 + */ + public static Collection flatten(long[] self) { + return flatten(toList(self), new ArrayList()); + } + + /** + * Flatten an array. This array and any nested arrays or + * collections have their contents (recursively) added to the new collection. + * + * @param self a float Array to flatten + * @return a flattened Collection + * @since 1.6.0 + */ + public static Collection flatten(float[] self) { + return flatten(toList(self), new ArrayList()); + } + + /** + * Flatten an array. This array and any nested arrays or + * collections have their contents (recursively) added to the new collection. + * + * @param self a double Array to flatten + * @return a flattened Collection + * @since 1.6.0 + */ + public static Collection flatten(double[] self) { + return flatten(toList(self), new ArrayList()); + } + + private static Collection flatten(Collection elements, Collection addTo) { + for (Object element : elements) { + if (element instanceof Collection) { + flatten((Collection) element, addTo); + } else if (element != null && element.getClass().isArray()) { + flatten(DefaultTypeTransformation.arrayAsCollection(element), addTo); + } else { + // found a leaf + addTo.add(element); + } + } + return addTo; + } + + /** + * Flatten a collection. This collection and any nested arrays or + * collections have their contents (recursively) added to the new collection. + * For any non-Array, non-Collection object which represents some sort + * of collective type, the supplied closure should yield the contained items; + * otherwise, the closure should just return any element which corresponds to a leaf. + * + * @param self a Collection + * @param flattenUsing a closure to determine how to flatten non-Array, non-Collection elements + * @return a flattened Collection + * @since 1.6.0 + */ + public static Collection flatten(Collection self, Closure flattenUsing) { + return flatten(self, createSimilarCollection(self), flattenUsing); + } + + private static Collection flatten(Collection elements, Collection addTo, Closure flattenUsing) { + for (Object element : elements) { + if (element instanceof Collection) { + flatten((Collection) element, addTo, flattenUsing); + } else if (element != null && element.getClass().isArray()) { + flatten(DefaultTypeTransformation.arrayAsCollection(element), addTo, flattenUsing); + } else { + T flattened = flattenUsing.call(new Object[]{element}); + boolean returnedSelf = flattened == element; + if (!returnedSelf && flattened instanceof Collection) { + List list = toList((Collection) flattened); + if (list.size() == 1 && list.get(0) == element) { + returnedSelf = true; + } + } + if (flattened instanceof Collection && !returnedSelf) { + flatten((Collection) flattened, addTo, flattenUsing); + } else { + addTo.add(flattened); + } + } + } + return addTo; + } + + /** + * Overloads the left shift operator to provide an easy way to append + * objects to a Collection. + *
    def list = [1,2]
    +     * list << 3
    +     * assert list == [1,2,3]
    + * + * @param self a Collection + * @param value an Object to be added to the collection. + * @return same collection, after the value was added to it. + * @since 1.0 + */ + public static Collection leftShift(Collection self, T value) { + self.add(value); + return self; + } + + /** + * Overloads the left shift operator to provide an easy way to append + * objects to a BlockingQueue. + * In case of bounded queue the method will block till space in the queue become available + *
    def list = new java.util.concurrent.LinkedBlockingQueue ()
    +     * list << 3 << 2 << 1
    +     * assert list.iterator().collect{it} == [3,2,1]
    + * + * @param self a Collection + * @param value an Object to be added to the collection. + * @return same collection, after the value was added to it. + * @since 1.7.1 + */ + public static BlockingQueue leftShift(BlockingQueue self, T value) throws InterruptedException { + self.put(value); + return self; + } + + /** + * Overloads the left shift operator to provide an easy way to append + * Map.Entry values to a Map. + * + * @param self a Map + * @param entry a Map.Entry to be added to the Map. + * @return same map, after the value has been added to it. + * @since 1.6.0 + */ + public static Map leftShift(Map self, Map.Entry entry) { + self.put(entry.getKey(), entry.getValue()); + return self; + } + + /** + * Overloads the left shift operator to provide an easy way to put + * one maps entries into another map. This allows the compact syntax + * map1 << map2; otherwise it's just a synonym for + * putAll though it returns the original map rather than + * being a void method. Example usage: + *
    def map = [a:1, b:2]
    +     * map << [c:3, d:4]
    +     * assert map == [a:1, b:2, c:3, d:4]
    + * + * @param self a Map + * @param other another Map whose entries should be added to the original Map. + * @return same map, after the values have been added to it. + * @since 1.7.2 + */ + public static Map leftShift(Map self, Map other) { + self.putAll(other); + return self; + } + + /** + * Overloads the left shift operator to provide an easy way to append multiple + * objects as string representations to a String. + * + * @param self a String + * @param value an Object + * @return a StringBuffer built from this string + * @since 1.0 + */ + public static StringBuffer leftShift(String self, Object value) { + return new StringBuffer(self).append(value); + } + + /** + * Overloads the left shift operator to provide an easy way to append multiple + * objects as string representations to a CharSequence. + * + * @param self a CharSequence + * @param value an Object + * @return a StringBuilder built from this CharSequence + * @since 1.8.2 + */ + public static StringBuilder leftShift(CharSequence self, Object value) { + return new StringBuilder(self).append(value); + } + + /** + * Overloads the left shift operator to provide syntactic sugar for appending to a StringBuilder. + * + * @param self a StringBuilder + * @param value an Object + * @return the original StringBuilder + * @since 1.8.2 + */ + public static StringBuilder leftShift(StringBuilder self, Object value) { + self.append(value); + return self; + } + + protected static StringWriter createStringWriter(String self) { + StringWriter answer = new StringWriter(); + answer.write(self); + return answer; + } + + protected static StringBufferWriter createStringBufferWriter(StringBuffer self) { + return new StringBufferWriter(self); + } + + /** + * Overloads the left shift operator to provide an easy way to append multiple + * objects as string representations to a StringBuffer. + * + * @param self a StringBuffer + * @param value a value to append + * @return the StringBuffer on which this operation was invoked + * @since 1.0 + */ + public static StringBuffer leftShift(StringBuffer self, Object value) { + self.append(value); + return self; + } + + /** + * Overloads the left shift operator to provide a mechanism to append + * values to a writer. + * + * @param self a Writer + * @param value a value to append + * @return the writer on which this operation was invoked + * @throws IOException if an I/O error occurs. + * @since 1.0 + */ + public static Writer leftShift(Writer self, Object value) throws IOException { + InvokerHelper.write(self, value); + return self; + } + + /** + * Implementation of the left shift operator for integral types. Non integral + * Number types throw UnsupportedOperationException. + * + * @param self a Number object + * @param operand the shift distance by which to left shift the number + * @return the resulting number + * @since 1.5.0 + */ + public static Number leftShift(Number self, Number operand) { + return NumberMath.leftShift(self, operand); + } + + /** + * Implementation of the right shift operator for integral types. Non integral + * Number types throw UnsupportedOperationException. + * + * @param self a Number object + * @param operand the shift distance by which to right shift the number + * @return the resulting number + * @since 1.5.0 + */ + public static Number rightShift(Number self, Number operand) { + return NumberMath.rightShift(self, operand); + } + + /** + * Implementation of the right shift (unsigned) operator for integral types. Non integral + * Number types throw UnsupportedOperationException. + * + * @param self a Number object + * @param operand the shift distance by which to right shift (unsigned) the number + * @return the resulting number + * @since 1.5.0 + */ + public static Number rightShiftUnsigned(Number self, Number operand) { + return NumberMath.rightShiftUnsigned(self, operand); + } + + /** + * A helper method so that dynamic dispatch of the writer.write(object) method + * will always use the more efficient Writable.writeTo(writer) mechanism if the + * object implements the Writable interface. + * + * @param self a Writer + * @param writable an object implementing the Writable interface + * @throws IOException if an I/O error occurs. + * @since 1.0 + */ + public static void write(Writer self, Writable writable) throws IOException { + writable.writeTo(self); + } + + /** + * Overloads the leftShift operator to provide an append mechanism to add values to a stream. + * + * @param self an OutputStream + * @param value a value to append + * @return a Writer + * @throws IOException if an I/O error occurs. + * @since 1.0 + */ + public static Writer leftShift(OutputStream self, Object value) throws IOException { + OutputStreamWriter writer = new FlushingStreamWriter(self); + leftShift(writer, value); + return writer; + } + + /** + * Overloads the leftShift operator to add objects to an ObjectOutputStream. + * + * @param self an ObjectOutputStream + * @param value an object to write to the stream + * @throws IOException if an I/O error occurs. + * @since 1.5.0 + */ + public static void leftShift(ObjectOutputStream self, Object value) throws IOException { + self.writeObject(value); + } + + /** + * Pipe an InputStream into an OutputStream for efficient stream copying. + * + * @param self stream on which to write + * @param in stream to read from + * @return the outputstream itself + * @throws IOException if an I/O error occurs. + * @since 1.0 + */ + public static OutputStream leftShift(OutputStream self, InputStream in) throws IOException { + byte[] buf = new byte[1024]; + while (true) { + int count = in.read(buf, 0, buf.length); + if (count == -1) break; + if (count == 0) { + Thread.yield(); + continue; + } + self.write(buf, 0, count); + } + self.flush(); + return self; + } + + /** + * Overloads the leftShift operator to provide an append mechanism to add bytes to a stream. + * + * @param self an OutputStream + * @param value a value to append + * @return an OutputStream + * @throws IOException if an I/O error occurs. + * @since 1.0 + */ + public static OutputStream leftShift(OutputStream self, byte[] value) throws IOException { + self.write(value); + self.flush(); + return self; + } + + // Primitive type array methods + //------------------------------------------------------------------------- + + /** + * Support the subscript operator with a range for a byte array + * + * @param array a byte array + * @param range a range indicating the indices for the items to retrieve + * @return list of the retrieved bytes + * @since 1.0 + */ + @SuppressWarnings("unchecked") + public static List getAt(byte[] array, Range range) { + return primitiveArrayGet(array, range); + } + + /** + * Support the subscript operator with a range for a char array + * + * @param array a char array + * @param range a range indicating the indices for the items to retrieve + * @return list of the retrieved chars + * @since 1.5.0 + */ + @SuppressWarnings("unchecked") + public static List getAt(char[] array, Range range) { + return primitiveArrayGet(array, range); + } + + /** + * Support the subscript operator with a range for a short array + * + * @param array a short array + * @param range a range indicating the indices for the items to retrieve + * @return list of the retrieved shorts + * @since 1.0 + */ + @SuppressWarnings("unchecked") + public static List getAt(short[] array, Range range) { + return primitiveArrayGet(array, range); + } + + /** + * Support the subscript operator with a range for an int array + * + * @param array an int array + * @param range a range indicating the indices for the items to retrieve + * @return list of the ints at the given indices + * @since 1.0 + */ + @SuppressWarnings("unchecked") + public static List getAt(int[] array, Range range) { + return primitiveArrayGet(array, range); + } + + /** + * Support the subscript operator with a range for a long array + * + * @param array a long array + * @param range a range indicating the indices for the items to retrieve + * @return list of the retrieved longs + * @since 1.0 + */ + @SuppressWarnings("unchecked") + public static List getAt(long[] array, Range range) { + return primitiveArrayGet(array, range); + } + + /** + * Support the subscript operator with a range for a float array + * + * @param array a float array + * @param range a range indicating the indices for the items to retrieve + * @return list of the retrieved floats + * @since 1.0 + */ + @SuppressWarnings("unchecked") + public static List getAt(float[] array, Range range) { + return primitiveArrayGet(array, range); + } + + /** + * Support the subscript operator with a range for a double array + * + * @param array a double array + * @param range a range indicating the indices for the items to retrieve + * @return list of the retrieved doubles + * @since 1.0 + */ + @SuppressWarnings("unchecked") + public static List getAt(double[] array, Range range) { + return primitiveArrayGet(array, range); + } + + /** + * Support the subscript operator with a range for a boolean array + * + * @param array a boolean array + * @param range a range indicating the indices for the items to retrieve + * @return list of the retrieved booleans + * @since 1.0 + */ + @SuppressWarnings("unchecked") + public static List getAt(boolean[] array, Range range) { + return primitiveArrayGet(array, range); + } + + /** + * Support the subscript operator with an IntRange for a byte array + * + * @param array a byte array + * @param range an IntRange indicating the indices for the items to retrieve + * @return list of the retrieved bytes + * @since 1.0 + */ + @SuppressWarnings("unchecked") + public static List getAt(byte[] array, IntRange range) { + return primitiveArrayGet(array, range); + } + + /** + * Support the subscript operator with an IntRange for a char array + * + * @param array a char array + * @param range an IntRange indicating the indices for the items to retrieve + * @return list of the retrieved chars + * @since 1.0 + */ + @SuppressWarnings("unchecked") + public static List getAt(char[] array, IntRange range) { + return primitiveArrayGet(array, range); + } + + /** + * Support the subscript operator with an IntRange for a short array + * + * @param array a short array + * @param range an IntRange indicating the indices for the items to retrieve + * @return list of the retrieved shorts + * @since 1.0 + */ + @SuppressWarnings("unchecked") + public static List getAt(short[] array, IntRange range) { + return primitiveArrayGet(array, range); + } + + /** + * Support the subscript operator with an IntRange for an int array + * + * @param array an int array + * @param range an IntRange indicating the indices for the items to retrieve + * @return list of the retrieved ints + * @since 1.0 + */ + @SuppressWarnings("unchecked") + public static List getAt(int[] array, IntRange range) { + return primitiveArrayGet(array, range); + } + + /** + * Support the subscript operator with an IntRange for a long array + * + * @param array a long array + * @param range an IntRange indicating the indices for the items to retrieve + * @return list of the retrieved longs + * @since 1.0 + */ + @SuppressWarnings("unchecked") + public static List getAt(long[] array, IntRange range) { + return primitiveArrayGet(array, range); + } + + /** + * Support the subscript operator with an IntRange for a float array + * + * @param array a float array + * @param range an IntRange indicating the indices for the items to retrieve + * @return list of the retrieved floats + * @since 1.0 + */ + @SuppressWarnings("unchecked") + public static List getAt(float[] array, IntRange range) { + return primitiveArrayGet(array, range); + } + + /** + * Support the subscript operator with an IntRange for a double array + * + * @param array a double array + * @param range an IntRange indicating the indices for the items to retrieve + * @return list of the retrieved doubles + * @since 1.0 + */ + @SuppressWarnings("unchecked") + public static List getAt(double[] array, IntRange range) { + return primitiveArrayGet(array, range); + } + + /** + * Support the subscript operator with an IntRange for a boolean array + * + * @param array a boolean array + * @param range an IntRange indicating the indices for the items to retrieve + * @return list of the retrieved booleans + * @since 1.0 + */ + @SuppressWarnings("unchecked") + public static List getAt(boolean[] array, IntRange range) { + return primitiveArrayGet(array, range); + } + + /** + * Support the subscript operator with an ObjectRange for a byte array + * + * @param array a byte array + * @param range an ObjectRange indicating the indices for the items to retrieve + * @return list of the retrieved bytes + * @since 1.0 + */ + @SuppressWarnings("unchecked") + public static List getAt(byte[] array, ObjectRange range) { + return primitiveArrayGet(array, range); + } + + /** + * Support the subscript operator with an ObjectRange for a char array + * + * @param array a char array + * @param range an ObjectRange indicating the indices for the items to retrieve + * @return list of the retrieved chars + * @since 1.0 + */ + @SuppressWarnings("unchecked") + public static List getAt(char[] array, ObjectRange range) { + return primitiveArrayGet(array, range); + } + + /** + * Support the subscript operator with an ObjectRange for a short array + * + * @param array a short array + * @param range an ObjectRange indicating the indices for the items to retrieve + * @return list of the retrieved shorts + * @since 1.0 + */ + @SuppressWarnings("unchecked") + public static List getAt(short[] array, ObjectRange range) { + return primitiveArrayGet(array, range); + } + + /** + * Support the subscript operator with an ObjectRange for an int array + * + * @param array an int array + * @param range an ObjectRange indicating the indices for the items to retrieve + * @return list of the retrieved ints + * @since 1.0 + */ + @SuppressWarnings("unchecked") + public static List getAt(int[] array, ObjectRange range) { + return primitiveArrayGet(array, range); + } + + /** + * Support the subscript operator with an ObjectRange for a long array + * + * @param array a long array + * @param range an ObjectRange indicating the indices for the items to retrieve + * @return list of the retrieved longs + * @since 1.0 + */ + @SuppressWarnings("unchecked") + public static List getAt(long[] array, ObjectRange range) { + return primitiveArrayGet(array, range); + } + + /** + * Support the subscript operator with an ObjectRange for a float array + * + * @param array a float array + * @param range an ObjectRange indicating the indices for the items to retrieve + * @return list of the retrieved floats + * @since 1.0 + */ + @SuppressWarnings("unchecked") + public static List getAt(float[] array, ObjectRange range) { + return primitiveArrayGet(array, range); + } + + /** + * Support the subscript operator with an ObjectRange for a double array + * + * @param array a double array + * @param range an ObjectRange indicating the indices for the items to retrieve + * @return list of the retrieved doubles + * @since 1.0 + */ + @SuppressWarnings("unchecked") + public static List getAt(double[] array, ObjectRange range) { + return primitiveArrayGet(array, range); + } + + /** + * Support the subscript operator with an ObjectRange for a byte array + * + * @param array a byte array + * @param range an ObjectRange indicating the indices for the items to retrieve + * @return list of the retrieved bytes + * @since 1.0 + */ + @SuppressWarnings("unchecked") + public static List getAt(boolean[] array, ObjectRange range) { + return primitiveArrayGet(array, range); + } + + /** + * Support the subscript operator with a collection for a byte array + * + * @param array a byte array + * @param indices a collection of indices for the items to retrieve + * @return list of the bytes at the given indices + * @since 1.0 + */ + @SuppressWarnings("unchecked") + public static List getAt(byte[] array, Collection indices) { + return primitiveArrayGet(array, indices); + } + + /** + * Support the subscript operator with a collection for a char array + * + * @param array a char array + * @param indices a collection of indices for the items to retrieve + * @return list of the chars at the given indices + * @since 1.0 + */ + @SuppressWarnings("unchecked") + public static List getAt(char[] array, Collection indices) { + return primitiveArrayGet(array, indices); + } + + /** + * Support the subscript operator with a collection for a short array + * + * @param array a short array + * @param indices a collection of indices for the items to retrieve + * @return list of the shorts at the given indices + * @since 1.0 + */ + @SuppressWarnings("unchecked") + public static List getAt(short[] array, Collection indices) { + return primitiveArrayGet(array, indices); + } + + /** + * Support the subscript operator with a collection for an int array + * + * @param array an int array + * @param indices a collection of indices for the items to retrieve + * @return list of the ints at the given indices + * @since 1.0 + */ + @SuppressWarnings("unchecked") + public static List getAt(int[] array, Collection indices) { + return primitiveArrayGet(array, indices); + } + + /** + * Support the subscript operator with a collection for a long array + * + * @param array a long array + * @param indices a collection of indices for the items to retrieve + * @return list of the longs at the given indices + * @since 1.0 + */ + @SuppressWarnings("unchecked") + public static List getAt(long[] array, Collection indices) { + return primitiveArrayGet(array, indices); + } + + /** + * Support the subscript operator with a collection for a float array + * + * @param array a float array + * @param indices a collection of indices for the items to retrieve + * @return list of the floats at the given indices + * @since 1.0 + */ + @SuppressWarnings("unchecked") + public static List getAt(float[] array, Collection indices) { + return primitiveArrayGet(array, indices); + } + + /** + * Support the subscript operator with a collection for a double array + * + * @param array a double array + * @param indices a collection of indices for the items to retrieve + * @return list of the doubles at the given indices + * @since 1.0 + */ + @SuppressWarnings("unchecked") + public static List getAt(double[] array, Collection indices) { + return primitiveArrayGet(array, indices); + } + + /** + * Support the subscript operator with a collection for a boolean array + * + * @param array a boolean array + * @param indices a collection of indices for the items to retrieve + * @return list of the booleans at the given indices + * @since 1.0 + */ + @SuppressWarnings("unchecked") + public static List getAt(boolean[] array, Collection indices) { + return primitiveArrayGet(array, indices); + } + + /** + * Support the subscript operator for a Bitset + * + * @param self a BitSet + * @param index index to retrieve + * @return value of the bit at the given index + * @see java.util.BitSet + * @since 1.5.0 + */ + public static boolean getAt(BitSet self, int index) { + return self.get(index); + } + + /** + * Support retrieving a subset of a BitSet using a Range + * + * @param self a BitSet + * @param range a Range defining the desired subset + * @return a new BitSet that represents the requested subset + * @see java.util.BitSet + * @see groovy.lang.IntRange + * @since 1.5.0 + */ + public static BitSet getAt(BitSet self, IntRange range) { + int from = DefaultTypeTransformation.intUnbox(range.getFrom()); + int to = DefaultTypeTransformation.intUnbox(range.getTo()); + + BitSet result = new BitSet(); + + int numberOfBits = to - from + 1; + int adjuster = 1; + int offset = from; + + if (range.isReverse()) { + adjuster = -1; + offset = to; + } + + for (int i = 0; i < numberOfBits; i++) { + result.set(i, self.get(offset + (adjuster * i))); + } + + return result; + } + +// public static Boolean putAt(boolean[] array, int idx, Boolean newValue) { +// return (Boolean) primitiveArrayPut(array, idx, newValue); +// } +// +// public static Byte putAt(byte[] array, int idx, Object newValue) { +// if (!(newValue instanceof Byte)) { +// Number n = (Number) newValue; +// newValue = new Byte(n.byteValue()); +// } +// return (Byte) primitiveArrayPut(array, idx, newValue); +// } +// +// public static Character putAt(char[] array, int idx, Object newValue) { +// if (newValue instanceof String) { +// String s = (String) newValue; +// if (s.length() != 1) throw new IllegalArgumentException("String of length 1 expected but got a bigger one"); +// char c = s.charAt(0); +// newValue = new Character(c); +// } +// return (Character) primitiveArrayPut(array, idx, newValue); +// } +// +// public static Short putAt(short[] array, int idx, Object newValue) { +// if (!(newValue instanceof Short)) { +// Number n = (Number) newValue; +// newValue = new Short(n.shortValue()); +// } +// return (Short) primitiveArrayPut(array, idx, newValue); +// } +// +// public static Integer putAt(int[] array, int idx, Object newValue) { +// if (!(newValue instanceof Integer)) { +// Number n = (Number) newValue; +// newValue = Integer.valueOf(n.intValue()); +// } +// array [normaliseIndex(idx,array.length)] = ((Integer)newValue).intValue(); +// return (Integer) newValue; +// } +// +// public static Long putAt(long[] array, int idx, Object newValue) { +// if (!(newValue instanceof Long)) { +// Number n = (Number) newValue; +// newValue = new Long(n.longValue()); +// } +// return (Long) primitiveArrayPut(array, idx, newValue); +// } +// +// public static Float putAt(float[] array, int idx, Object newValue) { +// if (!(newValue instanceof Float)) { +// Number n = (Number) newValue; +// newValue = new Float(n.floatValue()); +// } +// return (Float) primitiveArrayPut(array, idx, newValue); +// } +// +// public static Double putAt(double[] array, int idx, Object newValue) { +// if (!(newValue instanceof Double)) { +// Number n = (Number) newValue; +// newValue = new Double(n.doubleValue()); +// } +// return (Double) primitiveArrayPut(array, idx, newValue); +// } + + /** + * Support assigning a range of values with a single assignment statement. + * + * @param self a BitSet + * @param range the range of values to set + * @param value value + * @see java.util.BitSet + * @see groovy.lang.Range + * @since 1.5.0 + */ + public static void putAt(BitSet self, IntRange range, boolean value) { + int from = DefaultTypeTransformation.intUnbox(range.getFrom()); + int to = DefaultTypeTransformation.intUnbox(range.getTo()); + + // If this is a backwards range, reverse the arguments to set. + if (from > to) { + int tmp = to; + to = from; + from = tmp; + } + self.set(from, to + 1, value); + } + + /** + * Support subscript-style assignment for a BitSet. + * + * @param self a BitSet + * @param index index of the entry to set + * @param value value + * @see java.util.BitSet + * @since 1.5.0 + */ + public static void putAt(BitSet self, int index, boolean value) { + self.set(index, value); + } + + /** + * Allows arrays to behave similar to collections. + * @param array a boolean array + * @return the length of the array + * @see java.lang.reflect.Array#getLength(java.lang.Object) + * @since 1.5.0 + */ + public static int size(boolean[] array) { + return Array.getLength(array); + } + + /** + * Allows arrays to behave similar to collections. + * @param array a byte array + * @return the length of the array + * @see java.lang.reflect.Array#getLength(java.lang.Object) + * @since 1.0 + */ + public static int size(byte[] array) { + return Array.getLength(array); + } + + /** + * Allows arrays to behave similar to collections. + * @param array a char array + * @return the length of the array + * @see java.lang.reflect.Array#getLength(java.lang.Object) + * @since 1.0 + */ + public static int size(char[] array) { + return Array.getLength(array); + } + + /** + * Allows arrays to behave similar to collections. + * @param array a short array + * @return the length of the array + * @see java.lang.reflect.Array#getLength(java.lang.Object) + * @since 1.0 + */ + public static int size(short[] array) { + return Array.getLength(array); + } + + /** + * Allows arrays to behave similar to collections. + * @param array an int array + * @return the length of the array + * @see java.lang.reflect.Array#getLength(java.lang.Object) + * @since 1.0 + */ + public static int size(int[] array) { + return Array.getLength(array); + } + + /** + * Allows arrays to behave similar to collections. + * @param array a long array + * @return the length of the array + * @see java.lang.reflect.Array#getLength(java.lang.Object) + * @since 1.0 + */ + public static int size(long[] array) { + return Array.getLength(array); + } + + /** + * Allows arrays to behave similar to collections. + * @param array a float array + * @return the length of the array + * @see java.lang.reflect.Array#getLength(java.lang.Object) + * @since 1.0 + */ + public static int size(float[] array) { + return Array.getLength(array); + } + + /** + * Allows arrays to behave similar to collections. + * @param array a double array + * @return the length of the array + * @see java.lang.reflect.Array#getLength(java.lang.Object) + * @since 1.0 + */ + public static int size(double[] array) { + return Array.getLength(array); + } + + /** + * Converts this array to a List of the same size, with each element + * added to the list. + * + * @param array a byte array + * @return a list containing the contents of this array. + * @since 1.0 + */ + @SuppressWarnings("unchecked") + public static List toList(byte[] array) { + return DefaultTypeTransformation.primitiveArrayToList(array); + } + + /** + * Converts this array to a List of the same size, with each element + * added to the list. + * + * @param array a boolean array + * @return a list containing the contents of this array. + * @since 1.6.0 + */ + @SuppressWarnings("unchecked") + public static List toList(boolean[] array) { + return DefaultTypeTransformation.primitiveArrayToList(array); + } + + /** + * Converts this array to a List of the same size, with each element + * added to the list. + * + * @param array a char array + * @return a list containing the contents of this array. + * @since 1.0 + */ + @SuppressWarnings("unchecked") + public static List toList(char[] array) { + return DefaultTypeTransformation.primitiveArrayToList(array); + } + + /** + * Converts this array to a List of the same size, with each element + * added to the list. + * + * @param array a short array + * @return a list containing the contents of this array. + * @since 1.0 + */ + @SuppressWarnings("unchecked") + public static List toList(short[] array) { + return DefaultTypeTransformation.primitiveArrayToList(array); + } + + /** + * Converts this array to a List of the same size, with each element + * added to the list. + * + * @param array an int array + * @return a list containing the contents of this array. + * @since 1.0 + */ + @SuppressWarnings("unchecked") + public static List toList(int[] array) { + return DefaultTypeTransformation.primitiveArrayToList(array); + } + + /** + * Converts this array to a List of the same size, with each element + * added to the list. + * + * @param array a long array + * @return a list containing the contents of this array. + * @since 1.0 + */ + @SuppressWarnings("unchecked") + public static List toList(long[] array) { + return DefaultTypeTransformation.primitiveArrayToList(array); + } + + /** + * Converts this array to a List of the same size, with each element + * added to the list. + * + * @param array a float array + * @return a list containing the contents of this array. + * @since 1.0 + */ + @SuppressWarnings("unchecked") + public static List toList(float[] array) { + return DefaultTypeTransformation.primitiveArrayToList(array); + } + + /** + * Converts this array to a List of the same size, with each element + * added to the list. + * + * @param array a double array + * @return a list containing the contents of this array. + * @since 1.0 + */ + @SuppressWarnings("unchecked") + public static List toList(double[] array) { + return DefaultTypeTransformation.primitiveArrayToList(array); + } + + /** + * Converts this array to a Set, with each unique element + * added to the set. + * + * @param array a byte array + * @return a set containing the unique contents of this array. + * @since 1.8.0 + */ + @SuppressWarnings("unchecked") + public static Set toSet(byte[] array) { + return toSet(DefaultTypeTransformation.primitiveArrayToList(array)); + } + + /** + * Converts this array to a Set, with each unique element + * added to the set. + * + * @param array a boolean array + * @return a set containing the unique contents of this array. + * @since 1.8.0 + */ + @SuppressWarnings("unchecked") + public static Set toSet(boolean[] array) { + return toSet(DefaultTypeTransformation.primitiveArrayToList(array)); + } + + /** + * Converts this array to a Set, with each unique element + * added to the set. + * + * @param array a char array + * @return a set containing the unique contents of this array. + * @since 1.8.0 + */ + @SuppressWarnings("unchecked") + public static Set toSet(char[] array) { + return toSet(DefaultTypeTransformation.primitiveArrayToList(array)); + } + + /** + * Converts this array to a Set, with each unique element + * added to the set. + * + * @param array a short array + * @return a set containing the unique contents of this array. + * @since 1.8.0 + */ + @SuppressWarnings("unchecked") + public static Set toSet(short[] array) { + return toSet(DefaultTypeTransformation.primitiveArrayToList(array)); + } + + /** + * Converts this array to a Set, with each unique element + * added to the set. + * + * @param array an int array + * @return a set containing the unique contents of this array. + * @since 1.8.0 + */ + @SuppressWarnings("unchecked") + public static Set toSet(int[] array) { + return toSet(DefaultTypeTransformation.primitiveArrayToList(array)); + } + + /** + * Converts this array to a Set, with each unique element + * added to the set. + * + * @param array a long array + * @return a set containing the unique contents of this array. + * @since 1.8.0 + */ + @SuppressWarnings("unchecked") + public static Set toSet(long[] array) { + return toSet(DefaultTypeTransformation.primitiveArrayToList(array)); + } + + /** + * Converts this array to a Set, with each unique element + * added to the set. + * + * @param array a float array + * @return a set containing the unique contents of this array. + * @since 1.8.0 + */ + @SuppressWarnings("unchecked") + public static Set toSet(float[] array) { + return toSet(DefaultTypeTransformation.primitiveArrayToList(array)); + } + + /** + * Converts this array to a Set, with each unique element + * added to the set. + * + * @param array a double array + * @return a set containing the unique contents of this array. + * @since 1.8.0 + */ + @SuppressWarnings("unchecked") + public static Set toSet(double[] array) { + return toSet(DefaultTypeTransformation.primitiveArrayToList(array)); + } + + /** + * Convert a Collection to a Set. Always returns a new Set + * even if the Collection is already a Set. + *

    + * Example usage: + *

    +     * def result = [1, 2, 2, 2, 3].toSet()
    +     * assert result instanceof Set
    +     * assert result == [1, 2, 3] as Set
    +     * 
    + * + * @param self a collection + * @return a Set + * @since 1.8.0 + */ + public static Set toSet(Collection self) { + Set answer = new HashSet(self.size()); + answer.addAll(self); + return answer; + } + + /** + * Convert an iterator to a Set. The iterator will become + * exhausted of elements after making this conversion. + * + * @param self an iterator + * @return a Set + * @since 1.8.0 + */ + public static Set toSet(Iterator self) { + Set answer = new HashSet(); + while (self.hasNext()) { + answer.add(self.next()); + } + return answer; + } + + /** + * Convert an enumeration to a Set. + * + * @param self an enumeration + * @return a Set + * @since 1.8.0 + */ + public static Set toSet(Enumeration self) { + Set answer = new HashSet(); + while (self.hasMoreElements()) { + answer.add(self.nextElement()); + } + return answer; + } + + /** + * Implements the getAt(int) method for primitive type arrays. + * + * @param self an array object + * @param idx the index of interest + * @return the returned value from the array + * @since 1.5.0 + */ + protected static Object primitiveArrayGet(Object self, int idx) { + return Array.get(self, normaliseIndex(idx, Array.getLength(self))); + } + + /** + * Implements the getAt(Range) method for primitive type arrays. + * + * @param self an array object + * @param range the range of indices of interest + * @return the returned values from the array corresponding to the range + * @since 1.5.0 + */ + protected static List primitiveArrayGet(Object self, Range range) { + List answer = new ArrayList(); + for (Object next : range) { + int idx = DefaultTypeTransformation.intUnbox(next); + answer.add(primitiveArrayGet(self, idx)); + } + return answer; + } + + /** + * Implements the getAt(Collection) method for primitive type arrays. Each + * value in the collection argument is assumed to be a valid array index. + * The value at each index is then added to a list which is returned. + * + * @param self an array object + * @param indices the indices of interest + * @return the returned values from the array + * @since 1.0 + */ + protected static List primitiveArrayGet(Object self, Collection indices) { + List answer = new ArrayList(); + for (Object value : indices) { + if (value instanceof Range) { + answer.addAll(primitiveArrayGet(self, (Range) value)); + } else if (value instanceof List) { + answer.addAll(primitiveArrayGet(self, (List) value)); + } else { + int idx = DefaultTypeTransformation.intUnbox(value); + answer.add(primitiveArrayGet(self, idx)); + } + } + return answer; + } + + /** + * Implements the setAt(int idx) method for primitive type arrays. + * + * @param self an object + * @param idx the index of interest + * @param newValue the new value to be put into the index of interest + * @return the added value + * @since 1.5.0 + */ + protected static Object primitiveArrayPut(Object self, int idx, Object newValue) { + Array.set(self, normaliseIndex(idx, Array.getLength(self)), newValue); + return newValue; + } + + // String methods + //------------------------------------------------------------------------- + + /** + * Converts the given string into a Character object + * using the first character in the string. + * + * @param self a String + * @return the first Character + * @since 1.0 + */ + public static Character toCharacter(String self) { + return self.charAt(0); + } + + /** + * Converts the given string into a Boolean object. + * If the trimmed string is "true", "y" or "1" (ignoring case) + * then the result is true otherwise it is false. + * + * @param self a String + * @return The Boolean value + * @since 1.0 + */ + public static Boolean toBoolean(String self) { + final String trimmed = self.trim(); + + if ("true".equalsIgnoreCase(trimmed) || "y".equalsIgnoreCase(trimmed) || "1".equals(trimmed)) { + return Boolean.TRUE; + } else { + return Boolean.FALSE; + } + } + + /** + * Identity conversion which returns Boolean.TRUE for a true Boolean and Boolean.FALSE for a false Boolean. + * + * @param self a Boolean + * @return the original Boolean + * @since 1.7.6 + */ + public static Boolean toBoolean(Boolean self) { + return self; + } + + /** + * Convenience method to split a string (with whitespace as delimiter) + * Like tokenize, but returns an Array of Strings instead of a List + * + * @param self the string to split + * @return String[] result of split + * @since 1.5.0 + */ + public static String[] split(String self) { + StringTokenizer st = new StringTokenizer(self); + String[] strings = new String[st.countTokens()]; + for (int i = 0; i < strings.length; i++) { + strings[i] = st.nextToken(); + } + return strings; + } + + /** + * Convenience method to split a CharSequence (with whitespace as delimiter). + * Similar to tokenize, but returns an Array of CharSequence instead of a List. + * + * @param self the CharSequence to split + * @return CharSequence[] result of split + * @see #split(String) + * @since 1.8.2 + */ + public static CharSequence[] split(CharSequence self) { + return split(self.toString()); + } + + /** + * Convenience method to capitalize the first letter of a string + * (typically the first letter of a word). Example usage: + *
    +     * assert 'h'.capitalize() == 'H'
    +     * assert 'hello'.capitalize() == 'Hello'
    +     * assert 'hello world'.capitalize() == 'Hello world'
    +     * assert 'Hello World' ==
    +     *     'hello world'.split(' ').collect{ it.capitalize() }.join(' ')
    +     * 
    + * + * @param self The string to capitalize + * @return The capitalized String + * @since 1.7.3 + */ + public static String capitalize(String self) { + if (self == null || self.length() == 0) return self; + return Character.toUpperCase(self.charAt(0)) + self.substring(1); + } + + /** + * Convenience method to capitalize the first letter of a CharSequence. + * + * @param self The CharSequence to capitalize + * @return The capitalized CharSequence + * @see #capitalize(String) + * @since 1.8.2 + */ + public static CharSequence capitalize(CharSequence self) { + return capitalize(self.toString()); + } + + /** + * Expands all tabs into spaces with tabStops of size 8. + * + * @param self A String to expand + * @return The expanded String + * @since 1.7.3 + * @see #expand(java.lang.String, int) + */ + public static String expand(String self) { + return expand(self, 8); + } + + /** + * Expands all tabs into spaces with tabStops of size 8. + * + * @param self A CharSequence to expand + * @return The expanded CharSequence + * @see #expand(java.lang.String) + * @since 1.8.2 + */ + public static CharSequence expand(CharSequence self) { + return expand(self.toString(), 8); + } + + /** + * Expands all tabs into spaces. If the String has multiple + * lines, expand each line - restarting tab stops at the start + * of each line. + * + * @param self A String to expand + * @param tabStop The number of spaces a tab represents + * @return The expanded String + * @since 1.7.3 + */ + public static String expand(String self, int tabStop) { + if (self.length() == 0) return self; + try { + StringBuilder builder = new StringBuilder(); + for (String line : readLines(self)) { + builder.append(expandLine(line, tabStop)); + builder.append("\n"); + } + // remove the normalized ending line ending if it was not present + if (!self.endsWith("\n")) { + builder.deleteCharAt(builder.length() - 1); + } + return builder.toString(); + } catch (IOException e) { + /* ignore */ + } + return self; + } + + /** + * Expands all tabs into spaces. If the CharSequence has multiple + * lines, expand each line - restarting tab stops at the start + * of each line. + * + * @param self A CharSequence to expand + * @param tabStop The number of spaces a tab represents + * @return The expanded CharSequence + * @see #expand(String, int) + * @since 1.8.2 + */ + public static CharSequence expand(CharSequence self, int tabStop) { + return expand(self.toString(), tabStop); + } + + /** + * Expands all tabs into spaces. Assumes the String represents a single line of text. + * + * @param self A line to expand + * @param tabStop The number of spaces a tab represents + * @return The expanded String + * @since 1.7.3 + */ + public static String expandLine(String self, int tabStop) { + int index; + while ((index = self.indexOf('\t')) != -1) { + StringBuilder builder = new StringBuilder(self); + int count = tabStop - index % tabStop; + builder.deleteCharAt(index); + for (int i = 0; i < count; i++) builder.insert(index, " "); + self = builder.toString(); + } + return self; + } + + /** + * Expands all tabs into spaces. Assumes the CharSequence represents a single line of text. + * + * @param self A line to expand + * @param tabStop The number of spaces a tab represents + * @return The expanded CharSequence + * @see #expandLine(String, int) + * @since 1.8.2 + */ + public static CharSequence expandLine(CharSequence self, int tabStop) { + return expandLine(self.toString(), tabStop); + } + + /** + * Replaces sequences of whitespaces with tabs using tabStops of size 8. + * + * @param self A String to unexpand + * @return The unexpanded String + * @since 1.7.3 + * @see #unexpand(java.lang.String, int) + */ + public static String unexpand(String self) { + return unexpand(self, 8); + } + + /** + * Replaces sequences of whitespaces with tabs using tabStops of size 8. + * + * @param self A CharSequence to unexpand + * @return The unexpanded CharSequence + * @see #unexpand(java.lang.String) + * @since 1.8.2 + */ + public static CharSequence unexpand(CharSequence self) { + return unexpand(self.toString()); + } + + /** + * Replaces sequences of whitespaces with tabs. + * + * @param self A String to unexpand + * @param tabStop The number of spaces a tab represents + * @return The unexpanded String + * @since 1.7.3 + */ + public static String unexpand(String self, int tabStop) { + if (self.length() == 0) return self; + try { + StringBuilder builder = new StringBuilder(); + for (String line : readLines(self)) { + builder.append(unexpandLine(line, tabStop)); + builder.append("\n"); + } + // remove the normalized ending line ending if it was not present + if (!self.endsWith("\n")) { + builder.deleteCharAt(builder.length() - 1); + } + return builder.toString(); + } catch (IOException e) { + /* ignore */ + } + return self; + } + + /** + * Replaces sequences of whitespaces with tabs. + * + * @param self A CharSequence to unexpand + * @param tabStop The number of spaces a tab represents + * @return The unexpanded CharSequence + * @see #unexpand(String, int) + * @since 1.8.2 + */ + public static CharSequence unexpand(CharSequence self, int tabStop) { + return unexpand(self.toString(), tabStop); + } + + /** + * Replaces sequences of whitespaces with tabs within a line. + * + * @param self A line to unexpand + * @param tabStop The number of spaces a tab represents + * @return The unexpanded String + * @since 1.7.3 + */ + public static String unexpandLine(String self, int tabStop) { + StringBuilder builder = new StringBuilder(self); + int index = 0; + while (index + tabStop < builder.length()) { + // cut original string in tabstop-length pieces + String piece = builder.substring(index, index + tabStop); + // count trailing whitespace characters + int count = 0; + while ((count < tabStop) && (Character.isWhitespace(piece.charAt(tabStop - (count + 1))))) + count++; + // replace if whitespace was found + if (count > 0) { + piece = piece.substring(0, tabStop - count) + '\t'; + builder.replace(index, index + tabStop, piece); + index = index + tabStop - (count - 1); + } else + index = index + tabStop; + } + return builder.toString(); + } + + /** + * Replaces sequences of whitespaces with tabs within a line. + * + * @param self A line to unexpand + * @param tabStop The number of spaces a tab represents + * @return The unexpanded CharSequence + * @see #unexpandLine(String, int) + * @since 1.8.2 + */ + public static CharSequence unexpandLine(CharSequence self, int tabStop) { + return unexpandLine(self.toString(), tabStop); + } + + /** + * Convenience method to split a GString (with whitespace as delimiter). + * + * @param self the GString to split + * @return String[] result of split + * @see #split(java.lang.String) + * @since 1.6.1 + */ + public static String[] split(GString self) { + return split(self.toString()); + } + + /** + * Tokenize a String based on the given string delimiter. + * + * @param self a String + * @param token the delimiter + * @return a List of tokens + * @see java.util.StringTokenizer#StringTokenizer(java.lang.String, java.lang.String) + * @since 1.0 + */ + @SuppressWarnings("unchecked") + public static List tokenize(String self, String token) { + return InvokerHelper.asList(new StringTokenizer(self, token)); + } + + /** + * Tokenize a CharSequence based on the given CharSequence delimiter. + * + * @param self a CharSequence + * @param token the delimiter + * @return a List of tokens + * @see #tokenize(String, String) + * @since 1.8.2 + */ + public static List tokenize(CharSequence self, CharSequence token) { + return new ArrayList(tokenize(self.toString(), token.toString())); + } + + /** + * Tokenize a String based on the given character delimiter. + * For example: + *
    +     * char pathSep = ':'
    +     * assert "/tmp:/usr".tokenize(pathSep) == ["/tmp", "/usr"]
    +     * 
    + * + * @param self a String + * @param token the delimiter + * @return a List of tokens + * @see java.util.StringTokenizer#StringTokenizer(java.lang.String, java.lang.String) + * @since 1.7.2 + */ + public static List tokenize(String self, Character token) { + return tokenize(self, token.toString()); + } + + /** + * Tokenize a CharSequence based on the given character delimiter. + * + * @param self a CharSequence + * @param token the delimiter + * @return a List of tokens + * @see #tokenize(String, Character) + * @since 1.8.2 + */ + public static List tokenize(CharSequence self, Character token) { + return tokenize(self, token.toString()); + } + + /** + * Tokenize a String (with a whitespace as the delimiter). + * + * @param self a String + * @return a List of tokens + * @see java.util.StringTokenizer#StringTokenizer(java.lang.String) + * @since 1.0 + */ + @SuppressWarnings("unchecked") + public static List tokenize(String self) { + return InvokerHelper.asList(new StringTokenizer(self)); + } + + /** + * Tokenize a CharSequence (with a whitespace as the delimiter). + * + * @param self a CharSequence + * @return a List of tokens + * @see #tokenize(String) + * @since 1.8.2 + */ + public static List tokenize(CharSequence self) { + return new ArrayList(tokenize(self.toString())); + } + + /** + * Appends the String representation of the given operand to this string. + * + * @param left a String + * @param value any Object + * @return the new string with the object appended + * @since 1.0 + */ + public static String plus(String left, Object value) { + return left + toString(value); + } + + /** + * Appends the String representation of the given operand to this string. + * + * @param left a CharSequence + * @param value any Object + * @return the new CharSequence with the object appended + * @since 1.8.2 + */ + public static CharSequence plus(CharSequence left, Object value) { + return left + toString(value); + } + + /** + * Appends a String to the string representation of this number. + * + * @param value a Number + * @param right a String + * @return a String + * @since 1.0 + */ + public static String plus(Number value, String right) { + return toString(value) + right; + } + + /** + * Appends a String to this StringBuffer. + * + * @param left a StringBuffer + * @param value a String + * @return a String + * @since 1.0 + */ + public static String plus(StringBuffer left, String value) { + return left + value; + } + + /** + * Remove a part of a String. This replaces the first occurrence + * of target within self with '' and returns the result. If + * target is a regex Pattern, the first occurrence of that + * pattern will be removed (using regex matching), otherwise + * the first occurrence of target.toString() will be removed. + * + * @param self a String + * @param target an object representing the part to remove + * @return a String minus the part to be removed + * @since 1.0 + */ + public static String minus(String self, Object target) { + if (target instanceof Pattern) { + return ((Pattern)target).matcher(self).replaceFirst(""); + } + String text = toString(target); + int index = self.indexOf(text); + if (index == -1) return self; + int end = index + text.length(); + if (self.length() > end) { + return self.substring(0, index) + self.substring(end); + } + return self.substring(0, index); + } + + /** + * Remove a part of a CharSequence by replacing the first occurrence + * of target within self with '' and returns the result. + * + * @param self a CharSequence + * @param target an object representing the part to remove + * @return a CharSequence minus the part to be removed + * @see #minus(String, Object) + * @since 1.8.2 + */ + public static CharSequence minus(CharSequence self, Object target) { + return minus(self.toString(), target); + } + + /** + * Provide an implementation of contains() like + * {@link java.util.Collection#contains(java.lang.Object)} to make Strings more polymorphic. + * This method is not required on JDK 1.5 onwards + * + * @param self a String + * @param text a String to look for + * @return true if this string contains the given text + * @since 1.0 + */ + public static boolean contains(String self, String text) { + int idx = self.indexOf(text); + return idx >= 0; + } + + /** + * Provide an implementation of contains() like + * {@link java.util.Collection#contains(java.lang.Object)} to make CharSequences more polymorphic. + * + * @param self a CharSequence + * @param text the CharSequence to look for + * @return true if this CharSequence contains the given text + * @see #contains(String, String) + * @since 1.8.2 + */ + public static boolean contains(CharSequence self, CharSequence text) { + return contains(self.toString(), text.toString()); + } + + /** + * Checks whether the array contains the given value. + * + * @param self the array we are searching + * @param value the value being searched for + * @return true if the array contains the value + * @since 1.8.6 + */ + public static boolean contains(int[] self, Object value) { + for (int next : self) { + if (DefaultTypeTransformation.compareEqual(value, next)) return true; + } + return false; + } + + /** + * Checks whether the array contains the given value. + * + * @param self the array we are searching + * @param value the value being searched for + * @return true if the array contains the value + * @since 1.8.6 + */ + public static boolean contains(long[] self, Object value) { + for (long next : self) { + if (DefaultTypeTransformation.compareEqual(value, next)) return true; + } + return false; + } + + /** + * Checks whether the array contains the given value. + * + * @param self the array we are searching + * @param value the value being searched for + * @return true if the array contains the value + * @since 1.8.6 + */ + public static boolean contains(short[] self, Object value) { + for (short next : self) { + if (DefaultTypeTransformation.compareEqual(value, next)) return true; + } + return false; + } + + /** + * Checks whether the array contains the given value. + * + * @param self the array we are searching + * @param value the value being searched for + * @return true if the array contains the value + * @since 1.8.6 + */ + public static boolean contains(char[] self, Object value) { + for (char next : self) { + if (DefaultTypeTransformation.compareEqual(value, next)) return true; + } + return false; + } + + /** + * Checks whether the array contains the given value. + * + * @param self the array within which we count the number of occurrences + * @param value the value being searched for + * @return the number of occurrences + * @since 1.8.6 + */ + public static boolean contains(boolean[] self, Object value) { + for (boolean next : self) { + if (DefaultTypeTransformation.compareEqual(value, next)) return true; + } + return false; + } + + /** + * Checks whether the array contains the given value. + * + * @param self the array we are searching + * @param value the value being searched for + * @return true if the array contains the value + * @since 1.8.6 + */ + public static boolean contains(double[] self, Object value) { + for (double next : self) { + if (DefaultTypeTransformation.compareEqual(value, next)) return true; + } + return false; + } + + /** + * Checks whether the array contains the given value. + * + * @param self the array we are searching + * @param value the value being searched for + * @return true if the array contains the value + * @since 1.8.6 + */ + public static boolean contains(float[] self, Object value) { + for (float next : self) { + if (DefaultTypeTransformation.compareEqual(value, next)) return true; + } + return false; + } + + /** + * Checks whether the array contains the given value. + * + * @param self the array we are searching + * @param value the value being searched for + * @return true if the array contains the value + * @since 1.8.6 + */ + public static boolean contains(byte[] self, Object value) { + for (byte next : self) { + if (DefaultTypeTransformation.compareEqual(value, next)) return true; + } + return false; + } + + /** + * Checks whether the array contains the given value. + * + * @param self the array we are searching + * @param value the value being searched for + * @return true if the array contains the value + * @since 1.8.6 + */ + public static boolean contains(Object[] self, Object value) { + for (Object next : self) { + if (DefaultTypeTransformation.compareEqual(value, next)) return true; + } + return false; + } + + /** + * Count the number of occurrences of a substring. + * + * @param self a String + * @param text a substring + * @return the number of occurrences of the given string inside this String + * @since 1.0 + */ + public static int count(String self, String text) { + int answer = 0; + for (int idx = 0; true; idx++) { + idx = self.indexOf(text, idx); + if (idx >= 0) { + ++answer; + } else { + break; + } + } + return answer; + } + + /** + * Count the number of occurrences of a sub CharSequence. + * + * @param self a CharSequence + * @param text a sub CharSequence + * @return the number of occurrences of the given CharSequence inside this CharSequence + * @see #count(String, String) + * @since 1.8.2 + */ + public static int count(CharSequence self, CharSequence text) { + return count(self.toString(), text.toString()); + } + + /** + * This method is called by the ++ operator for the class String. + * It increments the last character in the given string. If the + * character in the string is Character.MAX_VALUE a Character.MIN_VALUE + * will be appended. The empty string is incremented to a string + * consisting of the character Character.MIN_VALUE. + * + * @param self a String + * @return an incremented String + * @since 1.0 + */ + public static String next(String self) { + StringBuilder buffer = new StringBuilder(self); + if (buffer.length() == 0) { + buffer.append(Character.MIN_VALUE); + } else { + char last = buffer.charAt(buffer.length() - 1); + if (last == Character.MAX_VALUE) { + buffer.append(Character.MIN_VALUE); + } else { + char next = last; + next++; + buffer.setCharAt(buffer.length() - 1, next); + } + } + return buffer.toString(); + } + + /** + * This method is called by the ++ operator for the class CharSequence. + * + * @param self a CharSequence + * @return an incremented CharSequence + * @see #next(String) + * @since 1.8.2 + */ + public static CharSequence next(CharSequence self) { + return next(self.toString()); + } + + /** + * This method is called by the -- operator for the class String. + * It decrements the last character in the given string. If the + * character in the string is Character.MIN_VALUE it will be deleted. + * The empty string can't be decremented. + * + * @param self a String + * @return a String with a decremented digit at the end + * @since 1.0 + */ + public static String previous(String self) { + StringBuilder buffer = new StringBuilder(self); + if (buffer.length() == 0) throw new IllegalArgumentException("the string is empty"); + char last = buffer.charAt(buffer.length() - 1); + if (last == Character.MIN_VALUE) { + buffer.deleteCharAt(buffer.length() - 1); + } else { + char next = last; + next--; + buffer.setCharAt(buffer.length() - 1, next); + } + return buffer.toString(); + } + + /** + * This method is called by the -- operator for the class CharSequence. + * + * @param self a CharSequence + * @return a CharSequence with a decremented digit at the end + * @see #previous(String) + * @since 1.8.2 + */ + public static CharSequence previous(CharSequence self) { + return previous(self.toString()); + } + + /** + * Executes the command specified by self as a command-line process. + *

    For more control over Process construction you can use + * java.lang.ProcessBuilder (JDK 1.5+).

    + * + * @param self a command line String + * @return the Process which has just started for this command line representation + * @throws IOException if an IOException occurs. + * @since 1.0 + */ + public static Process execute(final String self) throws IOException { + return Runtime.getRuntime().exec(self); + } + + /** + * Executes the command specified by self with environment defined by envp + * and under the working directory dir. + *

    For more control over Process construction you can use + * java.lang.ProcessBuilder (JDK 1.5+).

    + * + * @param self a command line String to be executed. + * @param envp an array of Strings, each element of which + * has environment variable settings in the format + * name=value, or + * null if the subprocess should inherit + * the environment of the current process. + * @param dir the working directory of the subprocess, or + * null if the subprocess should inherit + * the working directory of the current process. + * @return the Process which has just started for this command line representation. + * @throws IOException if an IOException occurs. + * @since 1.0 + */ + public static Process execute(final String self, final String[] envp, final File dir) throws IOException { + return Runtime.getRuntime().exec(self, envp, dir); + } + + /** + * Executes the command specified by self with environment defined + * by envp and under the working directory dir. + *

    For more control over Process construction you can use + * java.lang.ProcessBuilder (JDK 1.5+).

    + * + * @param self a command line String to be executed. + * @param envp a List of Objects (converted to Strings using toString), each member of which + * has environment variable settings in the format + * name=value, or + * null if the subprocess should inherit + * the environment of the current process. + * @param dir the working directory of the subprocess, or + * null if the subprocess should inherit + * the working directory of the current process. + * @return the Process which has just started for this command line representation. + * @throws IOException if an IOException occurs. + * @since 1.0 + */ + public static Process execute(final String self, final List envp, final File dir) throws IOException { + return execute(self, stringify(envp), dir); + } + + /** + * Executes the command specified by the given String array. + * The first item in the array is the command; the others are the parameters. + *

    For more control over Process construction you can use + * java.lang.ProcessBuilder (JDK 1.5+).

    + * + * @param commandArray an array of String containing the command name and + * parameters as separate items in the array. + * @return the Process which has just started for this command line representation. + * @throws IOException if an IOException occurs. + * @since 1.0 + */ + public static Process execute(final String[] commandArray) throws IOException { + return Runtime.getRuntime().exec(commandArray); + } + + /** + * Executes the command specified by the String array given in the first parameter, + * with the environment defined by envp and under the working directory dir. + * The first item in the array is the command; the others are the parameters. + *

    For more control over Process construction you can use + * java.lang.ProcessBuilder (JDK 1.5+).

    + * + * @param commandArray an array of String containing the command name and + * parameters as separate items in the array. + * @param envp an array of Strings, each member of which + * has environment variable settings in the format + * name=value, or + * null if the subprocess should inherit + * the environment of the current process. + * @param dir the working directory of the subprocess, or + * null if the subprocess should inherit + * the working directory of the current process. + * @return the Process which has just started for this command line representation. + * @throws IOException if an IOException occurs. + * @since 1.7.1 + */ + public static Process execute(final String[] commandArray, final String[] envp, final File dir) throws IOException { + return Runtime.getRuntime().exec(commandArray, envp, dir); + } + + /** + * Executes the command specified by the String array given in the first parameter, + * with the environment defined by envp and under the working directory dir. + * The first item in the array is the command; the others are the parameters. + *

    For more control over Process construction you can use + * java.lang.ProcessBuilder (JDK 1.5+).

    + * + * @param commandArray an array of String containing the command name and + * parameters as separate items in the array. + * @param envp a List of Objects (converted to Strings using toString), each member of which + * has environment variable settings in the format + * name=value, or + * null if the subprocess should inherit + * the environment of the current process. + * @param dir the working directory of the subprocess, or + * null if the subprocess should inherit + * the working directory of the current process. + * @return the Process which has just started for this command line representation. + * @throws IOException if an IOException occurs. + * @since 1.7.1 + */ + public static Process execute(final String[] commandArray, final List envp, final File dir) throws IOException { + return Runtime.getRuntime().exec(commandArray, stringify(envp), dir); + } + + /** + * Executes the command specified by the given list. The toString() method is called + * for each item in the list to convert into a resulting String. + * The first item in the list is the command the others are the parameters. + *

    For more control over Process construction you can use + * java.lang.ProcessBuilder (JDK 1.5+).

    + * + * @param commands a list containing the command name and + * parameters as separate items in the list. + * @return the Process which has just started for this command line representation. + * @throws IOException if an IOException occurs. + * @since 1.0 + */ + public static Process execute(final List commands) throws IOException { + return execute(stringify(commands)); + } + + /** + * Executes the command specified by the given list, + * with the environment defined by envp and under the working directory dir. + * The first item in the list is the command; the others are the parameters. The toString() + * method is called on items in the list to convert them to Strings. + *

    For more control over Process construction you can use + * java.lang.ProcessBuilder (JDK 1.5+).

    + * + * @param commands a List containing the command name and + * parameters as separate items in the list. + * @param envp an array of Strings, each member of which + * has environment variable settings in the format + * name=value, or + * null if the subprocess should inherit + * the environment of the current process. + * @param dir the working directory of the subprocess, or + * null if the subprocess should inherit + * the working directory of the current process. + * @return the Process which has just started for this command line representation. + * @throws IOException if an IOException occurs. + * @since 1.7.1 + */ + public static Process execute(final List commands, final String[] envp, final File dir) throws IOException { + return Runtime.getRuntime().exec(stringify(commands), envp, dir); + } + + /** + * Executes the command specified by the given list, + * with the environment defined by envp and under the working directory dir. + * The first item in the list is the command; the others are the parameters. The toString() + * method is called on items in the list to convert them to Strings. + *

    For more control over Process construction you can use + * java.lang.ProcessBuilder (JDK 1.5+).

    + * + * @param commands a List containing the command name and + * parameters as separate items in the list. + * @param envp a List of Objects (converted to Strings using toString), each member of which + * has environment variable settings in the format + * name=value, or + * null if the subprocess should inherit + * the environment of the current process. + * @param dir the working directory of the subprocess, or + * null if the subprocess should inherit + * the working directory of the current process. + * @return the Process which has just started for this command line representation. + * @throws IOException if an IOException occurs. + * @since 1.7.1 + */ + public static Process execute(final List commands, final List envp, final File dir) throws IOException { + return Runtime.getRuntime().exec(stringify(commands), stringify(envp), dir); + } + + private static String[] stringify(final List orig) { + if (orig == null) return null; + String[] result = new String[orig.size()]; + for (int i = 0; i < orig.size(); i++) { + result[i] = orig.get(i).toString(); + } + return result; + } + + /** + * Repeat a String a certain number of times. + * + * @param self a String to be repeated + * @param factor the number of times the String should be repeated + * @return a String composed of a repetition + * @throws IllegalArgumentException if the number of repetitions is < 0 + * @since 1.0 + */ + public static String multiply(String self, Number factor) { + int size = factor.intValue(); + if (size == 0) + return ""; + else if (size < 0) { + throw new IllegalArgumentException("multiply() should be called with a number of 0 or greater not: " + size); + } + StringBuilder answer = new StringBuilder(self); + for (int i = 1; i < size; i++) { + answer.append(self); + } + return answer.toString(); + } + + /** + * Repeat a CharSequence a certain number of times. + * + * @param self a CharSequence to be repeated + * @param factor the number of times the CharSequence should be repeated + * @return a CharSequence composed of a repetition + * @throws IllegalArgumentException if the number of repetitions is < 0 + * @since 1.8.2 + */ + public static CharSequence multiply(CharSequence self, Number factor) { + return multiply(self.toString(), factor); + } + + /** + * Returns the string representation of the given array. + * + * @param self an array + * @return the string representation + * @since 1.6.0 + */ + public static String toString(boolean[] self) { + return InvokerHelper.toString(self); + } + + /** + * Returns the string representation of the given array. + * + * @param self an array + * @return the string representation + * @since 1.6.0 + */ + public static String toString(byte[] self) { + return InvokerHelper.toString(self); + } + + /** + * Returns the string representation of the given array. + * + * @param self an array + * @return the string representation + * @since 1.6.0 + */ + public static String toString(char[] self) { + return InvokerHelper.toString(self); + } + + /** + * Returns the string representation of the given array. + * + * @param self an array + * @return the string representation + * @since 1.6.0 + */ + public static String toString(short[] self) { + return InvokerHelper.toString(self); + } + + /** + * Returns the string representation of the given array. + * + * @param self an array + * @return the string representation + * @since 1.6.0 + */ + public static String toString(int[] self) { + return InvokerHelper.toString(self); + } + + /** + * Returns the string representation of the given array. + * + * @param self an array + * @return the string representation + * @since 1.6.0 + */ + public static String toString(long[] self) { + return InvokerHelper.toString(self); + } + + /** + * Returns the string representation of the given array. + * + * @param self an array + * @return the string representation + * @since 1.6.0 + */ + public static String toString(float[] self) { + return InvokerHelper.toString(self); + } + + /** + * Returns the string representation of the given array. + * + * @param self an array + * @return the string representation + * @since 1.6.0 + */ + public static String toString(double[] self) { + return InvokerHelper.toString(self); + } + + /** + * Returns the string representation of the given map. + * + * @param self a Map + * @return the string representation + * @see #toMapString(java.util.Map) + * @since 1.0 + */ + public static String toString(AbstractMap self) { + return toMapString(self); + } + + /** + * Returns the string representation of this map. The string displays the + * contents of the map, i.e. [one:1, two:2, three:3]. + * + * @param self a Map + * @return the string representation + * @since 1.0 + */ + public static String toMapString(Map self) { + return toMapString(self, -1); + } + + /** + * Returns the string representation of this map. The string displays the + * contents of the map, i.e. [one:1, two:2, three:3]. + * + * @param self a Map + * @param maxSize stop after approximately this many characters and append '...' + * @return the string representation + * @since 1.0 + */ + public static String toMapString(Map self, int maxSize) { + return (self == null) ? "null" : InvokerHelper.toMapString(self, maxSize); + } + + /** + * Returns the string representation of the given collection. The string + * displays the contents of the collection, i.e. + * [1, 2, a]. + * + * @param self a Collection + * @return the string representation + * @see #toListString(java.util.Collection) + * @since 1.0 + */ + public static String toString(AbstractCollection self) { + return toListString(self); + } + + /** + * Returns the string representation of the given list. The string + * displays the contents of the list, similar to a list literal, i.e. + * [1, 2, a]. + * + * @param self a Collection + * @return the string representation + * @since 1.0 + */ + public static String toListString(Collection self) { + return toListString(self, -1); + } + + /** + * Returns the string representation of the given list. The string + * displays the contents of the list, similar to a list literal, i.e. + * [1, 2, a]. + * + * @param self a Collection + * @param maxSize stop after approximately this many characters and append '...' + * @return the string representation + * @since 1.7.3 + */ + public static String toListString(Collection self, int maxSize) { + return (self == null) ? "null" : InvokerHelper.toListString(self, maxSize); + } + + /** + * Returns the string representation of this array's contents. + * + * @param self an Object[] + * @return the string representation + * @see #toArrayString(java.lang.Object[]) + * @since 1.0 + */ + public static String toString(Object[] self) { + return toArrayString(self); + } + + /** + * Returns the string representation of the given array. The string + * displays the contents of the array, similar to an array literal, i.e. + * {1, 2, "a"}. + * + * @param self an Object[] + * @return the string representation + * @since 1.0 + */ + public static String toArrayString(Object[] self) { + return (self == null) ? "null" : InvokerHelper.toArrayString(self); + } + + /** + * Create a String representation of this object. + * @param value an object + * @return a string. + * @since 1.0 + */ + public static String toString(Object value) { + return InvokerHelper.toString(value); + } + + // Number based methods + //------------------------------------------------------------------------- + + /** + * Increment a Character by one. + * + * @param self a Character + * @return an incremented Character + * @since 1.5.7 + */ + public static Character next(Character self) { + return (char) (self + 1); + } + + /** + * Increment a Number by one. + * + * @param self a Number + * @return an incremented Number + * @since 1.0 + */ + public static Number next(Number self) { + return NumberNumberPlus.plus(self, ONE); + } + + /** + * Decrement a Character by one. + * + * @param self a Character + * @return a decremented Character + * @since 1.5.7 + */ + public static Character previous(Character self) { + return (char) (self - 1); + } + + /** + * Decrement a Number by one. + * + * @param self a Number + * @return a decremented Number + * @since 1.0 + */ + public static Number previous(Number self) { + return NumberNumberMinus.minus(self, ONE); + } + + /** + * Add a Character and a Number. The ordinal value of the Character + * is used in the addition (the ordinal value is the unicode + * value which for simple character sets is the ASCII value). + * This operation will always create a new object for the result, + * while the operands remain unchanged. + * + * @see java.lang.Integer#valueOf(int) + * @param left a Character + * @param right a Number + * @return the Number corresponding to the addition of left and right + * @since 1.0 + */ + public static Number plus(Character left, Number right) { + return NumberNumberPlus.plus(Integer.valueOf(left), right); + } + + /** + * Add a Number and a Character. The ordinal value of the Character + * is used in the addition (the ordinal value is the unicode + * value which for simple character sets is the ASCII value). + * + * @see java.lang.Integer#valueOf(int) + * @param left a Number + * @param right a Character + * @return The Number corresponding to the addition of left and right + * @since 1.0 + */ + public static Number plus(Number left, Character right) { + return NumberNumberPlus.plus(left, Integer.valueOf(right)); + } + + /** + * Add one Character to another. The ordinal values of the Characters + * are used in the addition (the ordinal value is the unicode + * value which for simple character sets is the ASCII value). + * This operation will always create a new object for the result, + * while the operands remain unchanged. + * + * @see #plus(java.lang.Number, java.lang.Character) + * @param left a Character + * @param right a Character + * @return the Number corresponding to the addition of left and right + * @since 1.0 + */ + public static Number plus(Character left, Character right) { + return plus(Integer.valueOf(left), right); + } + + /** + * Compare a Character and a Number. The ordinal value of the Character + * is used in the comparison (the ordinal value is the unicode + * value which for simple character sets is the ASCII value). + * + * @param left a Character + * @param right a Number + * @return the result of the comparison + * @since 1.0 + */ + public static int compareTo(Character left, Number right) { + return compareTo(Integer.valueOf(left), right); + } + + /** + * Compare a Number and a Character. The ordinal value of the Character + * is used in the comparison (the ordinal value is the unicode + * value which for simple character sets is the ASCII value). + * + * @param left a Number + * @param right a Character + * @return the result of the comparison + * @since 1.0 + */ + public static int compareTo(Number left, Character right) { + return compareTo(left, Integer.valueOf(right)); + } + + /** + * Compare two Characters. The ordinal values of the Characters + * are compared (the ordinal value is the unicode + * value which for simple character sets is the ASCII value). + * + * @param left a Character + * @param right a Character + * @return the result of the comparison + * @since 1.0 + */ + public static int compareTo(Character left, Character right) { + return compareTo(Integer.valueOf(left), right); + } + + /** + * Compare two Numbers. Equality (==) for numbers dispatches to this. + * + * @param left a Number + * @param right another Number to compare to + * @return the comparison of both numbers + * @since 1.0 + */ + public static int compareTo(Number left, Number right) { + /** @todo maybe a double dispatch thing to handle new large numbers? */ + return NumberMath.compareTo(left, right); + } + + /** + * Subtract a Number from a Character. The ordinal value of the Character + * is used in the subtraction (the ordinal value is the unicode + * value which for simple character sets is the ASCII value). + * + * @param left a Character + * @param right a Number + * @return the Number corresponding to the subtraction of right from left + * @since 1.0 + */ + public static Number minus(Character left, Number right) { + return NumberNumberMinus.minus(Integer.valueOf(left), right); + } + + /** + * Subtract a Character from a Number. The ordinal value of the Character + * is used in the subtraction (the ordinal value is the unicode + * value which for simple character sets is the ASCII value). + * + * @param left a Number + * @param right a Character + * @return the Number corresponding to the subtraction of right from left + * @since 1.0 + */ + public static Number minus(Number left, Character right) { + return NumberNumberMinus.minus(left, Integer.valueOf(right)); + } + + /** + * Subtract one Character from another. The ordinal values of the Characters + * is used in the comparison (the ordinal value is the unicode + * value which for simple character sets is the ASCII value). + * + * @param left a Character + * @param right a Character + * @return the Number corresponding to the subtraction of right from left + * @since 1.0 + */ + public static Number minus(Character left, Character right) { + return minus(Integer.valueOf(left), right); + } + + /** + * Multiply a Character by a Number. The ordinal value of the Character + * is used in the multiplication (the ordinal value is the unicode + * value which for simple character sets is the ASCII value). + * + * @param left a Character + * @param right a Number + * @return the Number corresponding to the multiplication of left by right + * @since 1.0 + */ + public static Number multiply(Character left, Number right) { + return NumberNumberMultiply.multiply(Integer.valueOf(left), right); + } + + /** + * Multiply a Number by a Character. The ordinal value of the Character + * is used in the multiplication (the ordinal value is the unicode + * value which for simple character sets is the ASCII value). + * + * @param left a Number + * @param right a Character + * @return the multiplication of left by right + * @since 1.0 + */ + public static Number multiply(Number left, Character right) { + return NumberNumberMultiply.multiply(Integer.valueOf(right), left); + } + + /** + * Multiply two Characters. The ordinal values of the Characters + * are used in the multiplication (the ordinal value is the unicode + * value which for simple character sets is the ASCII value). + * + * @param left a Character + * @param right another Character + * @return the Number corresponding to the multiplication of left by right + * @since 1.0 + */ + public static Number multiply(Character left, Character right) { + return multiply(Integer.valueOf(left), right); + } + + /** + * Multiply a BigDecimal and a Double. + * Note: This method was added to enforce the Groovy rule of + * BigDecimal*Double == Double. Without this method, the + * multiply(BigDecimal) method in BigDecimal would respond + * and return a BigDecimal instead. Since BigDecimal is preferred + * over Number, the Number*Number method is not chosen as in older + * versions of Groovy. + * + * @param left a BigDecimal + * @param right a Double + * @return the multiplication of left by right + * @since 1.0 + */ + public static Number multiply(BigDecimal left, Double right) { + return NumberMath.multiply(left, right); + } + + /** + * Multiply a BigDecimal and a BigInteger. + * Note: This method was added to enforce the Groovy rule of + * BigDecimal*long == long. Without this method, the + * multiply(BigDecimal) method in BigDecimal would respond + * and return a BigDecimal instead. Since BigDecimal is preferred + * over Number, the Number*Number method is not chosen as in older + * versions of Groovy. BigInteger is the fallback for all integer + * types in Groovy + * + * @param left a BigDecimal + * @param right a BigInteger + * @return the multiplication of left by right + * @since 1.0 + */ + public static Number multiply(BigDecimal left, BigInteger right) { + return NumberMath.multiply(left, right); + } + + /** + * Power of a Number to a certain exponent. Called by the '**' operator. + * + * @param self a Number + * @param exponent a Number exponent + * @return a Number to the power of a certain exponent + * @since 1.0 + */ + public static Number power(Number self, Number exponent) { + double base, exp, answer; + base = self.doubleValue(); + exp = exponent.doubleValue(); + + answer = Math.pow(base, exp); + if ((double) ((int) answer) == answer) { + return (int) answer; + } else if ((double) ((long) answer) == answer) { + return (long) answer; + } else { + return answer; + } + } + + /** + * Power of a BigDecimal to an integer certain exponent. If the + * exponent is positive, call the BigDecimal.pow(int) method to + * maintain precision. Called by the '**' operator. + * + * @param self a BigDecimal + * @param exponent an Integer exponent + * @return a Number to the power of a the exponent + */ + public static Number power(BigDecimal self, Integer exponent) { + if (exponent >= 0) { + return self.pow(exponent); + } else { + return power(self, (double) exponent); + } + } + + /** + * Power of a BigInteger to an integer certain exponent. If the + * exponent is positive, call the BigInteger.pow(int) method to + * maintain precision. Called by the '**' operator. + * + * @param self a BigInteger + * @param exponent an Integer exponent + * @return a Number to the power of a the exponent + */ + public static Number power(BigInteger self, Integer exponent) { + if (exponent >= 0) { + return self.pow(exponent); + } else { + return power(self, (double) exponent); + } + } + + /** + * Power of an integer to an integer certain exponent. If the + * exponent is positive, convert to a BigInteger and call + * BigInteger.pow(int) method to maintain precision. Called by the + * '**' operator. + * + * @param self an Integer + * @param exponent an Integer exponent + * @return a Number to the power of a the exponent + */ + public static Number power(Integer self, Integer exponent) { + if (exponent >= 0) { + BigInteger answer = BigInteger.valueOf(self).pow(exponent); + if (answer.compareTo(BI_INT_MIN) >= 0 && answer.compareTo(BI_INT_MAX) <= 0) { + return answer.intValue(); + } else { + return answer; + } + } else { + return power(self, (double) exponent); + } + } + + /** + * Power of a long to an integer certain exponent. If the + * exponent is positive, convert to a BigInteger and call + * BigInteger.pow(int) method to maintain precision. Called by the + * '**' operator. + * + * @param self a Long + * @param exponent an Integer exponent + * @return a Number to the power of a the exponent + */ + public static Number power(Long self, Integer exponent) { + if (exponent >= 0) { + BigInteger answer = BigInteger.valueOf(self).pow(exponent); + if (answer.compareTo(BI_LONG_MIN) >= 0 && answer.compareTo(BI_LONG_MAX) <= 0) { + return answer.longValue(); + } else { + return answer; + } + } else { + return power(self, (double) exponent); + } + } + + /** + * Divide a Character by a Number. The ordinal value of the Character + * is used in the division (the ordinal value is the unicode + * value which for simple character sets is the ASCII value). + * + * @param left a Character + * @param right a Number + * @return the Number corresponding to the division of left by right + * @since 1.0 + */ + public static Number div(Character left, Number right) { + return NumberNumberDiv.div(Integer.valueOf(left), right); + } + + /** + * Divide a Number by a Character. The ordinal value of the Character + * is used in the division (the ordinal value is the unicode + * value which for simple character sets is the ASCII value). + * + * @param left a Number + * @param right a Character + * @return the Number corresponding to the division of left by right + * @since 1.0 + */ + public static Number div(Number left, Character right) { + return NumberNumberDiv.div(left, Integer.valueOf(right)); + } + + /** + * Divide one Character by another. The ordinal values of the Characters + * are used in the division (the ordinal value is the unicode + * value which for simple character sets is the ASCII value). + * + * @param left a Character + * @param right another Character + * @return the Number corresponding to the division of left by right + * @since 1.0 + */ + public static Number div(Character left, Character right) { + return div(Integer.valueOf(left), right); + } + + /** + * Integer Divide a Character by a Number. The ordinal value of the Character + * is used in the division (the ordinal value is the unicode + * value which for simple character sets is the ASCII value). + * + * @param left a Character + * @param right a Number + * @return a Number (an Integer) resulting from the integer division operation + * @since 1.0 + */ + public static Number intdiv(Character left, Number right) { + return intdiv(Integer.valueOf(left), right); + } + + /** + * Integer Divide a Number by a Character. The ordinal value of the Character + * is used in the division (the ordinal value is the unicode + * value which for simple character sets is the ASCII value). + * + * @param left a Number + * @param right a Character + * @return a Number (an Integer) resulting from the integer division operation + * @since 1.0 + */ + public static Number intdiv(Number left, Character right) { + return intdiv(left, Integer.valueOf(right)); + } + + /** + * Integer Divide two Characters. The ordinal values of the Characters + * are used in the division (the ordinal value is the unicode + * value which for simple character sets is the ASCII value). + * + * @param left a Character + * @param right another Character + * @return a Number (an Integer) resulting from the integer division operation + * @since 1.0 + */ + public static Number intdiv(Character left, Character right) { + return intdiv(Integer.valueOf(left), right); + } + + /** + * Integer Divide two Numbers. + * + * @param left a Number + * @param right another Number + * @return a Number (an Integer) resulting from the integer division operation + * @since 1.0 + */ + public static Number intdiv(Number left, Number right) { + return NumberMath.intdiv(left, right); + } + + /** + * Bitwise OR together two numbers. + * + * @param left a Number + * @param right another Number to bitwise OR + * @return the bitwise OR of both Numbers + * @since 1.0 + */ + public static Number or(Number left, Number right) { + return NumberMath.or(left, right); + } + + /** + * Bitwise AND together two Numbers. + * + * @param left a Number + * @param right another Number to bitwise AND + * @return the bitwise AND of both Numbers + * @since 1.0 + */ + public static Number and(Number left, Number right) { + return NumberMath.and(left, right); + } + + /** + * Bitwise AND together two BitSets. + * + * @param left a BitSet + * @param right another BitSet to bitwise AND + * @return the bitwise AND of both BitSets + * @since 1.5.0 + */ + public static BitSet and(BitSet left, BitSet right) { + BitSet result = (BitSet) left.clone(); + result.and(right); + return result; + } + + /** + * Bitwise XOR together two BitSets. Called when the '^' operator is used + * between two bit sets. + * + * @param left a BitSet + * @param right another BitSet to bitwise AND + * @return the bitwise XOR of both BitSets + * @since 1.5.0 + */ + public static BitSet xor(BitSet left, BitSet right) { + BitSet result = (BitSet) left.clone(); + result.xor(right); + return result; + } + + /** + * Bitwise NEGATE a BitSet. + * + * @param self a BitSet + * @return the bitwise NEGATE of the BitSet + * @since 1.5.0 + */ + public static BitSet bitwiseNegate(BitSet self) { + BitSet result = (BitSet) self.clone(); + result.flip(0, result.size() - 1); + return result; + } + + /** + * Bitwise OR together two BitSets. Called when the '|' operator is used + * between two bit sets. + * + * @param left a BitSet + * @param right another BitSet to bitwise AND + * @return the bitwise OR of both BitSets + * @since 1.5.0 + */ + public static BitSet or(BitSet left, BitSet right) { + BitSet result = (BitSet) left.clone(); + result.or(right); + return result; + } + + /** + * Bitwise XOR together two Numbers. Called when the '|' operator is used. + * + * @param left a Number + * @param right another Number to bitwse XOR + * @return the bitwise XOR of both Numbers + * @since 1.0 + */ + public static Number xor(Number left, Number right) { + return NumberMath.xor(left, right); + } + + /** + * Performs a division modulus operation. Called by the '%' operator. + * + * @param left a Number + * @param right another Number to mod + * @return the modulus result + * @since 1.0 + */ + public static Number mod(Number left, Number right) { + return NumberMath.mod(left, right); + } + + /** + * Negates the number. Equivalent to the '-' operator when it preceeds + * a single operand, i.e. -10 + * + * @param left a Number + * @return the negation of the number + * @since 1.5.0 + */ + public static Number unaryMinus(Number left) { + return NumberMath.unaryMinus(left); + } + + + /** + * Executes the closure this many times, starting from zero. The current + * index is passed to the closure each time. + * Example: + *
    10.times {
    +     *   println it
    +     * }
    + * Prints the numbers 0 through 9. + * + * @param self a Number + * @param closure the closure to call a number of times + * @since 1.0 + */ + public static void times(Number self, Closure closure) { + for (int i = 0, size = self.intValue(); i < size; i++) { + closure.call(i); + if (closure.getDirective() == Closure.DONE) { + break; + } + } + } + + /** + * Iterates from this number up to the given number, inclusive, + * incrementing by one each time. + * + * @param self a Number + * @param to another Number to go up to + * @param closure the closure to call + * @since 1.0 + */ + public static void upto(Number self, Number to, Closure closure) { + int self1 = self.intValue(); + int to1 = to.intValue(); + if (self1 <= to1) { + for (int i = self1; i <= to1; i++) { + closure.call(i); + } + } else + throw new GroovyRuntimeException("Infinite loop in " + self + ".upto(" + to + ")"); + } + + /** + * Iterates from this number up to the given number, inclusive, + * incrementing by one each time. + * + * @param self a long + * @param to the end number + * @param closure the code to execute for each number + * @since 1.0 + */ + public static void upto(long self, Number to, Closure closure) { + long to1 = to.longValue(); + if (self <= to1) { + for (long i = self; i <= to1; i++) { + closure.call(i); + } + } else + throw new GroovyRuntimeException("Infinite loop in " + self + ".upto(" + to + ")"); + } + + /** + * Iterates from this number up to the given number, inclusive, + * incrementing by one each time. + * + * @param self a Long + * @param to the end number + * @param closure the code to execute for each number + * @since 1.0 + */ + public static void upto(Long self, Number to, Closure closure) { + long to1 = to.longValue(); + if (self <= to1) { + for (long i = self; i <= to1; i++) { + closure.call(i); + } + } else + throw new GroovyRuntimeException("Infinite loop in " + self + ".upto(" + to + ")"); + } + + /** + * Iterates from this number up to the given number, inclusive, + * incrementing by one each time. + * + * @param self a float + * @param to the end number + * @param closure the code to execute for each number + * @since 1.0 + */ + public static void upto(float self, Number to, Closure closure) { + float to1 = to.floatValue(); + if (self <= to1) { + for (float i = self; i <= to1; i++) { + closure.call(i); + } + } else + throw new GroovyRuntimeException("Infinite loop in " + self + ".upto(" + to + ")"); + } + + /** + * Iterates from this number up to the given number, inclusive, + * incrementing by one each time. + * + * @param self a Float + * @param to the end number + * @param closure the code to execute for each number + * @since 1.0 + */ + public static void upto(Float self, Number to, Closure closure) { + float to1 = to.floatValue(); + if (self <= to1) { + for (float i = self; i <= to1; i++) { + closure.call(i); + } + } else + throw new GroovyRuntimeException("Infinite loop in " + self + ".upto(" + to + ")"); + } + + /** + * Iterates from this number up to the given number, inclusive, + * incrementing by one each time. + * + * @param self a double + * @param to the end number + * @param closure the code to execute for each number + * @since 1.0 + */ + public static void upto(double self, Number to, Closure closure) { + double to1 = to.doubleValue(); + if (self <= to1) { + for (double i = self; i <= to1; i++) { + closure.call(i); + } + } else + throw new GroovyRuntimeException("Infinite loop in " + self + ".upto(" + to + ")"); + } + + /** + * Iterates from this number up to the given number, inclusive, + * incrementing by one each time. + * + * @param self a Double + * @param to the end number + * @param closure the code to execute for each number + * @since 1.0 + */ + public static void upto(Double self, Number to, Closure closure) { + double to1 = to.doubleValue(); + if (self <= to1) { + for (double i = self; i <= to1; i++) { + closure.call(i); + } + } else + throw new GroovyRuntimeException("Infinite loop in " + self + ".upto(" + to + ")"); + } + + /** + * Iterates from this number up to the given number, inclusive, + * incrementing by one each time. Example: + *
    0.upto( 10 ) {
    +     *   println it
    +     * }
    + * Prints numbers 0 to 10 + * + * @param self a BigInteger + * @param to the end number + * @param closure the code to execute for each number + * @since 1.0 + */ + public static void upto(BigInteger self, Number to, Closure closure) { + if (to instanceof BigDecimal) { + final BigDecimal one = BigDecimal.valueOf(10, 1); + BigDecimal self1 = new BigDecimal(self); + BigDecimal to1 = (BigDecimal) to; + if (self1.compareTo(to1) <= 0) { + for (BigDecimal i = self1; i.compareTo(to1) <= 0; i = i.add(one)) { + closure.call(i); + } + } else + throw new GroovyRuntimeException("Infinite loop in " + self + ".upto(" + to + ")"); + } else if (to instanceof BigInteger) { + final BigInteger one = BigInteger.valueOf(1); + BigInteger to1 = (BigInteger) to; + if (self.compareTo(to1) <= 0) { + for (BigInteger i = self; i.compareTo(to1) <= 0; i = i.add(one)) { + closure.call(i); + } + } else + throw new GroovyRuntimeException("Infinite loop in " + self + ".upto(" + to + ")"); + } else { + final BigInteger one = BigInteger.valueOf(1); + BigInteger to1 = new BigInteger(to.toString()); + if (self.compareTo(to1) <= 0) { + for (BigInteger i = self; i.compareTo(to1) <= 0; i = i.add(one)) { + closure.call(i); + } + } else + throw new GroovyRuntimeException("Infinite loop in " + self + ".upto(" + to + ")"); + } + } + + /** + * Iterates from this number up to the given number, inclusive, + * incrementing by one each time. + *
    0.1.upto( 10 ) {
    +     *   println it
    +     * }
    + * Prints numbers 0.1, 1.1, 2.1... to 9.1 + * + * @param self a BigDecimal + * @param to the end number + * @param closure the code to execute for each number + * @since 1.0 + */ + public static void upto(BigDecimal self, Number to, Closure closure) { + final BigDecimal one = BigDecimal.valueOf(10, 1); // That's what you get for "1.0". + if (to instanceof BigDecimal) { + BigDecimal to1 = (BigDecimal) to; + if (self.compareTo(to1) <= 0) { + for (BigDecimal i = self; i.compareTo(to1) <= 0; i = i.add(one)) { + closure.call(i); + } + } else + throw new GroovyRuntimeException("Infinite loop in " + self + ".upto(" + to + ")"); + } else if (to instanceof BigInteger) { + BigDecimal to1 = new BigDecimal((BigInteger) to); + if (self.compareTo(to1) <= 0) { + for (BigDecimal i = self; i.compareTo(to1) <= 0; i = i.add(one)) { + closure.call(i); + } + } else + throw new GroovyRuntimeException("Infinite loop in " + self + ".upto(" + to + ")"); + } else { + BigDecimal to1 = new BigDecimal(to.toString()); + if (self.compareTo(to1) <= 0) { + for (BigDecimal i = self; i.compareTo(to1) <= 0; i = i.add(one)) { + closure.call(i); + } + } else + throw new GroovyRuntimeException("Infinite loop in " + self + ".upto(" + to + ")"); + } + } + + /** + * Iterates from this number down to the given number, inclusive, + * decrementing by one each time. + * + * @param self a Number + * @param to another Number to go down to + * @param closure the closure to call + * @since 1.0 + */ + public static void downto(Number self, Number to, Closure closure) { + int self1 = self.intValue(); + int to1 = to.intValue(); + if (self1 >= to1) { + for (int i = self1; i >= to1; i--) { + closure.call(i); + } + } else + throw new GroovyRuntimeException("Infinite loop in " + self + ".downto(" + to + ")"); + } + + /** + * Iterates from this number down to the given number, inclusive, + * decrementing by one each time. + * + * @param self a long + * @param to the end number + * @param closure the code to execute for each number + * @since 1.0 + */ + public static void downto(long self, Number to, Closure closure) { + long to1 = to.longValue(); + if (self >= to1) { + for (long i = self; i >= to1; i--) { + closure.call(i); + } + } else + throw new GroovyRuntimeException("Infinite loop in " + self + ".downto(" + to + ")"); + } + + /** + * Iterates from this number down to the given number, inclusive, + * decrementing by one each time. + * + * @param self a Long + * @param to the end number + * @param closure the code to execute for each number + * @since 1.0 + */ + public static void downto(Long self, Number to, Closure closure) { + long to1 = to.longValue(); + if (self >= to1) { + for (long i = self; i >= to1; i--) { + closure.call(i); + } + } else + throw new GroovyRuntimeException("Infinite loop in " + self + ".downto(" + to + ")"); + } + + /** + * Iterates from this number down to the given number, inclusive, + * decrementing by one each time. + * + * @param self a float + * @param to the end number + * @param closure the code to execute for each number + * @since 1.0 + */ + public static void downto(float self, Number to, Closure closure) { + float to1 = to.floatValue(); + if (self >= to1) { + for (float i = self; i >= to1; i--) { + closure.call(i); + } + } else + throw new GroovyRuntimeException("Infinite loop in " + self + ".downto(" + to + ")"); + } + + /** + * Iterates from this number down to the given number, inclusive, + * decrementing by one each time. + * + * @param self a Float + * @param to the end number + * @param closure the code to execute for each number + * @since 1.0 + */ + public static void downto(Float self, Number to, Closure closure) { + float to1 = to.floatValue(); + if (self >= to1) { + for (float i = self; i >= to1; i--) { + closure.call(i); + } + } else + throw new GroovyRuntimeException("Infinite loop in " + self + ".downto(" + to + ")"); + } + + /** + * Iterates from this number down to the given number, inclusive, + * decrementing by one each time. + * + * @param self a double + * @param to the end number + * @param closure the code to execute for each number + * @since 1.0 + */ + public static void downto(double self, Number to, Closure closure) { + double to1 = to.doubleValue(); + if (self >= to1) { + for (double i = self; i >= to1; i--) { + closure.call(i); + } + } else + throw new GroovyRuntimeException("Infinite loop in " + self + ".downto(" + to + ")"); + } + + /** + * Iterates from this number down to the given number, inclusive, + * decrementing by one each time. + * + * @param self a Double + * @param to the end number + * @param closure the code to execute for each number + * @since 1.0 + */ + public static void downto(Double self, Number to, Closure closure) { + double to1 = to.doubleValue(); + if (self >= to1) { + for (double i = self; i >= to1; i--) { + closure.call(i); + } + } else + throw new GroovyRuntimeException("Infinite loop in " + self + ".downto(" + to + ")"); + } + + /** + * Iterates from this number down to the given number, inclusive, + * decrementing by one each time. + * + * @param self a BigInteger + * @param to the end number + * @param closure the code to execute for each number + * @since 1.0 + */ + public static void downto(BigInteger self, Number to, Closure closure) { + if (to instanceof BigDecimal) { + final BigDecimal one = BigDecimal.valueOf(10, 1); // That's what you get for "1.0". + final BigDecimal to1 = (BigDecimal) to; + final BigDecimal selfD = new BigDecimal(self); + if (selfD.compareTo(to1) >= 0) { + for (BigDecimal i = selfD; i.compareTo(to1) >= 0; i = i.subtract(one)) { + closure.call(i.toBigInteger()); + } + } else + throw new GroovyRuntimeException("Infinite loop in " + self + ".downto(" + to + ")"); + } else if (to instanceof BigInteger) { + final BigInteger one = BigInteger.valueOf(1); + final BigInteger to1 = (BigInteger) to; + if (self.compareTo(to1) >= 0) { + for (BigInteger i = self; i.compareTo(to1) >= 0; i = i.subtract(one)) { + closure.call(i); + } + } else + throw new GroovyRuntimeException("Infinite loop in " + self + ".downto(" + to + ")"); + } else { + final BigInteger one = BigInteger.valueOf(1); + final BigInteger to1 = new BigInteger(to.toString()); + if (self.compareTo(to1) >= 0) { + for (BigInteger i = self; i.compareTo(to1) >= 0; i = i.subtract(one)) { + closure.call(i); + } + } else + throw new GroovyRuntimeException("Infinite loop in " + self + ".downto(" + to + ")"); + } + } + + /** + * Iterates from this number down to the given number, inclusive, + * decrementing by one each time. Each number is passed to the closure. + * Example: + *
    10.5.downto(0) {
    +     *   println it
    +     * }
    + * Prints numbers 10.5, 9.5 ... to 0.5. + * + * @param self a BigDecimal + * @param to the end number + * @param closure the code to execute for each number + * @since 1.0 + */ + public static void downto(BigDecimal self, Number to, Closure closure) { + final BigDecimal one = BigDecimal.valueOf(10, 1); // Quick way to get "1.0". + if (to instanceof BigDecimal) { + BigDecimal to1 = (BigDecimal) to; + if (self.compareTo(to1) >= 0) { + for (BigDecimal i = self; i.compareTo(to1) >= 0; i = i.subtract(one)) { + closure.call(i); + } + } else + throw new GroovyRuntimeException("Infinite loop in " + self + ".downto(" + to + ")"); + } else if (to instanceof BigInteger) { + BigDecimal to1 = new BigDecimal((BigInteger) to); + if (self.compareTo(to1) >= 0) { + for (BigDecimal i = self; i.compareTo(to1) >= 0; i = i.subtract(one)) { + closure.call(i); + } + } else + throw new GroovyRuntimeException("Infinite loop in " + self + ".downto(" + to + ")"); + } else { + BigDecimal to1 = new BigDecimal(to.toString()); + if (self.compareTo(to1) >= 0) { + for (BigDecimal i = self; i.compareTo(to1) >= 0; i = i.subtract(one)) { + closure.call(i); + } + } else + throw new GroovyRuntimeException("Infinite loop in " + self + ".downto(" + to + ")"); + } + } + + /** + * Iterates from this number up to the given number using a step increment. + * Each intermediate number is passed to the given closure. Example: + *
    0.step( 10, 2 ) {
    +     *   println it
    +     * }
    + * Prints even numbers 0 through 8. + * + * @param self a Number to start with + * @param to a Number to go up to, exclusive + * @param stepNumber a Number representing the step increment + * @param closure the closure to call + * @since 1.0 + */ + public static void step(Number self, Number to, Number stepNumber, Closure closure) { + if (self instanceof BigDecimal || to instanceof BigDecimal || stepNumber instanceof BigDecimal) { + final BigDecimal zero = BigDecimal.valueOf(0, 1); // Same as "0.0". + BigDecimal self1 = (self instanceof BigDecimal) ? (BigDecimal) self : new BigDecimal(self.toString()); + BigDecimal to1 = (to instanceof BigDecimal) ? (BigDecimal) to : new BigDecimal(to.toString()); + BigDecimal stepNumber1 = (stepNumber instanceof BigDecimal) ? (BigDecimal) stepNumber : new BigDecimal(stepNumber.toString()); + if (stepNumber1.compareTo(zero) > 0 && to1.compareTo(self1) > 0) { + for (BigDecimal i = self1; i.compareTo(to1) < 0; i = i.add(stepNumber1)) { + closure.call(i); + } + } else if (stepNumber1.compareTo(zero) < 0 && to1.compareTo(self1) < 0) { + for (BigDecimal i = self1; i.compareTo(to1) > 0; i = i.add(stepNumber1)) { + closure.call(i); + } + } else if(self1.compareTo(to1) != 0) + throw new GroovyRuntimeException("Infinite loop in " + self1 + ".step(" + to1 + ", " + stepNumber1 + ")"); + } else if (self instanceof BigInteger || to instanceof BigInteger || stepNumber instanceof BigInteger) { + final BigInteger zero = BigInteger.valueOf(0); + BigInteger self1 = (self instanceof BigInteger) ? (BigInteger) self : new BigInteger(self.toString()); + BigInteger to1 = (to instanceof BigInteger) ? (BigInteger) to : new BigInteger(to.toString()); + BigInteger stepNumber1 = (stepNumber instanceof BigInteger) ? (BigInteger) stepNumber : new BigInteger(stepNumber.toString()); + if (stepNumber1.compareTo(zero) > 0 && to1.compareTo(self1) > 0) { + for (BigInteger i = self1; i.compareTo(to1) < 0; i = i.add(stepNumber1)) { + closure.call(i); + } + } else if (stepNumber1.compareTo(zero) < 0 && to1.compareTo(self1) < 0) { + for (BigInteger i = self1; i.compareTo(to1) > 0; i = i.add(stepNumber1)) { + closure.call(i); + } + } else if(self1.compareTo(to1) != 0) + throw new GroovyRuntimeException("Infinite loop in " + self1 + ".step(" + to1 + ", " + stepNumber1 + ")"); + } else { + int self1 = self.intValue(); + int to1 = to.intValue(); + int stepNumber1 = stepNumber.intValue(); + if (stepNumber1 > 0 && to1 > self1) { + for (int i = self1; i < to1; i += stepNumber1) { + closure.call(i); + } + } else if (stepNumber1 < 0 && to1 < self1) { + for (int i = self1; i > to1; i += stepNumber1) { + closure.call(i); + } + } else if(self1 != to1) + throw new GroovyRuntimeException("Infinite loop in " + self1 + ".step(" + to1 + ", " + stepNumber1 + ")"); + } + } + + /** + * Get the absolute value + * + * @param number a Number + * @return the absolute value of that Number + * @since 1.0 + */ + //Note: This method is NOT called if number is a BigInteger or BigDecimal because + //those classes implement a method with a better exact match. + public static int abs(Number number) { + return Math.abs(number.intValue()); + } + + /** + * Get the absolute value + * + * @param number a Long + * @return the absolute value of that Long + * @since 1.0 + */ + public static long abs(Long number) { + return Math.abs(number.longValue()); + } + + /** + * Get the absolute value + * + * @param number a Float + * @return the absolute value of that Float + * @since 1.0 + */ + public static float abs(Float number) { + return Math.abs(number.floatValue()); + } + + /** + * Get the absolute value + * + * @param number a Double + * @return the absolute value of that Double + * @since 1.0 + */ + public static double abs(Double number) { + return Math.abs(number); + } + + /** + * Round the value + * + * @param number a Float + * @return the rounded value of that Float + * @since 1.0 + */ + public static int round(Float number) { + return Math.round(number.floatValue()); + } + + /** + * Round the value + * + * @param number a Float + * @param precision the number of decimal places to keep + * @return the Float rounded to the number of decimal places specified by precision + * @since 1.6.0 + */ + public static float round(Float number, int precision) { + return (float)(Math.floor(number.doubleValue()*Math.pow(10,precision)+0.5)/Math.pow(10,precision)); + } + + /** + * Truncate the value + * + * @param number a Float + * @param precision the number of decimal places to keep + * @return the Float truncated to the number of decimal places specified by precision + * @since 1.6.0 + */ + public static float trunc(Float number, int precision) { + return (float)(Math.floor(number.doubleValue()*Math.pow(10,precision))/Math.pow(10,precision)); + } + + /** + * Truncate the value + * + * @param number a Double + * @return the Double truncated to 0 decimal places (i.e. a synonym for floor) + * @since 1.6.0 + */ + public static float trunc(Float number) { + return (float)Math.floor(number.doubleValue()); + } + + /** + * Round the value + * + * @param number a Double + * @return the rounded value of that Double + * @since 1.0 + */ + public static long round(Double number) { + return Math.round(number); + } + + /** + * Round the value + * + * @param number a Double + * @param precision the number of decimal places to keep + * @return the Double rounded to the number of decimal places specified by precision + * @since 1.6.4 + */ + public static double round(Double number, int precision) { + return Math.floor(number *Math.pow(10,precision)+0.5)/Math.pow(10,precision); + } + + /** + * Truncate the value + * + * @param number a Double + * @return the Double truncated to 0 decimal places (i.e. a synonym for floor) + * @since 1.6.4 + */ + public static double trunc(Double number) { + return Math.floor(number); + } + + /** + * Truncate the value + * + * @param number a Double + * @param precision the number of decimal places to keep + * @return the Double truncated to the number of decimal places specified by precision + * @since 1.6.4 + */ + public static double trunc(Double number, int precision) { + return Math.floor(number *Math.pow(10,precision))/Math.pow(10,precision); + } + + /** + * Parse a String into an Integer + * + * @param self a String + * @return an Integer + * @since 1.0 + */ + public static Integer toInteger(String self) { + return Integer.valueOf(self.trim()); + } + + /** + * Parse a String into a Long + * + * @param self a String + * @return a Long + * @since 1.0 + */ + public static Long toLong(String self) { + return Long.valueOf(self.trim()); + } + + /** + * Parse a String into a Short + * + * @param self a String + * @return a Short + * @since 1.5.7 + */ + public static Short toShort(String self) { + return Short.valueOf(self.trim()); + } + + /** + * Parse a String into a Float + * + * @param self a String + * @return a Float + * @since 1.0 + */ + public static Float toFloat(String self) { + return Float.valueOf(self.trim()); + } + + /** + * Parse a String into a Double + * + * @param self a String + * @return a Double + * @since 1.0 + */ + public static Double toDouble(String self) { + return Double.valueOf(self.trim()); + } + + /** + * Parse a String into a BigInteger + * + * @param self a String + * @return a BigInteger + * @since 1.0 + */ + public static BigInteger toBigInteger(String self) { + return new BigInteger(self.trim()); + } + + /** + * Parse a String into a BigDecimal + * + * @param self a String + * @return a BigDecimal + * @since 1.0 + */ + public static BigDecimal toBigDecimal(String self) { + return new BigDecimal(self.trim()); + } + + /** + * Determine if a String can be parsed into an Integer. + * + * @param self a String + * @return true if the string can be parsed + * @since 1.5.0 + */ + public static boolean isInteger(String self) { + try { + Integer.valueOf(self.trim()); + return true; + } catch (NumberFormatException nfe) { + return false; + } + } + + /** + * Determine if a String can be parsed into a Long. + * + * @param self a String + * @return true if the string can be parsed + * @since 1.5.0 + */ + public static boolean isLong(String self) { + try { + Long.valueOf(self.trim()); + return true; + } catch (NumberFormatException nfe) { + return false; + } + } + + /** + * Determine if a String can be parsed into a Float. + * + * @param self a String + * @return true if the string can be parsed + * @since 1.5.0 + */ + public static boolean isFloat(String self) { + try { + Float.valueOf(self.trim()); + return true; + } catch (NumberFormatException nfe) { + return false; + } + } + + /** + * Determine if a String can be parsed into a Double. + * + * @param self a String + * @return true if the string can be parsed + * @since 1.5.0 + */ + public static boolean isDouble(String self) { + try { + Double.valueOf(self.trim()); + return true; + } catch (NumberFormatException nfe) { + return false; + } + } + + /** + * Determine if a String can be parsed into a BigInteger. + * + * @param self a String + * @return true if the string can be parsed + * @since 1.5.0 + */ + public static boolean isBigInteger(String self) { + try { + new BigInteger(self.trim()); + return true; + } catch (NumberFormatException nfe) { + return false; + } + } + + /** + * Determine if a String can be parsed into a BigDecimal. + * + * @param self a String + * @return true if the string can be parsed + * @since 1.5.0 + */ + public static boolean isBigDecimal(String self) { + try { + new BigDecimal(self.trim()); + return true; + } catch (NumberFormatException nfe) { + return false; + } + } + + /** + * Determine if a String can be parsed into a Number. + * Synonym for 'isBigDecimal()'. + * + * @param self a String + * @return true if the string can be parsed + * @see #isBigDecimal(java.lang.String) + * @since 1.5.0 + */ + public static boolean isNumber(String self) { + return isBigDecimal(self); + } + + /** + * Parse a CharSequence into an Integer + * + * @param self a CharSequence + * @return an Integer + * @see #toInteger(java.lang.String) + * @since 1.8.2 + */ + public static Integer toInteger(CharSequence self) { + return toInteger(self.toString()); + } + + /** + * Parse a CharSequence into a Long + * + * @param self a CharSequence + * @return a Long + * @see #toLong(java.lang.String) + * @since 1.8.2 + */ + public static Long toLong(CharSequence self) { + return toLong(self.toString()); + } + + /** + * Parse a CharSequence into a Short + * + * @param self a CharSequence + * @return a Short + * @see #toShort(java.lang.String) + * @since 1.8.2 + */ + public static Short toShort(CharSequence self) { + return toShort(self.toString()); + } + + /** + * Parse a CharSequence into a Float + * + * @param self a CharSequence + * @return a Float + * @see #toFloat(java.lang.String) + * @since 1.8.2 + */ + public static Float toFloat(CharSequence self) { + return toFloat(self.toString()); + } + + /** + * Parse a CharSequence into a Double + * + * @param self a CharSequence + * @return a Double + * @see #toDouble(java.lang.String) + * @since 1.8.2 + */ + public static Double toDouble(CharSequence self) { + return toDouble(self.toString()); + } + + /** + * Parse a CharSequence into a BigInteger + * + * @param self a CharSequence + * @return a BigInteger + * @see #toBigInteger(java.lang.String) + * @since 1.8.2 + */ + public static BigInteger toBigInteger(CharSequence self) { + return toBigInteger(self.toString()); + } + + /** + * Parse a CharSequence into a BigDecimal + * + * @param self a CharSequence + * @return a BigDecimal + * @see #toBigDecimal(java.lang.String) + * @since 1.8.2 + */ + public static BigDecimal toBigDecimal(CharSequence self) { + return toBigDecimal(self.toString()); + } + + /** + * Determine if a CharSequence can be parsed as an Integer. + * + * @param self a CharSequence + * @return true if the CharSequence can be parsed + * @see #isInteger(java.lang.String) + * @since 1.8.2 + */ + public static boolean isInteger(CharSequence self) { + return isInteger(self.toString()); + } + + /** + * Determine if a CharSequence can be parsed as a Long. + * + * @param self a CharSequence + * @return true if the CharSequence can be parsed + * @see #isLong(java.lang.String) + * @since 1.8.2 + */ + public static boolean isLong(CharSequence self) { + return isLong(self.toString()); + } + + /** + * Determine if a CharSequence can be parsed as a Float. + * + * @param self a CharSequence + * @return true if the CharSequence can be parsed + * @see #isFloat(java.lang.String) + * @since 1.8.2 + */ + public static boolean isFloat(CharSequence self) { + return isFloat(self.toString()); + } + + /** + * Determine if a CharSequence can be parsed as a Double. + * + * @param self a CharSequence + * @return true if the CharSequence can be parsed + * @see #isDouble(java.lang.String) + * @since 1.8.2 + */ + public static boolean isDouble(CharSequence self) { + return isDouble(self.toString()); + } + + /** + * Determine if a CharSequence can be parsed as a BigInteger. + * + * @param self a CharSequence + * @return true if the CharSequence can be parsed + * @see #isBigInteger(java.lang.String) + * @since 1.8.2 + */ + public static boolean isBigInteger(CharSequence self) { + return isBigInteger(self.toString()); + } + + /** + * Determine if a CharSequence can be parsed as a BigDecimal. + * + * @param self a CharSequence + * @return true if the CharSequence can be parsed + * @see #isBigDecimal(java.lang.String) + * @since 1.8.2 + */ + public static boolean isBigDecimal(CharSequence self) { + return isBigDecimal(self.toString()); + } + + /** + * Determine if a CharSequence can be parsed as a Number. + * Synonym for 'isBigDecimal()'. + * + * @param self a CharSequence + * @return true if the CharSequence can be parsed + * @see #isNumber(java.lang.String) + * @since 1.8.2 + */ + public static boolean isNumber(CharSequence self) { + return isNumber(self.toString()); + } + + /** + * Determine if a Character is uppercase. + * Synonym for 'Character.isUpperCase(this)'. + * + * @param self a Character + * @return true if the character is uppercase + * @see java.lang.Character#isUpperCase(char) + * @since 1.5.7 + */ + public static boolean isUpperCase(Character self) { + return Character.isUpperCase(self); + } + + /** + * Determine if a Character is lowercase. + * Synonym for 'Character.isLowerCase(this)'. + * + * @param self a Character + * @return true if the character is lowercase + * @see java.lang.Character#isLowerCase(char) + * @since 1.5.7 + */ + public static boolean isLowerCase(Character self) { + return Character.isLowerCase(self); + } + + /** + * Determines if a character is a letter. + * Synonym for 'Character.isLetter(this)'. + * + * @param self a Character + * @return true if the character is a letter + * @see java.lang.Character#isLetter(char) + * @since 1.5.7 + */ + public static boolean isLetter(Character self) { + return Character.isLetter(self); + } + + /** + * Determines if a character is a digit. + * Synonym for 'Character.isDigit(this)'. + * + * @param self a Character + * @return true if the character is a digit + * @see java.lang.Character#isDigit(char) + * @since 1.5.7 + */ + public static boolean isDigit(Character self) { + return Character.isDigit(self); + } + + /** + * Determines if a character is a letter or digit. + * Synonym for 'Character.isLetterOrDigit(this)'. + * + * @param self a Character + * @return true if the character is a letter or digit + * @see java.lang.Character#isLetterOrDigit(char) + * @since 1.5.7 + */ + public static boolean isLetterOrDigit(Character self) { + return Character.isLetterOrDigit(self); + } + + /** + * Determines if a character is a whitespace character. + * Synonym for 'Character.isWhitespace(this)'. + * + * @param self a Character + * @return true if the character is a whitespace character + * @see java.lang.Character#isWhitespace(char) + * @since 1.5.7 + */ + public static boolean isWhitespace(Character self) { + return Character.isWhitespace(self); + } + + /** + * Converts the character to uppercase. + * Synonym for 'Character.toUpperCase(this)'. + * + * @param self a Character to convert + * @return the uppercase equivalent of the character, if any; + * otherwise, the character itself. + * @see java.lang.Character#isUpperCase(char) + * @see java.lang.String#toUpperCase() + * @since 1.5.7 + */ + public static char toUpperCase(Character self) { + return Character.toUpperCase(self); + } + + /** + * Converts the character to lowercase. + * Synonym for 'Character.toLowerCase(this)'. + * + * @param self a Character to convert + * @return the lowercase equivalent of the character, if any; + * otherwise, the character itself. + * @see java.lang.Character#isLowerCase(char) + * @see java.lang.String#toLowerCase() + * @since 1.5.7 + */ + public static char toLowerCase(Character self) { + return Character.toLowerCase(self); + } + + /** + * Transform a Number into an Integer + * + * @param self a Number + * @return an Integer + * @since 1.0 + */ + public static Integer toInteger(Number self) { + return self.intValue(); + } + + /** + * Transform a Number into a Long + * + * @param self a Number + * @return an Long + * @since 1.0 + */ + public static Long toLong(Number self) { + return self.longValue(); + } + + /** + * Transform a Number into a Float + * + * @param self a Number + * @return an Float + * @since 1.0 + */ + public static Float toFloat(Number self) { + return self.floatValue(); + } + + /** + * Transform a Number into a Double + * + * @param self a Number + * @return an Double + * @since 1.0 + */ + public static Double toDouble(Number self) { + // Conversions in which all decimal digits are known to be good. + if ((self instanceof Double) + || (self instanceof Long) + || (self instanceof Integer) + || (self instanceof Short) + || (self instanceof Byte)) + { + return self.doubleValue(); + } + + // Chances are this is a Float or a Big. + // With Float we're extending binary precision and that gets ugly in decimal. + // If we used Float.doubleValue() on 0.1f we get 0.10000000149011612. + // Note that this is different than casting '(double) 0.1f' which will do the + // binary extension just like in Java. + // With Bigs and other unknowns, this is likely to be the same. + + return Double.valueOf(self.toString()); + } + + /** + * Transform a Number into a BigDecimal + * + * @param self a Number + * @return an BigDecimal + * @since 1.0 + */ + public static BigDecimal toBigDecimal(Number self) { + // Quick method for scalars. + if ((self instanceof Long) + || (self instanceof Integer) + || (self instanceof Short) + || (self instanceof Byte)) + { + return BigDecimal.valueOf(self.longValue()); + } + + return new BigDecimal(self.toString()); + } + + /** + * Transform this number to a the given type, using the 'as' operator. The + * following types are supported in addition to the default + * {@link #asType(java.lang.Object, java.lang.Class)}: + *
      + *
    • BigDecimal
    • + *
    • BigInteger
    • + *
    • Double
    • + *
    • Float
    • + *
    + * @param self this number + * @param c the desired type of the transformed result + * @return an instance of the given type + * @since 1.0 + */ + @SuppressWarnings("unchecked") + public static T asType(Number self, Class c) { + if (c == BigDecimal.class) { + return (T) toBigDecimal(self); + } else if (c == BigInteger.class) { + return (T) toBigInteger(self); + } else if (c == Double.class) { + return (T) toDouble(self); + } else if (c == Float.class) { + return (T) toFloat(self); + } + return asType((Object) self, c); + } + + /** + * Transform this Number into a BigInteger. + * + * @param self a Number + * @return an BigInteger + * @since 1.0 + */ + public static BigInteger toBigInteger(Number self) { + if (self instanceof BigInteger) { + return (BigInteger) self; + } else if (self instanceof BigDecimal) { + return ((BigDecimal) self).toBigInteger(); + } else if (self instanceof Double) { + return new BigDecimal((Double)self).toBigInteger(); + } else if (self instanceof Float) { + return new BigDecimal((Float)self).toBigInteger(); + } else { + return new BigInteger(Long.toString(self.longValue())); + } + } + + // Boolean based methods + //------------------------------------------------------------------------- + + + /** + * Logical conjunction of two boolean operators. + * + * @param left left operator + * @param right right operator + * @return result of logical conjunction + * @since 1.0 + */ + public static Boolean and(Boolean left, Boolean right) { + return left && right; + } + + /** + * Logical disjunction of two boolean operators + * + * @param left left operator + * @param right right operator + * @return result of logical disjunction + * @since 1.0 + */ + public static Boolean or(Boolean left, Boolean right) { + return left || right; + } + + /** + * Logical implication of two boolean operators + * + * @param left left operator + * @param right right operator + * @return result of logical implication + * @since 1.8.3 + */ + public static Boolean implies(Boolean left, Boolean right) { + return !left || right; + } + + /** + * Exclusive disjunction of two boolean operators + * + * @param left left operator + * @param right right operator + * @return result of exclusive disjunction + * @since 1.0 + */ + public static Boolean xor(Boolean left, Boolean right) { + return left ^ right; + } + +// public static Boolean negate(Boolean left) { +// return Boolean.valueOf(!left.booleanValue()); +// } + + // File and stream based methods + //------------------------------------------------------------------------- + + /** + * Create an object output stream for this file. + * + * @param file a file + * @return an object output stream + * @throws IOException if an IOException occurs. + * @since 1.5.0 + */ + public static ObjectOutputStream newObjectOutputStream(File file) throws IOException { + return new ObjectOutputStream(new FileOutputStream(file)); + } + + /** + * Create an object output stream for this output stream. + * + * @param outputStream an output stream + * @return an object output stream + * @throws IOException if an IOException occurs. + * @since 1.5.0 + */ + public static ObjectOutputStream newObjectOutputStream(OutputStream outputStream) throws IOException { + return new ObjectOutputStream(outputStream); + } + + /** + * Create a new ObjectOutputStream for this file and then pass it to the + * closure. This method ensures the stream is closed after the closure + * returns. + * + * @param file a File + * @param closure a closure + * @return the value returned by the closure + * @throws IOException if an IOException occurs. + * @see #withStream(java.io.OutputStream, groovy.lang.Closure) + * @since 1.5.0 + */ + public static T withObjectOutputStream(File file, Closure closure) throws IOException { + return withStream(newObjectOutputStream(file), closure); + } + + /** + * Create a new ObjectOutputStream for this output stream and then pass it to the + * closure. This method ensures the stream is closed after the closure + * returns. + * + * @param outputStream am output stream + * @param closure a closure + * @return the value returned by the closure + * @throws IOException if an IOException occurs. + * @see #withStream(java.io.OutputStream, groovy.lang.Closure) + * @since 1.5.0 + */ + public static T withObjectOutputStream(OutputStream outputStream, Closure closure) throws IOException { + return withStream(newObjectOutputStream(outputStream), closure); + } + + /** + * Create an object input stream for this file. + * + * @param file a file + * @return an object input stream + * @throws IOException if an IOException occurs. + * @since 1.5.0 + */ + public static ObjectInputStream newObjectInputStream(File file) throws IOException { + return new ObjectInputStream(new FileInputStream(file)); + } + + /** + * Create an object input stream for this input stream. + * + * @param inputStream an input stream + * @return an object input stream + * @throws IOException if an IOException occurs. + * @since 1.5.0 + */ + public static ObjectInputStream newObjectInputStream(InputStream inputStream) throws IOException { + return new ObjectInputStream(inputStream); + } + + /** + * Create an object input stream for this input stream using the given class loader. + * + * @param inputStream an input stream + * @param classLoader the class loader to use when loading the class + * @return an object input stream + * @throws IOException if an IOException occurs. + * @since 1.5.0 + */ + public static ObjectInputStream newObjectInputStream(InputStream inputStream, final ClassLoader classLoader) throws IOException { + return new ObjectInputStream(inputStream) { + protected Class resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException { + return Class.forName(desc.getName(), true, classLoader); + + } + }; + } + + /** + * Create an object input stream for this file using the given class loader. + * + * @param file a file + * @param classLoader the class loader to use when loading the class + * @return an object input stream + * @throws IOException if an IOException occurs. + * @since 1.5.0 + */ + public static ObjectInputStream newObjectInputStream(File file, final ClassLoader classLoader) throws IOException { + return newObjectInputStream(new FileInputStream(file), classLoader); + } + + /** + * Iterates through the given file object by object. + * + * @param self a File + * @param closure a closure + * @throws IOException if an IOException occurs. + * @throws ClassNotFoundException if the class is not found. + * @see #eachObject(java.io.ObjectInputStream, groovy.lang.Closure) + * @since 1.0 + */ + public static void eachObject(File self, Closure closure) throws IOException, ClassNotFoundException { + eachObject(newObjectInputStream(self), closure); + } + + /** + * Iterates through the given object stream object by object. The + * ObjectInputStream is closed afterwards. + * + * @param ois an ObjectInputStream, closed after the operation + * @param closure a closure + * @throws IOException if an IOException occurs. + * @throws ClassNotFoundException if the class is not found. + * @since 1.0 + */ + public static void eachObject(ObjectInputStream ois, Closure closure) throws IOException, ClassNotFoundException { + try { + while (true) { + try { + Object obj = ois.readObject(); + // we allow null objects in the object stream + closure.call(obj); + } catch (EOFException e) { + break; + } + } + InputStream temp = ois; + ois = null; + temp.close(); + } finally { + closeWithWarning(ois); + } + } + + /** + * Create a new ObjectInputStream for this file and pass it to the closure. + * This method ensures the stream is closed after the closure returns. + * + * @param file a File + * @param closure a closure + * @return the value returned by the closure + * @throws IOException if an IOException occurs. + * @see #withStream(java.io.InputStream, groovy.lang.Closure) + * @since 1.5.2 + */ + public static T withObjectInputStream(File file, Closure closure) throws IOException { + return withStream(newObjectInputStream(file), closure); + } + + /** + * Create a new ObjectInputStream for this file associated with the given class loader and pass it to the closure. + * This method ensures the stream is closed after the closure returns. + * + * @param file a File + * @param classLoader the class loader to use when loading the class + * @param closure a closure + * @return the value returned by the closure + * @throws IOException if an IOException occurs. + * @see #withStream(java.io.InputStream, groovy.lang.Closure) + * @since 1.5.2 + */ + public static T withObjectInputStream(File file, ClassLoader classLoader, Closure closure) throws IOException { + return withStream(newObjectInputStream(file, classLoader), closure); + } + + /** + * Create a new ObjectInputStream for this file and pass it to the closure. + * This method ensures the stream is closed after the closure returns. + * + * @param inputStream an input stream + * @param closure a closure + * @return the value returned by the closure + * @throws IOException if an IOException occurs. + * @see #withStream(java.io.InputStream, groovy.lang.Closure) + * @since 1.5.0 + */ + public static T withObjectInputStream(InputStream inputStream, Closure closure) throws IOException { + return withStream(newObjectInputStream(inputStream), closure); + } + + /** + * Create a new ObjectInputStream for this file and pass it to the closure. + * This method ensures the stream is closed after the closure returns. + * + * @param inputStream an input stream + * @param classLoader the class loader to use when loading the class + * @param closure a closure + * @return the value returned by the closure + * @throws IOException if an IOException occurs. + * @see #withStream(java.io.InputStream, groovy.lang.Closure) + * @since 1.5.0 + */ + public static T withObjectInputStream(InputStream inputStream, ClassLoader classLoader, Closure closure) throws IOException { + return withStream(newObjectInputStream(inputStream, classLoader), closure); + } + + /** + * Iterates through this String line by line. Each line is passed + * to the given 1 or 2 arg closure. If a 2 arg closure is found + * the line count is passed as the second argument. + * + * @param self a String + * @param closure a closure + * @return the last value returned by the closure + * @throws java.io.IOException if an error occurs + * @see #eachLine(java.lang.String, int, groovy.lang.Closure) + * @since 1.5.5 + */ + public static T eachLine(String self, Closure closure) throws IOException { + return eachLine(self, 0, closure); + } + + /** + * Iterates through this CharSequence line by line. Each line is passed + * to the given 1 or 2 arg closure. If a 2 arg closure is found + * the line count is passed as the second argument. + * + * @param self a CharSequence + * @param closure a closure + * @return the last value returned by the closure + * @throws java.io.IOException if an error occurs + * @see #eachLine(java.lang.String, groovy.lang.Closure) + * @since 1.8.2 + */ + public static T eachLine(CharSequence self, Closure closure) throws IOException { + return eachLine(self.toString(), closure); + } + + /** + * Iterates through this String line by line. Each line is passed + * to the given 1 or 2 arg closure. If a 2 arg closure is found + * the line count is passed as the second argument. + * + * @param self a String + * @param firstLine the line number value used for the first line (default is 1, set to 0 to start counting from 0) + * @param closure a closure (arg 1 is line, optional arg 2 is line number) + * @return the last value returned by the closure + * @throws java.io.IOException if an error occurs + * @since 1.5.7 + */ + public static T eachLine(String self, int firstLine, Closure closure) throws IOException { + int count = firstLine; + T result = null; + for (String line : readLines(self)) { + result = callClosureForLine(closure, line, count); + count++; + } + return result; + } + + /** + * Iterates through this CharSequence line by line. Each line is passed + * to the given 1 or 2 arg closure. If a 2 arg closure is found + * the line count is passed as the second argument. + * + * @param self a CharSequence + * @param firstLine the line number value used for the first line (default is 1, set to 0 to start counting from 0) + * @param closure a closure (arg 1 is line, optional arg 2 is line number) + * @return the last value returned by the closure + * @throws java.io.IOException if an error occurs + * @see #eachLine(java.lang.String, int, groovy.lang.Closure) + * @since 1.8.2 + */ + public static T eachLine(CharSequence self, int firstLine, Closure closure) throws IOException { + return eachLine(self.toString(), firstLine, closure); + } + + /** + * Iterates through this file line by line. Each line is passed to the + * given 1 or 2 arg closure. The file is read using a reader which + * is closed before this method returns. + * + * @param self a File + * @param closure a closure (arg 1 is line, optional arg 2 is line number starting at line 1) + * @throws IOException if an IOException occurs. + * @return the last value returned by the closure + * @see #eachLine(java.io.File, int, groovy.lang.Closure) + * @since 1.5.5 + */ + public static T eachLine(File self, Closure closure) throws IOException { + return eachLine(self, 1, closure); + } + + /** + * Iterates through this file line by line. Each line is passed to the + * given 1 or 2 arg closure. The file is read using a reader which + * is closed before this method returns. + * + * @param self a File + * @param charset opens the file with a specified charset + * @param closure a closure (arg 1 is line, optional arg 2 is line number starting at line 1) + * @throws IOException if an IOException occurs. + * @return the last value returned by the closure + * @see #eachLine(java.io.File, java.lang.String, int, groovy.lang.Closure) + * @since 1.6.8 + */ + public static T eachLine(File self, String charset, Closure closure) throws IOException { + return eachLine(self, charset, 1, closure); + } + + /** + * Iterates through this file line by line. Each line is passed + * to the given 1 or 2 arg closure. The file is read using a reader + * which is closed before this method returns. + * + * @param self a File + * @param firstLine the line number value used for the first line (default is 1, set to 0 to start counting from 0) + * @param closure a closure (arg 1 is line, optional arg 2 is line number) + * @throws IOException if an IOException occurs. + * @return the last value returned by the closure + * @see #eachLine(java.io.Reader, int, groovy.lang.Closure) + * @since 1.5.7 + */ + public static T eachLine(File self, int firstLine, Closure closure) throws IOException { + return eachLine(newReader(self), firstLine, closure); + } + + /** + * Iterates through this file line by line. Each line is passed + * to the given 1 or 2 arg closure. The file is read using a reader + * which is closed before this method returns. + * + * @param self a File + * @param charset opens the file with a specified charset + * @param firstLine the line number value used for the first line (default is 1, set to 0 to start counting from 0) + * @param closure a closure (arg 1 is line, optional arg 2 is line number) + * @throws IOException if an IOException occurs. + * @return the last value returned by the closure + * @see #eachLine(java.io.Reader, int, groovy.lang.Closure) + * @since 1.6.8 + */ + public static T eachLine(File self, String charset, int firstLine, Closure closure) throws IOException { + return eachLine(newReader(self, charset), firstLine, closure); + } + + /** + * Iterates through this stream reading with the provided charset, passing each line to the + * given 1 or 2 arg closure. The stream is closed before this method returns. + * + * @param stream a stream + * @param charset opens the stream with a specified charset + * @param closure a closure (arg 1 is line, optional arg 2 is line number starting at line 1) + * @throws IOException if an IOException occurs. + * @return the last value returned by the closure + * @see #eachLine(java.io.InputStream, java.lang.String, int, groovy.lang.Closure) + * @since 1.5.5 + */ + public static T eachLine(InputStream stream, String charset, Closure closure) throws IOException { + return eachLine(stream, charset, 1, closure); + } + + /** + * Iterates through this stream reading with the provided charset, passing each line to + * the given 1 or 2 arg closure. The stream is closed after this method returns. + * + * @param stream a stream + * @param charset opens the stream with a specified charset + * @param firstLine the line number value used for the first line (default is 1, set to 0 to start counting from 0) + * @param closure a closure (arg 1 is line, optional arg 2 is line number) + * @return the last value returned by the closure + * @throws IOException if an IOException occurs. + * @see #eachLine(java.io.Reader, int, groovy.lang.Closure) + * @since 1.5.7 + */ + public static T eachLine(InputStream stream, String charset, int firstLine, Closure closure) throws IOException { + return eachLine(new InputStreamReader(stream, charset), firstLine, closure); + } + + /** + * Iterates through this stream, passing each line to the given 1 or 2 arg closure. + * The stream is closed before this method returns. + * + * @param stream a stream + * @param closure a closure (arg 1 is line, optional arg 2 is line number starting at line 1) + * @throws IOException if an IOException occurs. + * @return the last value returned by the closure + * @see #eachLine(java.io.InputStream, int, groovy.lang.Closure) + * @since 1.5.6 + */ + public static T eachLine(InputStream stream, Closure closure) throws IOException { + return eachLine(stream, 1, closure); + } + + /** + * Iterates through this stream, passing each line to the given 1 or 2 arg closure. + * The stream is closed before this method returns. + * + * @param stream a stream + * @param firstLine the line number value used for the first line (default is 1, set to 0 to start counting from 0) + * @param closure a closure (arg 1 is line, optional arg 2 is line number) + * @throws IOException if an IOException occurs. + * @return the last value returned by the closure + * @see #eachLine(java.io.Reader, int, groovy.lang.Closure) + * @since 1.5.7 + */ + public static T eachLine(InputStream stream, int firstLine, Closure closure) throws IOException { + return eachLine(new InputStreamReader(stream), firstLine, closure); + } + + /** + * Iterates through the lines read from the URL's associated input stream passing each + * line to the given 1 or 2 arg closure. The stream is closed before this method returns. + * + * @param url a URL to open and read + * @param closure a closure to apply on each line (arg 1 is line, optional arg 2 is line number starting at line 1) + * @return the last value returned by the closure + * @throws IOException if an IOException occurs. + * @see #eachLine(java.net.URL, int, groovy.lang.Closure) + * @since 1.5.6 + */ + public static T eachLine(URL url, Closure closure) throws IOException { + return eachLine(url, 1, closure); + } + + /** + * Iterates through the lines read from the URL's associated input stream passing each + * line to the given 1 or 2 arg closure. The stream is closed before this method returns. + * + * @param url a URL to open and read + * @param firstLine the line number value used for the first line (default is 1, set to 0 to start counting from 0) + * @param closure a closure to apply on each line (arg 1 is line, optional arg 2 is line number) + * @return the last value returned by the closure + * @throws IOException if an IOException occurs. + * @see #eachLine(java.io.InputStream, int, groovy.lang.Closure) + * @since 1.5.7 + */ + public static T eachLine(URL url, int firstLine, Closure closure) throws IOException { + return eachLine(url.openConnection().getInputStream(), firstLine, closure); + } + + /** + * Iterates through the lines read from the URL's associated input stream passing each + * line to the given 1 or 2 arg closure. The stream is closed before this method returns. + * + * @param url a URL to open and read + * @param charset opens the stream with a specified charset + * @param closure a closure to apply on each line (arg 1 is line, optional arg 2 is line number starting at line 1) + * @return the last value returned by the closure + * @throws IOException if an IOException occurs. + * @see #eachLine(java.net.URL, java.lang.String, int, groovy.lang.Closure) + * @since 1.5.6 + */ + public static T eachLine(URL url, String charset, Closure closure) throws IOException { + return eachLine(url, charset, 1, closure); + } + + /** + * Iterates through the lines read from the URL's associated input stream passing each + * line to the given 1 or 2 arg closure. The stream is closed before this method returns. + * + * @param url a URL to open and read + * @param charset opens the stream with a specified charset + * @param firstLine the line number value used for the first line (default is 1, set to 0 to start counting from 0) + * @param closure a closure to apply on each line (arg 1 is line, optional arg 2 is line number) + * @return the last value returned by the closure + * @throws IOException if an IOException occurs. + * @see #eachLine(java.io.Reader, int, groovy.lang.Closure) + * @since 1.5.7 + */ + public static T eachLine(URL url, String charset, int firstLine, Closure closure) throws IOException { + return eachLine(newReader(url, charset), firstLine, closure); + } + + /** + * Iterates through the given reader line by line. Each line is passed to the + * given 1 or 2 arg closure. If the closure has two arguments, the line count is passed + * as the second argument. The Reader is closed before this method returns. + * + * @param self a Reader, closed after the method returns + * @param closure a closure (arg 1 is line, optional arg 2 is line number starting at line 1) + * @throws IOException if an IOException occurs. + * @return the last value returned by the closure + * @see #eachLine(java.io.Reader, int, groovy.lang.Closure) + * @since 1.5.6 + */ + public static T eachLine(Reader self, Closure closure) throws IOException { + return eachLine(self, 1, closure); + } + + /** + * Iterates through the given reader line by line. Each line is passed to the + * given 1 or 2 arg closure. If the closure has two arguments, the line count is passed + * as the second argument. The Reader is closed before this method returns. + * + * @param self a Reader, closed after the method returns + * @param firstLine the line number value used for the first line (default is 1, set to 0 to start counting from 0) + * @param closure a closure which will be passed each line (or for 2 arg closures the line and line count) + * @return the last value returned by the closure + * @throws IOException if an IOException occurs. + * @since 1.5.7 + */ + public static T eachLine(Reader self, int firstLine, Closure closure) throws IOException { + BufferedReader br; + int count = firstLine; + T result = null; + + if (self instanceof BufferedReader) + br = (BufferedReader) self; + else + br = new BufferedReader(self); + + try { + while (true) { + String line = br.readLine(); + if (line == null) { + break; + } else { + result = callClosureForLine(closure, line, count); + count++; + } + } + Reader temp = self; + self = null; + temp.close(); + return result; + } finally { + closeWithWarning(self); + closeWithWarning(br); + } + } + + /** + * Iterates through this file line by line, splitting each line using + * the given regex separator. For each line, the given closure is called with + * a single parameter being the list of strings computed by splitting the line + * around matches of the given regular expression. + * Finally the resources used for processing the file are closed. + * + * @param self a File + * @param regex the delimiting regular expression + * @param closure a closure + * @throws IOException if an IOException occurs. + * @throws java.util.regex.PatternSyntaxException if the regular expression's syntax is invalid + * @return the last value returned by the closure + * @see #splitEachLine(java.io.Reader, java.lang.String, groovy.lang.Closure) + * @since 1.5.5 + */ + public static T splitEachLine(File self, String regex, Closure closure) throws IOException { + return splitEachLine(newReader(self), regex, closure); + } + + /** + * Iterates through this file line by line, splitting each line using + * the given separator Pattern. For each line, the given closure is called with + * a single parameter being the list of strings computed by splitting the line + * around matches of the given regular expression Pattern. + * Finally the resources used for processing the file are closed. + * + * @param self a File + * @param pattern the regular expression Pattern for the delimiter + * @param closure a closure + * @throws IOException if an IOException occurs. + * @return the last value returned by the closure + * @see #splitEachLine(java.io.Reader, java.util.regex.Pattern, groovy.lang.Closure) + * @since 1.6.8 + */ + public static T splitEachLine(File self, Pattern pattern, Closure closure) throws IOException { + return splitEachLine(newReader(self), pattern, closure); + } + + /** + * Iterates through this file line by line, splitting each line using + * the given regex separator. For each line, the given closure is called with + * a single parameter being the list of strings computed by splitting the line + * around matches of the given regular expression. + * Finally the resources used for processing the file are closed. + * + * @param self a File + * @param regex the delimiting regular expression + * @param charset opens the file with a specified charset + * @param closure a closure + * @throws IOException if an IOException occurs. + * @throws java.util.regex.PatternSyntaxException if the regular expression's syntax is invalid + * @return the last value returned by the closure + * @see #splitEachLine(java.io.Reader, java.lang.String, groovy.lang.Closure) + * @since 1.6.8 + */ + public static T splitEachLine(File self, String regex, String charset, Closure closure) throws IOException { + return splitEachLine(newReader(self, charset), regex, closure); + } + + /** + * Iterates through this file line by line, splitting each line using + * the given regex separator Pattern. For each line, the given closure is called with + * a single parameter being the list of strings computed by splitting the line + * around matches of the given regular expression. + * Finally the resources used for processing the file are closed. + * + * @param self a File + * @param pattern the regular expression Pattern for the delimiter + * @param charset opens the file with a specified charset + * @param closure a closure + * @throws IOException if an IOException occurs. + * @return the last value returned by the closure + * @see #splitEachLine(java.io.Reader, java.util.regex.Pattern, groovy.lang.Closure) + * @since 1.6.8 + */ + public static T splitEachLine(File self, Pattern pattern, String charset, Closure closure) throws IOException { + return splitEachLine(newReader(self, charset), pattern, closure); + } + + /** + * Iterates through the input stream associated with this URL line by line, splitting each line using + * the given regex separator. For each line, the given closure is called with + * a single parameter being the list of strings computed by splitting the line + * around matches of the given regular expression. + * Finally the resources used for processing the URL are closed. + * + * @param self a URL to open and read + * @param regex the delimiting regular expression + * @param closure a closure + * @throws IOException if an IOException occurs. + * @throws java.util.regex.PatternSyntaxException if the regular expression's syntax is invalid + * @return the last value returned by the closure + * @see #splitEachLine(java.io.Reader, java.lang.String, groovy.lang.Closure) + * @since 1.6.8 + */ + public static T splitEachLine(URL self, String regex, Closure closure) throws IOException { + return splitEachLine(newReader(self), regex, closure); + } + + /** + * Iterates through the input stream associated with this URL line by line, splitting each line using + * the given regex separator Pattern. For each line, the given closure is called with + * a single parameter being the list of strings computed by splitting the line + * around matches of the given regular expression. + * Finally the resources used for processing the URL are closed. + * + * @param self a URL to open and read + * @param pattern the regular expression Pattern for the delimiter + * @param closure a closure + * @throws IOException if an IOException occurs. + * @return the last value returned by the closure + * @see #splitEachLine(java.io.Reader, java.util.regex.Pattern, groovy.lang.Closure) + * @since 1.6.8 + */ + public static T splitEachLine(URL self, Pattern pattern, Closure closure) throws IOException { + return splitEachLine(newReader(self), pattern, closure); + } + + /** + * Iterates through the input stream associated with this URL line by line, splitting each line using + * the given regex separator. For each line, the given closure is called with + * a single parameter being the list of strings computed by splitting the line + * around matches of the given regular expression. + * Finally the resources used for processing the URL are closed. + * + * @param self a URL to open and read + * @param regex the delimiting regular expression + * @param charset opens the file with a specified charset + * @param closure a closure + * @throws IOException if an IOException occurs. + * @throws java.util.regex.PatternSyntaxException if the regular expression's syntax is invalid + * @return the last value returned by the closure + * @see #splitEachLine(java.io.Reader, java.lang.String, groovy.lang.Closure) + * @since 1.6.8 + */ + public static T splitEachLine(URL self, String regex, String charset, Closure closure) throws IOException { + return splitEachLine(newReader(self, charset), regex, closure); + } + + /** + * Iterates through the input stream associated with this URL line by line, splitting each line using + * the given regex separator Pattern. For each line, the given closure is called with + * a single parameter being the list of strings computed by splitting the line + * around matches of the given regular expression. + * Finally the resources used for processing the URL are closed. + * + * @param self a URL to open and read + * @param pattern the regular expression Pattern for the delimiter + * @param charset opens the file with a specified charset + * @param closure a closure + * @throws IOException if an IOException occurs. + * @return the last value returned by the closure + * @see #splitEachLine(java.io.Reader, java.util.regex.Pattern, groovy.lang.Closure) + * @since 1.6.8 + */ + public static T splitEachLine(URL self, Pattern pattern, String charset, Closure closure) throws IOException { + return splitEachLine(newReader(self, charset), pattern, closure); + } + + /** + * Iterates through the given reader line by line, splitting each line using + * the given regex separator. For each line, the given closure is called with + * a single parameter being the list of strings computed by splitting the line + * around matches of the given regular expression. The Reader is closed afterwards. + *

    + * Here is an example: + *

    +     * def s = 'The 3 quick\nbrown 4 fox'
    +     * def result = ''
    +     * new StringReader(s).splitEachLine(/\d/){ parts ->
    +     *     result += "${parts[0]}_${parts[1]}|"
    +     * }
    +     * assert result == 'The _ quick|brown _ fox|'
    +     * 
    + * + * @param self a Reader, closed after the method returns + * @param regex the delimiting regular expression + * @param closure a closure + * @throws IOException if an IOException occurs. + * @throws java.util.regex.PatternSyntaxException if the regular expression's syntax is invalid + * @return the last value returned by the closure + * @see java.lang.String#split(java.lang.String) + * @since 1.5.5 + */ + public static T splitEachLine(Reader self, String regex, Closure closure) throws IOException { + return splitEachLine(self, Pattern.compile(regex), closure); + } + + /** + * Iterates through the given reader line by line, splitting each line using + * the given regex separator Pattern. For each line, the given closure is called with + * a single parameter being the list of strings computed by splitting the line + * around matches of the given regular expression. The Reader is closed afterwards. + *

    + * Here is an example: + *

    +     * def s = 'The 3 quick\nbrown 4 fox'
    +     * def result = ''
    +     * new StringReader(s).splitEachLine(~/\d/){ parts ->
    +     *     result += "${parts[0]}_${parts[1]}|"
    +     * }
    +     * assert result == 'The _ quick|brown _ fox|'
    +     * 
    + * + * @param self a Reader, closed after the method returns + * @param pattern the regular expression Pattern for the delimiter + * @param closure a closure + * @throws IOException if an IOException occurs. + * @throws java.util.regex.PatternSyntaxException if the regular expression's syntax is invalid + * @return the last value returned by the closure + * @see java.lang.String#split(java.lang.String) + * @since 1.6.8 + */ + public static T splitEachLine(Reader self, Pattern pattern, Closure closure) throws IOException { + BufferedReader br; + T result = null; + + if (self instanceof BufferedReader) + br = (BufferedReader) self; + else + br = new BufferedReader(self); + + try { + while (true) { + String line = br.readLine(); + if (line == null) { + break; + } else { + List vals = Arrays.asList(pattern.split(line)); + result = closure.call(vals); + } + } + Reader temp = self; + self = null; + temp.close(); + return result; + } finally { + closeWithWarning(self); + closeWithWarning(br); + } + } + + /** + * Iterates through the given InputStream line by line using the specified + * encoding, splitting each line using the given separator. The list of tokens + * for each line is then passed to the given closure. Finally, the stream + * is closed. + * + * @param stream an InputStream + * @param regex the delimiting regular expression + * @param charset opens the stream with a specified charset + * @param closure a closure + * @throws IOException if an IOException occurs. + * @throws java.util.regex.PatternSyntaxException if the regular expression's syntax is invalid + * @return the last value returned by the closure + * @see #splitEachLine(java.io.Reader, java.lang.String, groovy.lang.Closure) + * @since 1.5.5 + */ + public static T splitEachLine(InputStream stream, String regex, String charset, Closure closure) throws IOException { + return splitEachLine(new BufferedReader(new InputStreamReader(stream, charset)), regex, closure); + } + + /** + * Iterates through the given InputStream line by line using the specified + * encoding, splitting each line using the given separator Pattern. The list of tokens + * for each line is then passed to the given closure. Finally, the stream + * is closed. + * + * @param stream an InputStream + * @param pattern the regular expression Pattern for the delimiter + * @param charset opens the stream with a specified charset + * @param closure a closure + * @throws IOException if an IOException occurs. + * @return the last value returned by the closure + * @see #splitEachLine(java.io.Reader, java.util.regex.Pattern, groovy.lang.Closure) + * @since 1.6.8 + */ + public static T splitEachLine(InputStream stream, Pattern pattern, String charset, Closure closure) throws IOException { + return splitEachLine(new BufferedReader(new InputStreamReader(stream, charset)), pattern, closure); + } + + /** + * Iterates through the given InputStream line by line, splitting each line using + * the given separator. The list of tokens for each line is then passed to + * the given closure. The stream is closed before the method returns. + * + * @param stream an InputStream + * @param regex the delimiting regular expression + * @param closure a closure + * @throws IOException if an IOException occurs. + * @throws java.util.regex.PatternSyntaxException if the regular expression's syntax is invalid + * @return the last value returned by the closure + * @see #splitEachLine(java.io.Reader, java.lang.String, groovy.lang.Closure) + * @since 1.5.6 + */ + public static T splitEachLine(InputStream stream, String regex, Closure closure) throws IOException { + return splitEachLine(new BufferedReader(new InputStreamReader(stream)), regex, closure); + } + + /** + * Iterates through the given InputStream line by line, splitting each line using + * the given separator Pattern. The list of tokens for each line is then passed to + * the given closure. The stream is closed before the method returns. + * + * @param stream an InputStream + * @param pattern the regular expression Pattern for the delimiter + * @param closure a closure + * @throws IOException if an IOException occurs. + * @return the last value returned by the closure + * @see #splitEachLine(java.io.Reader, java.util.regex.Pattern, groovy.lang.Closure) + * @since 1.6.8 + */ + public static T splitEachLine(InputStream stream, Pattern pattern, Closure closure) throws IOException { + return splitEachLine(new BufferedReader(new InputStreamReader(stream)), pattern, closure); + } + + /** + * Iterates through the given String line by line, splitting each line using + * the given separator. The list of tokens for each line is then passed to + * the given closure. + * + * @param self a String + * @param regex the delimiting regular expression + * @param closure a closure + * @return the last value returned by the closure + * @throws java.io.IOException if an error occurs + * @throws java.util.regex.PatternSyntaxException if the regular expression's syntax is invalid + * @see java.lang.String#split(java.lang.String) + * @since 1.5.5 + */ + public static T splitEachLine(String self, String regex, Closure closure) throws IOException { + return splitEachLine(self, Pattern.compile(regex), closure); + } + + /** + * Iterates through the given CharSequence line by line, splitting each line using + * the given separator. The list of tokens for each line is then passed to + * the given closure. + * + * @param self a CharSequence + * @param regex the delimiting regular expression + * @param closure a closure + * @return the last value returned by the closure + * @throws java.io.IOException if an error occurs + * @throws java.util.regex.PatternSyntaxException if the regular expression's syntax is invalid + * @see #splitEachLine(String, String, Closure) + * @since 1.8.2 + */ + public static T splitEachLine(CharSequence self, CharSequence regex, Closure closure) throws IOException { + return splitEachLine(self.toString(), regex.toString(), closure); + } + + /** + * Iterates through the given String line by line, splitting each line using + * the given separator Pattern. The list of tokens for each line is then passed to + * the given closure. + * + * @param self a String + * @param pattern the regular expression Pattern for the delimiter + * @param closure a closure + * @return the last value returned by the closure + * @throws java.io.IOException if an error occurs + * @see java.util.regex.Pattern#split(java.lang.CharSequence) + * @since 1.6.8 + */ + public static T splitEachLine(String self, Pattern pattern, Closure closure) throws IOException { + final List list = readLines(self); + T result = null; + for (String line : list) { + List vals = Arrays.asList(pattern.split(line)); + result = closure.call(vals); + } + return result; + } + + /** + * Iterates through the given CharSequence line by line, splitting each line using + * the given separator Pattern. The list of tokens for each line is then passed to + * the given closure. + * + * @param self a CharSequence + * @param pattern the regular expression Pattern for the delimiter + * @param closure a closure + * @return the last value returned by the closure + * @throws java.io.IOException if an error occurs + * @see #splitEachLine(String, Pattern, Closure) + * @since 1.8.2 + */ + public static T splitEachLine(CharSequence self, Pattern pattern, Closure closure) throws IOException { + return splitEachLine(self.toString(), pattern, closure); + } + + /** + * Read a single, whole line from the given Reader. + * + * @param self a Reader + * @return a line + * @throws IOException if an IOException occurs. + * @since 1.0 + */ + public static String readLine(Reader self) throws IOException { + if (self instanceof BufferedReader) { + BufferedReader br = (BufferedReader) self; + return br.readLine(); + } + if (self.markSupported()) { + return readLineFromReaderWithMark(self); + } + return readLineFromReaderWithoutMark(self); + } + + private static int charBufferSize = 4096; // half the default stream buffer size + private static int expectedLineLength = 160; // double the default line length + private static int EOF = -1; // End Of File + + /* + * This method tries to read subsequent buffers from the reader using a mark + */ + private static String readLineFromReaderWithMark(final Reader input) + throws IOException { + char[] cbuf = new char[charBufferSize]; + try { + input.mark(charBufferSize); + } catch (IOException e) { + // this should never happen + LOG.warn("Caught exception setting mark on supporting reader: " + e); + // fallback + return readLineFromReaderWithoutMark(input); + } + + // could be changed into do..while, but then + // we might create an additional StringBuffer + // instance at the end of the stream + int count = input.read(cbuf); + if (count == EOF) // we are at the end of the input data + return null; + + StringBuffer line = new StringBuffer(expectedLineLength); + // now work on the buffer(s) + int ls = lineSeparatorIndex(cbuf, count); + while (ls == -1) { + line.append(cbuf, 0, count); + count = input.read(cbuf); + if (count == EOF) { + // we are at the end of the input data + return line.toString(); + } + ls = lineSeparatorIndex(cbuf, count); + } + line.append(cbuf, 0, ls); + + // correct ls if we have \r\n + int skipLS = 1; + if (ls + 1 < count) { + // we are not at the end of the buffer + if (cbuf[ls] == '\r' && cbuf[ls + 1] == '\n') { + skipLS++; + } + } else { + if (cbuf[ls] == '\r' && input.read() == '\n') { + skipLS++; + } + } + + //reset() and skip over last linesep + input.reset(); + input.skip(line.length() + skipLS); + return line.toString(); + } + + /* + * This method reads without a buffer. + * It returns too many empty lines if \r\n combinations + * are used. Nothing can be done because we can't push + * back the character we have just read. + */ + private static String readLineFromReaderWithoutMark(Reader input) + throws IOException { + + int c = input.read(); + if (c == -1) + return null; + StringBuffer line = new StringBuffer(expectedLineLength); + + while (c != EOF && c != '\n' && c != '\r') { + char ch = (char) c; + line.append(ch); + c = input.read(); + } + return line.toString(); + } + + /* + * searches for \n or \r + * Returns -1 if not found. + */ + private static int lineSeparatorIndex(char[] array, int length) { + for (int k = 0; k < length; k++) { + if (isLineSeparator(array[k])) { + return k; + } + } + return -1; + } + + /* + * true if either \n or \r + */ + private static boolean isLineSeparator(char c) { + return c == '\n' || c == '\r'; + } + + static String lineSeparator = null; + + /** + * Return a String with lines (separated by LF, CR/LF, or CR) + * terminated by the platform specific line separator. + * + * @param self a String object + * @return the denormalized string + * @since 1.6.0 + */ + public static String denormalize(final String self) { + // Don't do this in static initializer because we may never be needed. + // TODO: Put this lineSeparator property somewhere everyone can use it. + if (lineSeparator == null) { + final StringWriter sw = new StringWriter(2); + try { + // We use BufferedWriter rather than System.getProperty because + // it has the security manager rigamarole to deal with the possible exception. + final BufferedWriter bw = new BufferedWriter(sw); + bw.newLine(); + bw.flush(); + lineSeparator = sw.toString(); + } catch (IOException ioe) { + // This shouldn't happen, but this is the same default used by + // BufferedWriter on a security exception. + lineSeparator = "\n"; + } + } + + final int len = self.length(); + + if (len < 1) { + return self; + } + + final StringBuilder sb = new StringBuilder((110 * len) / 100); + + int i = 0; + + while (i < len) { + final char ch = self.charAt(i++); + + switch (ch) { + case '\r': + sb.append(lineSeparator); + + // Eat the following LF if any. + if ((i < len) && (self.charAt(i) == '\n')) { + ++i; + } + + break; + + case '\n': + sb.append(lineSeparator); + break; + + default: + sb.append(ch); + break; + } + } + + return sb.toString(); + } + + /** + * Return a CharSequence with lines (separated by LF, CR/LF, or CR) + * terminated by the platform specific line separator. + * + * @param self a CharSequence object + * @return the denormalized CharSequence + * @see #denormalize(String) + * @since 1.8.2 + */ + public static CharSequence denormalize(final CharSequence self) { + return denormalize(self.toString()); + } + + /** + * Return a String with linefeeds and carriage returns normalized to linefeeds. + * + * @param self a String object + * @return the normalized string + * @since 1.6.0 + */ + public static String normalize(final String self) { + int nx = self.indexOf('\r'); + + if (nx < 0) { + return self; + } + + final int len = self.length(); + final StringBuilder sb = new StringBuilder(len); + + int i = 0; + + do { + sb.append(self, i, nx); + sb.append('\n'); + + if ((i = nx + 1) >= len) break; + + if (self.charAt(i) == '\n') { + // skip the LF in CR LF + if (++i >= len) break; + } + + nx = self.indexOf('\r', i); + } while (nx > 0); + + sb.append(self, i, len); + + return sb.toString(); + } + + /** + * Return a CharSequence with linefeeds and carriage returns normalized to linefeeds. + * + * @param self a CharSequence object + * @return the normalized CharSequence + * @see #normalize(String) + * @since 1.8.2 + */ + public static CharSequence normalize(final CharSequence self) { + return normalize(self.toString()); + } + + /** + * Return the lines of a String as a List of Strings. + * + * @param self a String object + * @return a list of lines + * @throws java.io.IOException if an error occurs + * @since 1.5.5 + */ + public static List readLines(String self) throws IOException { + return readLines(new StringReader(self)); + } + + /** + * Return the lines of a CharSequence as a List of CharSequence. + * + * @param self a CharSequence object + * @return a list of lines + * @throws java.io.IOException if an error occurs + * @since 1.8.2 + */ + public static List readLines(CharSequence self) throws IOException { + return new ArrayList(readLines(self.toString())); + } + + /** + * Reads the file into a list of Strings, with one item for each line. + * + * @param file a File + * @return a List of lines + * @throws IOException if an IOException occurs. + * @see #readLines(java.io.Reader) + * @since 1.0 + */ + public static List readLines(File file) throws IOException { + return readLines(newReader(file)); + } + + /** + * Reads the file into a list of Strings, with one item for each line. + * + * @param file a File + * @param charset opens the file with a specified charset + * @return a List of lines + * @throws IOException if an IOException occurs. + * @see #readLines(java.io.Reader) + * @since 1.6.8 + */ + public static List readLines(File file, String charset) throws IOException { + return readLines(newReader(file, charset)); + } + + /** + * Reads the stream into a list, with one element for each line. + * + * @param stream a stream + * @return a List of lines + * @throws IOException if an IOException occurs. + * @see #readLines(java.io.Reader) + * @since 1.0 + */ + public static List readLines(InputStream stream) throws IOException { + return readLines(newReader(stream)); + } + + /** + * Reads the stream into a list, with one element for each line. + * + * @param stream a stream + * @param charset opens the stream with a specified charset + * @return a List of lines + * @throws IOException if an IOException occurs. + * @see #readLines(java.io.Reader) + * @since 1.6.8 + */ + public static List readLines(InputStream stream, String charset) throws IOException { + return readLines(newReader(stream, charset)); + } + + /** + * Reads the URL contents into a list, with one element for each line. + * + * @param self a URL + * @return a List of lines + * @throws IOException if an IOException occurs. + * @see #readLines(java.io.Reader) + * @since 1.6.8 + */ + public static List readLines(URL self) throws IOException { + return readLines(newReader(self)); + } + + /** + * Reads the URL contents into a list, with one element for each line. + * + * @param self a URL + * @param charset opens the URL with a specified charset + * @return a List of lines + * @throws IOException if an IOException occurs. + * @see #readLines(java.io.Reader) + * @since 1.6.8 + */ + public static List readLines(URL self, String charset) throws IOException { + return readLines(newReader(self, charset)); + } + + /** + * Reads the reader into a list of Strings, with one entry for each line. + * The reader is closed before this method returns. + * + * @param reader a Reader + * @return a List of lines + * @throws IOException if an IOException occurs. + * @since 1.0 + */ + public static List readLines(Reader reader) throws IOException { + IteratorClosureAdapter closure = new IteratorClosureAdapter(reader); + eachLine(reader, closure); + return closure.asList(); + } + + /** + * Read the content of the File using the specified encoding and return it + * as a String. + * + * @param file the file whose content we want to read + * @param charset the charset used to read the content of the file + * @return a String containing the content of the file + * @throws IOException if an IOException occurs. + * @since 1.0 + */ + public static String getText(File file, String charset) throws IOException { + return getText(newReader(file, charset)); + } + + /** + * Read the content of the File and returns it as a String. + * + * @param file the file whose content we want to read + * @return a String containing the content of the file + * @throws IOException if an IOException occurs. + * @since 1.0 + */ + public static String getText(File file) throws IOException { + return getText(newReader(file)); + } + + /** + * Read the content of this URL and returns it as a String. + * + * @param url URL to read content from + * @return the text from that URL + * @throws IOException if an IOException occurs. + * @since 1.0 + */ + public static String getText(URL url) throws IOException { + return getText(url, CharsetToolkit.getDefaultSystemCharset().toString()); + } + + /** + * Read the content of this URL and returns it as a String. + * + * @param url URL to read content from + * @param parameters connection parameters + * @return the text from that URL + * @throws IOException if an IOException occurs. + * @since 1.8.1 + */ + public static String getText(URL url, Map parameters) throws IOException { + return getText(url, parameters, CharsetToolkit.getDefaultSystemCharset().toString()); + } + + /** + * Read the data from this URL and return it as a String. The connection + * stream is closed before this method returns. + * + * @param url URL to read content from + * @param charset opens the stream with a specified charset + * @return the text from that URL + * @throws IOException if an IOException occurs. + * @see java.net.URLConnection#getInputStream() + * @since 1.0 + */ + public static String getText(URL url, String charset) throws IOException { + BufferedReader reader = newReader(url, charset); + return getText(reader); + } + + /** + * Read the data from this URL and return it as a String. The connection + * stream is closed before this method returns. + * + * @param url URL to read content from + * @param parameters connection parameters + * @param charset opens the stream with a specified charset + * @return the text from that URL + * @throws IOException if an IOException occurs. + * @see java.net.URLConnection#getInputStream() + * @since 1.8.1 + */ + public static String getText(URL url, Map parameters, String charset) throws IOException { + BufferedReader reader = newReader(url, parameters, charset); + return getText(reader); + } + + /** + * Read the content of this InputStream and return it as a String. + * The stream is closed before this method returns. + * + * @param is an input stream + * @return the text from that URL + * @throws IOException if an IOException occurs. + * @since 1.0 + */ + public static String getText(InputStream is) throws IOException { + BufferedReader reader = new BufferedReader(new InputStreamReader(is)); + return getText(reader); + } + + /** + * Read the content of this InputStream using specified charset and return + * it as a String. The stream is closed before this method returns. + * + * @param is an input stream + * @param charset opens the stream with a specified charset + * @return the text from that URL + * @throws IOException if an IOException occurs. + * @since 1.0 + */ + public static String getText(InputStream is, String charset) throws IOException { + BufferedReader reader = new BufferedReader(new InputStreamReader(is, charset)); + return getText(reader); + } + + /** + * Read the content of the Reader and return it as a String. The reader + * is closed before this method returns. + * + * @param reader a Reader whose content we want to read + * @return a String containing the content of the buffered reader + * @throws IOException if an IOException occurs. + * @see #getText(java.io.BufferedReader) + * @since 1.0 + */ + public static String getText(Reader reader) throws IOException { + BufferedReader bufferedReader = new BufferedReader(reader); + return getText(bufferedReader); + } + + /** + * Read the content of the BufferedReader and return it as a String. + * The BufferedReader is closed afterwards. + * + * @param reader a BufferedReader whose content we want to read + * @return a String containing the content of the buffered reader + * @throws IOException if an IOException occurs. + * @since 1.0 + */ + public static String getText(BufferedReader reader) throws IOException { + StringBuilder answer = new StringBuilder(); + // reading the content of the file within a char buffer + // allow to keep the correct line endings + char[] charBuffer = new char[8192]; + int nbCharRead /* = 0*/; + try { + while ((nbCharRead = reader.read(charBuffer)) != -1) { + // appends buffer + answer.append(charBuffer, 0, nbCharRead); + } + Reader temp = reader; + reader = null; + temp.close(); + } finally { + closeWithWarning(reader); + } + return answer.toString(); + } + + /** + * Read the content of the File and returns it as a byte[]. + * + * @param file the file whose content we want to read + * @return a String containing the content of the file + * @throws IOException if an IOException occurs. + * @since 1.7.1 + */ + public static byte[] getBytes(File file) throws IOException { + return getBytes(new FileInputStream(file)); + } + + /** + * Read the content of this URL and returns it as a byte[]. + * + * @param url URL to read content from + * @return the byte[] from that URL + * @throws IOException if an IOException occurs. + * @since 1.7.1 + */ + public static byte[] getBytes(URL url) throws IOException { + return getBytes(url.openConnection().getInputStream()); + } + + /** + * Read the content of this InputStream and return it as a byte[]. + * The stream is closed before this method returns. + * + * @param is an input stream + * @return the byte[] from that InputStream + * @throws IOException if an IOException occurs. + * @since 1.7.1 + */ + public static byte[] getBytes(InputStream is) throws IOException { + ByteArrayOutputStream answer = new ByteArrayOutputStream(); + // reading the content of the file within a byte buffer + byte[] byteBuffer = new byte[8192]; + int nbByteRead /* = 0*/; + try { + while ((nbByteRead = is.read(byteBuffer)) != -1) { + // appends buffer + answer.write(byteBuffer, 0, nbByteRead); + } + } finally { + closeWithWarning(is); + } + return answer.toByteArray(); + } + + /** + * Write the bytes from the byte array to the File. + * + * @param file the file to write to + * @param bytes the byte[] to write to the file + * @throws IOException if an IOException occurs. + * @since 1.7.1 + */ + public static void setBytes(File file, byte[] bytes) throws IOException { + setBytes(new FileOutputStream(file), bytes); + } + + /** + * Write the byte[] to the output stream. + * The stream is closed before this method returns. + * + * @param os an output stream + * @param bytes the byte[] to write to the output stream + * @throws IOException if an IOException occurs. + * @since 1.7.1 + */ + public static void setBytes(OutputStream os, byte[] bytes) throws IOException { + try { + os.write(bytes); + } finally { + closeWithWarning(os); + } + } + + /** + * Write the text and append a newline (using the platform's line-ending). + * + * @param writer a BufferedWriter + * @param line the line to write + * @throws IOException if an IOException occurs. + * @since 1.0 + */ + public static void writeLine(BufferedWriter writer, String line) throws IOException { + writer.write(line); + writer.newLine(); + } + + /** + * Write the text to the File. + * + * @param file a File + * @param text the text to write to the File + * @throws IOException if an IOException occurs. + * @since 1.0 + */ + public static void write(File file, String text) throws IOException { + BufferedWriter writer = null; + try { + writer = newWriter(file); + writer.write(text); + writer.flush(); + + Writer temp = writer; + writer = null; + temp.close(); + } finally { + closeWithWarning(writer); + } + } + + /** + * Synonym for write(text) allowing file.text = 'foo'. + * + * @param file a File + * @param text the text to write to the File + * @throws IOException if an IOException occurs. + * @see #write(java.io.File, java.lang.String) + * @since 1.5.1 + */ + public static void setText(File file, String text) throws IOException { + write(file, text); + } + + /** + * Synonym for write(text, charset) allowing: + *
    +     * myFile.setText('some text', charset)
    +     * 
    + * or with some help from ExpandoMetaClass, you could do something like: + *
    +     * myFile.metaClass.setText = { String s -> delegate.setText(s, 'UTF-8') }
    +     * myfile.text = 'some text'
    +     * 
    + * + * @param file A File + * @param charset The charset used when writing to the file + * @param text The text to write to the File + * @throws IOException if an IOException occurs. + * @see #write(java.io.File, java.lang.String, java.lang.String) + * @since 1.7.3 + */ + public static void setText(File file, String text, String charset) throws IOException { + write(file, text, charset); + } + + /** + * Write the text to the File. + * + * @param file a File + * @param text the text to write to the File + * @return the original file + * @throws IOException if an IOException occurs. + * @since 1.0 + */ + public static File leftShift(File file, Object text) throws IOException { + append(file, text); + return file; + } + + /** + * Write bytes to a File. + * + * @param file a File + * @param bytes the byte array to append to the end of the File + * @return the original file + * @throws IOException if an IOException occurs. + * @since 1.5.0 + */ + public static File leftShift(File file, byte[] bytes) throws IOException { + append(file, bytes); + return file; + } + + /** + * Append binary data to the file. See {@link #append(java.io.File, java.io.InputStream)} + * @param file a File + * @param data an InputStream of data to write to the file + * @return the file + * @throws IOException if an IOException occurs. + * @since 1.5.0 + */ + public static File leftShift(File file, InputStream data) throws IOException { + append(file, data); + return file; + } + + /** + * Write the text to the File, using the specified encoding. + * + * @param file a File + * @param text the text to write to the File + * @param charset the charset used + * @throws IOException if an IOException occurs. + * @since 1.0 + */ + public static void write(File file, String text, String charset) throws IOException { + BufferedWriter writer = null; + try { + writer = newWriter(file, charset); + writer.write(text); + writer.flush(); + + Writer temp = writer; + writer = null; + temp.close(); + } finally { + closeWithWarning(writer); + } + } + + /** + * Append the text at the end of the File. + * + * @param file a File + * @param text the text to append at the end of the File + * @throws IOException if an IOException occurs. + * @since 1.0 + */ + public static void append(File file, Object text) throws IOException { + BufferedWriter writer = null; + try { + writer = newWriter(file, true); + InvokerHelper.write(writer, text); + writer.flush(); + + Writer temp = writer; + writer = null; + temp.close(); + } finally { + closeWithWarning(writer); + } + } + + /** + * Append bytes to the end of a File. + * + * @param file a File + * @param bytes the byte array to append to the end of the File + * @throws IOException if an IOException occurs. + * @since 1.5.1 + */ + public static void append(File file, byte[] bytes) throws IOException { + BufferedOutputStream stream = null; + try { + stream = new BufferedOutputStream( new FileOutputStream(file,true) ); + stream.write(bytes, 0, bytes.length); + stream.flush(); + + OutputStream temp = stream; + stream = null; + temp.close(); + } finally { + closeWithWarning(stream); + } + } + + /** + * Append binary data to the file. It will not be + * interpreted as text. + * @param self a File + * @param stream stream to read data from. + * @throws IOException if an IOException occurs. + * @since 1.5.0 + */ + public static void append(File self, InputStream stream ) throws IOException { + OutputStream out = new FileOutputStream( self, true ); + try { + leftShift( out, stream ); + } + finally { + closeWithWarning( out ); + } + } + + /** + * Append the text at the end of the File, using a specified encoding. + * + * @param file a File + * @param text the text to append at the end of the File + * @param charset the charset used + * @throws IOException if an IOException occurs. + * @since 1.0 + */ + public static void append(File file, Object text, String charset) throws IOException { + BufferedWriter writer = null; + try { + writer = newWriter(file, charset, true); + InvokerHelper.write(writer, text); + writer.flush(); + + Writer temp = writer; + writer = null; + temp.close(); + } finally { + closeWithWarning(writer); + } + } + + /** + * This method is used to throw useful exceptions when the eachFile* and eachDir closure methods + * are used incorrectly. + * + * @param dir The directory to check + * @throws FileNotFoundException if the given directory does not exist + * @throws IllegalArgumentException if the provided File object does not represent a directory + * @since 1.0 + */ + private static void checkDir(File dir) throws FileNotFoundException, IllegalArgumentException { + if (!dir.exists()) + throw new FileNotFoundException(dir.getAbsolutePath()); + if (!dir.isDirectory()) + throw new IllegalArgumentException("The provided File object is not a directory: " + dir.getAbsolutePath()); + } + + /** + * Invokes the closure for each 'child' file in this 'parent' folder/directory. + * Both regular files and subfolders/subdirectories can be processed depending + * on the fileType enum value. + * + * @param self a file object + * @param fileType if normal files or directories or both should be processed + * @param closure the closure to invoke + * @throws FileNotFoundException if the given directory does not exist + * @throws IllegalArgumentException if the provided File object does not represent a directory + * @since 1.7.1 + */ + public static void eachFile(final File self, final FileType fileType, final Closure closure) + throws FileNotFoundException, IllegalArgumentException { + checkDir(self); + final File[] files = self.listFiles(); + // null check because of http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4803836 + if (files == null) return; + for (File file : files) { + if (fileType == FileType.ANY || + (fileType != FileType.FILES && file.isDirectory()) || + (fileType != FileType.DIRECTORIES && file.isFile())) { + closure.call(file); + } + } + } + + /** + * Invokes the closure for each 'child' file in this 'parent' folder/directory. + * Both regular files and subfolders/subdirectories are processed. + * + * @param self a File (that happens to be a folder/directory) + * @param closure a closure (first parameter is the 'child' file) + * @throws FileNotFoundException if the given directory does not exist + * @throws IllegalArgumentException if the provided File object does not represent a directory + * @see java.io.File#listFiles() + * @see #eachFile(java.io.File, groovy.io.FileType, groovy.lang.Closure) + * @since 1.5.0 + */ + public static void eachFile(final File self, final Closure closure) throws FileNotFoundException, IllegalArgumentException { + eachFile(self, FileType.ANY, closure); + } + + /** + * Invokes the closure for each subdirectory in this directory, + * ignoring regular files. + * + * @param self a File (that happens to be a folder/directory) + * @param closure a closure (first parameter is the subdirectory file) + * @throws FileNotFoundException if the given directory does not exist + * @throws IllegalArgumentException if the provided File object does not represent a directory + * @see java.io.File#listFiles() + * @see #eachFile(java.io.File, groovy.io.FileType, groovy.lang.Closure) + * @since 1.0 + */ + public static void eachDir(File self, Closure closure) throws FileNotFoundException, IllegalArgumentException { + eachFile(self, FileType.DIRECTORIES, closure); + } + + /** + * Invokes the closure for each descendant file in this directory. + * Sub-directories are recursively searched in a depth-first fashion. + * Both regular files and subdirectories may be passed to the closure + * depending on the value of fileType. + * + * @param self a file object + * @param fileType if normal files or directories or both should be processed + * @param closure the closure to invoke on each file + * @throws FileNotFoundException if the given directory does not exist + * @throws IllegalArgumentException if the provided File object does not represent a directory + * @since 1.7.1 + */ + public static void eachFileRecurse(final File self, final FileType fileType, final Closure closure) + throws FileNotFoundException, IllegalArgumentException { + checkDir(self); + final File[] files = self.listFiles(); + // null check because of http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4803836 + if (files == null) return; + for (File file : files) { + if (file.isDirectory()) { + if (fileType != FileType.FILES) closure.call(file); + eachFileRecurse(file, fileType, closure); + } else if (fileType != FileType.DIRECTORIES) { + closure.call(file); + } + } + } + + /** + * Invokes closure for each descendant file in this directory tree. + * Sub-directories are recursively traversed as found. + * The traversal can be adapted by providing various options in the options Map according + * to the following keys:
    + *
    type
    A {@link groovy.io.FileType} enum to determine if normal files or directories or both are processed
    + *
    preDir
    A {@link groovy.lang.Closure} run before each directory is processed and optionally returning a {@link groovy.io.FileVisitResult} value + * which can be used to control subsequent processing.
    + *
    preRoot
    A boolean indicating that the 'preDir' closure should be applied at the root level
    + *
    postDir
    A {@link groovy.lang.Closure} run after each directory is processed and optionally returning a {@link groovy.io.FileVisitResult} value + * which can be used to control subsequent processing.
    + *
    postRoot
    A boolean indicating that the 'postDir' closure should be applied at the root level
    + *
    visitRoot
    A boolean indicating that the given closure should be applied for the root dir + * (not applicable if the 'type' is set to {@link groovy.io.FileType#FILES})
    + *
    maxDepth
    The maximum number of directory levels when recursing + * (default is -1 which means infinite, set to 0 for no recursion)
    + *
    filter
    A filter to perform on traversed files/directories (using the {@link #isCase(java.lang.Object, java.lang.Object)} method). If set, + * only files/dirs which match are candidates for visiting.
    + *
    nameFilter
    A filter to perform on the name of traversed files/directories (using the {@link #isCase(java.lang.Object, java.lang.Object)} method). If set, + * only files/dirs which match are candidates for visiting. (Must not be set if 'filter' is set)
    + *
    excludeFilter
    A filter to perform on traversed files/directories (using the {@link #isCase(java.lang.Object, java.lang.Object)} method). + * If set, any candidates which match won't be visited.
    + *
    excludeNameFilter
    A filter to perform on the names of traversed files/directories (using the {@link #isCase(java.lang.Object, java.lang.Object)} method). + * If set, any candidates which match won't be visited. (Must not be set if 'excludeFilter' is set)
    + *
    sort
    A {@link groovy.lang.Closure} which if set causes the files and subdirectories for each directory to be processed in sorted order. + * Note that even when processing only files, the order of visited subdirectories will be affected by this parameter.
    + *
    + * This example prints out file counts and size aggregates for groovy source files within a directory tree: + *
    +     * def totalSize = 0
    +     * def count = 0
    +     * def sortByTypeThenName = { a, b ->
    +     *     a.isFile() != b.isFile() ? a.isFile() <=> b.isFile() : a.name <=> b.name
    +     * }
    +     * rootDir.traverse(
    +     *         type         : FILES,
    +     *         nameFilter   : ~/.*\.groovy/,
    +     *         preDir       : { if (it.name == '.svn') return SKIP_SUBTREE },
    +     *         postDir      : { println "Found $count files in $it.name totalling $totalSize bytes"
    +     *                         totalSize = 0; count = 0 },
    +     *         postRoot     : true
    +     *         sort         : sortByTypeThenName
    +     * ) {it -> totalSize += it.size(); count++ }
    +     * 
    + * + * @param self a File + * @param options a Map of options to alter the traversal behavior + * @param closure the Closure to invoke on each file/directory and optionally returning a {@link groovy.io.FileVisitResult} value + * which can be used to control subsequent processing + * @throws FileNotFoundException if the given directory does not exist + * @throws IllegalArgumentException if the provided File object does not represent a directory or illegal filter combinations are supplied + * @see #sort(java.util.Collection, groovy.lang.Closure) + * @see groovy.io.FileVisitResult + * @see groovy.io.FileType + * @since 1.7.1 + */ + public static void traverse(final File self, final Map options, final Closure closure) + throws FileNotFoundException, IllegalArgumentException { + Number maxDepthNumber = asType(options.remove("maxDepth"), Number.class); + int maxDepth = maxDepthNumber == null ? -1 : maxDepthNumber.intValue(); + Boolean visitRoot = asType(get(options, "visitRoot", false), Boolean.class); + Boolean preRoot = asType(get(options, "preRoot", false), Boolean.class); + Boolean postRoot = asType(get(options, "postRoot", false), Boolean.class); + final Closure pre = (Closure) options.get("preDir"); + final Closure post = (Closure) options.get("postDir"); + final FileType type = (FileType) options.get("type"); + final Object filter = options.get("filter"); + final Object nameFilter = options.get("nameFilter"); + final Object excludeFilter = options.get("excludeFilter"); + final Object excludeNameFilter = options.get("excludeNameFilter"); + Object preResult = null; + if (preRoot && pre != null) { + preResult = pre.call(self); + } + if (preResult == FileVisitResult.TERMINATE || + preResult == FileVisitResult.SKIP_SUBTREE) return; + + FileVisitResult terminated = traverse(self, options, closure, maxDepth); + + if (type != FileType.FILES && visitRoot) { + if (closure != null && notFiltered(self, filter, nameFilter, excludeFilter, excludeNameFilter)) { + Object closureResult = closure.call(self); + if (closureResult == FileVisitResult.TERMINATE) return; + } + } + + if (postRoot && post != null && terminated != FileVisitResult.TERMINATE) post.call(self); + } + + private static boolean notFiltered(File file, Object filter, Object nameFilter, Object excludeFilter, Object excludeNameFilter) { + if (filter == null && nameFilter == null && excludeFilter == null && excludeNameFilter == null) return true; + if (filter != null && nameFilter != null) throw new IllegalArgumentException("Can't set both 'filter' and 'nameFilter'"); + if (excludeFilter != null && excludeNameFilter != null) throw new IllegalArgumentException("Can't set both 'excludeFilter' and 'excludeNameFilter'"); + Object filterToUse = null; + Object filterParam = null; + if (filter != null) { + filterToUse = filter; + filterParam = file; + } else if (nameFilter != null) { + filterToUse = nameFilter; + filterParam = file.getName(); + } + Object excludeFilterToUse = null; + Object excludeParam = null; + if (excludeFilter != null) { + excludeFilterToUse = excludeFilter; + excludeParam = file; + } else if (excludeNameFilter != null) { + excludeFilterToUse = excludeNameFilter; + excludeParam = file.getName(); + } + final MetaClass filterMC = filterToUse == null ? null : InvokerHelper.getMetaClass(filterToUse); + final MetaClass excludeMC = excludeFilterToUse == null ? null : InvokerHelper.getMetaClass(excludeFilterToUse); + boolean included = filterToUse == null || DefaultTypeTransformation.castToBoolean(filterMC.invokeMethod(filterToUse, "isCase", filterParam)); + boolean excluded = excludeFilterToUse != null && DefaultTypeTransformation.castToBoolean(excludeMC.invokeMethod(excludeFilterToUse, "isCase", excludeParam)); + return included && !excluded; + } + + /** + * Invokes the closure for each descendant file in this directory tree. + * Sub-directories are recursively traversed in a depth-first fashion. + * Convenience method for {@link #traverse(java.io.File, java.util.Map, groovy.lang.Closure)} when + * no options to alter the traversal behavior are required. + * + * @param self a File + * @param closure the Closure to invoke on each file/directory and optionally returning a {@link groovy.io.FileVisitResult} value + * which can be used to control subsequent processing + * @throws FileNotFoundException if the given directory does not exist + * @throws IllegalArgumentException if the provided File object does not represent a directory + * @see #traverse(java.io.File, java.util.Map, groovy.lang.Closure) + * @since 1.7.1 + */ + public static void traverse(final File self, final Closure closure) + throws FileNotFoundException, IllegalArgumentException { + traverse(self, new HashMap(), closure); + } + + /** + * Invokes the closure specified with key 'visit' in the options Map + * for each descendant file in this directory tree. Convenience method + * for {@link #traverse(java.io.File, java.util.Map, groovy.lang.Closure)} allowing the 'visit' closure + * to be included in the options Map rather than as a parameter. + * + * @param self a File + * @param options a Map of options to alter the traversal behavior + * @throws FileNotFoundException if the given directory does not exist + * @throws IllegalArgumentException if the provided File object does not represent a directory or illegal filter combinations are supplied + * @see #traverse(java.io.File, java.util.Map, groovy.lang.Closure) + * @since 1.7.1 + */ + public static void traverse(final File self, final Map options) + throws FileNotFoundException, IllegalArgumentException { + final Closure visit = (Closure) options.remove("visit"); + traverse(self, options, visit); + } + + private static FileVisitResult traverse(final File self, final Map options, final Closure closure, final int maxDepth) + throws FileNotFoundException, IllegalArgumentException { + checkDir(self); + final Closure pre = (Closure) options.get("preDir"); + final Closure post = (Closure) options.get("postDir"); + final FileType type = (FileType) options.get("type"); + final Object filter = options.get("filter"); + final Object nameFilter = options.get("nameFilter"); + final Object excludeFilter = options.get("excludeFilter"); + final Object excludeNameFilter = options.get("excludeNameFilter"); + final Closure sort = (Closure) options.get("sort"); + + final File[] origFiles = self.listFiles(); + // null check because of http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4803836 + if (origFiles != null) { + List files = Arrays.asList(origFiles); + if (sort != null) files = sort(files, sort); + for (File file : files) { + if (file.isDirectory()) { + if (type != FileType.FILES) { + if (closure != null && notFiltered(file, filter, nameFilter, excludeFilter, excludeNameFilter)) { + Object closureResult = closure.call(file); + if (closureResult == FileVisitResult.SKIP_SIBLINGS) break; + if (closureResult == FileVisitResult.TERMINATE) return FileVisitResult.TERMINATE; + } + } + if (maxDepth != 0) { + Object preResult = null; + if (pre != null) { + preResult = pre.call(file); + } + if (preResult == FileVisitResult.SKIP_SIBLINGS) break; + if (preResult == FileVisitResult.TERMINATE) return FileVisitResult.TERMINATE; + if (preResult != FileVisitResult.SKIP_SUBTREE) { + FileVisitResult terminated = traverse(file, options, closure, maxDepth - 1); + if (terminated == FileVisitResult.TERMINATE) return terminated; + } + Object postResult = null; + if (post != null) { + postResult = post.call(file); + } + if (postResult == FileVisitResult.SKIP_SIBLINGS) break; + if (postResult == FileVisitResult.TERMINATE) return FileVisitResult.TERMINATE; + } + } else if (type != FileType.DIRECTORIES) { + if (closure != null && notFiltered(file, filter, nameFilter, excludeFilter, excludeNameFilter)) { + Object closureResult = closure.call(file); + if (closureResult == FileVisitResult.SKIP_SIBLINGS) break; + if (closureResult == FileVisitResult.TERMINATE) return FileVisitResult.TERMINATE; + } + } + } + } + return FileVisitResult.CONTINUE; + } + + /** + * Invokes the closure for each descendant file in this directory. + * Sub-directories are recursively searched in a depth-first fashion. + * Both regular files and subdirectories are passed to the closure. + * + * @param self a File + * @param closure a closure + * @throws FileNotFoundException if the given directory does not exist + * @throws IllegalArgumentException if the provided File object does not represent a directory + * @see #eachFileRecurse(java.io.File, groovy.io.FileType, groovy.lang.Closure) + * @since 1.0 + */ + public static void eachFileRecurse(File self, Closure closure) throws FileNotFoundException, IllegalArgumentException { + eachFileRecurse(self, FileType.ANY, closure); + } + + /** + * Invokes the closure for each descendant directory of this directory. + * Sub-directories are recursively searched in a depth-first fashion. + * Only subdirectories are passed to the closure; regular files are ignored. + * + * @param self a directory + * @param closure a closure + * @throws FileNotFoundException if the given directory does not exist + * @throws IllegalArgumentException if the provided File object does not represent a directory + * @see #eachFileRecurse(java.io.File, groovy.io.FileType, groovy.lang.Closure) + * @since 1.5.0 + */ + public static void eachDirRecurse(final File self, final Closure closure) throws FileNotFoundException, IllegalArgumentException { + eachFileRecurse(self, FileType.DIRECTORIES, closure); + } + + /** + * Invokes the closure for each file whose name (file.name) matches the given nameFilter in the given directory + * - calling the {@link #isCase(java.lang.Object, java.lang.Object)} method to determine if a match occurs. This method can be used + * with different kinds of filters like regular expressions, classes, ranges etc. + * Both regular files and subdirectories may be candidates for matching depending + * on the value of fileType. + *
    +     * // collect names of files in baseDir matching supplied regex pattern
    +     * import static groovy.io.FileType.*
    +     * def names = []
    +     * baseDir.eachFileMatch FILES, ~/foo\d\.txt/, { names << it.name }
    +     * assert names == ['foo1.txt', 'foo2.txt']
    +     *
    +     * // remove all *.bak files in baseDir
    +     * baseDir.eachFileMatch FILES, ~/.*\.bak/, { File bak -> bak.delete() }
    +     *
    +     * // print out files > 4K in size from baseDir
    +     * baseDir.eachFileMatch FILES, { new File(baseDir, it).size() > 4096 }, { println "$it.name ${it.size()}" }
    +     * 
    + * + * @param self a file + * @param fileType whether normal files or directories or both should be processed + * @param nameFilter the filter to perform on the name of the file/directory (using the {@link #isCase(java.lang.Object, java.lang.Object)} method) + * @param closure the closure to invoke + * @throws FileNotFoundException if the given directory does not exist + * @throws IllegalArgumentException if the provided File object does not represent a directory + * @since 1.7.1 + */ + public static void eachFileMatch(final File self, final FileType fileType, final Object nameFilter, final Closure closure) + throws FileNotFoundException, IllegalArgumentException { + checkDir(self); + final File[] files = self.listFiles(); + // null check because of http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4803836 + if (files == null) return; + final MetaClass metaClass = InvokerHelper.getMetaClass(nameFilter); + for (final File currentFile : files) { + if ((fileType != FileType.FILES && currentFile.isDirectory()) || + (fileType != FileType.DIRECTORIES && currentFile.isFile())) { + if (DefaultTypeTransformation.castToBoolean(metaClass.invokeMethod(nameFilter, "isCase", currentFile.getName()))) + closure.call(currentFile); + } + } + } + + /** + * Invokes the closure for each file whose name (file.name) matches the given nameFilter in the given directory + * - calling the {@link #isCase(java.lang.Object, java.lang.Object)} method to determine if a match occurs. This method can be used + * with different kinds of filters like regular expressions, classes, ranges etc. + * Both regular files and subdirectories are matched. + * + * @param self a file + * @param nameFilter the nameFilter to perform on the name of the file (using the {@link #isCase(java.lang.Object, java.lang.Object)} method) + * @param closure the closure to invoke + * @throws FileNotFoundException if the given directory does not exist + * @throws IllegalArgumentException if the provided File object does not represent a directory + * @see #eachFileMatch(java.io.File, groovy.io.FileType, java.lang.Object, groovy.lang.Closure) + * @since 1.5.0 + */ + public static void eachFileMatch(final File self, final Object nameFilter, final Closure closure) + throws FileNotFoundException, IllegalArgumentException { + eachFileMatch(self, FileType.ANY, nameFilter, closure); + } + + /** + * Invokes the closure for each subdirectory whose name (dir.name) matches the given nameFilter in the given directory + * - calling the {@link #isCase(java.lang.Object, java.lang.Object)} method to determine if a match occurs. This method can be used + * with different kinds of filters like regular expressions, classes, ranges etc. + * Only subdirectories are matched; regular files are ignored. + * + * @param self a file + * @param nameFilter the nameFilter to perform on the name of the directory (using the {@link #isCase(java.lang.Object, java.lang.Object)} method) + * @param closure the closure to invoke + * @throws FileNotFoundException if the given directory does not exist + * @throws IllegalArgumentException if the provided File object does not represent a directory + * @see #eachFileMatch(java.io.File, groovy.io.FileType, java.lang.Object, groovy.lang.Closure) + * @since 1.5.0 + */ + public static void eachDirMatch(final File self, final Object nameFilter, final Closure closure) throws FileNotFoundException, IllegalArgumentException { + eachFileMatch(self, FileType.DIRECTORIES, nameFilter, closure); + } + + /** + * Deletes a directory with all contained files and subdirectories. + *

    The method returns + *

      + *
    • true, when deletion was successful
    • + *
    • true, when it is called for a non existing directory
    • + *
    • false, when it is called for a file which isn't a directory
    • + *
    • false, when directory couldn't be deleted
    • + *
    + *

    + * + * @param self a File + * @return true if the file doesn't exist or deletion was successful + * @since 1.6.0 + */ + public static boolean deleteDir(final File self) { + if (!self.exists()) + return true; + if (!self.isDirectory()) + return false; + + File[] files = self.listFiles(); + if (files == null) + // couldn't access files + return false; + + // delete contained files + boolean result = true; + for (File file : files) { + if (file.isDirectory()) { + if (!deleteDir(file)) + result = false; + } else { + if (!file.delete()) + result = false; + } + } + + // now delete directory itself + if(!self.delete()) + result = false; + + return result; + } + + /** + * Renames the file. It's a shortcut for {@link java.io.File#renameTo(File)} + * + * @param self a File + * @param newPathName The new pathname for the named file + * @return true if and only if the renaming succeeded; + * false otherwise + * @since 1.7.4 + */ + public static boolean renameTo(final File self, String newPathName) { + return self.renameTo(new File(newPathName)); + } + + /** + * Allows a simple syntax for using timers. This timer will execute the + * given closure after the given delay. + * + * @param timer a timer object + * @param delay the delay in milliseconds before running the closure code + * @param closure the closure to invoke + * @return The timer task which has been scheduled. + * @since 1.5.0 + */ + public static TimerTask runAfter(Timer timer, int delay, final Closure closure) { + TimerTask timerTask = new TimerTask() { + public void run() { + closure.call(); + } + }; + timer.schedule(timerTask, delay); + return timerTask; + } + + /** + * Create a buffered reader for this file. + * + * @param file a File + * @return a BufferedReader + * @throws IOException if an IOException occurs. + * @since 1.0 + */ + public static BufferedReader newReader(File file) throws IOException { + CharsetToolkit toolkit = new CharsetToolkit(file); + return toolkit.getReader(); + } + + /** + * Create a buffered reader for this file, using the specified + * charset as the encoding. + * + * @param file a File + * @param charset the charset for this File + * @return a BufferedReader + * @throws FileNotFoundException if the File was not found + * @throws UnsupportedEncodingException if the encoding specified is not supported + * @since 1.0 + */ + public static BufferedReader newReader(File file, String charset) + throws FileNotFoundException, UnsupportedEncodingException { + return new BufferedReader(new InputStreamReader(new FileInputStream(file), charset)); + } + + /** + * Creates a reader for this input stream. + * + * @param self an input stream + * @return a reader + * @since 1.0 + */ + public static BufferedReader newReader(InputStream self) { + return new BufferedReader(new InputStreamReader(self)); + } + + /** + * Creates a reader for this input stream, using the specified + * charset as the encoding. + * + * @param self an input stream + * @param charset the charset for this input stream + * @return a reader + * @throws UnsupportedEncodingException if the encoding specified is not supported + * @since 1.6.0 + */ + public static BufferedReader newReader(InputStream self, String charset) throws UnsupportedEncodingException { + return new BufferedReader(new InputStreamReader(self, charset)); + } + + /** + * Create a new BufferedReader for this file and then + * passes it into the closure, ensuring the reader is closed after the + * closure returns. + * + * @param file a file object + * @param closure a closure + * @return the value returned by the closure + * @throws IOException if an IOException occurs. + * @since 1.5.2 + */ + public static T withReader(File file, Closure closure) throws IOException { + return withReader(newReader(file), closure); + } + + /** + * Create a new BufferedReader for this file using the specified charset and then + * passes it into the closure, ensuring the reader is closed after the + * closure returns. + * + * @param file a file object + * @param charset the charset for this input stream + * @param closure a closure + * @return the value returned by the closure + * @throws IOException if an IOException occurs. + * @since 1.6.0 + */ + public static T withReader(File file, String charset, Closure closure) throws IOException { + return withReader(newReader(file, charset), closure); + } + + /** + * Create a buffered output stream for this file. + * + * @param file a file object + * @return the created OutputStream + * @throws IOException if an IOException occurs. + * @since 1.0 + */ + public static BufferedOutputStream newOutputStream(File file) throws IOException { + return new BufferedOutputStream(new FileOutputStream(file)); + } + + /** + * Creates a new data output stream for this file. + * + * @param file a file object + * @return the created DataOutputStream + * @throws IOException if an IOException occurs. + * @since 1.5.0 + */ + public static DataOutputStream newDataOutputStream(File file) throws IOException { + return new DataOutputStream(new FileOutputStream(file)); + } + + /** + * Creates a new OutputStream for this file and passes it into the closure. + * This method ensures the stream is closed after the closure returns. + * + * @param file a File + * @param closure a closure + * @return the value returned by the closure + * @throws IOException if an IOException occurs. + * @see #withStream(java.io.OutputStream, groovy.lang.Closure) + * @since 1.5.2 + */ + public static Object withOutputStream(File file, Closure closure) throws IOException { + return withStream(newOutputStream(file), closure); + } + + /** + * Create a new InputStream for this file and passes it into the closure. + * This method ensures the stream is closed after the closure returns. + * + * @param file a File + * @param closure a closure + * @return the value returned by the closure + * @throws IOException if an IOException occurs. + * @see #withStream(java.io.InputStream, groovy.lang.Closure) + * @since 1.5.2 + */ + public static Object withInputStream(File file, Closure closure) throws IOException { + return withStream(newInputStream(file), closure); + } + + /** + * Creates a new InputStream for this URL and passes it into the closure. + * This method ensures the stream is closed after the closure returns. + * + * @param url a URL + * @param closure a closure + * @return the value returned by the closure + * @throws IOException if an IOException occurs. + * @see #withStream(java.io.InputStream, groovy.lang.Closure) + * @since 1.5.2 + */ + public static T withInputStream(URL url, Closure closure) throws IOException { + return withStream(newInputStream(url), closure); + } + + /** + * Create a new DataOutputStream for this file and passes it into the closure. + * This method ensures the stream is closed after the closure returns. + * + * @param file a File + * @param closure a closure + * @return the value returned by the closure + * @throws IOException if an IOException occurs. + * @see #withStream(java.io.OutputStream, groovy.lang.Closure) + * @since 1.5.2 + */ + public static T withDataOutputStream(File file, Closure closure) throws IOException { + return withStream(newDataOutputStream(file), closure); + } + + /** + * Create a new DataInputStream for this file and passes it into the closure. + * This method ensures the stream is closed after the closure returns. + * + * @param file a File + * @param closure a closure + * @return the value returned by the closure + * @throws IOException if an IOException occurs. + * @see #withStream(java.io.InputStream, groovy.lang.Closure) + * @since 1.5.2 + */ + public static T withDataInputStream(File file, Closure closure) throws IOException { + return withStream(newDataInputStream(file), closure); + } + + /** + * Create a buffered writer for this file. + * + * @param file a File + * @return a BufferedWriter + * @throws IOException if an IOException occurs. + * @since 1.0 + */ + public static BufferedWriter newWriter(File file) throws IOException { + return new BufferedWriter(new FileWriter(file)); + } + + /** + * Creates a buffered writer for this file, optionally appending to the + * existing file content. + * + * @param file a File + * @param append true if data should be appended to the file + * @return a BufferedWriter + * @throws IOException if an IOException occurs. + * @since 1.0 + */ + public static BufferedWriter newWriter(File file, boolean append) throws IOException { + return new BufferedWriter(new FileWriter(file, append)); + } + + /** + * Helper method to create a buffered writer for a file. If the given + * charset is "UTF-16BE" or "UTF-16LE", the requisite byte order mark is + * written to the stream before the writer is returned. + * + * @param file a File + * @param charset the name of the encoding used to write in this file + * @param append true if in append mode + * @return a BufferedWriter + * @throws IOException if an IOException occurs. + * @since 1.0 + */ + public static BufferedWriter newWriter(File file, String charset, boolean append) throws IOException { + if (append) { + return new EncodingAwareBufferedWriter(new OutputStreamWriter(new FileOutputStream(file, append), charset)); + } else { + // first write the Byte Order Mark for Unicode encodings + FileOutputStream stream = new FileOutputStream(file); + if ("UTF-16BE".equals(charset)) { + writeUtf16Bom(stream, true); + } else if ("UTF-16LE".equals(charset)) { + writeUtf16Bom(stream, false); + } + return new EncodingAwareBufferedWriter(new OutputStreamWriter(stream, charset)); + } + } + + /** + * Creates a buffered writer for this file, writing data using the given + * encoding. + * + * @param file a File + * @param charset the name of the encoding used to write in this file + * @return a BufferedWriter + * @throws IOException if an IOException occurs. + * @since 1.0 + */ + public static BufferedWriter newWriter(File file, String charset) throws IOException { + return newWriter(file, charset, false); + } + + /** + * Write a Byte Order Mark at the beginning of the file + * + * @param stream the FileOutputStream to write the BOM to + * @param bigEndian true if UTF 16 Big Endian or false if Low Endian + * @throws IOException if an IOException occurs. + * @since 1.0 + */ + private static void writeUtf16Bom(FileOutputStream stream, boolean bigEndian) throws IOException { + if (bigEndian) { + stream.write(-2); + stream.write(-1); + } else { + stream.write(-1); + stream.write(-2); + } + } + + /** + * Creates a new BufferedWriter for this file, passes it to the closure, and + * ensures the stream is flushed and closed after the closure returns. + * + * @param file a File + * @param closure a closure + * @return the value returned by the closure + * @throws IOException if an IOException occurs. + * @since 1.5.2 + */ + public static T withWriter(File file, Closure closure) throws IOException { + return withWriter(newWriter(file), closure); + } + + /** + * Creates a new BufferedWriter for this file, passes it to the closure, and + * ensures the stream is flushed and closed after the closure returns. + * The writer will use the given charset encoding. + * + * @param file a File + * @param charset the charset used + * @param closure a closure + * @return the value returned by the closure + * @throws IOException if an IOException occurs. + * @since 1.5.2 + */ + public static T withWriter(File file, String charset, Closure closure) throws IOException { + return withWriter(newWriter(file, charset), closure); + } + + /** + * Create a new BufferedWriter which will append to this + * file. The writer is passed to the closure and will be closed before + * this method returns. + * + * @param file a File + * @param charset the charset used + * @param closure a closure + * @return the value returned by the closure + * @throws IOException if an IOException occurs. + * @since 1.5.2 + */ + public static T withWriterAppend(File file, String charset, Closure closure) throws IOException { + return withWriter(newWriter(file, charset, true), closure); + } + + /** + * Create a new BufferedWriter for this file in append mode. The writer + * is passed to the closure and is closed after the closure returns. + * + * @param file a File + * @param closure a closure + * @return the value returned by the closure + * @throws IOException if an IOException occurs. + * @since 1.5.2 + */ + public static T withWriterAppend(File file, Closure closure) throws IOException { + return withWriter(newWriter(file, true), closure); + } + + /** + * Create a new PrintWriter for this file. + * + * @param file a File + * @return the created PrintWriter + * @throws IOException if an IOException occurs. + * @since 1.0 + */ + public static PrintWriter newPrintWriter(File file) throws IOException { + return new GroovyPrintWriter(newWriter(file)); + } + + /** + * Create a new PrintWriter for this file, using specified + * charset. + * + * @param file a File + * @param charset the charset + * @return a PrintWriter + * @throws IOException if an IOException occurs. + * @since 1.0 + */ + public static PrintWriter newPrintWriter(File file, String charset) throws IOException { + return new GroovyPrintWriter(newWriter(file, charset)); + } + + /** + * Create a new PrintWriter for this file, using specified + * charset. + * + * @param writer a writer + * @return a PrintWriter + * @since 1.6.0 + */ + public static PrintWriter newPrintWriter(Writer writer) { + return new GroovyPrintWriter(writer); + } + + /** + * Create a new PrintWriter for this file which is then + * passed it into the given closure. This method ensures its the writer + * is closed after the closure returns. + * + * @param file a File + * @param closure the closure to invoke with the PrintWriter + * @return the value returned by the closure + * @throws IOException if an IOException occurs. + * @since 1.5.2 + */ + public static T withPrintWriter(File file, Closure closure) throws IOException { + return withWriter(newPrintWriter(file), closure); + } + + /** + * Create a new PrintWriter with a specified charset for + * this file. The writer is passed to the closure, and will be closed + * before this method returns. + * + * @param file a File + * @param charset the charset + * @param closure the closure to invoke with the PrintWriter + * @return the value returned by the closure + * @throws IOException if an IOException occurs. + * @since 1.5.2 + */ + public static T withPrintWriter(File file, String charset, Closure closure) throws IOException { + return withWriter(newPrintWriter(file, charset), closure); + } + + /** + * Create a new PrintWriter with a specified charset for + * this file. The writer is passed to the closure, and will be closed + * before this method returns. + * + * @param writer a writer + * @param closure the closure to invoke with the PrintWriter + * @return the value returned by the closure + * @throws IOException if an IOException occurs. + * @since 1.6.0 + */ + public static T withPrintWriter(Writer writer, Closure closure) throws IOException { + return withWriter(newPrintWriter(writer), closure); + } + + /** + * Allows this writer to be used within the closure, ensuring that it + * is flushed and closed before this method returns. + * + * @param writer the writer which is used and then closed + * @param closure the closure that the writer is passed into + * @return the value returned by the closure + * @throws IOException if an IOException occurs. + * @since 1.5.2 + */ + public static T withWriter(Writer writer, Closure closure) throws IOException { + try { + T result = closure.call(writer); + + try { + writer.flush(); + } catch (IOException e) { + // try to continue even in case of error + } + Writer temp = writer; + writer = null; + temp.close(); + return result; + } finally { + closeWithWarning(writer); + } + } + + /** + * Allows this reader to be used within the closure, ensuring that it + * is closed before this method returns. + * + * @param reader the reader which is used and then closed + * @param closure the closure that the writer is passed into + * @return the value returned by the closure + * @throws IOException if an IOException occurs. + * @since 1.5.2 + */ + public static T withReader(Reader reader, Closure closure) throws IOException { + try { + T result = closure.call(reader); + + Reader temp = reader; + reader = null; + temp.close(); + + return result; + } finally { + closeWithWarning(reader); + } + } + + /** + * Allows this input stream to be used within the closure, ensuring that it + * is flushed and closed before this method returns. + * + * @param stream the stream which is used and then closed + * @param closure the closure that the stream is passed into + * @return the value returned by the closure + * @throws IOException if an IOException occurs. + * @since 1.5.2 + */ + public static T withStream(InputStream stream, Closure closure) throws IOException { + try { + T result = closure.call(stream); + + InputStream temp = stream; + stream = null; + temp.close(); + + return result; + } finally { + closeWithWarning(stream); + } + } + + /** + * Helper method to create a new BufferedReader for a URL and then + * passes it to the closure. The reader is closed after the closure returns. + * + * @param url a URL + * @param closure the closure to invoke with the reader + * @return the value returned by the closure + * @throws IOException if an IOException occurs. + * @since 1.5.2 + */ + public static T withReader(URL url, Closure closure) throws IOException { + return withReader(url.openConnection().getInputStream(), closure); + } + + /** + * Helper method to create a new Reader for a URL and then + * passes it to the closure. The reader is closed after the closure returns. + * + * @param url a URL + * @param charset the charset used + * @param closure the closure to invoke with the reader + * @return the value returned by the closure + * @throws IOException if an IOException occurs. + * @since 1.5.6 + */ + public static T withReader(URL url, String charset, Closure closure) throws IOException { + return withReader(url.openConnection().getInputStream(), charset, closure); + } + + /** + * Helper method to create a new Reader for a stream and then + * passes it into the closure. The reader (and this stream) is closed after + * the closure returns. + * + * @see java.io.InputStreamReader + * @param in a stream + * @param closure the closure to invoke with the InputStream + * @return the value returned by the closure + * @throws IOException if an IOException occurs. + * @since 1.5.2 + */ + public static T withReader(InputStream in, Closure closure) throws IOException { + return withReader(new InputStreamReader(in), closure); + } + + /** + * Helper method to create a new Reader for a stream and then + * passes it into the closure. The reader (and this stream) is closed after + * the closure returns. + * + * @see java.io.InputStreamReader + * @param in a stream + * @param charset the charset used to decode the stream + * @param closure the closure to invoke with the reader + * @return the value returned by the closure + * @throws IOException if an IOException occurs. + * @since 1.5.6 + */ + public static T withReader(InputStream in, String charset, Closure closure) throws IOException { + return withReader(new InputStreamReader(in, charset), closure); + } + + /** + * Creates a writer from this stream, passing it to the given closure. + * This method ensures the stream is closed after the closure returns. + * + * @param stream the stream which is used and then closed + * @param closure the closure that the writer is passed into + * @return the value returned by the closure + * @throws IOException if an IOException occurs. + * @see #withWriter(java.io.Writer, groovy.lang.Closure) + * @since 1.5.2 + */ + public static T withWriter(OutputStream stream, Closure closure) throws IOException { + return withWriter(new OutputStreamWriter(stream), closure); + } + + /** + * Creates a writer from this stream, passing it to the given closure. + * This method ensures the stream is closed after the closure returns. + * + * @param stream the stream which is used and then closed + * @param charset the charset used + * @param closure the closure that the writer is passed into + * @return the value returned by the closure + * @throws IOException if an IOException occurs. + * @see #withWriter(java.io.Writer, groovy.lang.Closure) + * @since 1.5.2 + */ + public static T withWriter(OutputStream stream, String charset, Closure closure) throws IOException { + return withWriter(new OutputStreamWriter(stream, charset), closure); + } + + /** + * Passes this OutputStream to the closure, ensuring that the stream + * is closed after the closure returns, regardless of errors. + * + * @param os the stream which is used and then closed + * @param closure the closure that the stream is passed into + * @return the value returned by the closure + * @throws IOException if an IOException occurs. + * @since 1.5.2 + */ + public static T withStream(OutputStream os, Closure closure) throws IOException { + try { + T result = closure.call(os); + os.flush(); + + OutputStream temp = os; + os = null; + temp.close(); + + return result; + } finally { + closeWithWarning(os); + } + } + + /** + * Creates a buffered input stream for this file. + * + * @param file a File + * @return a BufferedInputStream of the file + * @throws FileNotFoundException if the file is not found. + * @since 1.0 + */ + public static BufferedInputStream newInputStream(File file) throws FileNotFoundException { + return new BufferedInputStream(new FileInputStream(file)); + } + + /** + * Creates an inputstream for this URL, with the possibility to set different connection parameters using the + * parameters map: + *
      + *
    • connectTimeout : the connection timeout
    • + *
    • readTimeout : the read timeout
    • + *
    • useCaches : set the use cache property for the URL connection
    • + *
    • allowUserInteraction : set the user interaction flag for the URL connection
    • + *
    • requestProperties : a map of properties to be passed to the URL connection
    • + *
    + * @param parameters an optional map specifying part or all of supported connection parameters + * @param url the url for which to create the inputstream + * @return an InputStream from the underlying URLConnection + * @throws IOException if an I/O error occurs while creating the input stream + * @since 1.8.1 + */ + private static InputStream configuredInputStream(Map parameters, URL url) throws IOException { + final URLConnection connection = url.openConnection(); + if (parameters!=null) { + if (parameters.containsKey("connectTimeout")) { + connection.setConnectTimeout(asType(parameters.get("connectTimeout"), Integer.class)); + } + if (parameters.containsKey("readTimeout")) { + connection.setReadTimeout(asType(parameters.get("readTimeout"), Integer.class)); + } + if (parameters.containsKey("useCaches")) { + connection.setUseCaches(asType(parameters.get("useCaches"), Boolean.class)); + } + if (parameters.containsKey("allowUserInteraction")) { + connection.setAllowUserInteraction(asType(parameters.get("allowUserInteraction"), Boolean.class)); + } + if (parameters.containsKey("requestProperties")) { + @SuppressWarnings("unchecked") + Map properties = (Map) parameters.get("requestProperties"); + for (Map.Entry entry : properties.entrySet()) { + connection.setRequestProperty(entry.getKey(), entry.getValue()); + } + } + + } + return connection.getInputStream(); + } + + /** + * Creates a buffered input stream for this URL. + * + * @param url a URL + * @return a BufferedInputStream for the URL + * @throws MalformedURLException is thrown if the URL is not well formed + * @throws IOException if an I/O error occurs while creating the input stream + * @since 1.5.2 + */ + public static BufferedInputStream newInputStream(URL url) throws MalformedURLException, IOException { + return new BufferedInputStream(configuredInputStream(null, url)); + } + + /** + * Creates a buffered input stream for this URL. + * + * @param url a URL + * @param parameters connection parameters + * @return a BufferedInputStream for the URL + * @throws MalformedURLException is thrown if the URL is not well formed + * @throws IOException if an I/O error occurs while creating the input stream + * @since 1.8.1 + */ + public static BufferedInputStream newInputStream(URL url, Map parameters) throws MalformedURLException, IOException { + return new BufferedInputStream(configuredInputStream(parameters, url)); + } + + /** + * Creates a buffered reader for this URL. + * + * @param url a URL + * @return a BufferedReader for the URL + * @throws MalformedURLException is thrown if the URL is not well formed + * @throws IOException if an I/O error occurs while creating the input stream + * @since 1.5.5 + */ + public static BufferedReader newReader(URL url) throws MalformedURLException, IOException { + return newReader(configuredInputStream(null, url)); + } + + /** + * Creates a buffered reader for this URL. + * + * @param url a URL + * @param parameters connection parameters + * @return a BufferedReader for the URL + * @throws MalformedURLException is thrown if the URL is not well formed + * @throws IOException if an I/O error occurs while creating the input stream + * @since 1.8.1 + */ + public static BufferedReader newReader(URL url, Map parameters) throws MalformedURLException, IOException { + return newReader(configuredInputStream(parameters, url)); + } + + /** + * Creates a buffered reader for this URL using the given encoding. + * + * @param url a URL + * @param charset opens the stream with a specified charset + * @return a BufferedReader for the URL + * @throws MalformedURLException is thrown if the URL is not well formed + * @throws IOException if an I/O error occurs while creating the input stream + * @since 1.5.5 + */ + public static BufferedReader newReader(URL url, String charset) throws MalformedURLException, IOException { + return new BufferedReader(new InputStreamReader(configuredInputStream(null, url), charset)); + } + + /** + * Creates a buffered reader for this URL using the given encoding. + * + * @param url a URL + * @param parameters connection parameters + * @param charset opens the stream with a specified charset + * @return a BufferedReader for the URL + * @throws MalformedURLException is thrown if the URL is not well formed + * @throws IOException if an I/O error occurs while creating the input stream + * @since 1.8.1 + */ + public static BufferedReader newReader(URL url, Map parameters, String charset) throws MalformedURLException, IOException { + return new BufferedReader(new InputStreamReader(configuredInputStream(parameters, url), charset)); + } + + /** + * Create a data input stream for this file + * + * @param file a File + * @return a DataInputStream of the file + * @throws FileNotFoundException if the file is not found. + * @since 1.5.0 + */ + public static DataInputStream newDataInputStream(File file) throws FileNotFoundException { + return new DataInputStream(new FileInputStream(file)); + } + + /** + * Traverse through each byte of this File + * + * @param self a File + * @param closure a closure + * @throws IOException if an IOException occurs. + * @see #eachByte(java.io.InputStream, groovy.lang.Closure) + * @since 1.0 + */ + public static void eachByte(File self, Closure closure) throws IOException { + BufferedInputStream is = newInputStream(self); + eachByte(is, closure); + } + + /** + * Traverse through the bytes of this File, bufferLen bytes at a time. + * + * @param self a File + * @param bufferLen the length of the buffer to use. + * @param closure a 2 parameter closure which is passed the byte[] and a number of bytes successfully read. + * @throws IOException if an IOException occurs. + * @see #eachByte(java.io.InputStream, int, groovy.lang.Closure) + * @since 1.7.4 + */ + public static void eachByte(File self, int bufferLen, Closure closure) throws IOException { + BufferedInputStream is = newInputStream(self); + eachByte(is, bufferLen, closure); + } + + /** + * Traverse through each byte of this Byte array. Alias for each. + * + * @param self a Byte array + * @param closure a closure + * @see #each(java.lang.Object, groovy.lang.Closure) + * @since 1.5.5 + */ + public static void eachByte(Byte[] self, Closure closure) { + each(self, closure); + } + + /** + * Traverse through each byte of this byte array. Alias for each. + * + * @param self a byte array + * @param closure a closure + * @see #each(java.lang.Object, groovy.lang.Closure) + * @since 1.5.5 + */ + public static void eachByte(byte[] self, Closure closure) { + each(self, closure); + } + + /** + * Traverse through each byte of the specified stream. The + * stream is closed after the closure returns. + * + * @param is stream to iterate over, closed after the method call + * @param closure closure to apply to each byte + * @throws IOException if an IOException occurs. + * @since 1.0 + */ + public static void eachByte(InputStream is, Closure closure) throws IOException { + try { + while (true) { + int b = is.read(); + if (b == -1) { + break; + } else { + closure.call((byte) b); + } + } + + InputStream temp = is; + is = null; + temp.close(); + } finally { + closeWithWarning(is); + } + } + + /** + * Traverse through each the specified stream reading bytes into a buffer + * and calling the 2 parameter closure with this buffer and the number of bytes. + * + * @param is stream to iterate over, closed after the method call. + * @param bufferLen the length of the buffer to use. + * @param closure a 2 parameter closure which is passed the byte[] and a number of bytes successfully read. + * @throws IOException if an IOException occurs. + * @since 1.8 + */ + public static void eachByte(InputStream is, int bufferLen, Closure closure) throws IOException { + byte[] buffer = new byte[ bufferLen ] ; + int bytesRead = 0 ; + try { + while ( ( bytesRead = is.read( buffer, 0, bufferLen ) ) > 0 ) { + closure.call( new Object[]{ buffer, bytesRead } ) ; + } + + InputStream temp = is; + is = null; + temp.close(); + } finally { + closeWithWarning(is); + } + } + + /** + * Reads the InputStream from this URL, passing each byte to the given + * closure. The URL stream will be closed before this method returns. + * + * @param url url to iterate over + * @param closure closure to apply to each byte + * @throws IOException if an IOException occurs. + * @see #eachByte(java.io.InputStream, groovy.lang.Closure) + * @since 1.0 + */ + public static void eachByte(URL url, Closure closure) throws IOException { + InputStream is = url.openConnection().getInputStream(); + eachByte(is, closure); + } + + /** + * Reads the InputStream from this URL, passing a byte[] and a number of bytes + * to the given closure. The URL stream will be closed before this method returns. + * + * @param url url to iterate over + * @param bufferLen the length of the buffer to use. + * @param closure a 2 parameter closure which is passed the byte[] and a number of bytes successfully read. + * @throws IOException if an IOException occurs. + * @see #eachByte(java.io.InputStream, int, groovy.lang.Closure) + * @since 1.8 + */ + public static void eachByte(URL url, int bufferLen, Closure closure) throws IOException { + InputStream is = url.openConnection().getInputStream(); + eachByte(is, bufferLen, closure); + } + + /** + * Transforms each character from this reader by passing it to the given + * closure. The Closure should return each transformed character, which + * will be passed to the Writer. The reader and writer will be both be + * closed before this method returns. + * + * @param self a Reader object + * @param writer a Writer to receive the transformed characters + * @param closure a closure that performs the required transformation + * @throws IOException if an IOException occurs. + * @since 1.5.0 + */ + public static void transformChar(Reader self, Writer writer, Closure closure) throws IOException { + int c; + try { + char[] chars = new char[1]; + while ((c = self.read()) != -1) { + chars[0] = (char) c; + writer.write((String) closure.call(new String(chars))); + } + writer.flush(); + + Writer temp2 = writer; + writer = null; + temp2.close(); + Reader temp1 = self; + self = null; + temp1.close(); + } finally { + closeWithWarning(self); + closeWithWarning(writer); + } + } + + /** + * Transforms the lines from a reader with a Closure and + * write them to a writer. Both Reader and Writer are + * closed after the operation. + * + * @param reader Lines of text to be transformed. Reader is closed afterwards. + * @param writer Where transformed lines are written. Writer is closed afterwards. + * @param closure Single parameter closure that is called to transform each line of + * text from the reader, before writing it to the writer. + * @throws IOException if an IOException occurs. + * @since 1.0 + */ + public static void transformLine(Reader reader, Writer writer, Closure closure) throws IOException { + BufferedReader br = new BufferedReader(reader); + BufferedWriter bw = new BufferedWriter(writer); + String line; + try { + while ((line = br.readLine()) != null) { + Object o = closure.call(line); + if (o != null) { + bw.write(o.toString()); + bw.newLine(); + } + } + bw.flush(); + + Writer temp2 = writer; + writer = null; + temp2.close(); + Reader temp1 = reader; + reader = null; + temp1.close(); + } finally { + closeWithWarning(br); + closeWithWarning(reader); + closeWithWarning(bw); + closeWithWarning(writer); + } + } + + /** + * Filter the lines from a reader and write them on the writer, + * according to a closure which returns true if the line should be included. + * Both Reader and Writer are closed after the operation. + * + * @param reader a reader, closed after the call + * @param writer a writer, closed after the call + * @param closure the closure which returns booleans + * @throws IOException if an IOException occurs. + * @since 1.0 + */ + public static void filterLine(Reader reader, Writer writer, Closure closure) throws IOException { + BufferedReader br = new BufferedReader(reader); + BufferedWriter bw = new BufferedWriter(writer); + String line; + try { + while ((line = br.readLine()) != null) { + if (DefaultTypeTransformation.castToBoolean(closure.call(line))) { + bw.write(line); + bw.newLine(); + } + } + bw.flush(); + + Writer temp2 = writer; + writer = null; + temp2.close(); + Reader temp1 = reader; + reader = null; + temp1.close(); + } finally { + closeWithWarning(br); + closeWithWarning(reader); + closeWithWarning(bw); + closeWithWarning(writer); + } + + } + + /** + * Filters the lines of a File and creates a Writable in return to + * stream the filtered lines. + * + * @param self a File + * @param closure a closure which returns a boolean indicating to filter + * the line or not + * @return a Writable closure + * @throws IOException if self is not readable + * @see #filterLine(java.io.Reader, groovy.lang.Closure) + * @since 1.0 + */ + public static Writable filterLine(File self, Closure closure) throws IOException { + return filterLine(newReader(self), closure); + } + + /** + * Filters the lines of a File and creates a Writable in return to + * stream the filtered lines. + * + * @param self a File + * @param charset opens the file with a specified charset + * @param closure a closure which returns a boolean indicating to filter + * the line or not + * @return a Writable closure + * @throws IOException if an IOException occurs + * @see #filterLine(java.io.Reader, groovy.lang.Closure) + * @since 1.6.8 + */ + public static Writable filterLine(File self, String charset, Closure closure) throws IOException { + return filterLine(newReader(self, charset), closure); + } + + /** + * Filter the lines from this File, and write them to the given writer based + * on the given closure predicate. + * + * @param self a File + * @param writer a writer destination to write filtered lines to + * @param closure a closure which takes each line as a parameter and returns + * true if the line should be written to this writer. + * @throws IOException if self is not readable + * @see #filterLine(java.io.Reader, java.io.Writer, groovy.lang.Closure) + * @since 1.0 + */ + public static void filterLine(File self, Writer writer, Closure closure) throws IOException { + filterLine(newReader(self), writer, closure); + } + + /** + * Filter the lines from this File, and write them to the given writer based + * on the given closure predicate. + * + * @param self a File + * @param writer a writer destination to write filtered lines to + * @param charset opens the file with a specified charset + * @param closure a closure which takes each line as a parameter and returns + * true if the line should be written to this writer. + * @throws IOException if an IO error occurs + * @see #filterLine(java.io.Reader, java.io.Writer, groovy.lang.Closure) + * @since 1.6.8 + */ + public static void filterLine(File self, Writer writer, String charset, Closure closure) throws IOException { + filterLine(newReader(self, charset), writer, closure); + } + + /** + * Filter the lines from this Reader, and return a Writable which can be + * used to stream the filtered lines to a destination. The closure should + * return true if the line should be passed to the writer. + * + * @param reader this reader + * @param closure a closure used for filtering + * @return a Writable which will use the closure to filter each line + * from the reader when the Writable#writeTo(Writer) is called. + * @since 1.0 + */ + public static Writable filterLine(Reader reader, final Closure closure) { + final BufferedReader br = new BufferedReader(reader); + return new Writable() { + public Writer writeTo(Writer out) throws IOException { + BufferedWriter bw = new BufferedWriter(out); + String line; + while ((line = br.readLine()) != null) { + if (DefaultTypeTransformation.castToBoolean(closure.call(line))) { + bw.write(line); + bw.newLine(); + } + } + bw.flush(); + return out; + } + + public String toString() { + StringWriter buffer = new StringWriter(); + try { + writeTo(buffer); + } catch (IOException e) { + throw new StringWriterIOException(e); + } + return buffer.toString(); + } + }; + } + + /** + * Filter lines from an input stream using a closure predicate. The closure + * will be passed each line as a String, and it should return + * true if the line should be passed to the writer. + * + * @param self an input stream + * @param predicate a closure which returns boolean and takes a line + * @return a writable which writes out the filtered lines + * @see #filterLine(java.io.Reader, groovy.lang.Closure) + * @since 1.0 + */ + public static Writable filterLine(InputStream self, Closure predicate) { + return filterLine(newReader(self), predicate); + } + + /** + * Filter lines from an input stream using a closure predicate. The closure + * will be passed each line as a String, and it should return + * true if the line should be passed to the writer. + * + * @param self an input stream + * @param charset opens the stream with a specified charset + * @param predicate a closure which returns boolean and takes a line + * @return a writable which writes out the filtered lines + * @throws UnsupportedEncodingException if the encoding specified is not supported + * @see #filterLine(java.io.Reader, groovy.lang.Closure) + * @since 1.6.8 + */ + public static Writable filterLine(InputStream self, String charset, Closure predicate) + throws UnsupportedEncodingException { + return filterLine(newReader(self, charset), predicate); + } + + /** + * Uses a closure to filter lines from this InputStream and pass them to + * the given writer. The closure will be passed each line as a String, and + * it should return true if the line should be passed to the + * writer. + * + * @param self the InputStream + * @param writer a writer to write output to + * @param predicate a closure which returns true if a line should be accepted + * @throws IOException if an IOException occurs. + * @see #filterLine(java.io.Reader, java.io.Writer, groovy.lang.Closure) + * @since 1.0 + */ + public static void filterLine(InputStream self, Writer writer, Closure predicate) + throws IOException { + filterLine(newReader(self), writer, predicate); + } + + /** + * Uses a closure to filter lines from this InputStream and pass them to + * the given writer. The closure will be passed each line as a String, and + * it should return true if the line should be passed to the + * writer. + * + * @param self the InputStream + * @param writer a writer to write output to + * @param charset opens the stream with a specified charset + * @param predicate a closure which returns true if a line should be accepted + * @throws IOException if an IOException occurs. + * @see #filterLine(java.io.Reader, java.io.Writer, groovy.lang.Closure) + * @since 1.6.8 + */ + public static void filterLine(InputStream self, Writer writer, String charset, Closure predicate) + throws IOException { + filterLine(newReader(self, charset), writer, predicate); + } + + /** + * Filter lines from a URL using a closure predicate. The closure + * will be passed each line as a String, and it should return + * true if the line should be passed to the writer. + * + * @param self a URL + * @param predicate a closure which returns boolean and takes a line + * @return a writable which writes out the filtered lines + * @throws IOException if an IO exception occurs + * @see #filterLine(java.io.Reader, groovy.lang.Closure) + * @since 1.6.8 + */ + public static Writable filterLine(URL self, Closure predicate) + throws IOException { + return filterLine(newReader(self), predicate); + } + + /** + * Filter lines from a URL using a closure predicate. The closure + * will be passed each line as a String, and it should return + * true if the line should be passed to the writer. + * + * @param self the URL + * @param charset opens the URL with a specified charset + * @param predicate a closure which returns boolean and takes a line + * @return a writable which writes out the filtered lines + * @throws IOException if an IO exception occurs + * @see #filterLine(java.io.Reader, groovy.lang.Closure) + * @since 1.6.8 + */ + public static Writable filterLine(URL self, String charset, Closure predicate) + throws IOException { + return filterLine(newReader(self, charset), predicate); + } + + /** + * Uses a closure to filter lines from this URL and pass them to + * the given writer. The closure will be passed each line as a String, and + * it should return true if the line should be passed to the + * writer. + * + * @param self the URL + * @param writer a writer to write output to + * @param predicate a closure which returns true if a line should be accepted + * @throws IOException if an IOException occurs. + * @see #filterLine(java.io.Reader, java.io.Writer, groovy.lang.Closure) + * @since 1.6.8 + */ + public static void filterLine(URL self, Writer writer, Closure predicate) + throws IOException { + filterLine(newReader(self), writer, predicate); + } + + /** + * Uses a closure to filter lines from this URL and pass them to + * the given writer. The closure will be passed each line as a String, and + * it should return true if the line should be passed to the + * writer. + * + * @param self the URL + * @param writer a writer to write output to + * @param charset opens the URL with a specified charset + * @param predicate a closure which returns true if a line should be accepted + * @throws IOException if an IOException occurs. + * @see #filterLine(java.io.Reader, java.io.Writer, groovy.lang.Closure) + * @since 1.6.8 + */ + public static void filterLine(URL self, Writer writer, String charset, Closure predicate) + throws IOException { + filterLine(newReader(self, charset), writer, predicate); + } + + /** + * Reads the content of the file into a byte array. + * + * @param file a File + * @return a byte array with the contents of the file. + * @throws IOException if an IOException occurs. + * @since 1.0 + */ + public static byte[] readBytes(File file) throws IOException { + byte[] bytes = new byte[(int) file.length()]; + FileInputStream fileInputStream = new FileInputStream(file); + DataInputStream dis = new DataInputStream(fileInputStream); + try { + dis.readFully(bytes); + InputStream temp = dis; + dis = null; + temp.close(); + } finally { + closeWithWarning(dis); + } + return bytes; + } + + // ================================ + // Socket and ServerSocket methods + + /** + * Passes the Socket's InputStream and OutputStream to the closure. The + * streams will be closed after the closure returns, even if an exception + * is thrown. + * + * @param socket a Socket + * @param closure a Closure + * @return the value returned by the closure + * @throws IOException if an IOException occurs. + * @since 1.5.2 + */ + public static T withStreams(Socket socket, Closure closure) throws IOException { + InputStream input = socket.getInputStream(); + OutputStream output = socket.getOutputStream(); + try { + T result = closure.call(new Object[]{input, output}); + + InputStream temp1 = input; + input = null; + temp1.close(); + OutputStream temp2 = output; + output = null; + temp2.close(); + + return result; + } finally { + closeWithWarning(input); + closeWithWarning(output); + } + } + + /** + * Creates an InputObjectStream and an OutputObjectStream from a Socket, and + * passes them to the closure. The streams will be closed after the closure + * returns, even if an exception is thrown. + * + * @param socket this Socket + * @param closure a Closure + * @return the value returned by the closure + * @throws IOException if an IOException occurs. + * @since 1.5.0 + */ + public static T withObjectStreams(Socket socket, Closure closure) throws IOException { + InputStream input = socket.getInputStream(); + OutputStream output = socket.getOutputStream(); + ObjectOutputStream oos = new ObjectOutputStream(output); + ObjectInputStream ois = new ObjectInputStream(input); + try { + T result = closure.call(new Object[]{ois, oos}); + + InputStream temp1 = ois; + ois = null; + temp1.close(); + temp1 = input; + input = null; + temp1.close(); + OutputStream temp2 = oos; + oos = null; + temp2.close(); + temp2 = output; + output = null; + temp2.close(); + + return result; + } finally { + closeWithWarning(ois); + closeWithWarning(input); + closeWithWarning(oos); + closeWithWarning(output); + } + } + + /** + * Overloads the left shift operator to provide an append mechanism to + * add things to the output stream of a socket + * + * @param self a Socket + * @param value a value to append + * @return a Writer + * @throws IOException if an IOException occurs. + * @since 1.0 + */ + public static Writer leftShift(Socket self, Object value) throws IOException { + return leftShift(self.getOutputStream(), value); + } + + /** + * Overloads the left shift operator to provide an append mechanism + * to add bytes to the output stream of a socket + * + * @param self a Socket + * @param value a value to append + * @return an OutputStream + * @throws IOException if an IOException occurs. + * @since 1.0 + */ + public static OutputStream leftShift(Socket self, byte[] value) throws IOException { + return leftShift(self.getOutputStream(), value); + } + + /** + * Accepts a connection and passes the resulting Socket to the closure + * which runs in a new Thread. + * + * @param serverSocket a ServerSocket + * @param closure a Closure + * @return a Socket + * @throws IOException if an IOException occurs. + * @see java.net.ServerSocket#accept() + * @since 1.0 + */ + public static Socket accept(ServerSocket serverSocket, final Closure closure) throws IOException { + return accept(serverSocket, true, closure); + } + + /** + * Accepts a connection and passes the resulting Socket to the closure + * which runs in a new Thread or the calling thread, as needed. + * + * @param serverSocket a ServerSocket + * @param runInANewThread This flag should be true, if the closure should be invoked in a new thread, else false. + * @param closure a Closure + * @return a Socket + * @throws IOException if an IOException occurs. + * @see java.net.ServerSocket#accept() + * @since 1.7.6 + */ + public static Socket accept(ServerSocket serverSocket, final boolean runInANewThread, + final Closure closure) throws IOException { + final Socket socket = serverSocket.accept(); + if(runInANewThread) { + new Thread(new Runnable() { + public void run() { + invokeClosureWithSocket(socket, closure); + } + }).start(); + } else { + invokeClosureWithSocket(socket, closure); + } + return socket; + } + + private static void invokeClosureWithSocket(Socket socket, Closure closure) { + try { + closure.call(socket); + } finally { + if (socket != null) { + try { + socket.close(); + } catch (IOException e) { + LOG.warn("Caught exception closing socket: " + e); + } + } + } + } + + /** + * Converts this File to a {@link groovy.lang.Writable}. + * + * @param file a File + * @return a File which wraps the input file and which implements Writable + * @since 1.0 + */ + public static File asWritable(File file) { + return new WritableFile(file); + } + + /** + * Converts this File to a {@link groovy.lang.Writable} or delegates to default + * {@link #asType(java.lang.Object, java.lang.Class)}. + * + * @param f a File + * @param c the desired class + * @return the converted object + * @since 1.0 + */ + @SuppressWarnings("unchecked") + public static T asType(File f, Class c) { + if (c == Writable.class) { + return (T) asWritable(f); + } + return asType((Object) f, c); + } + + /** + * Allows a file to return a Writable implementation that can output itself + * to a Writer stream. + * + * @param file a File + * @param encoding the encoding to be used when reading the file's contents + * @return File which wraps the input file and which implements Writable + * @since 1.0 + */ + public static File asWritable(File file, String encoding) { + return new WritableFile(file, encoding); + } + + /** + * Converts the given String into a List of strings of one character. + * + * @param self a String + * @return a List of characters (a 1-character String) + * @since 1.0 + */ + public static List toList(String self) { + int size = self.length(); + List answer = new ArrayList(size); + for (int i = 0; i < size; i++) { + answer.add(self.substring(i, i + 1)); + } + return answer; + } + + /** + * Converts the given CharSequence into a List of CharSequence of one character. + * + * @param self a CharSequence + * @return a List of characters (a 1-character CharSequence) + * @see #toSet(String) + * @since 1.8.2 + */ + public static List toList(CharSequence self) { + return new ArrayList(toList(self.toString())); + } + + /** + * Converts the given String into a Set of unique strings of one character. + *

    + * Example usage: + *

    +     * assert 'groovy'.toSet() == ['v', 'g', 'r', 'o', 'y'] as Set
    +     * assert "abc".toSet().iterator()[0] instanceof String
    +     * 
    + * + * @param self a String + * @return a Set of unique character Strings (each a 1-character String) + * @since 1.8.0 + */ + public static Set toSet(String self) { + return new HashSet(toList(self)); + } + + /** + * Converts the given CharSequence into a Set of unique CharSequence of one character. + * + * @param self a CharSequence + * @return a Set of unique character CharSequence (each a 1-character CharSequence) + * @see #toSet(String) + * @since 1.8.2 + */ + public static Set toSet(CharSequence self) { + return new HashSet(toList(self)); + } + + /** + * Converts the given String into an array of characters. + * Alias for toCharArray. + * + * @param self a String + * @return an array of characters + * @see java.lang.String#toCharArray() + * @since 1.6.0 + */ + public static char[] getChars(String self) { + return self.toCharArray(); + } + + /** + * Converts the given CharSequence into an array of characters. + * + * @param self a CharSequence + * @return an array of characters + * @see #getChars(String) + * @since 1.8.2 + */ + public static char[] getChars(CharSequence self) { + return getChars(self.toString()); + } + + /** + * Converts the GString to a File, or delegates to the default + * {@link #asType(java.lang.Object, java.lang.Class)} + * + * @param self a GString + * @param c the desired class + * @return the converted object + * @since 1.5.0 + */ + @SuppressWarnings("unchecked") + public static T asType(GString self, Class c) { + if (c == File.class) { + return (T) new File(self.toString()); + } else if (Number.class.isAssignableFrom(c)) { + return asType(self.toString(), c); + } + return asType((Object) self, c); + } + + /** + *

    Provides a method to perform custom 'dynamic' type conversion + * to the given class using the as operator.

    + * Example: '123' as Double + *

    By default, the following types are supported: + *

      + *
    • List
    • + *
    • BigDecimal
    • + *
    • BigInteger
    • + *
    • Long
    • + *
    • Integer
    • + *
    • Short
    • + *
    • Byte
    • + *
    • Character
    • + *
    • Double
    • + *
    • Float
    • + *
    • File
    • + *
    • Subclasses of Enum (Java 5 and above)
    • + *
    + * If any other type is given, the call is delegated to + * {@link #asType(java.lang.Object, java.lang.Class)}. + * + * @param self a String + * @param c the desired class + * @return the converted object + * @since 1.0 + */ + @SuppressWarnings("unchecked") + public static T asType(String self, Class c) { + if (c == List.class) { + return (T) toList(self); + } else if (c == BigDecimal.class) { + return (T) toBigDecimal(self); + } else if (c == BigInteger.class) { + return (T) toBigInteger(self); + } else if (c == Long.class || c == Long.TYPE) { + return (T) toLong(self); + } else if (c == Integer.class || c == Integer.TYPE) { + return (T) toInteger(self); + } else if (c == Short.class || c == Short.TYPE) { + return (T) toShort(self); + } else if (c == Byte.class || c == Byte.TYPE) { + return (T) Byte.valueOf(self.trim()); + } else if (c == Character.class || c == Character.TYPE) { + return (T) toCharacter(self); + } else if (c == Double.class || c == Double.TYPE) { + return (T) toDouble(self); + } else if (c == Float.class || c == Float.TYPE) { + return (T) toFloat(self); + } else if (c == File.class) { + return (T) new File(self); + } else if (DefaultTypeTransformation.isEnumSubclass(c)) { + return (T) InvokerHelper.invokeMethod(c, "valueOf", new Object[]{ self }); + } + return asType((Object) self, c); + } + + /** + *

    Provides a method to perform custom 'dynamic' type conversion + * to the given class using the as operator. + * + * @param self a CharSequence + * @param c the desired class + * @return the converted object + * @see #asType(String, Class) + * @since 1.8.2 + */ + public static T asType(CharSequence self, Class c) { + return asType(self.toString(), c); + } + + /** + * Process each regex group matched substring of the given string. If the closure + * parameter takes one argument, an array with all match groups is passed to it. + * If the closure takes as many arguments as there are match groups, then each + * parameter will be one match group. + * + * @param self the source string + * @param regex a Regex string + * @param closure a closure with one parameter or as much parameters as groups + * @return the source string + * @since 1.6.0 + */ + public static String eachMatch(String self, String regex, Closure closure) { + return eachMatch(self, Pattern.compile(regex), closure); + } + + /** + * Process each regex group matched substring of the given CharSequence. If the closure + * parameter takes one argument, an array with all match groups is passed to it. + * If the closure takes as many arguments as there are match groups, then each + * parameter will be one match group. + * + * @param self the source CharSequence + * @param regex a Regex CharSequence + * @param closure a closure with one parameter or as much parameters as groups + * @return the source CharSequence + * @see #eachMatch(String, String, groovy.lang.Closure) + * @since 1.8.2 + */ + public static String eachMatch(CharSequence self, CharSequence regex, Closure closure) { + return eachMatch(self.toString(), regex.toString(), closure); + } + + /** + * Process each regex group matched substring of the given pattern. If the closure + * parameter takes one argument, an array with all match groups is passed to it. + * If the closure takes as many arguments as there are match groups, then each + * parameter will be one match group. + * + * @param self the source string + * @param pattern a regex Pattern + * @param closure a closure with one parameter or as much parameters as groups + * @return the source string + * @since 1.6.1 + */ + public static String eachMatch(String self, Pattern pattern, Closure closure) { + Matcher m = pattern.matcher(self); + each(m, closure); + return self; + } + + /** + * Process each regex group matched substring of the given pattern. If the closure + * parameter takes one argument, an array with all match groups is passed to it. + * If the closure takes as many arguments as there are match groups, then each + * parameter will be one match group. + * + * @param self the source CharSequence + * @param pattern a regex Pattern + * @param closure a closure with one parameter or as much parameters as groups + * @return the source CharSequence + * @see #eachMatch(String, Pattern, groovy.lang.Closure) + * @since 1.8.2 + */ + public static String eachMatch(CharSequence self, Pattern pattern, Closure closure) { + return eachMatch(self.toString(), pattern, closure); + } + + /** + * Iterates over the elements of an iterable collection of items and returns + * the index of the first item that matches the condition specified in the closure. + * + * @param self the iteration object over which to iterate + * @param closure the filter to perform a match on the collection + * @return an integer that is the index of the first matched object or -1 if no match was found + * @since 1.0 + */ + public static int findIndexOf(Object self, Closure closure) { + return findIndexOf(self, 0, closure); + } + + /** + * Iterates over the elements of an iterable collection of items, starting from a + * specified startIndex, and returns the index of the first item that matches the + * condition specified in the closure. + * + * @param self the iteration object over which to iterate + * @param startIndex start matching from this index + * @param closure the filter to perform a match on the collection + * @return an integer that is the index of the first matched object or -1 if no match was found + * @since 1.5.0 + */ + public static int findIndexOf(Object self, int startIndex, Closure closure) { + int result = -1; + int i = 0; + for (Iterator iter = InvokerHelper.asIterator(self); iter.hasNext(); i++) { + Object value = iter.next(); + if (i < startIndex) { + continue; + } + if (DefaultTypeTransformation.castToBoolean(closure.call(value))) { + result = i; + break; + } + } + return result; + } + + /** + * Iterates over the elements of an iterable collection of items and returns + * the index of the last item that matches the condition specified in the closure. + * + * @param self the iteration object over which to iterate + * @param closure the filter to perform a match on the collection + * @return an integer that is the index of the last matched object or -1 if no match was found + * @since 1.5.2 + */ + public static int findLastIndexOf(Object self, Closure closure) { + return findLastIndexOf(self, 0, closure); + } + + /** + * Iterates over the elements of an iterable collection of items, starting + * from a specified startIndex, and returns the index of the last item that + * matches the condition specified in the closure. + * + * @param self the iteration object over which to iterate + * @param startIndex start matching from this index + * @param closure the filter to perform a match on the collection + * @return an integer that is the index of the last matched object or -1 if no match was found + * @since 1.5.2 + */ + public static int findLastIndexOf(Object self, int startIndex, Closure closure) { + int result = -1; + int i = 0; + for (Iterator iter = InvokerHelper.asIterator(self); iter.hasNext(); i++) { + Object value = iter.next(); + if (i < startIndex) { + continue; + } + if (DefaultTypeTransformation.castToBoolean(closure.call(value))) { + result = i; + } + } + return result; + } + + /** + * Iterates over the elements of an iterable collection of items and returns + * the index values of the items that match the condition specified in the closure. + * + * @param self the iteration object over which to iterate + * @param closure the filter to perform a match on the collection + * @return a list of numbers corresponding to the index values of all matched objects + * @since 1.5.2 + */ + public static List findIndexValues(Object self, Closure closure) { + return findIndexValues(self, 0, closure); + } + + /** + * Iterates over the elements of an iterable collection of items, starting from + * a specified startIndex, and returns the index values of the items that match + * the condition specified in the closure. + * + * @param self the iteration object over which to iterate + * @param startIndex start matching from this index + * @param closure the filter to perform a match on the collection + * @return a list of numbers corresponding to the index values of all matched objects + * @since 1.5.2 + */ + public static List findIndexValues(Object self, Number startIndex, Closure closure) { + List result = new ArrayList(); + long count = 0; + long startCount = startIndex.longValue(); + for (Iterator iter = InvokerHelper.asIterator(self); iter.hasNext(); count++) { + Object value = iter.next(); + if (count < startCount) { + continue; + } + if (DefaultTypeTransformation.castToBoolean(closure.call(value))) { + result.add(count); + } + } + return result; + } + + /** + * Iterates through the classloader parents until it finds a loader with a class + * named "org.codehaus.groovy.tools.RootLoader". If there is no such class + * null will be returned. The name is used for comparison because + * a direct comparison using == may fail as the class may be loaded through + * different classloaders. + * + * @param self a ClassLoader + * @return the rootLoader for the ClassLoader + * @see org.codehaus.groovy.tools.RootLoader + * @since 1.5.0 + */ + public static ClassLoader getRootLoader(ClassLoader self) { + while (true) { + if (self == null) return null; + if (isRootLoaderClassOrSubClass(self)) return self; + self = self.getParent(); + } + } + + private static boolean isRootLoaderClassOrSubClass(ClassLoader self) { + Class current = self.getClass(); + while(!current.getName().equals(Object.class.getName())) { + if(current.getName().equals(RootLoader.class.getName())) return true; + current = current.getSuperclass(); + } + + return false; + } + + + /** + * Converts a given object to a type. This method is used through + * the "as" operator and is overloadable as any other operator. + * + * @param obj the object to convert + * @param type the goal type + * @return the resulting object + * @since 1.0 + */ + @SuppressWarnings("unchecked") + public static T asType(Object obj, Class type) { + if (String.class == type) { + return (T) InvokerHelper.toString(obj); + } + + // fall back to cast + try { + return (T) DefaultTypeTransformation.castToType(obj, type); + } + catch (GroovyCastException e) { + MetaClass mc = InvokerHelper.getMetaClass(obj); + if (mc instanceof ExpandoMetaClass) { + ExpandoMetaClass emc = (ExpandoMetaClass) mc; + Object mixedIn = emc.castToMixedType(obj, type); + if (mixedIn != null) + return (T) mixedIn; + } + if (type.isInterface()) { + try { + List interfaces = new ArrayList(); + interfaces.add(type); + return (T) ProxyGenerator.INSTANCE.instantiateDelegate(interfaces, obj); + } catch (GroovyRuntimeException cause) { + // ignore + } + } + throw e; + } + } + + private static Object asArrayType(Object object, Class type) { + if (type.isAssignableFrom(object.getClass())) { + return object; + } + Collection list = DefaultTypeTransformation.asCollection(object); + int size = list.size(); + Class elementType = type.getComponentType(); + Object array = Array.newInstance(elementType, size); + int idx = 0; + + if (boolean.class.equals(elementType)) { + for (Iterator iter = list.iterator(); iter.hasNext(); idx++) { + Object element = iter.next(); + Array.setBoolean(array, idx, (Boolean) InvokerHelper.invokeStaticMethod(DefaultGroovyMethods.class, "asType", new Object[]{element, boolean.class})); + } + } else if (byte.class.equals(elementType)) { + for (Iterator iter = list.iterator(); iter.hasNext(); idx++) { + Object element = iter.next(); + Array.setByte(array, idx, (Byte) InvokerHelper.invokeStaticMethod(DefaultGroovyMethods.class, "asType", new Object[]{element, byte.class})); + } + } else if (char.class.equals(elementType)) { + for (Iterator iter = list.iterator(); iter.hasNext(); idx++) { + Object element = iter.next(); + Array.setChar(array, idx, (Character) InvokerHelper.invokeStaticMethod(DefaultGroovyMethods.class, "asType", new Object[]{element, char.class})); + } + } else if (double.class.equals(elementType)) { + for (Iterator iter = list.iterator(); iter.hasNext(); idx++) { + Object element = iter.next(); + Array.setDouble(array, idx, (Double) InvokerHelper.invokeStaticMethod(DefaultGroovyMethods.class, "asType", new Object[]{element, double.class})); + } + } else if (float.class.equals(elementType)) { + for (Iterator iter = list.iterator(); iter.hasNext(); idx++) { + Object element = iter.next(); + Array.setFloat(array, idx, (Float) InvokerHelper.invokeStaticMethod(DefaultGroovyMethods.class, "asType", new Object[]{element, float.class})); + } + } else if (int.class.equals(elementType)) { + for (Iterator iter = list.iterator(); iter.hasNext(); idx++) { + Object element = iter.next(); + Array.setInt(array, idx, (Integer) InvokerHelper.invokeStaticMethod(DefaultGroovyMethods.class, "asType", new Object[]{element, int.class})); + } + } else if (long.class.equals(elementType)) { + for (Iterator iter = list.iterator(); iter.hasNext(); idx++) { + Object element = iter.next(); + Array.setLong(array, idx, (Long) InvokerHelper.invokeStaticMethod(DefaultGroovyMethods.class, "asType", new Object[]{element, long.class})); + } + } else if (short.class.equals(elementType)) { + for (Iterator iter = list.iterator(); iter.hasNext(); idx++) { + Object element = iter.next(); + Array.setShort(array, idx, (Short) InvokerHelper.invokeStaticMethod(DefaultGroovyMethods.class, "asType", new Object[]{element, short.class})); + } + } else for (Iterator iter = list.iterator(); iter.hasNext(); idx++) { + Object element = iter.next(); + Array.set(array, idx, InvokerHelper.invokeStaticMethod(DefaultGroovyMethods.class, "asType", new Object[]{element, elementType})); + } + return array; + } + + /** + * Convenience method to dynamically create a new instance of this + * class. Calls the default constructor. + * + * @param c a class + * @return a new instance of this class + * @since 1.0 + */ + @SuppressWarnings("unchecked") + public static T newInstance(Class c) { + return (T) InvokerHelper.invokeConstructorOf(c, null); + } + + /** + * Helper to construct a new instance from the given arguments. + * The constructor is called based on the number and types in the + * args array. Use newInstance(null) or simply + * newInstance() for the default (no-arg) constructor. + * + * @param c a class + * @param args the constructor arguments + * @return a new instance of this class. + * @since 1.0 + */ + @SuppressWarnings("unchecked") + public static T newInstance(Class c, Object[] args) { + if (args == null) args = new Object[]{null}; + return (T) InvokerHelper.invokeConstructorOf(c, args); + } + + + /** + * Adds a "metaClass" property to all class objects so you can use the syntax + * String.metaClass.myMethod = { println "foo" } + * + * @param c The java.lang.Class instance + * @return An MetaClass instance + * @since 1.5.0 + */ + public static MetaClass getMetaClass(Class c) { + MetaClassRegistry metaClassRegistry = GroovySystem.getMetaClassRegistry(); + MetaClass mc = metaClassRegistry.getMetaClass(c); + if (mc instanceof ExpandoMetaClass + || mc instanceof DelegatingMetaClass && ((DelegatingMetaClass) mc).getAdaptee() instanceof ExpandoMetaClass) + return mc; + else { + return new HandleMetaClass(mc); + } + } + + /** + * Obtains a MetaClass for an object either from the registry or in the case of + * a GroovyObject from the object itself. + * + * @param obj The object in question + * @return The MetaClass + * @since 1.5.0 + */ + public static MetaClass getMetaClass(Object obj) { + MetaClass mc = InvokerHelper.getMetaClass(obj); + return new HandleMetaClass(mc, obj); + } + + /** + * Obtains a MetaClass for an object either from the registry or in the case of + * a GroovyObject from the object itself. + * + * @param obj The object in question + * @return The MetaClass + * @since 1.6.0 + */ + public static MetaClass getMetaClass(GroovyObject obj) { + // we need this method as trick to guarantee correct method selection + return getMetaClass((Object)obj); + } + + /** + * Sets the metaclass for a given class. + * + * @param self the class whose metaclass we wish to set + * @param metaClass the new MetaClass + * @since 1.6.0 + */ + public static void setMetaClass(Class self, MetaClass metaClass) { + final MetaClassRegistry metaClassRegistry = GroovySystem.getMetaClassRegistry(); + if (metaClass == null) + metaClassRegistry.removeMetaClass(self); + else { + if (metaClass instanceof HandleMetaClass) { + metaClassRegistry.setMetaClass(self, ((HandleMetaClass)metaClass).getAdaptee()); + } else { + metaClassRegistry.setMetaClass(self, metaClass); + } + if (self==NullObject.class) { + NullObject.getNullObject().setMetaClass(metaClass); + } + } + } + + /** + * Set the metaclass for an object + * @param self the object whose metaclass we want to set + * @param metaClass the new metaclass value + * @since 1.6.0 + */ + public static void setMetaClass(Object self, MetaClass metaClass) { + if (metaClass instanceof HandleMetaClass) + metaClass = ((HandleMetaClass)metaClass).getAdaptee(); + + if (self instanceof GroovyObject) { + ((GroovyObject)self).setMetaClass(metaClass); + disablePrimitiveOptimization(self); + } else if (self instanceof Class) { + ((MetaClassRegistryImpl)GroovySystem.getMetaClassRegistry()).setMetaClass((Class)self, metaClass); + } else { + ((MetaClassRegistryImpl)GroovySystem.getMetaClassRegistry()).setMetaClass(self, metaClass); + } + } + + private static void disablePrimitiveOptimization(Object self) { + Field sdyn; + Class c = self.getClass(); + try { + sdyn = c.getDeclaredField(Verifier.STATIC_METACLASS_BOOL); + sdyn.setBoolean(null, true); + } catch (Throwable e) { + //DO NOTHING + } + } + + /** + * Sets/updates the metaclass for a given class to a closure. + * + * @param self the class whose metaclass we wish to update + * @param closure the closure representing the new metaclass + * @return the new metaclass value + * @throws GroovyRuntimeException if the metaclass can't be set for this class + * @since 1.6.0 + */ + public static MetaClass metaClass (Class self, Closure closure){ + MetaClassRegistry metaClassRegistry = GroovySystem.getMetaClassRegistry(); + MetaClass mc = metaClassRegistry.getMetaClass(self); + + if (mc instanceof ExpandoMetaClass) { + ((ExpandoMetaClass) mc).define(closure); + return mc; + } + else { + if (mc instanceof DelegatingMetaClass && ((DelegatingMetaClass) mc).getAdaptee() instanceof ExpandoMetaClass) { + ((ExpandoMetaClass)((DelegatingMetaClass) mc).getAdaptee()).define(closure); + return mc; + } + else { + if (mc instanceof DelegatingMetaClass && ((DelegatingMetaClass) mc).getAdaptee().getClass() == MetaClassImpl.class) { + ExpandoMetaClass emc = new ExpandoMetaClass(self, false, true); + emc.initialize(); + emc.define(closure); + ((DelegatingMetaClass) mc).setAdaptee(emc); + return mc; + } + else { + if (mc.getClass() == MetaClassImpl.class) { + // default case + mc = new ExpandoMetaClass(self, false, true); + mc.initialize(); + ((ExpandoMetaClass)mc).define(closure); + metaClassRegistry.setMetaClass(self, mc); + return mc; + } + else { + throw new GroovyRuntimeException("Can't add methods to custom meta class " + mc); + } + } + } + } + } + + /** + * Sets/updates the metaclass for a given object to a closure. + * + * @param self the object whose metaclass we wish to update + * @param closure the closure representing the new metaclass + * @return the new metaclass value + * @throws GroovyRuntimeException if the metaclass can't be set for this object + * @since 1.6.0 + */ + public static MetaClass metaClass (Object self, Closure closure){ + MetaClass emc = hasPerInstanceMetaClass(self); + if (emc == null) { + final ExpandoMetaClass metaClass = new ExpandoMetaClass(self.getClass(), false, true); + metaClass.initialize(); + metaClass.define(closure); + setMetaClass(self, metaClass); + return metaClass; + } + else { + if (emc instanceof ExpandoMetaClass) { + ((ExpandoMetaClass)emc).define(closure); + return emc; + } + else { + if (emc instanceof DelegatingMetaClass && ((DelegatingMetaClass)emc).getAdaptee() instanceof ExpandoMetaClass) { + ((ExpandoMetaClass)((DelegatingMetaClass)emc).getAdaptee()).define(closure); + return emc; + } + else { + throw new RuntimeException("Can't add methods to non-ExpandoMetaClass " + emc); + } + } + } + } + + private static MetaClass hasPerInstanceMetaClass(Object object) { + if (object instanceof GroovyObject) { + MetaClass mc = ((GroovyObject)object).getMetaClass(); + if (mc == GroovySystem.getMetaClassRegistry().getMetaClass(object.getClass()) || mc.getClass() == MetaClassImpl.class) + return null; + else + return mc; + } + else { + ClassInfo info = ClassInfo.getClassInfo(object.getClass()); + info.lock(); + try { + return info.getPerInstanceMetaClass(object); + } + finally { + info.unlock(); + } + } + } + + /** + * Attempts to create an Iterator for the given object by first + * converting it to a Collection. + * + * @param a an array + * @return an Iterator for the given Array. + * @see org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation#asCollection(java.lang.Object[]) + * @since 1.6.4 + */ + public static Iterator iterator(T[] a) { + return DefaultTypeTransformation.asCollection(a).iterator(); + } + + /** + * Attempts to create an Iterator for the given object by first + * converting it to a Collection. + * + * @param o an object + * @return an Iterator for the given Object. + * @see org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation#asCollection(java.lang.Object) + * @since 1.0 + */ + public static Iterator iterator(Object o) { + return DefaultTypeTransformation.asCollection(o).iterator(); + } + + /** + * Allows an Enumeration to behave like an Iterator. Note that the + * {@link java.util.Iterator#remove() remove()} method is unsupported since the + * underlying Enumeration does not provide a mechanism for removing items. + * + * @param enumeration an Enumeration object + * @return an Iterator for the given Enumeration + * @since 1.0 + */ + public static Iterator iterator(final Enumeration enumeration) { + return new Iterator() { + private T last; + + public boolean hasNext() { + return enumeration.hasMoreElements(); + } + + public T next() { + last = enumeration.nextElement(); + return last; + } + + public void remove() { + throw new UnsupportedOperationException("Cannot remove() from an Enumeration"); + } + }; + } + + /** + * Returns an {@link java.util.Iterator} which traverses each match. + * + * @param matcher a Matcher object + * @return an Iterator for a Matcher + * @see java.util.regex.Matcher#group() + * @since 1.0 + */ + public static Iterator iterator(final Matcher matcher) { + matcher.reset(); + return new Iterator() { + private boolean found /* = false */; + private boolean done /* = false */; + + public boolean hasNext() { + if (done) { + return false; + } + if (!found) { + found = matcher.find(); + if (!found) { + done = true; + } + } + return found; + } + + public Object next() { + if (!found) { + if (!hasNext()) { + throw new NoSuchElementException(); + } + } + found = false; + + if (hasGroup(matcher)) { + // are we using groups? + // yes, so return the specified group as list + List list = new ArrayList(matcher.groupCount()); + for (int i = 0; i <= matcher.groupCount(); i++) { + list.add(matcher.group(i)); + } + return list; + } else { + // not using groups, so return the nth + // occurrence of the pattern + return matcher.group(); + } + } + + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + + /** + * Creates an iterator which will traverse through the reader a line at a time. + * + * @param self a Reader object + * @return an Iterator for the Reader + * @see java.io.BufferedReader#readLine() + * @since 1.5.0 + */ + public static Iterator iterator(Reader self) { + final BufferedReader bufferedReader; + if (self instanceof BufferedReader) + bufferedReader = (BufferedReader) self; + else + bufferedReader = new BufferedReader(self); + return new Iterator() { + String nextVal /* = null */; + boolean nextMustRead = true; + boolean hasNext = true; + + public boolean hasNext() { + if (nextMustRead && hasNext) { + try { + nextVal = readNext(); + nextMustRead = false; + } catch (IOException e) { + hasNext = false; + } + } + return hasNext; + } + + public String next() { + String retval = null; + if (nextMustRead) { + try { + retval = readNext(); + } catch (IOException e) { + hasNext = false; + } + } else + retval = nextVal; + nextMustRead = true; + return retval; + } + + private String readNext() throws IOException { + String nv = bufferedReader.readLine(); + if (nv == null) + hasNext = false; + return nv; + } + + public void remove() { + throw new UnsupportedOperationException("Cannot remove() from a Reader Iterator"); + } + }; + } + + /** + * Standard iterator for a input stream which iterates through the stream + * content in a byte-based fashion. + * + * @param self an InputStream object + * @return an Iterator for the InputStream + * @since 1.5.0 + */ + public static Iterator iterator(InputStream self) { + return iterator(new DataInputStream(self)); + } + + /** + * Standard iterator for a data input stream which iterates through the + * stream content a Byte at a time. + * + * @param self a DataInputStream object + * @return an Iterator for the DataInputStream + * @since 1.5.0 + */ + public static Iterator iterator(final DataInputStream self) { + return new Iterator() { + Byte nextVal; + boolean nextMustRead = true; + boolean hasNext = true; + + public boolean hasNext() { + if (nextMustRead && hasNext) { + try { + nextVal = self.readByte(); + nextMustRead = false; + } catch (IOException e) { + hasNext = false; + } + } + return hasNext; + } + + public Byte next() { + Byte retval = null; + if (nextMustRead) { + try { + retval = self.readByte(); + } catch (IOException e) { + hasNext = false; + } + } else + retval = nextVal; + nextMustRead = true; + return retval; + } + + public void remove() { + throw new UnsupportedOperationException("Cannot remove() from a DataInputStream Iterator"); + } + }; + } + + /** + * An identity function for iterators, supporting 'duck-typing' when trying to get an + * iterator for each object within a collection, some of which may already be iterators. + * + * @param self an iterator object + * @return itself + * @since 1.5.0 + */ + public static Iterator iterator(Iterator self) { + return self; + } + + /** + *

    Returns an object satisfying Groovy truth if the implementing MetaClass responds to + * a method with the given name and arguments types. + * + *

    Note that this method's return value is based on realised methods and does not take into account + * objects or classes that implement invokeMethod or methodMissing + * + *

    This method is "safe" in that it will always return a value and never throw an exception + * + * @param self The object to inspect + * @param name The name of the method of interest + * @param argTypes The argument types to match against + * @return A List of MetaMethods matching the argument types which will be empty if no matching methods exist + * @see groovy.lang.MetaObjectProtocol#respondsTo(java.lang.Object, java.lang.String, java.lang.Object[]) + * @since 1.6.0 + */ + public static List respondsTo(Object self, String name, Object[] argTypes) { + return InvokerHelper.getMetaClass(self).respondsTo(self, name, argTypes); + } + + /** + *

    Returns an object satisfying Groovy truth if the implementing MetaClass responds to + * a method with the given name regardless of the arguments. + * + *

    Note that this method's return value is based on realised methods and does not take into account + * objects or classes that implement invokeMethod or methodMissing + * + *

    This method is "safe" in that it will always return a value and never throw an exception + * + * @param self The object to inspect + * @param name The name of the method of interest + * @return A List of MetaMethods matching the given name or an empty list if no matching methods exist + * @see groovy.lang.MetaObjectProtocol#respondsTo(java.lang.Object, java.lang.String) + * @since 1.6.1 + */ + public static List respondsTo(Object self, String name) { + return InvokerHelper.getMetaClass(self).respondsTo(self, name); + } + + /** + *

    Returns true of the implementing MetaClass has a property of the given name + * + *

    Note that this method will only return true for realised properties and does not take into + * account implementation of getProperty or propertyMissing + * + * @param self The object to inspect + * @param name The name of the property of interest + * @return The found MetaProperty or null if it doesn't exist + * @see groovy.lang.MetaObjectProtocol#hasProperty(java.lang.Object, java.lang.String) + * @since 1.6.1 + */ + public static MetaProperty hasProperty(Object self, String name) { + return InvokerHelper.getMetaClass(self).hasProperty(self, name); + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/codehaus/groovy/runtime/DefaultGroovyMethodsSupport.java b/javaUtilities/yajsw/src/main/java/org/codehaus/groovy/runtime/DefaultGroovyMethodsSupport.java new file mode 100644 index 0000000000..a313f25f49 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/codehaus/groovy/runtime/DefaultGroovyMethodsSupport.java @@ -0,0 +1,268 @@ +/* + * Copyright 2003-2008 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.codehaus.groovy.runtime; + +import groovy.lang.EmptyRange; +import groovy.lang.Range; + +import java.io.Closeable; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Hashtable; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Queue; +import java.util.Set; +import java.util.SortedMap; +import java.util.SortedSet; +import java.util.Stack; +import java.util.TreeMap; +import java.util.TreeSet; +import java.util.Vector; + +import org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation; +import org.jboss.netty.logging.InternalLogger; +import org.jboss.netty.logging.InternalLoggerFactory; + +/** + * Support methods for DefaultGroovyMethods and PluginDefaultMethods. + */ +public class DefaultGroovyMethodsSupport { + + //private static final Logger LOG = Logger.getLogger(DefaultGroovyMethodsSupport.class.getName()); + private static final InternalLogger LOG = InternalLoggerFactory.getInstance(DefaultGroovyMethodsSupport.class.getName()); + + // helper method for getAt and putAt + protected static RangeInfo subListBorders(int size, Range range) { + int from = normaliseIndex(DefaultTypeTransformation.intUnbox(range.getFrom()), size); + int to = normaliseIndex(DefaultTypeTransformation.intUnbox(range.getTo()), size); + boolean reverse = range.isReverse(); + if (from > to) { + // support list[1..-1] + int tmp = to; + to = from; + from = tmp; + reverse = !reverse; + } + return new RangeInfo(from, to + 1, reverse); + } + + // helper method for getAt and putAt + protected static RangeInfo subListBorders(int size, EmptyRange range) { + int from = normaliseIndex(DefaultTypeTransformation.intUnbox(range.getFrom()), size); + return new RangeInfo(from, from, false); + } + + /** + * This converts a possibly negative index to a real index into the array. + * + * @param i the unnormalised index + * @param size the array size + * @return the normalised index + */ + protected static int normaliseIndex(int i, int size) { + int temp = i; + if (i < 0) { + i += size; + } + if (i < 0) { + throw new ArrayIndexOutOfBoundsException("Negative array index [" + temp + "] too large for array size " + size); + } + return i; + } + + /** + * Close the Closeable. Logging a warning if any problems occur. + * + * @param c the thing to close + */ + public static void closeWithWarning(Closeable c) { + if (c != null) { + try { + c.close(); + } catch (IOException e) { + LOG.warn("Caught exception during close(): " + e); + } + } + } + + /** + * Close the Closeable. Ignore any problems that might occur. + * + * @param c the thing to close + */ + public static void closeQuietly(Closeable c) { + if (c != null) { + try { + c.close(); + } catch (IOException e) { + /* ignore */ + } + } + } + + protected static class RangeInfo { + public final int from; + public final int to; + public final boolean reverse; + + public RangeInfo(int from, int to, boolean reverse) { + this.from = from; + this.to = to; + this.reverse = reverse; + } + } + + protected static Collection cloneSimilarCollection(Collection orig, int newCapacity) { + Collection answer = (Collection) cloneObject(orig); + if (answer != null) return answer; + + // fall back to creation + answer = createSimilarCollection(orig, newCapacity); + answer.addAll(orig); + return answer; + } + + private static Object cloneObject(Object orig) { + if (orig instanceof Cloneable) { + try { + return InvokerHelper.invokeMethod(orig, "clone", new Object[0]); + } catch (Exception ex) { + // ignore + } + } + return null; + } + + protected static Collection createSimilarOrDefaultCollection(Object object) { + if (object instanceof Collection) { + return createSimilarCollection((Collection) object); + } + return new ArrayList(); + } + + protected static Collection createSimilarCollection(Collection collection) { + return createSimilarCollection(collection, collection.size()); + } + + protected static Collection createSimilarCollection(Collection orig, int newCapacity) { + if (orig instanceof Set) { + return createSimilarSet((Set) orig); + } + if (orig instanceof List) { + return createSimilarList((List) orig, newCapacity); + } + if (orig instanceof Queue) { + return new LinkedList(); + } + return new ArrayList(newCapacity); + } + + protected static List createSimilarList(List orig, int newCapacity) { + if (orig instanceof LinkedList) + return new LinkedList(); + + if (orig instanceof Stack) + return new Stack(); + + if (orig instanceof Vector) + return new Vector(); + + return new ArrayList(newCapacity); + } + + protected static Set createSimilarSet(Set orig) { + if (orig instanceof SortedSet) { + return new TreeSet(((SortedSet)orig).comparator()); + } + return new LinkedHashSet(); + } + + protected static Map createSimilarMap(Map orig) { + if (orig instanceof SortedMap) { + return new TreeMap(((SortedMap)orig).comparator()); + } + if (orig instanceof Properties) { + return (Map) new Properties(); + } + if (orig instanceof Hashtable) { + return new Hashtable(); + } + return new LinkedHashMap(); + } + + protected static Map cloneSimilarMap(Map orig) { + Map answer = (Map) cloneObject(orig); + if (answer != null) return answer; + + // fall back to some defaults + if (orig instanceof TreeMap) + return new TreeMap(orig); + + if (orig instanceof Properties) { + Map map = (Map) new Properties(); + map.putAll(orig); + return map; + } + + if (orig instanceof Hashtable) + return new Hashtable(orig); + + return new LinkedHashMap(orig); + } + + /** + * Determines if all items of this array are of the same type. + * + * @param cols an array of collections + * @return true if the collections are all of the same type + */ + protected static boolean sameType(Collection[] cols) { + List all = new LinkedList(); + for (Collection col : cols) { + all.addAll(col); + } + if (all.size() == 0) + return true; + + Object first = all.get(0); + + //trying to determine the base class of the collections + //special case for Numbers + Class baseClass; + if (first instanceof Number) { + baseClass = Number.class; + } else if (first == null) { + baseClass = NullObject.class; + } else { + baseClass = first.getClass(); + } + + for (Collection col : cols) { + for (Object o : col) { + if (!baseClass.isInstance(o)) { + return false; + } + } + } + return true; + } +} diff --git a/javaUtilities/yajsw/src/main/java/org/codehaus/groovy/runtime/MetaClassHelper.java b/javaUtilities/yajsw/src/main/java/org/codehaus/groovy/runtime/MetaClassHelper.java new file mode 100644 index 0000000000..4c68d01924 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/codehaus/groovy/runtime/MetaClassHelper.java @@ -0,0 +1,1043 @@ +/* + * Copyright 2003-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.codehaus.groovy.runtime; + +import groovy.lang.Closure; +import groovy.lang.GString; +import groovy.lang.GroovyRuntimeException; +import groovy.lang.MetaMethod; + +import java.lang.reflect.Array; +import java.lang.reflect.Constructor; +import java.lang.reflect.Modifier; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.codehaus.groovy.reflection.CachedClass; +import org.codehaus.groovy.reflection.ParameterTypes; +import org.codehaus.groovy.reflection.ReflectionCache; +import org.codehaus.groovy.runtime.wrappers.Wrapper; +import org.codehaus.groovy.util.FastArray; +import org.jboss.netty.logging.InternalLogger; +import org.jboss.netty.logging.InternalLoggerFactory; + +/** + * @author John Wilson + * @author Jochen Theodorou + */ +public class MetaClassHelper { + + public static final Object[] EMPTY_ARRAY = {}; + public static final Class[] EMPTY_TYPE_ARRAY = {}; + public static final Object[] ARRAY_WITH_NULL = {null}; + // protected static final Logger LOG = Logger.getLogger(MetaClassHelper.class.getName()); + protected static final InternalLogger LOG = InternalLoggerFactory.getInstance(MetaClassHelper.class.getName()); + + private static final int MAX_ARG_LEN = 12; + private static final int + OBJECT_SHIFT = 23, INTERFACE_SHIFT = 0, + PRIMITIVE_SHIFT = 21, VARGS_SHIFT = 44; + /* dist binary layout: + * 0-20: interface + * 21-22: primitive dist + * 23-43: object dist + * 44-48: vargs penalty + */ + + public static final Class[] EMPTY_CLASS_ARRAY = new Class[0]; + + public static boolean accessibleToConstructor(final Class at, final Constructor constructor) { + boolean accessible = false; + final int modifiers = constructor.getModifiers(); + if (Modifier.isPublic(modifiers)) { + accessible = true; + } else if (Modifier.isPrivate(modifiers)) { + accessible = at.getName().equals(constructor.getName()); + } else if (Modifier.isProtected(modifiers)) { + Boolean isAccessible = checkCompatiblePackages(at, constructor); + if (isAccessible != null) { + accessible = isAccessible; + } else { + boolean flag = false; + Class clazz = at; + while (!flag && clazz != null) { + if (clazz.equals(constructor.getDeclaringClass())) { + flag = true; + break; + } + if (clazz.equals(Object.class)) { + break; + } + clazz = clazz.getSuperclass(); + } + accessible = flag; + } + } else { + Boolean isAccessible = checkCompatiblePackages(at, constructor); + if (isAccessible != null) { + accessible = isAccessible; + } + } + return accessible; + } + + private static Boolean checkCompatiblePackages(Class at, Constructor constructor) { + if (at.getPackage() == null && constructor.getDeclaringClass().getPackage() == null) { + return Boolean.TRUE; + } + if (at.getPackage() == null && constructor.getDeclaringClass().getPackage() != null) { + return Boolean.FALSE; + } + if (at.getPackage() != null && constructor.getDeclaringClass().getPackage() == null) { + return Boolean.FALSE; + } + if (at.getPackage().equals(constructor.getDeclaringClass().getPackage())) { + return Boolean.TRUE; + } + return null; + } + + public static Object[] asWrapperArray(Object parameters, Class componentType) { + Object[] ret = null; + if (componentType == boolean.class) { + boolean[] array = (boolean[]) parameters; + ret = new Object[array.length]; + for (int i = 0; i < array.length; i++) { + ret[i] = array[i]; + } + } else if (componentType == char.class) { + char[] array = (char[]) parameters; + ret = new Object[array.length]; + for (int i = 0; i < array.length; i++) { + ret[i] = array[i]; + } + } else if (componentType == byte.class) { + byte[] array = (byte[]) parameters; + ret = new Object[array.length]; + for (int i = 0; i < array.length; i++) { + ret[i] = array[i]; + } + } else if (componentType == int.class) { + int[] array = (int[]) parameters; + ret = new Object[array.length]; + for (int i = 0; i < array.length; i++) { + ret[i] = array[i]; + } + } else if (componentType == short.class) { + short[] array = (short[]) parameters; + ret = new Object[array.length]; + for (int i = 0; i < array.length; i++) { + ret[i] = array[i]; + } + } else if (componentType == long.class) { + long[] array = (long[]) parameters; + ret = new Object[array.length]; + for (int i = 0; i < array.length; i++) { + ret[i] = array[i]; + } + } else if (componentType == double.class) { + double[] array = (double[]) parameters; + ret = new Object[array.length]; + for (int i = 0; i < array.length; i++) { + ret[i] = array[i]; + } + } else if (componentType == float.class) { + float[] array = (float[]) parameters; + ret = new Object[array.length]; + for (int i = 0; i < array.length; i++) { + ret[i] = array[i]; + } + } + + return ret; + } + + + /** + * @param list the original list + * @param parameterType the resulting array type + * @return the constructed array + */ + public static Object asPrimitiveArray(List list, Class parameterType) { + Class arrayType = parameterType.getComponentType(); + Object objArray = Array.newInstance(arrayType, list.size()); + for (int i = 0; i < list.size(); i++) { + Object obj = list.get(i); + if (arrayType.isPrimitive()) { + if (obj instanceof Integer) { + Array.setInt(objArray, i, (Integer) obj); + } else if (obj instanceof Double) { + Array.setDouble(objArray, i, (Double) obj); + } else if (obj instanceof Boolean) { + Array.setBoolean(objArray, i, (Boolean) obj); + } else if (obj instanceof Long) { + Array.setLong(objArray, i, (Long) obj); + } else if (obj instanceof Float) { + Array.setFloat(objArray, i, (Float) obj); + } else if (obj instanceof Character) { + Array.setChar(objArray, i, (Character) obj); + } else if (obj instanceof Byte) { + Array.setByte(objArray, i, (Byte) obj); + } else if (obj instanceof Short) { + Array.setShort(objArray, i, (Short) obj); + } + } else { + Array.set(objArray, i, obj); + } + } + return objArray; + } + + private static final Class[] PRIMITIVES = { + byte.class, Byte.class, short.class, Short.class, + int.class, Integer.class, long.class, Long.class, + BigInteger.class, float.class, Float.class, + double.class, Double.class, BigDecimal.class, + Number.class, Object.class + }; + private static final int[][] PRIMITIVE_DISTANCE_TABLE = { + // byte Byte short Short int Integer long Long BigInteger float Float double Double BigDecimal, Number, Object + /* byte*/{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,}, + /*Byte*/{1, 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,}, + /*short*/{14, 15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,}, + /*Short*/{14, 15, 1, 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,}, + /*int*/{14, 15, 12, 13, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,}, + /*Integer*/{14, 15, 12, 13, 1, 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,}, + /*long*/{14, 15, 12, 13, 10, 11, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,}, + /*Long*/{14, 15, 12, 13, 10, 11, 1, 0, 2, 3, 4, 5, 6, 7, 8, 9,}, + /*BigInteger*/{9, 10, 7, 8, 5, 6, 3, 4, 0, 14, 15, 12, 13, 11, 1, 2,}, + /*float*/{14, 15, 12, 13, 10, 11, 8, 9, 7, 0, 1, 2, 3, 4, 5, 6,}, + /*Float*/{14, 15, 12, 13, 10, 11, 8, 9, 7, 1, 0, 2, 3, 4, 5, 6,}, + /*double*/{14, 15, 12, 13, 10, 11, 8, 9, 7, 5, 6, 0, 1, 2, 3, 4,}, + /*Double*/{14, 15, 12, 13, 10, 11, 8, 9, 7, 5, 6, 1, 0, 2, 3, 4,}, + /*BigDecimal*/{14, 15, 12, 13, 10, 11, 8, 9, 7, 5, 6, 3, 4, 0, 1, 2,}, + /*Number*/{14, 15, 12, 13, 10, 11, 8, 9, 7, 5, 6, 3, 4, 2, 0, 1,}, + /*Object*/{14, 15, 12, 13, 10, 11, 8, 9, 7, 5, 6, 3, 4, 2, 1, 0,}, + }; + + private static int getPrimitiveIndex(Class c) { + for (byte i = 0; i < PRIMITIVES.length; i++) { + if (PRIMITIVES[i] == c) return i; + } + return -1; + } + + private static int getPrimitiveDistance(Class from, Class to) { + // we know here that from!=to, so a distance of 0 is never valid + // get primitive type indexes + int fromIndex = getPrimitiveIndex(from); + int toIndex = getPrimitiveIndex(to); + if (fromIndex == -1 || toIndex == -1) return -1; + return PRIMITIVE_DISTANCE_TABLE[toIndex][fromIndex]; + } + + private static int getMaximumInterfaceDistance(Class c, Class interfaceClass) { + // -1 means a mismatch + if (c == null) return -1; + // 0 means a direct match + if (c == interfaceClass) return 0; + Class[] interfaces = c.getInterfaces(); + int max = -1; + for (Class anInterface : interfaces) { + int sub = getMaximumInterfaceDistance(anInterface, interfaceClass); + // we need to keep the -1 to track the mismatch, a +1 + // by any means could let it look like a direct match + // we want to add one, because there is an interface between + // the interface we search for and the interface we are in. + if (sub != -1) sub++; + // we are interested in the longest path only + max = Math.max(max, sub); + } + // we do not add one for super classes, only for interfaces + int superClassMax = getMaximumInterfaceDistance(c.getSuperclass(), interfaceClass); + return Math.max(max, superClassMax); + } + + private static long calculateParameterDistance(Class argument, CachedClass parameter) { + /** + * note: when shifting with 32 bit, you should only shift on a long. If you do + * that with an int, then i==(i<<32), which means you loose the shift + * information + */ + + if (parameter.getTheClass() == argument) return 0; + + if (parameter.isInterface()) { + return getMaximumInterfaceDistance(argument, parameter.getTheClass()) << INTERFACE_SHIFT; + } + + long objectDistance = 0; + if (argument != null) { + long pd = getPrimitiveDistance(parameter.getTheClass(), argument); + if (pd != -1) return pd << PRIMITIVE_SHIFT; + + // add one to dist to be sure interfaces are preferred + objectDistance += PRIMITIVES.length + 1; + + // GROOVY-5114 : if we have to choose between two methods + // foo(Object[]) and foo(Object) and that the argument is an array type + // then the array version should be preferred + if (argument.isArray() && !parameter.isArray) { + objectDistance+=4; + } + Class clazz = ReflectionCache.autoboxType(argument); + while (clazz != null) { + if (clazz == parameter.getTheClass()) break; + if (clazz == GString.class && parameter.getTheClass() == String.class) { + objectDistance += 2; + break; + } + clazz = clazz.getSuperclass(); + objectDistance += 3; + } + } else { + // choose the distance to Object if a parameter is null + // this will mean that Object is preferred over a more + // specific type + Class clazz = parameter.getTheClass(); + if (clazz.isPrimitive()) { + objectDistance += 2; + } else { + while (clazz != Object.class) { + clazz = clazz.getSuperclass(); + objectDistance += 2; + } + } + } + return objectDistance << OBJECT_SHIFT; + } + + public static long calculateParameterDistance(Class[] arguments, ParameterTypes pt) { + CachedClass[] parameters = pt.getParameterTypes(); + if (parameters.length == 0) return 0; + + long ret = 0; + int noVargsLength = parameters.length - 1; + + // if the number of parameters does not match we have + // a vargs usage + // + // case A: arguments.lengthparameters.length + // + // In this case all arguments with a index bigger than + // paramMinus1 are part of the vargs, so a + // distance calculation needs to be done against + // parameters[noVargsLength].getComponentType() + // + // VArgs penalty: 2l+arguments.length-parameters.length + // + // case C: arguments.length==parameters.length && + // isAssignableFrom( parameters[noVargsLength], + // arguments[noVargsLength] ) + // + // In this case we have no vargs, so calculate directly + // + // VArgs penalty: 0l + // + // case D: arguments.length==parameters.length && + // !isAssignableFrom( parameters[noVargsLength], + // arguments[noVargsLength] ) + // + // In this case we have a vargs case again, we need + // to calculate arguments[noVargsLength] against + // parameters[noVargsLength].getComponentType + // + // VArgs penalty: 2l + // + // This gives: VArgs_penalty(C) case B + * def foo(a,b,Object[] c) {2} -> case A + * assert foo(new Object(),new Object()) == 2 + * --> A preferred over B + * + * A vs C : + * def foo(Object[] a) {1} -> case B + * def foo(a,b) {2} -> case C + * assert foo(new Object(),new Object()) == 2 + * --> C preferred over A + * + * A vs D : + * def foo(Object[] a) {1} -> case D + * def foo(a,Object[] b) {2} -> case A + * assert foo(new Object()) == 2 + * --> A preferred over D + * + * This gives C case B + * def foo(a,b) {2} -> case C + * assert foo(new Object(),new Object()) == 2 + * --> C preferred over B, matches C case B + * def foo(a,Object[] b) {2} -> case D + * assert foo(new Object(),new Object()) == 2 + * --> D preferred over B + * + * This gives C parameters.length) { + // case B + // we give our a vargs penalty for each exceeding argument and iterate + // by using parameters[noVargsLength].getComponentType() + ret += (2l + arguments.length - parameters.length) << VARGS_SHIFT; // penalty for vargs + CachedClass vargsType = ReflectionCache.getCachedClass(parameters[noVargsLength].getTheClass().getComponentType()); + for (int i = noVargsLength; i < arguments.length; i++) { + ret += calculateParameterDistance(arguments[i], vargsType); + } + } else { + // case A + // we give a penalty for vargs, since we have no direct + // match for the last argument + ret += 1l << VARGS_SHIFT; + } + + return ret; + } + + /** + * This is the complement to the java.beans.Introspector.decapitalize(String) method. + * We handle names that begin with an initial lowerCase followed by upperCase specially + * (which is to make no change). + * See GROOVY-3211. + * + * @param property the property name to capitalize + * @return the name capitalized, except when we don't + */ + public static String capitalize(final String property) { + final String rest = property.substring(1); + + // Funky rule so that names like 'pNAME' will still work. + if (Character.isLowerCase(property.charAt(0)) && (rest.length() > 0) && Character.isUpperCase(rest.charAt(0))) { + return property; + } + + return property.substring(0, 1).toUpperCase() + rest; + } + + /** + * @param methods the methods to choose from + * @return the method with 1 parameter which takes the most general type of + * object (e.g. Object) + */ + public static Object chooseEmptyMethodParams(FastArray methods) { + Object vargsMethod = null; + final int len = methods.size(); + final Object[] data = methods.getArray(); + for (int i = 0; i != len; ++i) { + Object method = data[i]; + final ParameterTypes pt = (ParameterTypes) method; + CachedClass[] paramTypes = pt.getParameterTypes(); + int paramLength = paramTypes.length; + if (paramLength == 0) { + return method; + } else if (paramLength == 1 && pt.isVargsMethod(EMPTY_ARRAY)) { + vargsMethod = method; + } + } + return vargsMethod; + } + + /** + * @param methods the methods to choose from + * @return the method with 1 parameter which takes the most general type of + * object (e.g. Object) ignoring primitive types + */ + public static Object chooseMostGeneralMethodWith1NullParam(FastArray methods) { + // let's look for methods with 1 argument which matches the type of the + // arguments + CachedClass closestClass = null; + CachedClass closestVargsClass = null; + Object answer = null; + int closestDist = -1; + final int len = methods.size(); + for (int i = 0; i != len; ++i) { + final Object[] data = methods.getArray(); + Object method = data[i]; + final ParameterTypes pt = (ParameterTypes) method; + CachedClass[] paramTypes = pt.getParameterTypes(); + int paramLength = paramTypes.length; + if (paramLength == 0 || paramLength > 2) continue; + + CachedClass theType = paramTypes[0]; + if (theType.isPrimitive) continue; + + if (paramLength == 2) { + if (!pt.isVargsMethod(ARRAY_WITH_NULL)) continue; + if (closestClass == null) { + closestVargsClass = paramTypes[1]; + closestClass = theType; + answer = method; + } else if (closestClass.getTheClass() == theType.getTheClass()) { + if (closestVargsClass == null) continue; + CachedClass newVargsClass = paramTypes[1]; + if (isAssignableFrom(newVargsClass.getTheClass(), closestVargsClass.getTheClass())) { + closestVargsClass = newVargsClass; + answer = method; + } + } else if (isAssignableFrom(theType.getTheClass(), closestClass.getTheClass())) { + closestVargsClass = paramTypes[1]; + closestClass = theType; + answer = method; + } + } else { + if (closestClass == null || isAssignableFrom(theType.getTheClass(), closestClass.getTheClass())) { + closestVargsClass = null; + closestClass = theType; + answer = method; + closestDist = -1; + } else { + // closestClass and theType are not in a subtype relation, we need + // to check the distance to Object + if (closestDist == -1) closestDist = closestClass.getSuperClassDistance(); + int newDist = theType.getSuperClassDistance(); + if (newDist < closestDist) { + closestDist = newDist; + closestVargsClass = null; + closestClass = theType; + answer = method; + } + } + } + } + return answer; + } + + // + + private static int calculateSimplifiedClassDistanceToObject(Class clazz) { + int objectDistance = 0; + while (clazz != null) { + clazz = clazz.getSuperclass(); + objectDistance++; + } + return objectDistance; + } + + + /** + * @param list a list of MetaMethods + * @param method the MetaMethod of interest + * @return true if a method of the same matching prototype was found in the + * list + */ + public static boolean containsMatchingMethod(List list, MetaMethod method) { + for (Object aList : list) { + MetaMethod aMethod = (MetaMethod) aList; + CachedClass[] params1 = aMethod.getParameterTypes(); + CachedClass[] params2 = method.getParameterTypes(); + if (params1.length == params2.length) { + boolean matches = true; + for (int i = 0; i < params1.length; i++) { + if (params1[i] != params2[i]) { + matches = false; + break; + } + } + if (matches) { + return true; + } + } + } + return false; + } + + /** + * param instance array to the type array + * + * @param args the arguments + * @return the types of the arguments + */ + public static Class[] convertToTypeArray(Object[] args) { + if (args == null) + return null; + int s = args.length; + Class[] ans = new Class[s]; + for (int i = 0; i < s; i++) { + Object o = args[i]; + if (o == null) { + ans[i] = null; + } else if (o instanceof Wrapper) { + ans[i] = ((Wrapper) o).getType(); + } else { + ans[i] = o.getClass(); + } + } + return ans; + } + + public static Object makeCommonArray(Object[] arguments, int offset, Class fallback) { + // arguments.length>0 && !=null + Class baseClass = null; + for (int i = offset; i < arguments.length; i++) { + if (arguments[i] == null) continue; + Class argClass = arguments[i].getClass(); + if (baseClass == null) { + baseClass = argClass; + } else { + for (; baseClass != Object.class; baseClass = baseClass.getSuperclass()) { + if (baseClass.isAssignableFrom(argClass)) break; + } + } + } + if (baseClass == null) { + // all arguments were null + baseClass = fallback; + } + /* + * If no specific super class has been found and type fallback is an interface, check if all arg classes + * implement it. If yes, then that interface is the common type across arguments. + */ + if (baseClass == Object.class && fallback.isInterface()) { + int tmpCount = 0; + for (int i = offset; i < arguments.length; i++) { + if (arguments[i] != null) { + Class argClass, tmpClass; + Set intfs = new HashSet(); + tmpClass = argClass = arguments[i].getClass(); + for (; tmpClass != Object.class; tmpClass = tmpClass.getSuperclass()) { + intfs.addAll(Arrays.asList(tmpClass.getInterfaces())); + } + if (intfs.contains(fallback)) { + tmpCount++; + } + } + } + // all arg classes implement interface fallback, so use that as the array component type + if (tmpCount == arguments.length - offset) { + baseClass = fallback; + } + } + Object result = makeArray(null, baseClass, arguments.length - offset); + System.arraycopy(arguments, offset, result, 0, arguments.length - offset); + return result; + } + + public static Object makeArray(Object obj, Class secondary, int length) { + Class baseClass = secondary; + if (obj != null) { + baseClass = obj.getClass(); + } + /*if (GString.class.isAssignableFrom(baseClass)) { + baseClass = GString.class; + }*/ + return Array.newInstance(baseClass, length); + } + + public static GroovyRuntimeException createExceptionText(String init, MetaMethod method, Object object, Object[] args, Throwable reason, boolean setReason) { + return new GroovyRuntimeException( + init + + method + + " on: " + + object + + " with arguments: " + + InvokerHelper.toString(args) + + " reason: " + + reason, + setReason ? reason : null); + } + + protected static String getClassName(Object object) { + if (object == null) return null; + return (object instanceof Class) ? ((Class) object).getName() : object.getClass().getName(); + } + + /** + * Returns a callable object for the given method name on the object. + * The object acts like a Closure in that it can be called, like a closure + * and passed around - though really its a method pointer, not a closure per se. + * + * @param object the object containing the method + * @param methodName the method of interest + * @return the resulting closure-like method pointer + */ + public static Closure getMethodPointer(Object object, String methodName) { + return new MethodClosure(object, methodName); + } + + public static boolean isAssignableFrom(Class classToTransformTo, Class classToTransformFrom) { + if (classToTransformTo == classToTransformFrom) return true; + if (classToTransformFrom == null) return true; + if (classToTransformTo == Object.class) return true; + + classToTransformTo = ReflectionCache.autoboxType(classToTransformTo); + classToTransformFrom = ReflectionCache.autoboxType(classToTransformFrom); + if (classToTransformTo == classToTransformFrom) return true; + + // note: there is no coercion for boolean and char. Range matters, precision doesn't + if (classToTransformTo == Integer.class) { + if (classToTransformFrom == Integer.class + || classToTransformFrom == Short.class + || classToTransformFrom == Byte.class + || classToTransformFrom == BigInteger.class) + return true; + } else if (classToTransformTo == Double.class) { + if (classToTransformFrom == Double.class + || classToTransformFrom == Integer.class + || classToTransformFrom == Long.class + || classToTransformFrom == Short.class + || classToTransformFrom == Byte.class + || classToTransformFrom == Float.class + || classToTransformFrom == BigDecimal.class + || classToTransformFrom == BigInteger.class) + return true; + } else if (classToTransformTo == BigDecimal.class) { + if (classToTransformFrom == Double.class + || classToTransformFrom == Integer.class + || classToTransformFrom == Long.class + || classToTransformFrom == Short.class + || classToTransformFrom == Byte.class + || classToTransformFrom == Float.class + || classToTransformFrom == BigDecimal.class + || classToTransformFrom == BigInteger.class) + return true; + } else if (classToTransformTo == BigInteger.class) { + if (classToTransformFrom == Integer.class + || classToTransformFrom == Long.class + || classToTransformFrom == Short.class + || classToTransformFrom == Byte.class + || classToTransformFrom == BigInteger.class) + return true; + } else if (classToTransformTo == Long.class) { + if (classToTransformFrom == Long.class + || classToTransformFrom == Integer.class + || classToTransformFrom == Short.class + || classToTransformFrom == Byte.class) + return true; + } else if (classToTransformTo == Float.class) { + if (classToTransformFrom == Float.class + || classToTransformFrom == Integer.class + || classToTransformFrom == Long.class + || classToTransformFrom == Short.class + || classToTransformFrom == Byte.class) + return true; + } else if (classToTransformTo == Short.class) { + if (classToTransformFrom == Short.class + || classToTransformFrom == Byte.class) + return true; + } else if (classToTransformTo == String.class) { + if (classToTransformFrom == String.class || + GString.class.isAssignableFrom(classToTransformFrom)) { + return true; + } + } + + return ReflectionCache.isAssignableFrom(classToTransformTo, classToTransformFrom); + } + + public static boolean isGenericSetMethod(MetaMethod method) { + return (method.getName().equals("set")) + && method.getParameterTypes().length == 2; + } + + protected static boolean isSuperclass(Class clazz, Class superclass) { + while (clazz != null) { + if (clazz == superclass) return true; + clazz = clazz.getSuperclass(); + } + return false; + } + + public static boolean parametersAreCompatible(Class[] arguments, Class[] parameters) { + if (arguments.length != parameters.length) return false; + for (int i = 0; i < arguments.length; i++) { + if (!isAssignableFrom(parameters[i], arguments[i])) return false; + } + return true; + } + + public static void logMethodCall(Object object, String methodName, Object[] arguments) { + /* + String className = getClassName(object); + String logname = "methodCalls." + className + "." + methodName; + Logger objLog = Logger.getLogger(logname); + if (!objLog.isLoggable(Level.FINER)) return; + StringBuffer msg = new StringBuffer(methodName); + msg.append("("); + if (arguments != null) { + for (int i = 0; i < arguments.length;) { + msg.append(normalizedValue(arguments[i])); + if (++i < arguments.length) { + msg.append(","); + } + } + } + msg.append(")"); + objLog.logp(Level.FINER, className, msg.toString(), "called from MetaClass.invokeMethod"); + */ + } + + protected static String normalizedValue(Object argument) { + String value; + try { + value = argument.toString(); + if (value.length() > MAX_ARG_LEN) { + value = value.substring(0, MAX_ARG_LEN - 2) + ".."; + } + if (argument instanceof String) { + value = "\'" + value + "\'"; + } + } catch (Exception e) { + value = shortName(argument); + } + return value; + } + + protected static String shortName(Object object) { + if (object == null || object.getClass() == null) return "unknownClass"; + String name = getClassName(object); + if (name == null) return "unknownClassName"; // *very* defensive... + int lastDotPos = name.lastIndexOf('.'); + if (lastDotPos < 0 || lastDotPos >= name.length() - 1) return name; + return name.substring(lastDotPos + 1); + } + + public static Class[] wrap(Class[] classes) { + Class[] wrappedArguments = new Class[classes.length]; + for (int i = 0; i < wrappedArguments.length; i++) { + Class c = classes[i]; + if (c == null) continue; + if (c.isPrimitive()) { + if (c == Integer.TYPE) { + c = Integer.class; + } else if (c == Byte.TYPE) { + c = Byte.class; + } else if (c == Long.TYPE) { + c = Long.class; + } else if (c == Double.TYPE) { + c = Double.class; + } else if (c == Float.TYPE) { + c = Float.class; + } + } else if (isSuperclass(c, GString.class)) { + c = String.class; + } + wrappedArguments[i] = c; + } + return wrappedArguments; + } + + public static boolean sameClasses(Class[] params, Object[] arguments, boolean weakNullCheck) { + if (params.length != arguments.length) + return false; + + for (int i = params.length - 1; i >= 0; i--) { + Object arg = arguments[i]; + if (arg == null) { + if (!weakNullCheck) + return false; + } else { + if (params[i] != arg.getClass() + && (!(arg instanceof Wrapper) || params[i] != ((Wrapper) arg).getType())) + return false; + } + } + + return true; + } + + public static boolean sameClasses(Class[] params, Object[] arguments) { + if (params.length != arguments.length) + return false; + + for (int i = params.length - 1; i >= 0; i--) { + Object arg = arguments[i]; + if (arg == null) { + if (params[i] != null) + return false; + } else { + if (params[i] != arg.getClass() && !(arg instanceof Wrapper && params[i] == ((Wrapper) arg).getType())) + return false; + } + } + + return true; + } + + public static boolean sameClasses(Class[] params) { + if (params.length != 0) + return false; + + return true; + } + + public static boolean sameClasses(Class[] params, Object arg1) { + if (params.length != 1) + return false; + + if (arg1 == null + || (params[0] != arg1.getClass() + && (!(arg1 instanceof Wrapper) + || params[0] != ((Wrapper) arg1).getType()))) + return false; + + return true; + } + + public static boolean sameClasses(Class[] params, Object arg1, Object arg2) { + if (params.length != 2) + return false; + + if (arg1 == null + || (params[0] != arg1.getClass() + && (!(arg1 instanceof Wrapper) + || params[0] != ((Wrapper) arg1).getType()))) + return false; + + if (arg2 == null + || (params[1] != arg2.getClass() + && (!(arg2 instanceof Wrapper) + || params[1] != ((Wrapper) arg2).getType()))) + return false; + + return true; + } + + public static boolean sameClasses(Class[] params, Object arg1, Object arg2, Object arg3) { + if (params.length != 3) + return false; + + if (arg1 == null + || (params[0] != arg1.getClass() + && (!(arg1 instanceof Wrapper) + || params[0] != ((Wrapper) arg1).getType()))) + return false; + + if (arg2 == null + || (params[1] != arg2.getClass() + && (!(arg2 instanceof Wrapper) + || params[1] != ((Wrapper) arg2).getType()))) + return false; + + if (arg3 == null + || (params[2] != arg3.getClass() + && (!(arg3 instanceof Wrapper) + || params[2] != ((Wrapper) arg3).getType()))) + return false; + + return true; + } + + public static boolean sameClasses(Class[] params, Object arg1, Object arg2, Object arg3, Object arg4) { + if (params.length != 4) + return false; + + if (arg1 == null + || (params[0] != arg1.getClass() + && (!(arg1 instanceof Wrapper) + || params[0] != ((Wrapper) arg1).getType()))) + return false; + + if (arg2 == null + || (params[1] != arg2.getClass() + && (!(arg2 instanceof Wrapper) + || params[1] != ((Wrapper) arg2).getType()))) + return false; + + if (arg3 == null + || (params[2] != arg3.getClass() + && (!(arg3 instanceof Wrapper) + || params[2] != ((Wrapper) arg3).getType()))) + return false; + + if (arg4 == null + || (params[3] != arg4.getClass() + && (!(arg4 instanceof Wrapper) + || params[3] != ((Wrapper) arg4).getType()))) + return false; + + return true; + } + + public static boolean sameClass(Class[] params, Object arg) { + return !(arg == null + || (params[0] != arg.getClass() + && (!(arg instanceof Wrapper) + || params[0] != ((Wrapper) arg).getType()))); + + } + + public static Class[] castArgumentsToClassArray(Object[] argTypes) { + if (argTypes == null) return EMPTY_CLASS_ARRAY; + Class[] classes = new Class[argTypes.length]; + for (int i = 0; i < argTypes.length; i++) { + Object argType = argTypes[i]; + if (argType instanceof Class) { + classes[i] = (Class) argType; + } else if (argType == null) { + classes[i] = null; + } else { +// throw new IllegalArgumentException("Arguments to method [respondsTo] must be of type java.lang.Class!"); + classes[i] = argType.getClass(); + } + } + return classes; + } + + public static void unwrap(Object[] arguments) { + // + // Temp code to ignore wrapped parameters + // The New MOP will deal with these properly + // + for (int i = 0; i != arguments.length; i++) { + if (arguments[i] instanceof Wrapper) { + arguments[i] = ((Wrapper) arguments[i]).unwrap(); + } + } + } +} diff --git a/javaUtilities/yajsw/src/main/java/org/jboss/netty/logging/InternalLoggerFactory.java b/javaUtilities/yajsw/src/main/java/org/jboss/netty/logging/InternalLoggerFactory.java new file mode 100644 index 0000000000..d49de72d87 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/jboss/netty/logging/InternalLoggerFactory.java @@ -0,0 +1,155 @@ +/* + * Copyright 2009 Red Hat, Inc. + * + * Red Hat licenses this file to you under the Apache License, version 2.0 + * (the "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package org.jboss.netty.logging; + +import org.jboss.netty.util.internal.StackTraceSimplifier; + +/** + * Creates an {@link InternalLogger} or changes the default factory + * implementation. This factory allows you to choose what logging framework + * Netty should use. The default factory is {@link JdkLogger2Factory}. + * You can change it to your preferred logging framework before other Netty + * classes are loaded: + *

    + * {@link InternalLoggerFactory}.setDefaultFactory(new {@link Log4JLoggerFactory}());
    + * 
    + * Please note that the new default factory is effective only for the classes + * which were loaded after the default factory is changed. Therefore, + * {@link #setDefaultFactory(InternalLoggerFactory)} should be called as early + * as possible and shouldn't be called more than once. + * + * @author The Netty Project + * @author Trustin Lee + * + * @version $Rev: 2122 $, $Date: 2010-02-02 11:00:04 +0900 (Tue, 02 Feb 2010) $ + * + * @apiviz.landmark + * @apiviz.has org.jboss.netty.logging.InternalLogger oneway - - creates + */ +public abstract class InternalLoggerFactory { + private static volatile InternalLoggerFactory defaultFactory = new SimpleLoggerFactory(); + + static { + // Load the dependent classes in advance to avoid the case where + // the VM fails to load the required classes because of too many open + // files. + StackTraceSimplifier.simplify(new Exception()); + } + + /** + * Returns the default factory. The initial default factory is + * {@link JdkLogger2Factory}. + */ + public static InternalLoggerFactory getDefaultFactory() { + return defaultFactory; + } + + /** + * Changes the default factory. + */ + public static void setDefaultFactory(InternalLoggerFactory defaultFactory) { + if (defaultFactory == null) { + throw new NullPointerException("defaultFactory"); + } + InternalLoggerFactory.defaultFactory = defaultFactory; + } + + /** + * Creates a new logger instance with the name of the specified class. + */ + public static InternalLogger getInstance(Class clazz) { + return getInstance(clazz.getName()); + } + + /** + * Creates a new logger instance with the specified name. + */ + public static InternalLogger getInstance(String name) { + final InternalLogger logger = getDefaultFactory().newInstance(name); + return new InternalLogger() { + + public void debug(String msg) { + logger.debug(msg); + } + + public void debug(String msg, Throwable cause) { + StackTraceSimplifier.simplify(cause); + logger.debug(msg, cause); + } + + public void error(String msg) { + logger.error(msg); + } + + public void error(String msg, Throwable cause) { + StackTraceSimplifier.simplify(cause); + logger.error(msg, cause); + } + + public void info(String msg) { + logger.info(msg); + } + + public void info(String msg, Throwable cause) { + StackTraceSimplifier.simplify(cause); + logger.info(msg, cause); + } + + public boolean isDebugEnabled() { + return logger.isDebugEnabled(); + } + + public boolean isErrorEnabled() { + return logger.isErrorEnabled(); + } + + public boolean isInfoEnabled() { + return logger.isInfoEnabled(); + } + + public boolean isWarnEnabled() { + return logger.isWarnEnabled(); + } + + public void warn(String msg) { + logger.warn(msg); + } + + public void warn(String msg, Throwable cause) { + StackTraceSimplifier.simplify(cause); + logger.warn(msg, cause); + } + + public boolean isEnabled(InternalLogLevel level) { + return logger.isEnabled(level); + } + + public void log(InternalLogLevel level, String msg) { + logger.log(level, msg); + } + + public void log(InternalLogLevel level, String msg, Throwable cause) { + StackTraceSimplifier.simplify(cause); + logger.log(level, msg, cause); + } + }; + } + + /** + * Creates a new logger instance with the specified name. + */ + public abstract InternalLogger newInstance(String name); +} diff --git a/javaUtilities/yajsw/src/main/java/org/jboss/netty/logging/JdkLogger2.java b/javaUtilities/yajsw/src/main/java/org/jboss/netty/logging/JdkLogger2.java new file mode 100644 index 0000000000..3bf6d99cf7 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/jboss/netty/logging/JdkLogger2.java @@ -0,0 +1,91 @@ +/* + * Copyright 2009 Red Hat, Inc. + * + * Red Hat licenses this file to you under the Apache License, version 2.0 + * (the "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package org.jboss.netty.logging; + +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * java.util.logging + * logger. + * + * @author The Netty Project + * @author Trustin Lee + * + * @version $Rev: 2080 $, $Date: 2010-01-26 18:04:19 +0900 (Tue, 26 Jan 2010) $ + * + */ +class JdkLogger2 extends AbstractInternalLogger { + + private final Logger logger; + + JdkLogger2(Logger logger) { + this.logger = logger; + } + + public void debug(String msg) { + logger.log(Level.FINE, msg); + } + + public void debug(String msg, Throwable cause) { + logger.log(Level.FINE, msg, cause); + } + + public void error(String msg) { + logger.log(Level.SEVERE, msg); + } + + public void error(String msg, Throwable cause) { + logger.log(Level.SEVERE, msg, cause); + } + + public void info(String msg) { + logger.log(Level.INFO, msg); + } + + public void info(String msg, Throwable cause) { + logger.log(Level.INFO, msg, cause); + } + + public boolean isDebugEnabled() { + return logger.isLoggable(Level.FINE); + } + + public boolean isErrorEnabled() { + return logger.isLoggable(Level.SEVERE); + } + + public boolean isInfoEnabled() { + return logger.isLoggable(Level.INFO); + } + + public boolean isWarnEnabled() { + return logger.isLoggable(Level.WARNING); + } + + public void warn(String msg) { + logger.log(Level.WARNING, msg); + } + + public void warn(String msg, Throwable cause) { + logger.log(Level.WARNING, msg, cause); + } + + @Override + public String toString() { + return logger.toString(); + } +} diff --git a/javaUtilities/yajsw/src/main/java/org/jboss/netty/logging/JdkLogger2Factory.java b/javaUtilities/yajsw/src/main/java/org/jboss/netty/logging/JdkLogger2Factory.java new file mode 100644 index 0000000000..cdd14903ae --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/jboss/netty/logging/JdkLogger2Factory.java @@ -0,0 +1,44 @@ +/* + * Copyright 2009 Red Hat, Inc. + * + * Red Hat licenses this file to you under the Apache License, version 2.0 + * (the "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package org.jboss.netty.logging; + + +/** + * Logger factory which creates a + * java.util.logging + * logger. + * + * @author The Netty Project + * @author Trustin Lee + * + * @version $Rev: 2080 $, $Date: 2010-01-26 18:04:19 +0900 (Tue, 26 Jan 2010) $ + * + */ +public class JdkLogger2Factory extends InternalLoggerFactory { + + java.util.logging.Logger logger; + + public JdkLogger2Factory(java.util.logging.Logger logger) + { + this.logger = logger; + } + + @Override + public InternalLogger newInstance(String name) { + return new JdkLogger2(logger); + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/jboss/netty/logging/SimpleLogger.java b/javaUtilities/yajsw/src/main/java/org/jboss/netty/logging/SimpleLogger.java new file mode 100644 index 0000000000..54364d167f --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/jboss/netty/logging/SimpleLogger.java @@ -0,0 +1,87 @@ +package org.jboss.netty.logging; + +public class SimpleLogger implements InternalLogger +{ + + public void debug(String arg0) + { + System.out.println(arg0); + } + + public void debug(String arg0, Throwable arg1) + { + System.out.println(arg0); + arg1.printStackTrace(); + } + + public void error(String arg0) + { + System.out.println(arg0); + } + + public void error(String arg0, Throwable arg1) + { + System.out.println(arg0); + arg1.printStackTrace(); + } + + public void info(String arg0) + { + System.out.println(arg0); + } + + public void info(String arg0, Throwable arg1) + { + System.out.println(arg0); + arg1.printStackTrace(); + } + + public boolean isDebugEnabled() + { + return true; + } + + public boolean isEnabled(InternalLogLevel arg0) + { + return true; + } + + public boolean isErrorEnabled() + { + return true; + } + + public boolean isInfoEnabled() + { + return true; + } + + public boolean isWarnEnabled() + { + return true; + } + + public void log(InternalLogLevel arg0, String arg1) + { + // TODO Auto-generated method stub + + } + + public void log(InternalLogLevel arg0, String arg1, Throwable arg2) + { + // TODO Auto-generated method stub + + } + + public void warn(String arg0) + { + System.out.println(arg0); + } + + public void warn(String arg0, Throwable arg1) + { + System.out.println(arg0); + arg1.printStackTrace(); + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/jboss/netty/logging/SimpleLoggerFactory.java b/javaUtilities/yajsw/src/main/java/org/jboss/netty/logging/SimpleLoggerFactory.java new file mode 100644 index 0000000000..28adeb0447 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/jboss/netty/logging/SimpleLoggerFactory.java @@ -0,0 +1,12 @@ +package org.jboss.netty.logging; + +public class SimpleLoggerFactory extends InternalLoggerFactory +{ + + @Override + public InternalLogger newInstance(String name) + { + return new SimpleLogger(); + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/Constants.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/Constants.java new file mode 100644 index 0000000000..1d533716d5 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/Constants.java @@ -0,0 +1,142 @@ +/* This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + *

    + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ +package org.rzo.yajsw; + +// TODO: Auto-generated Javadoc +/** + * The Interface Constants. + */ +public interface Constants +{ + + /** The Constant DEFAULT_PORT. */ + public static final int DEFAULT_PORT = 15003; + + /** The Constant DEFAULT_SO_TIMEOUT. */ + public static final int DEFAULT_SO_TIMEOUT = 10000; + + /** The Constant DEFAULT_CPU_TIMEOUT. */ + public static final int DEFAULT_CPU_TIMEOUT = 10000; + + /** The Constant DEFAULT_STARTUP_TIMEOUT. */ + public static final int DEFAULT_STARTUP_TIMEOUT = 30; + + /** The Constant DEFAULT_MAX_FAILED_INVOCATIONS. */ + public static final int DEFAULT_MAX_FAILED_INVOCATIONS = 5; + + /** The Constant DEFAULT_SUCCESSFUL_INVOCATION_TIME. */ + public static final int DEFAULT_SUCCESSFUL_INVOCATION_TIME = 60; + + public static final int DEFAULT_EXIT_ON_MAIN_TERMINATE = -1; + + public static final int DEFAULT_EXIT_ON_MAIN_EXCEPTION = 999; + + /** The Constant DEFAULT_PING_INTERVAL. */ + public static final int DEFAULT_PING_INTERVAL = 5; + + /** The Constant DEFAULT_PING_TIMEOUT. */ + public static final int DEFAULT_PING_TIMEOUT = 30; + + /** The Constant DEFAULT_RESTART_DELAY. */ + public static final int DEFAULT_RESTART_DELAY = 5; + + /** The Constant DEFAULT_SHUTDOWN_TIMEOUT. */ + public static final int DEFAULT_SHUTDOWN_TIMEOUT = 30; + + /** The Constant DEFAULT_JVM_EXIT_TIMEOUT. */ + public static final int DEFAULT_JVM_EXIT_TIMEOUT = 15; + + public static final String DEFAULT_DAEMON_RUN_LEVEL_DIR = "rc5.d"; + public static final String DEFAULT_DAEMON_TEMPLATE = "conf/daemon.vm"; + public static final String DEFAULT_DAEMON_DIR = "/etc/init.d"; + public static final String DEFAULT_DAEMON_PID_DIR = "/var/run"; + public static final String DEFAULT_DAEMON_K_ORDER = "99"; + public static final String DEFAULT_DAEMON_S_ORDER = "99"; + + public static final int DEFAULT_EXIT_CODE_STOP = 0; + public static final int DEFAULT_EXIT_CODE_KILL = 999; + public static final int DEFAULT_EXIT_CODE_FATAL = 999; + + public static final String DEFAULT_SERVICE_START_TYPE = "AUTO_START"; + + public static final boolean DEFAULT_CONSOLE_VISIBLE = false; + + public static final String DEFAULT_CONTROL = "TIGHT"; + + public static final boolean DEFAULT_RELOAD_CONFIGURATION = false; + + public static final boolean DFAULT_CACHE_LOCAL = false; + + /** The Constant WRAPPER_MSG_START. */ + public static final byte WRAPPER_MSG_START = (byte) 100; + + /** The Constant WRAPPER_MSG_STOP. */ + public static final byte WRAPPER_MSG_STOP = (byte) 101; + + /** The Constant WRAPPER_MSG_RESTART. */ + public static final byte WRAPPER_MSG_RESTART = (byte) 102; + + /** The Constant WRAPPER_MSG_PING. */ + public static final byte WRAPPER_MSG_PING = (byte) 103; + + /** The Constant WRAPPER_MSG_STOP_PENDING. */ + public static final byte WRAPPER_MSG_STOP_PENDING = (byte) 104; + + /** The Constant WRAPPER_MSG_START_PENDING. */ + public static final byte WRAPPER_MSG_START_PENDING = (byte) 105; + + /** The Constant WRAPPER_MSG_STARTED. */ + public static final byte WRAPPER_MSG_STARTED = (byte) 106; + + /** The Constant WRAPPER_MSG_STOPPED. */ + public static final byte WRAPPER_MSG_STOPPED = (byte) 107; + + /** The Constant WRAPPER_MSG_KEY. */ + public static final byte WRAPPER_MSG_KEY = (byte) 110; + + /** The Constant WRAPPER_MSG_BADKEY. */ + public static final byte WRAPPER_MSG_BADKEY = (byte) 111; + + /** The Constant WRAPPER_MSG_LOW_LOG_LEVEL. */ + public static final byte WRAPPER_MSG_LOW_LOG_LEVEL = (byte) 112; + + /** The Constant WRAPPER_MSG_PING_TIMEOUT. */ + public static final byte WRAPPER_MSG_PING_TIMEOUT = (byte) 113; + + /** The Constant WRAPPER_MSG_SERVICE_CONTROL_CODE. */ + public static final byte WRAPPER_MSG_SERVICE_CONTROL_CODE = (byte) 114; + + /** The Constant WRAPPER_MSG_PROPERTIES. */ + public static final byte WRAPPER_MSG_PROPERTIES = (byte) 115; + + /** The Constant WRAPPER_MSG_OKKEY. */ + public static final byte WRAPPER_MSG_OKKEY = (byte) 116; + + /** The Constant WRAPPER_MSG_STOP_TIMER. */ + public static final byte WRAPPER_MSG_STOP_TIMER = (byte) 117; + + /** The Constant WRAPPER_MSG_THREAD_DUMP. */ + public static final byte WRAPPER_MSG_THREAD_DUMP = (byte) 118; + + public static final byte WRAPPER_MSG_SERVICE_STARTUP = (byte) 119; + + public static final byte WRAPPER_MSG_GC = (byte) 120; + + public static final byte WRAPPER_MSG_DUMP_HEAP = (byte) 121; + + public static final int DEFAULT_RMI_PORT = 1099; + + public static final String DEFAULT_LOG_FORMAT = "LPNTM"; + + public static final boolean DEFAULT_CONSOLE_MINIMIZED = false; + + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/WrapperExe.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/WrapperExe.java new file mode 100644 index 0000000000..302dc5d640 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/WrapperExe.java @@ -0,0 +1,828 @@ +/* This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + *

    + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ +package org.rzo.yajsw; + +import java.io.File; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.regex.Pattern; + +import org.apache.commons.cli2.Argument; +import org.apache.commons.cli2.CommandLine; +import org.apache.commons.cli2.Group; +import org.apache.commons.cli2.Option; +import org.apache.commons.cli2.builder.ArgumentBuilder; +import org.apache.commons.cli2.builder.DefaultOptionBuilder; +import org.apache.commons.cli2.builder.GroupBuilder; +import org.apache.commons.cli2.commandline.Parser; +import org.apache.commons.cli2.option.DefaultOption; +import org.apache.commons.cli2.util.HelpFormatter; +import org.apache.commons.cli2.validation.FileValidator; +import org.apache.commons.cli2.validation.NumberValidator; +import org.apache.commons.configuration.Configuration; +import org.apache.commons.configuration.MapConfiguration; +import org.rzo.yajsw.boot.WrapperLoader; +import org.rzo.yajsw.config.YajswConfiguration; +import org.rzo.yajsw.config.YajswConfigurationImpl; +import org.rzo.yajsw.os.OperatingSystem; +import org.rzo.yajsw.os.ms.win.w32.WindowsXPProcess; +import org.rzo.yajsw.tools.ConfigGenerator; +import org.rzo.yajsw.tray.TrayIconMain; +import org.rzo.yajsw.util.VFSFileValidator; +import org.rzo.yajsw.wrapper.WrappedProcess; +import org.rzo.yajsw.wrapper.WrappedProcessFactory; +import org.rzo.yajsw.wrapper.WrappedProcessList; +import org.rzo.yajsw.wrapper.WrappedService; + +import com.sun.jna.PlatformEx; + +// TODO: Auto-generated Javadoc +/** + * The Class WrapperExe. + */ +public class WrapperExe +{ + + /** The group. */ + static Group group; + + /** The cl. */ + static CommandLine cl; + + /** The conf file. */ + static String confFile; + + static List confFileList; + + /** The properties. */ + static List properties; + + /** The cmds. */ + static List cmds; + + /** The pid. */ + static int pid; + + /** The pid. */ + static String defaultFile; + + /** The Constant OPTION_C. */ + static final int OPTION_C = 0; + + /** The Constant OPTION_T. */ + static final int OPTION_T = 1; + + /** The Constant OPTION_P. */ + static final int OPTION_P = 2; + + /** The Constant OPTION_T. */ + static final int OPTION_TX = 91; + + /** The Constant OPTION_P. */ + static final int OPTION_PX = 92; + + /** The Constant OPTION_I. */ + static final int OPTION_I = 3; + + /** The Constant OPTION_R. */ + static final int OPTION_R = 4; + + /** The Constant OPTION_N. */ + static final int OPTION_N = 5; + + /** The Constant OPTION_G. */ + static final int OPTION_G = 6; + + /** The Constant OPTION_D. */ + static final int OPTION_D = 7; + + /** The Constant OPTION_Q. */ + static final int OPTION_Q = 8; + + /** The Constant OPTION_QS. */ + static final int OPTION_QS = 9; + + /** The Constant OPTION_Y. */ + static final int OPTION_Y = 10; + + /** The Constant OPTION_QX. */ + static final int OPTION_QX = 11; + static final int OPTION_RW = 12; + + /** The Constant CONF_FILE. */ + //static final String CONF_FILE = "confFile"; + + /** The Constant PROPERTIES. */ + //static final String PROPERTIES = "properties"; + static final String ARGS = "arguments"; + + /** The Constant PID. */ + static final String PID = "pid"; + + /** The Constant DEFAULT_FILE. */ + static final String DEFAULT_FILE = "default configuration file"; + + static WrappedService _service = null; + + static boolean _exitOnTerminate = true; + + static int _exitCode = 0; + + static Map _properties = new HashMap(); + + private static WrappedService getService() + { + if (_service != null) + return _service; + prepareProperties(); + _service = new WrappedService(); + if (confFileList != null && confFileList.size() > 1) + _service.setConfFilesList(confFileList); + _service.setLocalConfiguration(new MapConfiguration(_properties)); + _service.init(); + return _service; + } + + /** + * The main method. + * + * @param args + * the arguments + */ + public static void main(String[] args) + { + /* + * bkowal + * Suppress extraneous output. + */ + /* + System.out.println("YAJSW: "+YajswVersion.YAJSW_VERSION); + System.out.println("OS : "+YajswVersion.OS_VERSION); + System.out.println("JVM : "+YajswVersion.JAVA_VERSION); + */ + String wrapperJar = WrapperLoader.getWrapperJar(); + String homeDir = new File(wrapperJar).getParent(); + if (!OperatingSystem.instance().setWorkingDir(homeDir)) + System.out.println("could not set working dir, pls check configuration or user rights: "+homeDir); + + // System.out.println(System.getProperty("java.class.path")); + buildOptions(); + parseCommand(args); + if (cmds != null && cmds.size() > 0) + for (Iterator it = cmds.iterator(); it.hasNext();) + { + Object cmd = it.next(); + if (cmd instanceof DefaultOption) + executeCommand((Option) cmd); + } + else + executeCommand(group.findOption("c")); + if (_exitOnTerminate) + Runtime.getRuntime().halt(_exitCode); + } + + private static File File(String property) + { + // TODO Auto-generated method stub + return null; + } + + /** + * Execute command. + * + * @param cmd + * the cmd + */ + private static void executeCommand(Option cmd) + { + switch (cmd.getId()) + { + case OPTION_C: + doConsole(); + break; + case OPTION_T: + doStart(); + break; + case OPTION_P: + doStop(); + break; + case OPTION_TX: + doStartPosix(); + break; + case OPTION_PX: + doStopPosix(); + break; + case OPTION_I: + doInstall(); + break; + case OPTION_R: + doRemove(); + break; + case OPTION_RW: + doRemoveWait(); + break; + case OPTION_N: + pid = ((Long) cl.getValue(cmd)).intValue(); + doReconnect(); + break; + case OPTION_G: + pid = ((Long) cl.getValue(cmd)).intValue(); + doGenerate(); + break; + case OPTION_D: + break; + case OPTION_Q: + doState(); + break; + case OPTION_QS: + doStateSilent(); + case OPTION_QX: + doStatePosix(); + break; + case OPTION_Y: + doStartTrayIcon(); + break; + default: + System.out.println("unimplemented option " + cmd.getPreferredName()); + } + } + + /** + * Do reconnect. + */ + private static void doReconnect() + { + prepareProperties(); + Configuration localConf = new MapConfiguration(_properties); + YajswConfiguration conf = new YajswConfigurationImpl(localConf, true); + WrappedProcess w = WrappedProcessFactory.createProcess(conf); + + System.out.println("************* RECONNECTING WRAPPER TO PID " + pid + " ***********************"); + System.out.println(); + + if (w.reconnect(pid)) + System.out.println("Connected to PID " + pid); + else + System.out.println("NOT connected to PID " + pid); + _exitOnTerminate = false; + + } + + /** + * Do remove. + */ + private static void doRemove() + { + prepareProperties(); + WrappedService w = getService(); + System.out.println("************* REMOVING " + w.getServiceName() + " ***********************"); + System.out.println(); + boolean result = w.uninstall(); + + if (PlatformEx.isWinVista() && w.requiresElevate()) + { + System.out.println("try uac elevate"); + WindowsXPProcess.elevateMe(); + return; + } + if (result) + System.out.println("Service " + w.getServiceName() + " removed"); + else + System.out.println("Service " + w.getServiceName() + " NOT removed"); + + } + + private static void doRemoveWait() + { + prepareProperties(); + WrappedService w = getService(); + System.out.println("************* REMOVING " + w.getServiceName() + " ***********************"); + System.out.println(); + boolean result = w.uninstall(); + + if (PlatformEx.isWinVista() && w.requiresElevate()) + { + System.out.println("try uac elevate"); + WindowsXPProcess.elevateMe(); + return; + } + if (result) + { + while (w.isInstalled()) + try + { + Thread.sleep(1000); + } + catch (InterruptedException e) + { + Thread.currentThread().interrupt(); + return; + } + System.out.println("Service " + w.getServiceName() + " removed"); + } + else + System.out.println("Service " + w.getServiceName() + " NOT removed"); + + } + + /** + * Do install. + */ + private static void doInstall() + { + WrappedService w = getService(); + System.out.println("************* INSTALLING " + w.getServiceName() + " ***********************"); + System.out.println(); + int i = 0; + while (w.isInstalled() && i < 10) + { + if (PlatformEx.isWinVista() && w.requiresElevate()) + { + System.out.println("try uac elevate"); + WindowsXPProcess.elevateMe(); + return; + } + + i++; + w.uninstall(); + try + { + Thread.sleep(2000); + } + catch (InterruptedException e) + { + e.printStackTrace(); + Thread.currentThread().interrupt(); + } + } + + + boolean result = w.install(); + if (PlatformEx.isWinVista() && w.requiresElevate()) + { + System.out.println("try uac elevate"); + WindowsXPProcess.elevateMe(); + return; + } + + if (result) + System.out.println("Service " + w.getServiceName() + " installed"); + else + System.out.println("Service " + w.getServiceName() + " NOT installed"); + + } + + /** + * Do stop. + */ + private static void doStop() + { + WrappedService w = getService(); + + System.out.println("************* STOPPING " + w.getServiceName() + " ***********************"); + System.out.println(); + + try + { + w.stop(); + if (PlatformEx.isWinVista() && w.requiresElevate()) + { + System.out.println("try uac elevate"); + WindowsXPProcess.elevateMe(); + return; + } + + if (w.isRunning()) + System.out.println("Service " + w.getServiceName() + " NOT stopped"); + else + System.out.println("Service " + w.getServiceName() + " stopped"); + } + catch (Exception e) + { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + private static void doStopPosix() + { + WrappedService w = getService(); + + System.out.println("************* STOPPING " + w.getServiceName() + " ***********************"); + System.out.println(); + + try + { + w.stopProcess(); + } + catch (Exception e) + { + e.printStackTrace(); + } + if (!w.isRunning()) + { + System.out.println("Service " + w.getServiceName() + " stopped"); + _exitCode = 0; + _exitOnTerminate = true; + } + else + { + System.out.println("Service" + w.getServiceName() + " NOT stopped"); + _exitCode = 1; + _exitOnTerminate = true; + } + + } + + /** + * Do start. + */ + private static void doStart() + { + WrappedService w = getService(); + // w.setDebug(true); + w.init(); + + System.out.println("************* STARTING " + w.getServiceName() + " ***********************"); + System.out.println(); + + w.start(); + try + { + Thread.sleep(1000); + } + catch (InterruptedException e1) + { + // TODO Auto-generated catch block + e1.printStackTrace(); + } + if (PlatformEx.isWinVista() && w.requiresElevate()) + { + System.out.println("try uac elevate"); + WindowsXPProcess.elevateMe(); + return; + } + int i = 0; + while (!w.isRunning() && i++ < 30) + { + try + { + Thread.sleep(1000); + } + catch (InterruptedException e) + { + e.printStackTrace(); + return; + } + if (!w.isStarting()) + break; + } + + if (w.isRunning()) + { + System.out.println("Service " + w.getServiceName() + " started"); + _exitCode = 0; + _exitOnTerminate = true; + } + else + { + System.out.println("Service " + w.getServiceName() + " NOT started"); + _exitCode = 1; + _exitOnTerminate = true; + } + + } + + private static void doStartPosix() + { + WrappedService w = getService(); + System.out.println("************* STARTING " + w.getServiceName() + " ***********************"); + System.out.println(); + + w.startProcess(); + int i = 0; + while (!w.isRunning() && i < 10) + { + i++; + try + { + Thread.sleep(2000); + } + catch (InterruptedException e) + { + e.printStackTrace(); + } + } + if (w.isRunning()) + System.out.println("Service " + w.getServiceName() + " started"); + else + System.out.println("Service " + w.getServiceName() + " NOT started"); + _exitOnTerminate = true; + + } + + /** + * Do start. + */ + private static void doStartTrayIcon() + { + prepareProperties(); + String[] args; + if (_service != null) + args = new String[] + { _service.getConfigLocalPath() }; + else + args = new String[] + { confFile }; + try + { + TrayIconMain.main(args); + } + catch (Exception e) + { + e.printStackTrace(); + } + _exitOnTerminate = true; + } + + private static void doState() + { + prepareProperties(); + WrappedService w = getService(); + int state = w.state(); + System.out.print("Name : "); + System.out.println(w.getServiceName()); + System.out.print("Installed : "); + System.out.println(w.isInstalled(state)); + System.out.print("Running : "); + System.out.println(w.isRunning(state)); + System.out.print("Interactive : "); + System.out.println(w.isInteractive(state)); + System.out.print("Automatic : "); + System.out.println(w.isAutomatic(state)); + System.out.print("Manual : "); + System.out.println(w.isManual(state)); + System.out.print("Disabled : "); + System.out.println(w.isDisabled(state)); + System.out.print("Paused : "); + System.out.println(w.isPaused(state)); + System.out.print("Unknown : "); + System.out.println(w.isStateUnknown(state)); + } + + private static void doStateSilent() + { + prepareProperties(); + WrappedService w = getService(); + w.init(); + int state = w.state(); + } + + private static void doStatePosix() + { + prepareProperties(); + WrappedService w = getService(); + int state = w.state(); + if (w.isRunning(state)) + _exitCode = 0; + else + _exitCode = 3; + _exitOnTerminate = true; + } + + /** + * Do console. + */ + private static void doConsole() + { + prepareProperties(); + final WrappedProcessList list = WrappedProcessFactory.createProcessList(_properties, confFileList, true); + list.startAll(); +// Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() +// { +// +// public void run() +// { +// list.onStopWrapper(); +// } +// +// })); + _exitOnTerminate = false; + } + + private static void doGenerate() + { + System.out.println("************* GENERATING YAJSW CONFIGURATION FOR PID " + pid + " ***********************"); + System.out.println(); + if (defaultFile != null) + ConfigGenerator.generate(pid, new File(defaultFile), new File(confFile)); + else + ConfigGenerator.generate(pid, null, new File(confFile)); + + } + + /** + * Prepare properties. + */ + private static void prepareProperties() + { + if (confFile != null) + _properties.put("wrapper.config", confFile); + if (defaultFile != null) + _properties.put("wrapperx.default.config", defaultFile); + if (properties != null) + for (Iterator it = properties.iterator(); it.hasNext();) + { + String prop = (String) it.next(); + String key = prop.substring(0, prop.indexOf('=')); + String value = prop.substring(prop.indexOf('=') + 1); + _properties.put(key, value); + } + } + + /** + * Parses the command. + * + * @param args + * the args + */ + private static void parseCommand(String[] args) + { + Parser parser = new Parser(); + + // configure a HelpFormatter + HelpFormatter hf = new HelpFormatter(); + DefaultOptionBuilder oBuilder = new DefaultOptionBuilder(); + ; + + // configure a parser + Parser p = new Parser(); + p.setGroup(group); + p.setHelpFormatter(hf); + p.setHelpOption(oBuilder.withLongName("help").withShortName("?").create()); + cl = p.parseAndHelp(args); + + // abort application if no CommandLine was parsed + if (cl == null) + { + System.exit(-1); + } + cmds = cl.getOptions(); + try + { + List arguments = cl.getValues(ARGS); + properties = new ArrayList(); + confFileList = new ArrayList(); + for (Object obj : arguments) + { + String arg = (String) obj; + if (Pattern.matches("wrapper\\..*=.*", arg)) + properties.add(arg); + else + confFileList.add(arg); + } + if (confFileList.isEmpty()) + System.out.println("no wrapper config file found "); + else + confFile = (String) confFileList.get(0); + /* + confFileList = cl.getValues(CONF_FILE); + if (confFileList == null || confFileList.isEmpty()) + System.out.println("no wrapper config file found "); + else + confFile = (String) confFileList.get(0); + */ + } + catch (Exception ex) + { + System.out.println("no wrapper config file found "); + } + try + { + defaultFile = (String) cl.getValue(cl.getOption("-d")); + if (defaultFile != null) + defaultFile = new File(defaultFile).getCanonicalPath(); + } + catch (Exception ex) + { + // no defaults -> maybe ok + } + //properties = cl.getValues(PROPERTIES); + + } + + /** + * Builds the options. + */ + private static void buildOptions() + { + DefaultOptionBuilder oBuilder = new DefaultOptionBuilder("-", "--", true); + ArgumentBuilder aBuilder = new ArgumentBuilder(); + GroupBuilder gBuilder = new GroupBuilder(); + + gBuilder.withOption(oBuilder.reset().withId(OPTION_C).withShortName("c").withLongName("console").withDescription( + "run as a Console application").create()); + gBuilder.withOption(oBuilder.reset().withId(OPTION_T).withShortName("t").withLongName("start").withDescription( + "starT an NT service or Unix daemon").create()); + gBuilder.withOption(oBuilder.reset().withId(OPTION_P).withShortName("p").withLongName("stop").withDescription( + "stoP a running NT service or Unix daemon").create()); + gBuilder.withOption(oBuilder.reset().withId(OPTION_TX).withShortName("tx").withLongName("startx").withDescription( + "starT -internal a Posix daemon").create()); + gBuilder.withOption(oBuilder.reset().withId(OPTION_PX).withShortName("px").withLongName("stopx").withDescription( + "stoP -internal- a running Posix daemon").create()); + gBuilder.withOption(oBuilder.reset().withId(OPTION_I).withShortName("i").withLongName("install").withDescription( + "Install an NT service or Unix daemon").create()); + gBuilder.withOption(oBuilder.reset().withId(OPTION_R).withShortName("r").withLongName("remove").withDescription( + "Remove an NT service or Unix daemon").create()); + gBuilder.withOption(oBuilder.reset().withId(OPTION_RW).withShortName("rw").withLongName("removeWait").withDescription( + "Remove an NT service or Unix daemon and wait until it is removed").create()); + gBuilder.withOption(oBuilder.reset().withId(OPTION_Q).withShortName("q").withLongName("query").withDescription( + "Query the status of an NT service or Unix daemon").create()); + gBuilder.withOption(oBuilder.reset().withId(OPTION_Y).withShortName("y").withLongName("tray").withDescription("Start System Tray Icon") + .create()); + gBuilder.withOption(oBuilder.reset().withId(OPTION_QS).withShortName("qs").withLongName("querysilent").withDescription( + "Silent Query the status of an NT service or Unix daemon").create()); + gBuilder.withOption(oBuilder.reset().withId(OPTION_QX).withShortName("qx").withLongName("queryposix").withDescription( + "Query the status of a posix daemon. Return status as exit code").create()); + + Argument pid = aBuilder.reset().withName(PID).withDescription("PID of process to reconnect to").withMinimum(1).withMaximum(1).withValidator( + NumberValidator.getIntegerInstance()).create(); + + gBuilder.withOption(oBuilder.reset().withId(OPTION_N).withShortName("n").withLongName("reconnect").withDescription( + "recoNnect to existing application").withArgument(pid).create()); + + Argument pid2 = aBuilder.reset().withName(PID).withDescription("PID of process to reconnect to").withMinimum(1).withMaximum(1).withValidator( + NumberValidator.getIntegerInstance()).create(); + + Argument defaultFile = aBuilder.reset().withName(DEFAULT_FILE).withDescription("Default Configuration File").withMinimum(0).withMaximum(1) + .withValidator(VFSFileValidator.getExistingFileInstance().setBase(".")).create(); + /* + * GroupBuilder childGbuilder = new GroupBuilder(); DefaultOptionBuilder + * childoObuilder = new DefaultOptionBuilder("-", "--", true); + * + * childGbuilder.withName(DEFAULT_FILE).withMinimum(0).withMaximum(1). + * withOption( + * childoObuilder.withId(OPTION_D).withArgument(defaultFile). + * withShortName("d").withLongName("defaultConf").withDescription( + * "Default Configuration File").create()); + * + * + * + * gBuilder.withOption(oBuilder.reset().withId(OPTION_G).withShortName("g" + * ).withLongName("genconf").withDescription( + * "Generate configuration file from pid" + * ).withArgument(pid2).withChildren(childGbuilder.create()).create()); + */ + + gBuilder.withOption(oBuilder.reset().withId(OPTION_D).withShortName("d").withLongName("defaultConf").withDescription( + "Default Configuration File").withArgument(defaultFile).create()); + + gBuilder.withOption(oBuilder.reset().withId(OPTION_G).withShortName("g").withLongName("genconf").withDescription( + "Generate configuration file from pid").withArgument(pid2).create()); + + FileValidator fValidator = VFSFileValidator.getExistingFileInstance().setBase("."); + fValidator.setFile(false); + // fValidator.setReadable(true); + gBuilder.withOption(aBuilder.reset().withName(ARGS).withDescription("Arguments: a list of configuration files, for example conf/wrapper.conf followed by an optional list of configuration name-value pairs, for example wrapper.debug=true") + .withMinimum(1).create()); + + /* + Validator pValidator = new Validator() + { + + public void validate(List values) throws InvalidArgumentException + { + for (Iterator it = values.iterator(); it.hasNext();) + { + String p = (String) it.next(); + if (!Pattern.matches("wrapper\\..*=.*", p)) + { + throw new InvalidArgumentException(p); + } + } + + } + + }; + gBuilder.withOption(aBuilder.reset().withName(PROPERTIES).withDescription( + "are configuration name-value pairs which override values. For example: wrapper.debug=true").withMinimum(0).withValidator(pValidator) + .create()); + */ + + gBuilder.withMaximum(3); + group = gBuilder.create(); + + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/WrapperExeBooter.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/WrapperExeBooter.java new file mode 100644 index 0000000000..dfdf230a22 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/WrapperExeBooter.java @@ -0,0 +1,46 @@ +/* This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + *

    + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ +package org.rzo.yajsw; + +import java.lang.reflect.Method; +import java.net.URLClassLoader; + +import org.rzo.yajsw.boot.WrapperLoader; + +public class WrapperExeBooter +{ + + /** + * The main method. Loads the libs required by YAJSW and starts + * WrapperExe.main + * + * @param args + * the arguments + */ + public static void main(String[] args) + { + URLClassLoader cl = WrapperLoader.getWrapperClassLoader(); + Thread.currentThread().setContextClassLoader(cl); + try + { + Class cls = Class.forName("org.rzo.yajsw.WrapperExe", true, cl); + Method mainMethod = cls.getDeclaredMethod("main", new Class[] + { String[].class }); + mainMethod.invoke(null, new Object[] + { args }); + } + catch (Exception e) + { + e.printStackTrace(); + } + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/YajswVersion.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/YajswVersion.java new file mode 100644 index 0000000000..568692d922 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/YajswVersion.java @@ -0,0 +1,8 @@ +package org.rzo.yajsw; + +public class YajswVersion +{ + public static final String YAJSW_VERSION = "yajsw-stable-11.04"; + public static final String OS_VERSION = System.getProperty("os.name")+"/"+System.getProperty("os.version")+"/"+System.getProperty("os.arch"); + public static final String JAVA_VERSION = System.getProperty("java.vendor")+"/"+System.getProperty("java.version"); +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/action/Action.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/action/Action.java new file mode 100644 index 0000000000..2807900b40 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/action/Action.java @@ -0,0 +1,12 @@ +package org.rzo.yajsw.action; + +import java.io.IOException; +import java.io.PrintStream; + +import org.jboss.netty.channel.Channel; +import org.rzo.yajsw.controller.Message; + +public interface Action +{ + public void execute(Message msg, Channel session, PrintStream out, Object data) throws IOException; +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/action/ActionFactory.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/action/ActionFactory.java new file mode 100644 index 0000000000..b3f8a85626 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/action/ActionFactory.java @@ -0,0 +1,43 @@ +package org.rzo.yajsw.action; + +import java.io.PrintStream; + +import org.jboss.netty.channel.Channel; +import org.rzo.yajsw.Constants; +import org.rzo.yajsw.controller.Message; + +public class ActionFactory +{ + public static Action getAction(Message msg) + { + String cls = null; + if (msg.getCode() == Constants.WRAPPER_MSG_THREAD_DUMP) + { + String version = System.getProperty("java.specification.version"); + if (version.startsWith("1.6")) + cls = "org.rzo.yajsw.action.ThreadDumpImpl6"; + else + cls = "org.rzo.yajsw.action.ThreadDumpImpl5"; + } + if (cls != null) + try + { + Class cl = ActionFactory.class.getClassLoader().loadClass(cls); + return (Action) cl.newInstance(); + } + catch (Throwable ex) + { + ex.printStackTrace(); + } + return new Action() + { + + public void execute(Message msg, Channel session, PrintStream out, Object data) + { + System.out.println("Error No Action for " + msg); + } + + }; + + } +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/action/ThreadDumpImpl5.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/action/ThreadDumpImpl5.java new file mode 100644 index 0000000000..6e973e9856 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/action/ThreadDumpImpl5.java @@ -0,0 +1,49 @@ +package org.rzo.yajsw.action; + +import java.io.IOException; +import java.io.PrintStream; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +import org.jboss.netty.channel.Channel; +import org.rzo.yajsw.controller.Message; + +public class ThreadDumpImpl5 implements Action +{ + public void execute(Message msg, Channel session, PrintStream out, Object data) throws IOException + { + Map allThreads = Thread.getAllStackTraces(); + Iterator iterator = allThreads.keySet().iterator(); + StringBuffer stringBuffer = new StringBuffer(); + Set ids = new HashSet(); + if (data != null) + for (long id : (long[])data) + { + ids.add(id); + } + while (iterator.hasNext()) + { + Thread key = (Thread) iterator.next(); + if (data != null && !ids.contains(key.getId())) + continue; + StackTraceElement[] trace = (StackTraceElement[]) allThreads.get(key); + stringBuffer.append(key + "\r\n"); + for (int i = 0; i < trace.length; i++) + { + stringBuffer.append(" " + trace[i] + "\r\n"); + } + stringBuffer.append("\r\n"); + } + out.println(stringBuffer.toString()); + out.flush(); + } + + public static void main(String[] args) throws IOException + { + Action a = (Action) new ThreadDumpImpl5(); + a.execute(null, null, System.out, null); + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/action/ThreadDumpImpl6.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/action/ThreadDumpImpl6.java new file mode 100644 index 0000000000..cdac59a63c --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/action/ThreadDumpImpl6.java @@ -0,0 +1,74 @@ +package org.rzo.yajsw.action; + +import java.io.IOException; +import java.io.PrintStream; +import java.lang.management.ManagementFactory; +import java.lang.management.MonitorInfo; +import java.lang.management.ThreadInfo; +import java.lang.management.ThreadMXBean; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import org.jboss.netty.channel.Channel; +import org.rzo.yajsw.controller.Message; + +public class ThreadDumpImpl6 implements Action +{ + public void execute(Message msg, Channel session, PrintStream out, Object data) throws IOException + { + final ThreadMXBean thbean = ManagementFactory.getThreadMXBean(); + final long[] ids = (long[]) (data == null ? thbean.getAllThreadIds() : data); + Map threads = Thread.getAllStackTraces(); + Map threadIds = new HashMap(); + for (Iterator it = threads.keySet().iterator(); it.hasNext();) + { + Thread t = (Thread) it.next(); + threadIds.put(t.getId(), t); + } + + synchronized (ids) + { + + ThreadInfo[] infos; + if (!thbean.isObjectMonitorUsageSupported() || !thbean.isSynchronizerUsageSupported()) + infos = thbean.getThreadInfo(ids); + else + infos = thbean.getThreadInfo(ids, true, true); + + for (ThreadInfo info : infos) + { + String locked = info.getLockOwnerName(); + String daemon = threadIds.get(info.getThreadId()).isDaemon() ? "DAEMON" : ""; + locked = locked == null ? "" : "locked by " + locked; + out.println(String.format("%1$s %2$s %5$s - %3$s %4$s", info.getThreadId(), info.getThreadName(), info.getThreadState(), locked, + daemon)); + MonitorInfo[] monitorInfos = info.getLockedMonitors(); + StackTraceElement[] stackTraceElements = info.getStackTrace(); + int k = 0; + int i = 0; + for (StackTraceElement trace : stackTraceElements) + { + out.println(String.format(" %1$s", trace)); + if (monitorInfos.length > k && monitorInfos[k].getLockedStackDepth() == i) + { + out.println(String.format(" - lock %2$s@%1$s", monitorInfos[k].getClassName(), Integer.toHexString(monitorInfos[k] + .getIdentityHashCode()))); + k++; + } + i++; + } + + } + out.flush(); + } + + } + + public static void main(String[] args) throws IOException + { + Action a = (Action) new ThreadDumpImpl6(); + a.execute(null, null, System.out, null); + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/app/WrapperManagerImpl.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/app/WrapperManagerImpl.java new file mode 100644 index 0000000000..0eb65a1f30 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/app/WrapperManagerImpl.java @@ -0,0 +1,1732 @@ +/* This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + *

    + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ +package org.rzo.yajsw.app; + +import java.io.File; +import java.io.IOException; +import java.io.OutputStream; +import java.io.PrintStream; +import java.lang.management.GarbageCollectorMXBean; +import java.lang.management.ManagementFactory; +import java.lang.management.MemoryMXBean; +import java.lang.management.ThreadMXBean; +import java.lang.reflect.Method; +import java.net.InetSocketAddress; +import java.net.MalformedURLException; +import java.net.Socket; +import java.net.URL; +import java.text.MessageFormat; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.Iterator; +import java.util.List; +import java.util.Properties; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; +import java.util.jar.Attributes; +import java.util.jar.JarFile; +import java.util.jar.Manifest; + +import javax.management.MBeanServer; +import javax.management.MBeanServerFactory; +import javax.management.ObjectName; + +import org.apache.commons.configuration.Configuration; +import org.apache.commons.logging.LogFactory; +import org.jboss.netty.bootstrap.ClientBootstrap; +import org.jboss.netty.channel.Channel; +import org.jboss.netty.channel.ChannelFuture; +import org.jboss.netty.channel.ChannelHandlerContext; +import org.jboss.netty.channel.ChannelPipeline; +import org.jboss.netty.channel.ChannelPipelineFactory; +import org.jboss.netty.channel.ChannelStateEvent; +import org.jboss.netty.channel.Channels; +import org.jboss.netty.channel.ExceptionEvent; +import org.jboss.netty.channel.MessageEvent; +import org.jboss.netty.channel.SimpleChannelHandler; +import org.jboss.netty.channel.socket.oio.OioClientSocketChannelFactory; +import org.jboss.netty.handler.codec.frame.DelimiterBasedFrameDecoder; +import org.jboss.netty.handler.codec.frame.Delimiters; +import org.jboss.netty.logging.InternalLogger; +import org.jboss.netty.logging.SimpleLoggerFactory; +import org.rzo.yajsw.Constants; +import org.rzo.yajsw.YajswVersion; +import org.rzo.yajsw.action.Action; +import org.rzo.yajsw.action.ActionFactory; +import org.rzo.yajsw.config.ConfigUtils; +import org.rzo.yajsw.config.YajswConfiguration; +import org.rzo.yajsw.config.YajswConfigurationImpl; +import org.rzo.yajsw.controller.Message; +import org.rzo.yajsw.controller.jvm.MessageDecoder; +import org.rzo.yajsw.controller.jvm.MessageEncoder; +import org.rzo.yajsw.io.CyclicBufferFileInputStream; +import org.rzo.yajsw.io.CyclicBufferFilePrintStream; +import org.rzo.yajsw.io.TeeInputStream; +import org.rzo.yajsw.io.TeeOutputStream; +import org.rzo.yajsw.nettyutils.SystemOutLoggingFilter; +import org.rzo.yajsw.os.OperatingSystem; +import org.rzo.yajsw.script.Script; +import org.rzo.yajsw.script.ScriptFactory; +import org.rzo.yajsw.util.Cycler; +import org.rzo.yajsw.util.DaemonThreadFactory; +import org.rzo.yajsw.wrapper.AlphanumComparator; + +import com.sun.management.HotSpotDiagnosticMXBean; + +// TODO: Auto-generated Javadoc +/** + * The Class WrapperManagerImpl. + */ +public class WrapperManagerImpl implements WrapperManager, Constants, WrapperManagerImplMBean +{ + + /** The _port. */ + int _port = DEFAULT_PORT; + + /** The _debug. */ + boolean _debug = false; + + /** The log. */ + final InternalLogger log = SimpleLoggerFactory.getInstance("WrapperManager"); + + /** The _started. */ + volatile boolean _started = false; + + /** The _key. */ + String _key; + + /** The _ping interval. */ + int _pingInterval = 5; + + /** The connector. */ + ClientBootstrap connector; + + /** The _session. */ + volatile Channel _session; + + /** The _stopping. */ + volatile boolean _stopping = false; + + /** The _config. */ + Configuration _config; + + /** The instance. */ + static WrapperManagerImpl instance; + + /** The _exit code. */ + int _exitCode = 0; + + /** The main method. */ + Method mainMethod = null; + + /** The main method args. */ + String[] mainMethodArgs = null; + + /** The exit on main terminate. */ + int exitOnMainTerminate = -1; + private int exitOnException = 999; + + /** The _my pid. */ + volatile int _myPid = -1; + + boolean _externalStop = false; + + String _groovyScript = null; + + Cycler _pinger; + + OutputStream _outStream; + OutputStream _errStream; + + volatile boolean _appearHanging = false; + + boolean _overrideStdErr = false; + + boolean _haltAppOnWrapper = false; + + Lock _lock = new ReentrantLock(); + Condition _connectEnd = _lock.newCondition(); + Executor executor = Executors.newCachedThreadPool(new DaemonThreadFactory("yajsw-pool", Thread.MAX_PRIORITY)); + + long _startupTimeout = 0; + + String shutdownScript = null; + + Properties _properties; + + volatile boolean _dumpingHeap = false; + + volatile String _stopReason = null; + + float currentPercentHeap = -1; + long minorGCDuration = -1; + long fullGCDuration = -1; + final MemoryMXBean memoryBean = ManagementFactory.getMemoryMXBean(); + final long maxHeap = memoryBean.getHeapMemoryUsage().getMax(); + final Object _heapDataLock = new Object(); + boolean _sendHeapData = false; + + private long lastMinorCollectionCount; + private long lastMinorCollectionTime; + + private long lastFullCollectionCount; + private long lastFullCollectionTime; + + Long usedHeap = null; + Long timeMinorGC = null; + Long timeFullGC = null; + Long lastUsedHeap = null; + + GarbageCollectorMXBean minorGCBean; + GarbageCollectorMXBean fullGCBean; + + MessageFormat gcFormat = null; + + boolean _initGCBeans = false; + + private String getSystemProperty(String key) + { + String result = System.getProperty(key); + if (result != null && result.contains("\"")) + result.replaceAll("\"", ""); + return result; + } + + /* + * (non-Javadoc) + * + * @see org.rzo.yajsw.WrapperManager#init(java.lang.String[], + * java.lang.ClassLoader) + */ + public void init(String[] args, ClassLoader wrapperClassLoader) + { + /* + * System.out.println(Scheduler.class.getClassLoader()); + * System.out.println(Configuration.class.getClassLoader()); + * System.out.flush(); try { Thread.sleep(10000); } catch + * (InterruptedException e1) { // TODO Auto-generated catch block + * e1.printStackTrace(); } + */ + /* + * bkowal + * Suppress extraneous output unless debug is enabled. + */ + if (_debug) + { + System.out.println("YAJSW: "+YajswVersion.YAJSW_VERSION); + System.out.println("OS : "+YajswVersion.OS_VERSION); + System.out.println("JVM : "+YajswVersion.JAVA_VERSION); + } + // set commons logging for vfs -> avoid using default java logger + ClassLoader currentClassLoader = Thread.currentThread().getContextClassLoader(); + Thread.currentThread().setContextClassLoader(wrapperClassLoader); + //String commonsLog = getSystemProperty("org.apache.commons.logging.Log"); + //System.setProperty("org.apache.commons.logging.Log", "org.apache.commons.logging.impl.SimpleLog"); + LogFactory.getFactory().setAttribute("org.apache.commons.logging.Log", "org.apache.commons.logging.impl.SimpleLog"); + instance = this; + String outFile = getSystemProperty("wrapper.teeName"); + String outPath = getSystemProperty("wrapper.tmp.path"); + String vStr = getSystemProperty("wrapper.console.visible"); + boolean visible = vStr != null && vStr.equals("true"); + if (outFile != null) + { + teeSystemStreams(outFile, outPath, visible); + } + + String preScript = getSystemProperty("wrapper.app.pre.script"); + if (preScript != null & !"".equals(preScript)) + try + { + if (_debug) + System.out.println("wrapped process: executing pre script " + preScript); + Script script = ScriptFactory.createScript(preScript, "wrapper.app.pre.script", null, new String[0], log, 0, "UTF-8", false, _debug); + if (script != null) + script.execute(); + else + System.out.println("wrapped process: executing pre script error: could not open script"); + } + catch (Throwable ex) + { + ex.printStackTrace(); + } + + YajswConfigurationImpl config = new YajswConfigurationImpl(); + // config.setDebug(false); + config.init(); + _debug = config.getBoolean("wrapper.debug", false); + logJavaInfo(args); + + try + { + _overrideStdErr = config.getBoolean("wrapper.java.dump.override", false); + } + catch (Exception ex) + { + System.out.println("Error getting wrapper.java.dump.override " + ex.getMessage()); + } + try + { + String mainClassName = config.getString("wrapper.java.app.mainclass"); + String jarName = config.getString("wrapper.java.app.jar"); + String groovyScript = config.getString("wrapper.groovy"); + if (mainClassName == null && jarName == null && groovyScript == null) + mainClassName = config.getString("wrapper.app.parameter.1"); + if (_debug) + System.out.println("mainClass/jar/script: " + mainClassName + "/" + jarName + "/" + groovyScript); + if (jarName == null && mainClassName == null && groovyScript == null) + { + System.out.println("missing main class name or jar file or groovy file. please check configuration"); + return; + } + if (jarName != null) + { + mainMethod = loadJar(jarName); + } + else if (mainClassName != null) + try + { + Class cls = ClassLoader.getSystemClassLoader().loadClass(mainClassName);// Class.forName(mainClassName, + // currentContext); + mainMethod = cls.getMethod("main", new Class[] + { String[].class }); + } + catch (Exception e) + { + System.out.println("error finding main method in class: " + mainClassName + " : " + e.getMessage()); + // log.throwing(WrapperMain.class.getName(), "main", e); + e.printStackTrace(); + return; + } + else + _groovyScript = groovyScript; + + String stopConfig = config.getString("wrapper.stop.conf"); + if (stopConfig != null) + { + File f = new File(stopConfig); + _externalStop = true; + } + if (_debug) + System.out.println("external stop " + _externalStop); + + exitOnMainTerminate = config.getInt("wrapper.exit_on_main_terminate", DEFAULT_EXIT_ON_MAIN_TERMINATE); + + exitOnException = config.getInt("wrapper.exit_on_main_exception", DEFAULT_EXIT_ON_MAIN_EXCEPTION); + + mainMethodArgs = getAppParam((Configuration) config); + setConfiguration((Configuration) config); + if (_config.getBoolean("wrapper.java.jmx", false)) + registerMBean(config); + + String control = _config.getString("wrapper.control", DEFAULT_CONTROL); + if ("TIGHT".equals(control) || "APPLICATION".equals(control)) + _haltAppOnWrapper = true; + + setKey(_config.getString("wrapper.key")); + // setDebug(true); + setPort(_config.getInt("wrapper.port")); + setPingInterval(_config.getInt("wrapper.ping.interval", Constants.DEFAULT_PING_INTERVAL)); + + _startupTimeout = _config.getInt("wrapper.startup.timeout", DEFAULT_STARTUP_TIMEOUT) * 1000; + + shutdownScript = _config.getString("wrapper.app.shutdown.script", null); + if (shutdownScript != null && !"".equals(shutdownScript)) + { + Runtime.getRuntime().addShutdownHook(new Thread() + { + public void run() + { + executeShutdownScript(); + } + }); + + } + + try + { + _sendHeapData = config.getBoolean("wrapper.java.monitor.gc.restart", false) + || config.getBoolean("wrapper.java.monitor.heap", false); + } + catch (Exception ex) + { + System.out.println("error reading wrapper.java.monitor.*.restart"); + } + + monitorDeadLocks(config); + monitorHeap(config); + monitorGc(config); + + if (_debug) + System.out.println("terminated WrapperManager.init()"); + //if (commonsLog != null) + // System.setProperty("org.apache.commons.logging.Log", commonsLog); + LogFactory.getFactory().removeAttribute("org.apache.commons.logging.Log"); + + + } + catch (Throwable ex) + { + ex.printStackTrace(); + } + if (currentClassLoader != null) + Thread.currentThread().setContextClassLoader(currentClassLoader); + + } + + private void monitorDeadLocks(YajswConfigurationImpl config) + { + if (config.getBoolean("wrapper.java.monitor.deadlock", false)) + { + final long cycle = config.getLong("wrapper.java.monitor.deadlock.interval", 30) * 1000; + final ThreadMXBean bean = ManagementFactory.getThreadMXBean(); + if (_debug) + System.out.println("monitor deadlock: start"); + executor.execute(new Runnable() + { + + public void run() + { + while (!_stopping) + { + long[] ids = bean.findDeadlockedThreads(); + if (ids != null && ids.length > 0) + { + System.err.println("wrapper.java.monitor.deadlock: DEADLOCK IN THREADS: "); + threadDump(ids); + // exit loop once we find a deadlock + return; + } + try + { + Thread.sleep(cycle); + } + catch (InterruptedException e) + { + return; + } + } + + } + + }); + + } + } + + volatile boolean _heapNotified = false; + + private void monitorHeap(YajswConfigurationImpl config) + { + if (config.getBoolean("wrapper.java.monitor.heap", false)) + { + final long cycle = config.getLong("wrapper.java.monitor.heap.interval", 30) * 1000; + final int thresholdPercent = config.getInt("wrapper.java.monitor.heap.threshold.percent", 95); + if (_debug) + System.out.println("monitor heap: start"); + executor.execute(new Runnable() + { + + public void run() + { + while (!_stopping) + { + synchronized (_heapDataLock) + { + currentPercentHeap = ((float) memoryBean.getHeapMemoryUsage().getUsed() / maxHeap) * 100; + if (currentPercentHeap > thresholdPercent) + { + if (!_heapNotified) + { + System.err.println("wrapper.java.monitor.heap: HEAP SIZE EXCEEDS THRESHOLD: " + + memoryBean.getHeapMemoryUsage().getUsed() + "/" + maxHeap); + _heapNotified = true; + } + } + else if (_heapNotified) + { + System.err.println("wrapper.java.monitor.heap: HEAP SIZE OK: " + memoryBean.getHeapMemoryUsage().getUsed() + "/" + + maxHeap); + _heapNotified = false; + + } + } + try + { + Thread.sleep(cycle); + } + catch (InterruptedException e) + { + return; + } + } + + } + + }); + + } + } + + private void monitorGc(YajswConfigurationImpl config) + { + initGCBeans(); + String mFormat = config.getString("wrapper.java.monitor.gc", null); + if (mFormat != null) + try + { + if (_debug) + System.out.println("monitor GC: " + mFormat); + gcFormat = new MessageFormat(mFormat); + final long cycle = config.getLong("wrapper.java.monitor.gc.interval", 1) * 1000; + + if (_debug) + { + System.out.println("monitor gc: minorGCBean/fullGCBean: " + minorGCBean.getName() + "/" + fullGCBean.getName()); + + System.out.println("monitor gc: start cycle " + cycle + "ms"); + } + executor.execute(new Runnable() + { + public void run() + { + if (minorGCBean == null) + { + System.err.println("monitor gc: could not find minorGCBean -> abort monitor"); + return; + } + if (fullGCBean == null) + { + System.err.println("monitor gc: could not find fullGCBean -> abort monitor"); + return; + } + try + { + while (!_stopping) + { + getGCData(); + // Sleep a little bit before the next poll + Thread.sleep(cycle); + } + } + catch (Exception ex) + { + // Do nothing except exit this thread + ex.printStackTrace(); + } + System.err.println("monitor gc: end"); + + } + }); + } + catch (Exception ex) + { + System.err.println("monitor gc: exception: " + ex); + ex.printStackTrace(); + } + } + + private void initGCBeans() + { + if (_initGCBeans) + return; + GarbageCollectorMXBean minorGCBeanX = null; + GarbageCollectorMXBean fullGCBeanX = null; + + try + { + List gcMBeans = ManagementFactory.getGarbageCollectorMXBeans(); + for (GarbageCollectorMXBean gcBean : gcMBeans) + { + if (gcBean.getName().toLowerCase().contains("copy")) + { + minorGCBeanX = gcBean; + } + else if ("ParNew".equals(gcBean.getName())) + { + minorGCBeanX = gcBean; + } + else if (gcBean.getName().toLowerCase().contains("scavenge")) + { + minorGCBeanX = gcBean; + } + else if (gcBean.getName().toLowerCase().contains("marksweep")) + { + fullGCBeanX = gcBean; + } + else + { + System.err.println("Unable to classify GarbageCollectorMXBean [" + gcBean.getName() + "]"); + } + } + } + catch (Throwable e) + { + System.out.println("error getting GC beans"); + } + minorGCBean = minorGCBeanX; + fullGCBean = fullGCBeanX; + _initGCBeans = true; + + } + + private void getGCData() + { + initGCBeans(); + if (minorGCBean == null || fullGCBean == null) + return; + if (minorGCBean.getCollectionCount() != lastMinorCollectionCount) + { + long diffCount = minorGCBean.getCollectionCount() - lastMinorCollectionCount; + long diffTime = minorGCBean.getCollectionTime() - lastMinorCollectionTime; + if (diffCount != 0 && diffCount != 1) + timeMinorGC = diffTime / diffCount; + else + timeMinorGC = diffTime; + usedHeap = memoryBean.getHeapMemoryUsage().getUsed(); + + lastMinorCollectionCount = minorGCBean.getCollectionCount(); + lastMinorCollectionTime = minorGCBean.getCollectionTime(); + } + + if (fullGCBean.getCollectionCount() != lastFullCollectionCount) + { + long diffCount = fullGCBean.getCollectionCount() - lastFullCollectionCount; + long diffTime = fullGCBean.getCollectionTime() - lastFullCollectionTime; + if (diffCount != 0 && diffCount != 1) + timeFullGC = diffTime / diffCount; + else + timeFullGC = diffTime; + + lastFullCollectionCount = fullGCBean.getCollectionCount(); + lastFullCollectionTime = fullGCBean.getCollectionTime(); + } + usedHeap = memoryBean.getHeapMemoryUsage().getUsed(); + if (usedHeap != null) + { + if (timeMinorGC == null) + timeMinorGC = 0L; + if (timeFullGC == null) + timeFullGC = 0L; + + // remember data to be sent by ping + synchronized (_heapDataLock) + { + currentPercentHeap = ((float) usedHeap / maxHeap) * 100; + if (minorGCDuration == -1) + minorGCDuration = 0; + if (fullGCDuration == -1) + fullGCDuration = 0; + minorGCDuration += timeMinorGC; + fullGCDuration += timeFullGC; + } + + lastUsedHeap = usedHeap; + + if (gcFormat != null) + System.err.println(gcFormat.format(new Object[] + { usedHeap, timeMinorGC, timeFullGC })); + usedHeap = null; + timeMinorGC = null; + timeFullGC = null; + } + + } + + private void registerMBean(YajswConfiguration config) + { + MBeanServer server = null; + ArrayList servers = MBeanServerFactory.findMBeanServer(null); + try + { + if (servers != null && servers.size() > 0) + server = (MBeanServer) servers.get(0); + if (server != null) + { + String name = config.getString("wrapper.console.title"); + if (name == null) + name = config.getString("wrapper.ntservice.name"); + if (name == null) + name = "yajsw.noname"; + ObjectName oName = new ObjectName("Wrapper", "name", name); + server.registerMBean(this, oName); + // System.out.println("found mbean server: " + + // server.toString()); + } + else + System.out.println("ERROR: no mbean server found "); + } + catch (Exception e) + { + e.printStackTrace(); + } + + } + + /* + * (non-Javadoc) + * + * @see org.rzo.yajsw.WrapperManager#getMainMethod() + */ + public Method getMainMethod() + { + return mainMethod; + } + + /* + * (non-Javadoc) + * + * @see org.rzo.yajsw.WrapperManager#getMainMethodArgs() + */ + public Object[] getMainMethodArgs() + { + return mainMethodArgs; + } + + /* + * (non-Javadoc) + * + * @see org.rzo.yajsw.WrapperManager#isExitOnMainTerminate() + */ + public int getExitOnMainTerminate() + { + if (_debug) + System.out.println("exit on main terminate " + exitOnMainTerminate); + return exitOnMainTerminate; + } + + public int getExitOnException() + { + if (_debug) + System.out.println("exit on main exception " + exitOnException); + return exitOnException; + } + + /** + * Load jar. + * + * @param jarName + * the jar name + * + * @return the method + */ + private Method loadJar(String jarName) + { + URL url = null; + try + { + url = new File(jarName).toURI().toURL(); + } + catch (MalformedURLException e2) + { + e2.printStackTrace(); + return null; + } + Manifest manifest; + try + { + manifest = new JarFile(new File(jarName)).getManifest(); + } + catch (IOException e1) + { + e1.printStackTrace(); + return null; + } + Attributes attr = manifest.getMainAttributes(); + + String cl = attr.getValue("Class-Path"); + ClassLoader loader = null; + /* if (cl != null) + { + ArrayList classpath = new ArrayList(); + String[] clArr = cl.split(" "); + for (int i = 0; i < clArr.length; i++) + { + String file = clArr[i]; + File myFile; + try + { + myFile = new File(file); + classpath.add(myFile); + } + catch (Exception e) + { + e.printStackTrace(); + } + } + + URL[] urlsArr = new URL[classpath.size()]; + int i = 0; + for (Iterator it = classpath.iterator(); it.hasNext(); i++) + try + { + urlsArr[i] = ((File) it.next()).toURI().toURL(); + } + catch (Exception e) + { + e.printStackTrace(); + } + + loader = new URLClassLoader(urlsArr, ClassLoader.getSystemClassLoader()); + } +*/ if (loader == null) + loader = ClassLoader.getSystemClassLoader(); + + String mainClassName = attr.getValue("Main-Class"); + if (mainClassName == null) + return null; + Method mainMethod = null; + try + { + Class cls = loader.loadClass(mainClassName);// cl.loadClass(mainClassName); + mainMethod = cls.getMethod("main", new Class[] + { String[].class }); + } + catch (Exception ex) + { + ex.printStackTrace(); + System.err.println("ERROR: could not load main method from class/jar: "+mainClassName+"/"+jarName); + } + + return mainMethod; + } + + private File createRWfile(String path, String fname) + { + File pFile = new File(path); + try + { + if (!pFile.exists()) + pFile.mkdirs(); + } + catch (Exception ex) + { + ex.printStackTrace(); + } + File result = new File(path, fname); + + if (OperatingSystem.instance().isPosix()) + { + String absPath = result.getAbsolutePath(); + /* + * bkowal + * Suppress extraneous output unless debug is enabled. + */ + if (_debug) + { + System.out.println("createRWfile " + absPath); + } + try + { + if (!result.exists()) + { + result.createNewFile(); + } + result.deleteOnExit(); + Process p = Runtime.getRuntime().exec("chmod 777 " + absPath); + // p.waitFor(); + Thread.sleep(500); + p.destroy(); + } + catch (Exception ex) + { + ex.printStackTrace(System.out); + } + } + + return result; + } + + /** + * Tee system streams. + * + * @param outFile + * the out file + * @param path + * the path + * @param visible + * the visible + */ + private void teeSystemStreams(String outFile, String path, boolean visible) + { + File fOut = createRWfile(path, "out_" + outFile); + // if (fOut.exists()) + // fOut.delete(); + fOut.deleteOnExit(); + File fErr = createRWfile(path, "err_" + outFile); + // if (fErr.exists()) + // fErr.delete(); + fErr.deleteOnExit(); + File fIn = createRWfile(path, "in_" + outFile); + fIn.deleteOnExit(); + + try + { + PrintStream wrapperOut = (PrintStream) new CyclicBufferFilePrintStream(fOut); + TeeOutputStream newOut = (TeeOutputStream) new TeeOutputStream(); + newOut.connect(wrapperOut); + // pipe output to console only if it is visible + if (visible) + newOut.connect(System.out); + _outStream = wrapperOut; + System.setOut(new PrintStream(newOut)); + } + catch (Throwable e) + { + e.printStackTrace(System.out); + } + + try + { + + PrintStream wrapperErr = (PrintStream) new CyclicBufferFilePrintStream(fErr); + TeeOutputStream newErr = (TeeOutputStream) new TeeOutputStream(); + newErr.connect(wrapperErr); + // pipe output to console only if it is visible + if (visible) + newErr.connect(System.err); + _errStream = newErr; + System.setErr(new PrintStream(newErr)); + } + catch (Throwable e) + { + e.printStackTrace(); + } + + try + { + CyclicBufferFileInputStream wrapperIn = new CyclicBufferFileInputStream(fIn); + TeeInputStream newIn = (TeeInputStream) new TeeInputStream(); + newIn.connect(wrapperIn); + newIn.connect(System.in); + System.setIn(newIn); + } + catch (Throwable e) + { + e.printStackTrace(); + } + } + + /** + * Log java info. + * + * @param args + * the args + */ + private void logJavaInfo(String[] args) + { + if (_debug) + { + System.out.println("APP user name=" + getSystemProperty("user.name")); + System.out.println("APP working dir=" + getSystemProperty("user.dir")); + System.out.println("APP java version=" + getSystemProperty("java.version")); + System.out.println("APP class path=" + getSystemProperty("java.class.path")); + System.out.println("APP library path=" + getSystemProperty("java.library.path")); + } + String[] files = getSystemProperty("java.class.path").split(File.pathSeparator); + for (int i = 0; i < files.length; i++) + { + File f = new File(files[i]); + if (!f.exists()) + System.err.println("Classpath File not found: " + files[i]); + } + + if (_debug) + { + String argsStr = "Application args: "; + if (args != null && args.length > 0) + for (int i = 0; i < args.length; i++) + { + argsStr += args[i] + " "; + } + else + argsStr += "no args"; + System.out.println(argsStr); + } + + } + + /** + * Gets the app param. + * + * @param config + * the config + * + * @return the app param + */ + private String[] getAppParam(Configuration config) + { + ArrayList result = new ArrayList(); + ArrayList keys = new ArrayList(); + for (Iterator it = config.getKeys("wrapper.app.parameter"); it.hasNext();) + { + keys.add(it.next()); + } + Collections.sort(keys, new AlphanumComparator()); + for (Iterator it = keys.listIterator(); it.hasNext();) + { + String arg = config.getString((String) it.next()); + if (arg != null) + result.add(arg); + } + String[] args = new String[result.size()]; + int i = 0; + for (Iterator it = result.iterator(); it.hasNext(); i++) + { + args[i] = (String) it.next(); + } + if (_debug) + { + System.out.println("args: "); + for (String arg : args) + System.out.println(arg); + } + return args; + } + + /** + * Sets the configuration. + * + * @param config + * the new configuration + */ + void setConfiguration(Configuration config) + { + _config = config; + } + + /* + * (non-Javadoc) + * + * @see org.rzo.yajsw.WrapperManager#start() + */ + public void start() + { + try + { + // hack: avoid netty hangs on connect + if (_config.getBoolean("wrapper.console.pipestreams", false)) + { + Socket dummy = new Socket("127.0.0.1", _port); + OutputStream out = dummy.getOutputStream(); + out.close(); + dummy.close(); + } + } + catch (Throwable e1) + { + if (_debug) + e1.printStackTrace(); + } + + connector = new ClientBootstrap(new OioClientSocketChannelFactory( + // executor, + executor)); + // add logging + ChannelPipelineFactory pf; + if (_debug) + { + pf = new ChannelPipelineFactory() + { + public ChannelPipeline getPipeline() throws Exception + { + return Channels.pipeline(new SystemOutLoggingFilter("WrapperManager"), new DelimiterBasedFrameDecoder(8192, true, Delimiters + .nulDelimiter()), new MessageEncoder(), new MessageDecoder(), new WrapperHandler()); + } + }; + } + else + { + pf = new ChannelPipelineFactory() + { + public ChannelPipeline getPipeline() throws Exception + { + return Channels.pipeline(new DelimiterBasedFrameDecoder(8192, true, Delimiters.nulDelimiter()), new MessageEncoder(), + new MessageDecoder(), new WrapperHandler()); + } + }; + } + connector.setPipelineFactory(pf); + + // pinger is a cycler with high priority threads + // sends ping messages within a ping interval + _pinger = new Cycler(getPingInterval(), 0, Executors.newCachedThreadPool(new DaemonThreadFactory("pinger", Thread.MAX_PRIORITY)), + new Runnable() + { + long start = System.currentTimeMillis(); + + public void run() + { + ChannelFuture future; + if (_session != null && _session.isConnected() && !_stopping && !_appearHanging) + { + synchronized (_heapDataLock) + { + if (_sendHeapData) + { + if (minorGCDuration == -1) + getGCData(); + future = _session.write(new Message(Constants.WRAPPER_MSG_PING, "" + currentPercentHeap + ";" + minorGCDuration + + ";" + fullGCDuration + ";" + lastUsedHeap)); + currentPercentHeap = -1; + minorGCDuration = -1; + fullGCDuration = -1; + } + else + { + future = _session.write(new Message(Constants.WRAPPER_MSG_PING, "")); + } + } + try + { + future.await(10000); + } + catch (InterruptedException e) + { + e.printStackTrace(); + } + start = System.currentTimeMillis(); + } + else if ((_haltAppOnWrapper && (System.currentTimeMillis() - start) > _startupTimeout) && !_stopping) + { + System.out.println("no connection to wrapper during " + (_startupTimeout / 1000) + "seconds -> System.exit(-1)"); + System.out + .println("if this is due to server overload consider increasing yajsw configuration property wrapper.startup.timeout"); + System.exit(-1); + } + } + }); + _pinger.start(); + + // connect + connector.setOption("remoteAddress", new InetSocketAddress("127.0.0.1", _port)); + connector.setOption("connectTimeoutMillis", 10 * 1000); + connector.setOption("reuseAddress", true); + connector.setOption("tcpNoDelay", true); + + // handler should process messages in a separate thread + + reconnect(); + /* + * try { if (_config.getInt("wrapper.action.port") > 0) { _actionHandler + * = new ActionHandler(this, _config); _actionHandler.start(); } } catch + * (Exception ex) { log.info("could not start action handler " + + * ex.getMessage()); } + */ + + } + + /** + * Reconnect. + */ + private void reconnect() + { + // try connecting, if we could not sleep then retry + while (!_started) + { + if (_debug) + // log.fine("connecting to port " + _port); + System.out.println("connecting to port " + _port); + final ChannelFuture future1 = connector.connect(); + try + { + Thread.yield(); + // System.out.println("connecting wait future "); + future1.await(10000); + // System.out.println("after connecting wait future "); + _started = future1.isSuccess(); + } + catch (Exception e1) + { + // TODO Auto-generated catch block + System.out.println("error connecting to wrapper: " + e1); + e1.printStackTrace(); + } + /* + * executor.execute(new Runnable() { + * + * public void run() { future1.addListener(new + * ChannelFutureListener() { public void + * operationComplete(ChannelFuture future) throws Exception { + * _lock.lock(); System.out.println("future" + future.isSuccess()); + * _started = future.isSuccess(); _connectEnd.signal(); + * _lock.unlock(); + * + * } }); } + * + * }); + * + * _lock.lock(); try { _connectEnd.await(); } catch + * (InterruptedException e1) { // TODO Auto-generated catch block + * e1.printStackTrace(); } _lock.unlock(); + * System.out.println("started "+_started); + */ + + if (_started) + { + if (_debug) + System.out.println("WrapperManager: channel connected, sending key"); + future1.getChannel().write(new Message(Constants.WRAPPER_MSG_KEY, _key)); + } + else + try + { + if (_debug) + // log.fine("connection failed -> sleep then retry"); + System.out.println("connection failed -> sleep then retry"); + _started = false; + Thread.sleep(5000); + } + catch (InterruptedException e) + { + e.printStackTrace(); + } + } + } + + /** + * The Class WrapperHandler. + */ + class WrapperHandler extends SimpleChannelHandler + { + + @Override + public void channelDisconnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception + { + if (_debug) + System.out.println("session closed"); + _started = false; + try + { + _session.close(); + } + catch (Throwable ex) + { + ex.printStackTrace(); + } + _session = null; + if (!_stopping) + { + if (_debug) + System.out.println("try reconnect"); + executor.execute(new Runnable() + { + public void run() + { + try + { + Thread.sleep(5000); + } + catch (InterruptedException e) + { + e.printStackTrace(); + } + reconnect(); + } + }); + } + } + + @Override + public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception + { + if (_stopping) + return; + Channel session = ctx.getChannel(); + Message msg = (Message) e.getMessage(); + if (msg.getCode() == Constants.WRAPPER_MSG_STOP) + try + { + /* + * bkowal + * Suppress extraneous output unless debug is enabled. + */ + if (_debug) + { + System.out.println("wrapper manager received stop command"); + } + _stopping = true; + if (session != null) + session.close(); + // Thread.sleep(100); + if (msg.getMessage() != null && msg.getMessage().length() > 0) + try + { + String[] txt = msg.getMessage().split(":"); + if (txt[0].length() > 0) + _exitCode = Integer.parseInt(txt[0]); + if (txt.length > 1 && txt[1].length() > 0) + _stopReason = txt[1]; + } + catch (Exception ex) + { + // DO NOTHING + } + if (!_externalStop) + System.exit(_exitCode); + } + catch (Exception ex) + { + ex.printStackTrace(); + } + else if (msg.getCode() == Constants.WRAPPER_MSG_OKKEY) + { + _session = session; + try + { + _myPid = Integer.parseInt(msg.getMessage()); + } + catch (Exception ex) + { + ex.printStackTrace(); + } + } + else if (msg.getCode() == Constants.WRAPPER_MSG_THREAD_DUMP) + { + threadDump(); + } + else if (msg.getCode() == Constants.WRAPPER_MSG_GC) + { + gc(); + } + else if (msg.getCode() == Constants.WRAPPER_MSG_DUMP_HEAP) + { + dumpHeap(msg.getMessage()); + } + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception + { + /* + * if (_debug) // log.log(Level.FINE, "exceptionCaught", + * e.getCause()); e.getCause().printStackTrace(); + */ + if (_debug) + { + Throwable th = e.getCause(); + if (th == null) + return; + String msg = th.getMessage(); + if (msg == null) + return; + System.err.println(msg); + if (!msg.toLowerCase().contains("connection refused")) + th.printStackTrace(System.err); + } + + } + + } + + /** + * Gets the port. + * + * @return the port + */ + int getPort() + { + return _port; + } + + /** + * Sets the port. + * + * @param port + * the new port + */ + public void setPort(int port) + { + _port = port; + } + + /** + * Checks if is debug. + * + * @return true, if is debug + */ + boolean isDebug() + { + return _debug; + } + + /** + * Sets the debug. + * + * @param debug + * the new debug + */ + void setDebug(boolean debug) + { + _debug = debug; + } + + /** + * Gets the key. + * + * @return the key + */ + String getKey() + { + return _key; + } + + /** + * Sets the key. + * + * @param key + * the new key + */ + public void setKey(String key) + { + _key = key; + } + + /** + * Checks if is started. + * + * @return true, if is started + */ + boolean isStarted() + { + return _started; + } + + /** + * Gets the ping interval. + * + * @return the ping interval + */ + int getPingInterval() + { + return _pingInterval; + } + + /** + * Sets the ping interval. + * + * @param pingInterval + * the new ping interval + */ + void setPingInterval(int pingInterval) + { + _pingInterval = pingInterval * 1000; + } + + /* + * (non-Javadoc) + * + * @see org.rzo.yajsw.WrapperManager#stop() + */ + public void stop() + { + if (_session != null) + while (_session != null && !_stopping) + { + _session.write(new Message(Constants.WRAPPER_MSG_STOP, null)); + try + { + Thread.sleep(500); + } + catch (InterruptedException e) + { + e.printStackTrace(); + } + } + else + System.exit(0); + + } + + /** + * Stop timer. + */ + public void stopTimer() + { + if (_session != null) + while (_session != null && !_stopping) + { + _session.write(new Message(Constants.WRAPPER_MSG_STOP_TIMER, null)); + try + { + Thread.sleep(500); + } + catch (InterruptedException e) + { + e.printStackTrace(); + } + } + } + + /** + * Restart. + */ + public void restart() + { + if (_session != null) + while (_session != null && !_stopping) + { + _session.write(new Message(Constants.WRAPPER_MSG_RESTART, null)); + try + { + Thread.sleep(100); + } + catch (InterruptedException e) + { + e.printStackTrace(); + } + } + else + System.out.println("not connected to wrapper -> cannot send restart command"); + } + + /** + * Instance. + * + * @return the wrapper manager impl + */ + public static WrapperManagerImpl instance() + { + return instance; + } + + /* + * (non-Javadoc) + * + * @see org.rzo.yajsw.WrapperManager#getPid() + */ + public int getPid() + { + return _myPid; + } + + public boolean isControlledByWrapper() + { + return _started; + } + + public boolean isLaunchedAsService() + { + return _config.getBoolean("wrapper.service", false); + } + + public String getGroovyScript() + { + return _groovyScript; + } + + public void threadDump() + { + System.out.println("yajsw: thread dump requested"); + threadDump(null); + } + + public void threadDump(long[] ids) + { + Message m = new Message(Constants.WRAPPER_MSG_THREAD_DUMP, null); + Action a = ActionFactory.getAction(m); + try + { + if (_overrideStdErr) + a.execute(m, _session, new PrintStream(_errStream), ids); + else + a.execute(m, _session, System.err, ids); + + } + catch (IOException e) + { + e.printStackTrace(); + } + } + + public void gc() + { + System.out.println("yajsw: gc requested"); + System.gc(); + } + + public void dumpHeap(final String fileName) + { + if (_dumpingHeap) + return; + System.out.println("yajsw: dumpHeap requested " + fileName); + _dumpingHeap = true; + executor.execute(new Runnable() + { + + public void run() + { + try + { + File file; + if (fileName == null || fileName.length() < 1) + file = new File("."); + else + file = new File(fileName); + File parent; + if (file.isDirectory()) + { + parent = file; + file = new File(parent, "dump" + "_" + new SimpleDateFormat("yyyy_MM_dd-hh_mm").format(new Date()) + ".hprof"); + } + else + parent = file.getParentFile(); + + if (!parent.exists()) + parent.mkdirs(); + + // com.sun.management.HotSpotDiagnosticMXBean mb = + // sun.management.ManagementFactory.getDiagnosticMXBean(); + com.sun.management.HotSpotDiagnosticMXBean mb = ManagementFactory.newPlatformMXBeanProxy(ManagementFactory + .getPlatformMBeanServer(), HOTSPOT_BEAN_NAME, HotSpotDiagnosticMXBean.class); + File dumpFile = new File(parent, file.getName()); + mb.dumpHeap(dumpFile.getAbsolutePath(), true); + System.out.println("yajsw: dumpHeap done " + dumpFile.getAbsolutePath()); + } + catch (Throwable ex) + { + ex.printStackTrace(); + } + finally + { + _dumpingHeap = false; + } + } + + }); + + } + + private static final String HOTSPOT_BEAN_NAME = "com.sun.management:type=HotSpotDiagnostic"; + + public static void main(String[] args) throws MalformedURLException + { + String name = "c:/test test/start.jar"; + System.out.println(new File(name).exists()); + System.out.println(new File(new File(name).toURI().getPath()).exists()); + WrapperManager wm = new WrapperManagerImpl(); + ((WrapperManagerImpl) wm).loadJar("c:/test test/start.jar"); + synchronized (wm) + { + wm.threadDump(); + } + } + + public boolean isAppearHanging() + { + return _appearHanging; + } + + public void setAppearHanging(boolean appearHanging) + { + _appearHanging = appearHanging; + } + + public void reportServiceStartup() + { + boolean reported = false; + while (!reported && !_stopping) + { + if (_session == null || !_session.isConnected()) + try + { + Thread.sleep(500); + } + catch (Exception ex) + { + + } + else + { + _session.write(new Message(Constants.WRAPPER_MSG_SERVICE_STARTUP, null)); + reported = true; + } + } + } + + private void executeShutdownScript() + { + if (shutdownScript != null & !"".equals(shutdownScript)) + { + Script script = ScriptFactory.createScript(shutdownScript, "wrapper.app.shutdown.script", null, new String[0], log, 0, _config + .getString("wrapper.script.encoding"), _config.getBoolean("wrapper.script.reload", false), _debug); + if (script != null) + script.execute(); + } + + } + + public void executeScript(String scriptFileName, ClassLoader wrapperClassLoader) + { + System.out.println("initializing script " + scriptFileName); + ClassLoader currentClassLoader = Thread.currentThread().getContextClassLoader(); + Thread.currentThread().setContextClassLoader(wrapperClassLoader); + try + { + Script script = ScriptFactory.createScript(scriptFileName, "", null, (String[]) getMainMethodArgs(), null, 0, _config + .getString("wrapper.script.encoding"), _config.getBoolean("wrapper.script.reload", false), _debug); + if (script != null) + script.execute(); + else + System.err.println("error opening script script: " + scriptFileName); + } + catch (Throwable ex) + { + System.err.println("error executing script: " + scriptFileName); + ex.printStackTrace(); + } + Thread.currentThread().setContextClassLoader(currentClassLoader); + + } + + public void signalStopping(int timeoutHint) + { + try + { + if (_session == null || !_session.isConnected()) + { + final ChannelFuture future1 = connector.connect(); + future1.await(); + future1.isSuccess(); + _session = future1.getChannel(); + } + ChannelFuture wFuture = _session.write(new Message(Constants.WRAPPER_MSG_STOP_PENDING, String.valueOf(timeoutHint))); + wFuture.await(); + } + catch (Exception e) + { + e.printStackTrace(); + } + finally + { + _session.close(); + _session = null; + } + + } + + public Properties getProperties() + { + if (_properties == null) + _properties = ConfigUtils.asProperties(_config); + return _properties; + } + + public String getStopReason() + { + return _stopReason; + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/app/WrapperManagerImplMBean.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/app/WrapperManagerImplMBean.java new file mode 100644 index 0000000000..8b1d128585 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/app/WrapperManagerImplMBean.java @@ -0,0 +1,27 @@ +package org.rzo.yajsw.app; + +public interface WrapperManagerImplMBean +{ + public void restart(); + + public void start(); + + public void stop(); + + public void stopTimer(); + + public void threadDump(); + + boolean isControlledByWrapper(); + + boolean isLaunchedAsService(); + + public boolean isAppearHanging(); + + public void setAppearHanging(boolean appearHanging); + + public void dumpHeap(String fileName); + + public String getStopReason(); + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/cache/Cache.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/cache/Cache.java new file mode 100644 index 0000000000..f1b988c20a --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/cache/Cache.java @@ -0,0 +1,171 @@ +package org.rzo.yajsw.cache; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; +import java.util.regex.Pattern; + +import org.apache.commons.configuration.Configuration; +import org.apache.commons.vfs2.AllFileSelector; +import org.apache.commons.vfs2.FileObject; +import org.apache.commons.vfs2.FileSelectInfo; +import org.apache.commons.vfs2.FileSelector; +import org.apache.commons.vfs2.FileSystemException; +import org.apache.commons.vfs2.FileType; +import org.apache.commons.vfs2.VFS; +import org.apache.commons.vfs2.impl.DefaultFileSystemManager; +import org.apache.commons.vfs2.provider.local.LocalFile; +import org.rzo.yajsw.Constants; +import org.rzo.yajsw.config.YajswConfigurationImpl; +import org.rzo.yajsw.util.VFSUtils; + +public class Cache +{ + boolean _loaded = false; + + public boolean load(YajswConfigurationImpl config) + { + try + { + // reload if not yet loaded, or wrapper.restart.reload_configuration + // is true + boolean reload = config.getBoolean("wrapper.restart.reload_configuration", Constants.DEFAULT_RELOAD_CONFIGURATION); + if (!reload && _loaded) + return true; + String workingDir = config.getString("wrapper.working.dir", "."); + String base = config.getString("wrapper.base", workingDir); + String cache = config.getCache(); + + DefaultFileSystemManager fsManager = (DefaultFileSystemManager) VFS.getManager(); + + FileObject basef = VFSUtils.resolveFile(".", base); + FileObject cachef = VFSUtils.resolveFile(".", cache); + if (!(cachef instanceof LocalFile)) + { + System.out.println("cache must be a local folder -> abort"); + return false; + } + if (!cachef.exists()) + cachef.createFolder(); + if (cachef.getType() != FileType.FOLDER) + { + System.out.println("cache must be a folder -> abort"); + return false; + } + boolean cacheLocal = config.getBoolean("wrapper.cache.local", Constants.DFAULT_CACHE_LOCAL); + + Configuration resources = config.subset("wrapper.resource"); + for (Iterator it = resources.getKeys(); it.hasNext();) + { + String key = (String) it.next(); + loadFiles("wrapper.resource." + key, config, basef, cachef, cacheLocal, fsManager); + } + + Configuration classpath = config.subset("wrapper.java.classpath"); + for (Iterator it = classpath.getKeys(); it.hasNext();) + { + String key = (String) it.next(); + loadFiles("wrapper.java.classpath." + key, config, basef, cachef, cacheLocal, fsManager); + } + + _loaded = true; + return true; + } + catch (Exception ex) + { + ex.printStackTrace(); + } + return false; + } + + private void loadFiles(String key, YajswConfigurationImpl config, FileObject basef, FileObject cachef, boolean cacheLocal, + DefaultFileSystemManager fsManager) + { + try + { + String value = config.getString(key); + List files = VFSUtils.resolveFiles(basef, value, fsManager); + int count = 0; + for (Iterator it = files.iterator(); it.hasNext();) + { + FileObject source = (FileObject) it.next(); + FileObject destination = loadFile(value, source, basef, cachef, cacheLocal, fsManager); + if (destination != null) + { + config.getFileConfiguration().setProperty(key + "$" + count++, destination.getURL().getFile().substring(2)); + // System.out.println("set configuration "+key+" "+destination.getURL().getFile()); + } + } + } + catch (Exception ex) + { + ex.printStackTrace(); + } + } + + + private FileObject loadFile(String value, FileObject source, FileObject basef, FileObject cachef, boolean cacheLocal, + DefaultFileSystemManager fsManager) + { + try + { + boolean isLocal = source instanceof LocalFile; + if (isLocal && !cacheLocal) + return null; + FileObject destination = null; + boolean absolute = false; + // assume value is absolute + try + { + destination = VFSUtils.resolveFile((String) null, value); + absolute = true; + } + catch (Exception ex) + { + } + if (!absolute) + destination = VFSUtils.resolveFile(cachef, basef.getName().getRelativeName(source.getName())); + else + { + String fileName = destination.getName().getBaseName(); + destination = VFSUtils.resolveFile(cachef, fileName); + } + if (fileChanged(source, destination)) + { + destination.copyFrom(source, new AllFileSelector()); + destination.getContent().setLastModifiedTime(source.getContent().getLastModifiedTime()); + System.out.println("file loaded " + source.getName() + " -> " + destination.getName()); + } + return destination; + } + catch (Exception ex) + { + ex.printStackTrace(); + } + return null; + } + + private boolean fileChanged(FileObject source, FileObject destination) + { + try + { + return !destination.exists() || source.getContent().getLastModifiedTime() != destination.getContent().getLastModifiedTime(); + } + catch (FileSystemException e) + { + e.printStackTrace(); + return true; + } + } + + public static void main(String[] args) + { + Cache c = new Cache(); + System.setProperty("wrapper.config", "http://localhost:8080/wrapper.helloworld.conf"); + YajswConfigurationImpl config = new YajswConfigurationImpl(); + c.load(config); + + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/condition/Condition.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/condition/Condition.java new file mode 100644 index 0000000000..83643a0f8a --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/condition/Condition.java @@ -0,0 +1,119 @@ +package org.rzo.yajsw.condition; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Timer; +import java.util.TimerTask; + +import org.jboss.netty.logging.InternalLogger; +import org.rzo.yajsw.config.YajswConfigurationImpl; +import org.rzo.yajsw.script.Script; +import org.rzo.yajsw.script.ScriptFactory; +import org.rzo.yajsw.wrapper.WrappedProcess; + +public class Condition +{ + Script _script; + + /** The _config. */ + YajswConfigurationImpl _config; + + /** The _wp. */ + WrappedProcess _wp; + + /** The _has trigger. */ + boolean _hasTrigger = false; + + /** The _triggered. */ + boolean _triggered = false; + + long _period = -1; + + static Timer _timer = new Timer("yajsw.condition"); + + InternalLogger _log; + + boolean _debug = false; + + public Condition(YajswConfigurationImpl config, WrappedProcess wrappedProcess, InternalLogger log) + { + _config = config; + _wp = wrappedProcess; + _log = log; + } + + public void init() + { + String fileName = _config.getString("wrapper.condition.script"); + if (fileName == null) + return; + File f = new File(fileName); + if (!f.exists() || !f.isFile()) + { + try + { + System.out.println("file not found -> ignoring condition script " + f.getCanonicalPath()); + } + catch (IOException e) + { + e.printStackTrace(); + } + } + _debug = _config.getBoolean("wrapper.debug", false); + List args = _config.getList("wrapper.condition.script.args", new ArrayList()); + String[] argsArr = new String[args.size()]; + for (int i = 0; i < argsArr.length; i++) + argsArr[i] = args.get(i).toString(); + _script = ScriptFactory.createScript(fileName, "condition", _wp, argsArr, _log, 0, _config.getString("wrapper.script.encoding"), _config.getBoolean("wrapper.script.reload", false), _debug); + _hasTrigger = _script != null; + _period = _config.getLong("wrapper.condition.cycle", -1) * 1000; + } + + public void stop() + { + _timer.cancel(); + _triggered = false; + } + + public boolean isHasTrigger() + { + return _hasTrigger; + } + + public boolean isTriggered() + { + return _triggered; + } + + public void start() + { + _triggered = true; + if (_period > 0) + _timer.schedule(new TimerTask() + { + + @Override + public void run() + { + _script.execute(); + } + + }, new Date(), _period); + else + _timer.schedule(new TimerTask() + { + + @Override + public void run() + { + _script.execute(); + } + + }, new Date()); + + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/config/ConfigUtils.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/config/ConfigUtils.java new file mode 100644 index 0000000000..39ddba6cb1 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/config/ConfigUtils.java @@ -0,0 +1,23 @@ +package org.rzo.yajsw.config; + +import java.util.Iterator; +import java.util.Properties; + +import org.apache.commons.configuration.Configuration; + + +public class ConfigUtils +{ + + public static Properties asProperties(Configuration config) + { + Properties result = new Properties(); + for (Iterator it = config.getKeys(); it.hasNext(); ) + { + String key = (String) it.next(); + result.setProperty(key, config.getProperty(key).toString()); + } + return result; + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/config/YajswConfiguration.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/config/YajswConfiguration.java new file mode 100644 index 0000000000..5ef5c94b92 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/config/YajswConfiguration.java @@ -0,0 +1,47 @@ +/* This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + *

    + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ +package org.rzo.yajsw.config; + +// TODO: Auto-generated Javadoc +/** + * The Interface AjswConfiguration. + */ +public interface YajswConfiguration +{ + + /** + * Sets the debug. + * + * @param b + * the new debug + */ + void setDebug(boolean b); + + /** + * Inits the. + */ + void init(); + + /** + * Gets the string. + * + * @param string + * the string + * + * @return the string + */ + String getString(String string); + + String getString(String string, String string1); + + public String getCachedPath(); + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/config/YajswConfigurationImpl.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/config/YajswConfigurationImpl.java new file mode 100644 index 0000000000..7903fa8271 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/config/YajswConfigurationImpl.java @@ -0,0 +1,616 @@ +/* This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + *

    + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ +package org.rzo.yajsw.config; + +import java.io.File; +import java.io.IOException; +import java.net.URISyntaxException; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Properties; +import java.util.Set; + +import org.apache.commons.configuration.CompositeConfiguration; +import org.apache.commons.configuration.Configuration; +import org.apache.commons.configuration.ConfigurationBinding; +import org.apache.commons.configuration.ConfigurationConverter; +import org.apache.commons.configuration.ConfigurationException; +import org.apache.commons.configuration.FileOptionsProvider; +import org.apache.commons.configuration.FileSystem; +import org.apache.commons.configuration.GInterpolator; +import org.apache.commons.configuration.Interpolator; +import org.apache.commons.configuration.MapConfiguration; +import org.apache.commons.configuration.PropertiesConfiguration; +import org.apache.commons.configuration.VFSFileSystem; +import org.apache.commons.vfs2.FileObject; +import org.apache.commons.vfs2.FileSystemException; +import org.apache.commons.vfs2.VFS; +import org.apache.commons.vfs2.impl.DefaultFileSystemManager; +import org.apache.commons.vfs2.provider.local.LocalFile; +import org.jboss.netty.logging.InternalLogger; +import org.jboss.netty.logging.InternalLoggerFactory; +import org.rzo.yajsw.config.jnlp.JnlpSupport; +import org.rzo.yajsw.os.OperatingSystem; +import org.rzo.yajsw.script.GroovyScript; +import org.rzo.yajsw.util.CaseInsensitiveMap; +import org.rzo.yajsw.util.CommonsLoggingAdapter; +import org.rzo.yajsw.util.VFSUtils; + +import com.sun.jna.Platform; + +// TODO: Auto-generated Javadoc +/** + * The Class AjswConfigurationImpl. + */ +public class YajswConfigurationImpl extends CompositeConfiguration implements YajswConfiguration +{ + + /** The log. */ + InternalLogger log = InternalLoggerFactory.getInstance(this.getClass().getName()); + + /** The _system properties. */ + Configuration _systemProperties; + + /** The _local configuration. */ + Configuration _localConfiguration; + + /** The _system configuration. */ + CompositeConfiguration _systemConfiguration = new CompositeConfiguration(); + + /** The debug. */ + boolean debug = false; + + /** The _use system properties. */ + boolean _useSystemProperties = true; + + boolean _isStopper = false; + + boolean _init = false; + + PropertiesConfiguration _fileConfiguration = null; + + Interpolator _interpolator; + + Set _interpolated = new HashSet(); + + Map _scriptUtils = null; + + boolean _isJavaDebug = false; + + /** + * Instantiates a new ajsw configuration impl. + */ + public YajswConfigurationImpl() + { + init(); + } + + /** + * Instantiates a new ajsw configuration impl. + * + * @param debug + * the debug + */ + public YajswConfigurationImpl(boolean debug) + { + setDebug(debug); + init(); + } + + public YajswConfigurationImpl(Configuration localConfiguration, boolean useSystemProperties) + { + this(localConfiguration, useSystemProperties, null); + } + + /** + * Instantiates a new ajsw configuration impl. + * + * @param localConfiguration + * the local configuration + * @param useSystemProperties + * the use system properties + */ + public YajswConfigurationImpl(Configuration localConfiguration, boolean useSystemProperties, Map scriptUtils) + { + _localConfiguration = localConfiguration; + _useSystemProperties = useSystemProperties; + _scriptUtils = scriptUtils; + init(); + } + + public Interpolator getYajswInterpolator() + { + return _interpolator; + } + + + /* + * (non-Javadoc) + * + * @see org.rzo.yajsw.AjswConfiguration#init() + */ + @SuppressWarnings({ "rawtypes", "unchecked" }) + public void init() + { + if (_init) + return; + /* + * ConfigurationInterpolator in = (ConfigurationInterpolator) + * getSubstitutor().getVariableResolver(); StrLookup orgLookup = + * in.getDefaultLookup(); in.setDefaultLookup(new + * MyStrLookup(orgLookup)); + */ + if (_scriptUtils == null) + _scriptUtils = new HashMap(); + _interpolator = new GInterpolator(this, true, null, _scriptUtils); + try + { + this.setInterpolator(_interpolator); + } + catch (Exception e1) + { + e1.printStackTrace(); + } + + if (_localConfiguration != null) + { + _systemConfiguration.addConfiguration(_localConfiguration); + if (debug) + log.debug("added local configuration "); + } + // order of adding configurations to composite is important + // first added hides the others + + // load configuration from System Properties + if (_useSystemProperties) + { + _systemProperties = ConfigurationConverter.getConfiguration((Properties) System.getProperties().clone()); + _systemConfiguration.addConfiguration(_systemProperties); + if (debug) + log.debug("added system configuration "); + } + //_systemConfiguration.addConfiguration(new EnvironmentConfiguration()); + _systemConfiguration.addConfiguration(new MapConfiguration(!OperatingSystem.instance().isPosix() ? new CaseInsensitiveMap(System.getenv()) : new HashMap(System.getenv()))); + + + addConfiguration(_systemConfiguration); + // check if we have config file + String configFile = (String) getProperty("wrapper.config"); + if (configFile != null && configFile.contains("\"")) + configFile = configFile.replaceAll("\"", ""); + + // load configuration from file + if (configFile == null) + { + if (debug) + log.warn("configuration file not set"); + } + else if (!fileExists(configFile)) + log.error("configuration file not found: " + configFile); + else + { + // check if we have a jnlp file + if (configFile.endsWith(".jnlp")) + try + { + JnlpSupport jnlp = new JnlpSupport(configFile); + _fileConfiguration = jnlp.toConfiguration((String) getProperty("wrapperx.default.config")); + _fileConfiguration.setFileName(configFile); + addConfiguration(_fileConfiguration); + } + catch (Exception ex) + { + ex.printStackTrace(); + return; + } + // else try a standard configuration + if (_fileConfiguration == null) + try + { + // enable VFS + FileSystem fs = new VFSFileSystem(); + fs.setLogger(new CommonsLoggingAdapter(log)); + fs.setFileOptionsProvider(new FileOptionsProvider() + { + + public Map getOptions() + { + Map result = new HashMap(); + String httpProxy = System.getProperty("http.proxyHost"); + String httpPort = System.getProperty("http.proxyPort"); + if (httpProxy != null) + { + int port = 8080; + if (httpPort != null) + try + { + port = Integer.parseInt(httpPort); + } + catch (Exception ex) + { + ex.printStackTrace(); + } + result.put(FileOptionsProvider.PROXY_HOST, httpProxy); + result.put(FileOptionsProvider.PROXY_PORT, port); + } + return result; + + } + + }); + FileSystem.setDefaultFileSystem(fs); + // allow for conditional incldues -> first createn an empty + // properties conf + _fileConfiguration = new PropertiesConfiguration(); + // then set the file name and load it + _fileConfiguration.setFileName(configFile); + /* + try + { + _fileConfiguration.setBasePath(new File(".").getCanonicalPath()); + } + catch (IOException e) + { + // TODO Auto-generated catch block + e.printStackTrace(); + } + */ + + _fileConfiguration.append(_systemConfiguration); + if (_interpolator != null) + try + { + _fileConfiguration.setInterpolator(new GInterpolator(_fileConfiguration, true, null, _scriptUtils)); + } + catch (Exception e) + { + // TODO Auto-generated catch block + e.printStackTrace(); + } + //System.out.println("platform "+_systemConfiguration.getString("platform")); + _fileConfiguration.load(); + String encoding = _fileConfiguration.getString("wrapper.conf.encoding"); + // if we have an encoding: reload the file with the given encoding. + if (encoding != null) + { + _fileConfiguration = new PropertiesConfiguration(); + _fileConfiguration.setEncoding(encoding); + // then set the file name and load it + _fileConfiguration.setFileName(configFile); + /* + try + { + _fileConfiguration.setBasePath(new File(".").getCanonicalPath()); + } + catch (IOException e) + { + // TODO Auto-generated catch block + e.printStackTrace(); + } + */ + _fileConfiguration.append(_systemConfiguration); + if (_interpolator != null) + try + { + _fileConfiguration.setInterpolator(new GInterpolator(_fileConfiguration, true, null, _scriptUtils)); + } + catch (Exception e) + { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + _fileConfiguration.load(); + } + + addConfiguration(_fileConfiguration); + } + catch (ConfigurationException e) + { + log.error("error loading configuration file AsjwConfiguration", e); + } + if (!isLocalFile()) + { + // if no working dir is defined: set working dir to the cache + if (_fileConfiguration.getProperty("wrapper.working.dir") == null) + try + { + _fileConfiguration.setProperty("wrapper.working.dir", new File(getCache()).getCanonicalPath().replaceAll("\\\\", "/")); + } + catch (IOException e) + { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + // if no cache path is defined in the file configuration then + // set it, so it can be accessed by the wrapper for example to + // get + // a system tray icon + if (_fileConfiguration.containsKey("wrapper.cache")) + _fileConfiguration.setProperty("wrapper.cache", getCache()); + } + + } + + // load configuration from System Environement + //addConfiguration(getConfiguration(System.getenv())); + //_systemConfiguration.addConfiguration(new EnvironmentConfiguration()); + _isStopper = this.getBoolean("wrapper.stopper", false); + try + { + _isJavaDebug = this.getInt("wrapper.java.debug.port", -1) != -1; + } + catch (Exception ex) + { + ex.printStackTrace(); + } + + for (Iterator it = getKeys("wrapper.config.script"); it.hasNext(); ) + try + { + String key = (String) it.next(); + String script = getString(key); + String bind = key.substring(key.lastIndexOf(".")+1); + _scriptUtils.put(bind, new GroovyScript(script, "", null, null, 0, log, null, false)); + } + catch (Exception e) + { + log.error("error reading script", e); + } + + + _init = true; + } + + private boolean fileExists(String file) + { + try + { + // this hack is no longer required, changed VFS to init without providers.xm. + //String current = System.getProperty("javax.xml.parsers.DocumentBuilderFactory"); + //System.setProperty("javax.xml.parsers.DocumentBuilderFactory", "com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl"); + DefaultFileSystemManager fsManager = (DefaultFileSystemManager) VFS.getManager(); + //if (current != null) + // System.setProperty("javax.xml.parsers.DocumentBuilderFactory", current); + //else + // System.clearProperty("javax.xml.parsers.DocumentBuilderFactory"); + FileObject f = VFSUtils.resolveFile(".", file); + return f.exists(); + } + catch (FileSystemException e) + { + e.printStackTrace(); + return false; + } + } + + /** + * Checks if is debug. + * + * @return true, if is debug + */ + boolean isDebug() + { + return debug; + } + + /* + * (non-Javadoc) + * + * @see org.rzo.yajsw.AjswConfiguration#setDebug(boolean) + */ + public void setDebug(boolean debug) + { + this.debug = debug; + } + + protected Object resolveContainerStore(String key) + { + Object result = null; + if (key == null) + result = null; + if (_isJavaDebug) + { + if (key.equals("wrapper.startup.timeout")) + result = Integer.MAX_VALUE / 1000; + else if (key.equals("wrapper.shutdown.timeout")) + result = (Integer.MAX_VALUE / 1000); + else if (key.equals("wrapper.ping.timeout")) + result = (Integer.MAX_VALUE / 1000); + } + if (result != null) + return result; + if (!_isStopper) + result = super.resolveContainerStore(key); + else if (key.startsWith("wrapper.on_exit")) + return null; + else if (key.startsWith("wrapper.exit_on_main_terminate")) + result = "0"; + else if (key.startsWith("wrapper.daemon")) + result = null; + else if (key.contains(".script")) + result = null; + else if (key.contains(".filter")) + result = null; + else if (key.contains(".pidfile")) + result = null; + // allow stopper to have its own logging + // else if (key.contains(".log")) + // return null; + else if (key.contains(".ntservice")) + result = null; + else if (key.contains(".jmx")) + result = null; + else if (key.contains(".lockfile")) + result = null; + else if (key.contains(".stop.conf")) + result = null; + else if (key.equals("wrapper.tray")) + result = null; + else + result = super.resolveContainerStore(key); + + if (_interpolator != null && result != null && !result.equals(_interpolator.interpolate(result))) + _interpolated.add(key); + return result; + } + + public Set getLookupSet() + { + return _interpolated; + } + + public Map getEnvLookupSet() + { + if (_interpolator != null) + return ((ConfigurationBinding)((GInterpolator)_interpolator).getBinding()).getUsedEnvVars(); + return new HashMap(); + } + + public static void main(String[] args) + { + YajswConfigurationImpl c = new YajswConfigurationImpl(); + c.setProperty("t1", "x"); + c.setProperty("t2", "${t1}"); + System.out.println(c.getString("t2")); + for (Iterator it = c.getInterpolator().prefixSet().iterator(); it.hasNext();) + System.out.println(it.next()); + } + + + public CompositeConfiguration getSystemConfiguration() + { + return _systemConfiguration; + } + + public void reload() + { + if (_fileConfiguration != null) + _fileConfiguration.reload(); + } + + public boolean isLocalFile() + { + if (_fileConfiguration == null) + return true; + try + { + String name = _fileConfiguration.getFileName(); + if (name.endsWith(".jnlp")) + return false; + + FileObject f = VFSUtils.resolveFile(".", name); + return f instanceof LocalFile; + } + catch (Exception ex) + { + ex.printStackTrace(); + return true; + } + + } + + public String getCache() + { + return getString("wrapper.cache", "yajsw_cache"); + } + + public String getCachedPath() + { + return getCachedPath(true); + } + + public String getCachedPath(boolean save) + { + if (_fileConfiguration == null) + return null; + if (isLocalFile()) + try + { + return new File(_fileConfiguration.getURL().toURI()).getCanonicalPath(); + } + catch (IOException e) + { + e.printStackTrace(); + return _fileConfiguration.getFileName(); + } + catch (URISyntaxException e) + { + // TODO Auto-generated catch block + e.printStackTrace(); + } + try + { + String cache = getCache() + "/conf"; + String fileName = _fileConfiguration.getFileSystem().getFileName(_fileConfiguration.getFileName()); + File cf = new File(cache); + if (!cf.exists()) + cf.mkdirs(); + if (fileName.endsWith(".jnlp")) + fileName = fileName + ".conf"; + // configuration file in the cache + File cn = new File(cf, fileName); + + if (save) + { + // interpolate the file so that no includes are required + PropertiesConfiguration c2 = (PropertiesConfiguration) _fileConfiguration.interpolatedConfiguration(); + + // save the file + c2.save(cn); + } + return cn.getCanonicalPath(); + + } + catch (Exception ex) + { + ex.printStackTrace(); + return _fileConfiguration.getFileName(); + } + + } + + public Configuration getFileConfiguration() + { + // TODO Auto-generated method stub + return _fileConfiguration; + } + + public String getString(String key) + { + String value = super.getString(key); + /* changed upon request, but should we really do this ? cross platform Portability of configurations ? + if (value != null && + !key.contains("account") && + !key.contains("wrapper.image") && + !key.contains("wrapper.app.env") && + !key.contains("wrapper.java.monitor.gc")) + value = value.replaceAll("\\\\", "/"); + */ + return value; + } + + public long getConfigFileTime() + { + // TODO Auto-generated method stub + return -1; + } + + public boolean isStopper() + { + return _isStopper; + } + + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/config/jnlp/JnlpSupport.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/config/jnlp/JnlpSupport.java new file mode 100644 index 0000000000..10fbb6fb1d --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/config/jnlp/JnlpSupport.java @@ -0,0 +1,169 @@ +package org.rzo.yajsw.config.jnlp; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; + +import org.apache.commons.configuration.ConfigurationException; +import org.apache.commons.configuration.PropertiesConfiguration; +import org.apache.commons.vfs2.FileObject; +import org.rzo.yajsw.util.VFSUtils; +import org.w3c.dom.Document; +import org.w3c.dom.NodeList; +import org.xml.sax.SAXException; + +public class JnlpSupport +{ + Document _doc = null; + + public JnlpSupport(String file) throws ParserConfigurationException, SAXException, IOException + { + FileObject fo = VFSUtils.resolveFile(".", file); + if (fo == null || !fo.exists()) + throw new FileNotFoundException(file); + InputStream in = fo.getContent().getInputStream(); + _doc = parseJnlp(in); + in.close(); + } + + public PropertiesConfiguration toConfiguration(String defaultsFile) throws ConfigurationException, IOException + { + int i = 1; + PropertiesConfiguration jnlpConf = new PropertiesConfiguration(); + + List jars = getJars(_doc); + for (Iterator it = jars.listIterator(); it.hasNext();) + { + jnlpConf.setProperty("wrapper.java.classpath." + i++, it.next()); + } + + jnlpConf.setProperty("wrapper.base", getCodebase(_doc)); + + jnlpConf.setProperty("wrapper.java.app.mainclass", getMainClass(_doc)); + + i = 1; + for (Iterator it = getArguments(_doc).listIterator(); it.hasNext();) + { + jnlpConf.setProperty("wrapper.app.parameter." + i++, it.next()); + } + i = 1; + List props = getResourceProperties(_doc); + for (Iterator it = props.listIterator(); it.hasNext();) + { + jnlpConf.setProperty("wrapper.java.additional." + i++, it.next()); + } + + i = 1; + List resources = getResources(_doc); + for (Iterator it = resources.listIterator(); it.hasNext();) + { + jnlpConf.setProperty("wrapper.resource." + i++, it.next()); + } + + if (defaultsFile == null || "".equals(defaultsFile)) + return jnlpConf; + + // jnlpConf.addProperty("include", defaultsFile); + + if (defaultsFile != null) + { + PropertiesConfiguration defaultsConf = new PropertiesConfiguration(); + FileObject fo = VFSUtils.resolveFile(".", defaultsFile); + InputStream in = fo.getContent().getInputStream(); + defaultsConf.load(in); + in.close(); + for (Iterator it = defaultsConf.getKeys(); it.hasNext();) + { + String key = (String) it.next(); + if (jnlpConf.containsKey(key)) + System.out.println("configuration conflict: " + key); + else + jnlpConf.addProperty(key, defaultsConf.getProperty(key)); + } + } + + return jnlpConf; + + } + + private Document parseJnlp(InputStream in) throws ParserConfigurationException, SAXException, IOException + { + Document doc = null; + // Get Document Builder Factory + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + + // Turn off validation, and turn off namespaces + factory.setValidating(false); + factory.setNamespaceAware(false); + + DocumentBuilder builder = factory.newDocumentBuilder(); + + doc = builder.parse(in); + + return doc; + } + + private static List getJars(Document doc) + { + ArrayList result = new ArrayList(); + NodeList args = doc.getElementsByTagName("jar"); + for (int i = 0; i < args.getLength(); i++) + { + result.add(args.item(i).getAttributes().getNamedItem("href").getTextContent()); + } + return result; + } + + private static List getResources(Document doc) + { + ArrayList result = new ArrayList(); + NodeList args = doc.getElementsByTagName("icon"); + for (int i = 0; i < args.getLength(); i++) + { + result.add(args.item(i).getAttributes().getNamedItem("href").getTextContent()); + } + return result; + } + + private static List getResourceProperties(Document doc) + { + ArrayList result = new ArrayList(); + NodeList args = doc.getElementsByTagName("property"); + for (int i = 0; i < args.getLength(); i++) + { + String key = args.item(i).getAttributes().getNamedItem("name").getTextContent(); + String value = args.item(i).getAttributes().getNamedItem("value").getTextContent(); + result.add("-D" + key + "=" + value); + } + return result; + } + + private static List getArguments(Document doc) + { + ArrayList result = new ArrayList(); + NodeList args = doc.getElementsByTagName("argument"); + for (int i = 0; i < args.getLength(); i++) + { + result.add(args.item(i).getTextContent()); + } + return result; + } + + private static Object getMainClass(Document doc) + { + return doc.getElementsByTagName("application-desc").item(0).getAttributes().getNamedItem("main-class").getTextContent(); + } + + private static String getCodebase(Document doc) + { + return doc.getElementsByTagName("jnlp").item(0).getAttributes().getNamedItem("codebase").getTextContent(); + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/controller/AbstractController.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/controller/AbstractController.java new file mode 100644 index 0000000000..d2eda3b98c --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/controller/AbstractController.java @@ -0,0 +1,154 @@ +package org.rzo.yajsw.controller; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; +import java.util.logging.Logger; + +import org.apache.commons.collections.map.MultiValueMap; +import org.rzo.yajsw.Constants; +import org.rzo.yajsw.controller.jvm.Controller; +import org.rzo.yajsw.util.DaemonThreadFactory; +import org.rzo.yajsw.wrapper.WrappedProcess; + +public abstract class AbstractController implements Constants, Controller +{ + Logger _logger = Logger.getLogger(getClass().getName()); + /** The _listeners. */ + protected Map _listeners = Collections.synchronizedMap(MultiValueMap.decorate(new HashMap(), HashSet.class)); + /** The _wrapped java process. */ + public WrappedProcess _wrappedProcess; + protected static final Executor executor = Executors.newCachedThreadPool(new DaemonThreadFactory("controller")); + /** The _state. */ + public int _state = 0; + /** The _debug. */ + protected boolean _debug = false; + + /** + * The listener interface for receiving controller events. The class that is + * interested in processing a controller event implements this interface, + * and the object created with that class is registered with a component + * using the component's addControllerListener method. When + * the controller event occurs, that object's appropriate + * method is invoked. + * + * @see ControllerEvent + */ + public interface ControllerListener + { + + /** + * Fire. + */ + public void fire(); + } + + public AbstractController(WrappedProcess wrappedProcess) + { + _wrappedProcess = wrappedProcess; + } + + public void setDebug(boolean debug) + { + _debug = debug; + } + + public void setLogger(Logger logger) + { + _logger = logger; + } + + public Logger getLog() + { + return _logger; + } + + abstract public boolean start(); + + /** + * Adds the listener. + * + * @param state + * the state + * @param listener + * the listener + */ + public void addListener(int state, ControllerListener listener) + { + _listeners.put(state, listener); + } + + /** + * Handle listeners. + * + * @param state + * the state + */ + protected void handleListeners(int state) + { + // synchronized(_listeners) + { + if (_listeners != null) + { + Collection listeners = (Collection)_listeners.get(state); + if (listeners == null) + return; + listeners = new ArrayList(listeners); + //synchronized (_listeners) + { + for (ControllerListener listener : listeners) + try + { + listener.fire(); + } + catch (Exception ex) + { + ex.printStackTrace(); + } + } + } + } + } + + /** + * Gets the state. + * + * @return the state + */ + public int getState() + { + return _state; + } + + /** + * Sets the state. + * + * @param state + * the new state + */ + public void setState(int state) + { + synchronized (this) + { + if (_state == state) + return; + if (_debug) + _logger.info("Controller State: " + stateAsStr(_state) + " -> " + stateAsStr(state)); + _state = state; + } + logStateChange(state); + handleListeners(state); + } + + public abstract void logStateChange(int state); + + public abstract String stateAsStr(int state); + + + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/controller/Message.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/controller/Message.java new file mode 100644 index 0000000000..49b0257510 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/controller/Message.java @@ -0,0 +1,140 @@ +/* This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + *

    + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ +package org.rzo.yajsw.controller; + +import org.rzo.yajsw.Constants; + +// TODO: Auto-generated Javadoc +/** + * The Class Message. + */ +public class Message implements Constants +{ + + /** The _code. */ + private byte _code; + + /** The _message. */ + private String _message = ""; + + /** + * Instantiates a new message. + * + * @param code + * the code + * @param message + * the message + */ + public Message(byte code, String message) + { + _code = code; + if (message != null) + _message = message; + } + + /** + * Gets the code. + * + * @return the code + */ + public byte getCode() + { + return _code; + } + + /** + * Gets the message. + * + * @return the message + */ + public String getMessage() + { + return _message; + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#toString() + */ + @Override + public String toString() + { + return "Message:" + codeToString(_code) + ":" + _message; + } + + /** + * Code to string. + * + * @param code + * the code + * + * @return the string + */ + public static String codeToString(byte code) + { + switch (code) + { + case WRAPPER_MSG_START: + return "START"; + + case WRAPPER_MSG_STOP: + return "STOP"; + + case WRAPPER_MSG_RESTART: + return "RESTART"; + + case WRAPPER_MSG_PING: + return "PING"; + + case WRAPPER_MSG_STOP_PENDING: + return "STOP_PENDING"; + + case WRAPPER_MSG_START_PENDING: + return "START_PENDING"; + + case WRAPPER_MSG_STARTED: + return "STARTED"; + + case WRAPPER_MSG_STOPPED: + return "STOPPED"; + + case WRAPPER_MSG_KEY: + return "KEY"; + + case WRAPPER_MSG_BADKEY: + return "BADKEY"; + + case WRAPPER_MSG_LOW_LOG_LEVEL: + return "LOW_LOG_LEVEL"; + + case WRAPPER_MSG_PING_TIMEOUT: + return "PING_TIMEOUT"; + + case WRAPPER_MSG_SERVICE_CONTROL_CODE: + return "SERVICE_CONTROL_CODE"; + + case WRAPPER_MSG_PROPERTIES: + return "PROPERTIES"; + + case WRAPPER_MSG_OKKEY: + return "OKKEY"; + + case WRAPPER_MSG_THREAD_DUMP: + return "THREAD_DUMP"; + + default: + return "UNKNOWN(" + code + ")"; + + } + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/controller/jvm/Controller.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/controller/jvm/Controller.java new file mode 100644 index 0000000000..3882b042a5 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/controller/jvm/Controller.java @@ -0,0 +1,28 @@ +package org.rzo.yajsw.controller.jvm; + +import java.util.logging.Logger; + +import org.rzo.yajsw.controller.AbstractController.ControllerListener; + +public interface Controller +{ + + void setDebug(boolean debug); + + void setLogger(Logger wrapperLogger); + + boolean start(); + + void stop(int state, String reason); + + void addListener(int stateStopped, ControllerListener listenerStopped); + + void reset(); + + void processStarted(); + + void processFailed(); + + void beginWaitForStartup(); + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/controller/jvm/ControllerHandler.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/controller/jvm/ControllerHandler.java new file mode 100644 index 0000000000..31532e1c52 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/controller/jvm/ControllerHandler.java @@ -0,0 +1,155 @@ +package org.rzo.yajsw.controller.jvm; + +import org.jboss.netty.channel.ChannelHandlerContext; +import org.jboss.netty.channel.ChannelPipelineCoverage; +import org.jboss.netty.channel.ChannelStateEvent; +import org.jboss.netty.channel.ExceptionEvent; +import org.jboss.netty.channel.MessageEvent; +import org.jboss.netty.channel.SimpleChannelUpstreamHandler; +import org.rzo.yajsw.Constants; +import org.rzo.yajsw.controller.Message; + +@ChannelPipelineCoverage("one") +public class ControllerHandler extends SimpleChannelUpstreamHandler implements Constants +{ + + JVMController _controller; + + ControllerHandler(JVMController controller) + { + _controller = controller; + } + + @Override + public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception + { + Message message = (Message) e.getMessage(); + switch (message.getCode()) + { + case WRAPPER_MSG_KEY: + // check if JVM sent us correct key + if (_controller._key.equals(message.getMessage())) + { + // we set the channel not in channelConnected, + _controller._channel = ctx.getChannel(); + _controller.setState(JVMController.STATE_LOGGED_ON); + _controller.startupOK(); + ctx.getChannel().write(new Message(Constants.WRAPPER_MSG_OKKEY, "" + _controller._wrappedProcess.getAppPid())); + if (_controller.isDebug()) + _controller.getLog().info("Correct key"); + } + // if not: announce it and close session + else + { + if (_controller.isDebug()) + _controller.getLog().info("Wrong key -> closing session"); + ctx.getChannel().write(new Message(Constants.WRAPPER_MSG_BADKEY, null)); + ctx.getChannel().close(); + } + break; + case Constants.WRAPPER_MSG_STOP: + if (_controller._wrappedProcess != null) + _controller._wrappedProcess.stop("APPLICATION"); + break; + + case Constants.WRAPPER_MSG_STOP_TIMER: + if (_controller._wrappedProcess != null) + _controller._wrappedProcess.stopTimer(); + break; + + case Constants.WRAPPER_MSG_RESTART: + if (_controller._wrappedProcess != null) + _controller._wrappedProcess.restartInternal(); + + break; + + case Constants.WRAPPER_MSG_PING: + _controller.pingReceived(); + String msg = message.getMessage(); + if (msg != null) + { + String[] values = msg.split(";"); + if (values.length == 4) + try { + float heap = Float.parseFloat(values[0]); + long minGC = Long.parseLong(values[1]); + long fullGC = Long.parseLong(values[2]); + long heapInBytes = Long.parseLong(values[3]); + _controller.setHeap(heap, minGC, fullGC, heapInBytes); + } + catch (Exception ex) + { + ex.printStackTrace(); + } + } + break; + + case Constants.WRAPPER_MSG_SERVICE_STARTUP: + _controller.serviceStartup(); + break; + + case Constants.WRAPPER_MSG_STOP_PENDING: + if (_controller._wrappedProcess != null) { + _controller._wrappedProcess.signalStopping(Long.valueOf(message.getMessage())); + } + break; + + } + } + + @Override + public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception + { + + synchronized (_controller) + { + // we accept only one session. if we already have one -> close the + // new session + if (_controller._channel != null && _controller._channel != ctx.getChannel()) + { + if (_controller.isDebug()) + _controller.getLog().info("session already established -> ignore further sessions"); + ctx.getChannel().close(); + } + else if (_controller._channel == null) + { + _controller.setState(JVMController.STATE_ESTABLISHED); + // a hacker may establish a connection but does not send the key, thus locking the controller for the process. + // we leave him connected, so he does not keep polling us, but we allow further connections, until we get the key from our app. + //TODO if we have a real bandit: timeout of connections which do not send the key. + //_controller._channel = ctx.getChannel(); + + } + } + + } + + @Override + public void channelDisconnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception + { + synchronized (_controller) + { + if (_controller._channel == ctx.getChannel()) + { + // stop processing outgoing messages + _controller.workerExecutor.shutdownNow(); + + // stop the controller + _controller._channel = null; + _controller.setState(JVMController.STATE_WAITING_CLOSED); + if (_controller.isDebug()) + _controller.getLog().info("session closed -> waiting"); + } + + } + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception + { + if (_controller.isDebug()) + _controller.getLog().info(e.getCause().getMessage()); + + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/controller/jvm/ControllerPipelineFactory.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/controller/jvm/ControllerPipelineFactory.java new file mode 100644 index 0000000000..941f2358c7 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/controller/jvm/ControllerPipelineFactory.java @@ -0,0 +1,111 @@ +package org.rzo.yajsw.controller.jvm; + +import static org.jboss.netty.channel.Channels.pipeline; + +import java.net.InetAddress; +import java.net.UnknownHostException; + +import org.jboss.netty.channel.ChannelEvent; +import org.jboss.netty.channel.ChannelHandlerContext; +import org.jboss.netty.channel.ChannelPipeline; +import org.jboss.netty.channel.ChannelPipelineFactory; +import org.jboss.netty.channel.MessageEvent; +import org.jboss.netty.handler.codec.frame.DelimiterBasedFrameDecoder; +import org.jboss.netty.handler.codec.frame.Delimiters; +import org.rzo.yajsw.Constants; +import org.rzo.yajsw.controller.Message; +import org.rzo.yajsw.nettyutils.ChannelGroupFilter; +import org.rzo.yajsw.nettyutils.Condition; +import org.rzo.yajsw.nettyutils.ConditionFilter; +import org.rzo.yajsw.nettyutils.LoggingFilter; +import org.rzo.yajsw.nettyutils.WhitelistFilter; + +class ControllerPipelineFactory implements ChannelPipelineFactory +{ + + JVMController _controller; + boolean _debug = false; + + ControllerPipelineFactory(JVMController controller, boolean debug) + { + _controller = controller; + _debug = debug; + } + + public ChannelPipeline getPipeline() throws Exception + { + + ChannelPipeline pipeline = pipeline(); // Note the static import. + if (_debug) + pipeline.addLast("logging1", new LoggingFilter(_controller.getLog(), "controller")); + + // allow new connections only if state != LOGGED_ON + // and only if state != PROCESS_KILLED + pipeline.addLast("checkWaiting", new ConditionFilter(new Condition() + { + public boolean isOk(ChannelHandlerContext ctx, ChannelEvent e) + { + boolean result = true; + int currentState = _controller.getState(); + if (currentState == JVMController.STATE_LOGGED_ON) + { + _controller.getLog().info("app already logged on -> rejecting new connection"); + result = false; + } + else if (currentState == JVMController.STATE_PROCESS_KILLED) + { + _controller.getLog().info("app not running -> rejecting new connection"); + result = false; + } + return result; + } + })); + + // create a firewall allowing only localhosts to connect + WhitelistFilter firewall = new WhitelistFilter(); + try + { + firewall.allowAll(InetAddress.getAllByName("127.0.0.1")); + firewall.allow(InetAddress.getLocalHost()); + pipeline.addLast("firewall", firewall); + } + catch (UnknownHostException e) + { + _controller.getLog().throwing(JVMController.class.getName(), "start", e); + } + + // add a framer to split incoming bytes to message chunks + pipeline.addLast("framer", new DelimiterBasedFrameDecoder(8192, true, Delimiters.nulDelimiter())); + + // add messge codec + pipeline.addLast("messageEncoder", new MessageEncoder()); + pipeline.addLast("messageDecoder", new MessageDecoder()); + + if (_controller.isDebug()) + { + pipeline.addLast("logging", new LoggingFilter(_controller.getLog(), "controller")); + _controller.getLog().info("jvm controller set set netty logger"); + } + + // if we found our partner close all other open connections + pipeline.addLast("removeConnected", new ChannelGroupFilter(new Condition() + { + public boolean isOk(ChannelHandlerContext ctx, ChannelEvent e) + { + boolean result = false; + if (e instanceof MessageEvent) + { + Message m = (Message) ((MessageEvent) e).getMessage(); + result = m.getCode() == Constants.WRAPPER_MSG_OKKEY; + } + return result; + } + })); + + // at last add the message handler + pipeline.addLast("handler", new ControllerHandler(_controller)); + + return pipeline; + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/controller/jvm/JVMController.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/controller/jvm/JVMController.java new file mode 100644 index 0000000000..6aa6147030 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/controller/jvm/JVMController.java @@ -0,0 +1,780 @@ +/* This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + *

    + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ +package org.rzo.yajsw.controller.jvm; + +import java.net.InetSocketAddress; +import java.util.Collections; +import java.util.Set; +import java.util.TreeSet; +import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +import org.jboss.netty.bootstrap.ServerBootstrap; +import org.jboss.netty.channel.Channel; +import org.jboss.netty.channel.ChannelFuture; +import org.jboss.netty.channel.socket.oio.OioServerSocketChannelFactory; +import org.rzo.yajsw.Constants; +import org.rzo.yajsw.controller.AbstractController; +import org.rzo.yajsw.controller.Message; +import org.rzo.yajsw.util.Cycler; +import org.rzo.yajsw.util.DaemonThreadFactory; +import org.rzo.yajsw.wrapper.WrappedJavaProcess; +import org.rzo.yajsw.wrapper.WrappedProcess; + +// TODO: Auto-generated Javadoc +/** + * The Class Controller. + */ +public class JVMController extends AbstractController +{ + + /** The Constant STATE_UNKNOWN. */ + static final int STATE_UNKNOWN = 0; + + /** The Constant STATE_WAITING. */ + static final int STATE_WAITING = 1; + + /** The Constant STATE_ESTABLISHED. */ + static final int STATE_ESTABLISHED = 2; + + /** The Constant STATE_LOGGED_ON. */ + static final int STATE_LOGGED_ON = 3; + + /** The Constant STATE_STARTUP_TIMEOUT. */ + public static final int STATE_STARTUP_TIMEOUT = 4; + + /** The Constant STATE_WAITING_CLOSED. */ + public static final int STATE_WAITING_CLOSED = 5; + + /** The Constant STATE_USER_STOP. */ + public static final int STATE_USER_STOP = 6; + + public static final int STATE_PING_TIMEOUT = 7; + + public static final int STATE_PROCESS_KILLED = 8; + + public static final int STATE_THRESHOLD = 9; + + /** The _port. */ + int _port = DEFAULT_PORT; + + int _minPort = DEFAULT_PORT; + + int _maxPort = 65535; + + /** The _startup timeout. */ + int _startupTimeout = DEFAULT_STARTUP_TIMEOUT * 1000; + + /** The _key. */ + String _key; + + /** The _ping timeout. */ + int _pingTimeout = 10; + + boolean _pingOK = false; + + static Executor _pingExecutor = Executors.newCachedThreadPool(new DaemonThreadFactory("pinger")); + + /** The _session. */ + // IoSession _session; + volatile Channel _channel; + + /** The Constant pool. */ + // static final SimpleIoProcessorPool pool = new + // SimpleIoProcessorPool(NioProcessor.class); + /** + * To avoid tcp handle leak: Destroy the acceptor at stop and create new one + * on each start. + * + * The _acceptor. + */ + // NioSocketAcceptor _acceptor = null; + ServerBootstrap _acceptor = null; + volatile Channel _parentChannel; + + /** The _init. */ + boolean _init = false; + + /** The Constant _usedPorts. */ + static final Set _usedPorts = Collections.synchronizedSet(new TreeSet()); + + /** The Constant _scheduler. */ + static private final ScheduledThreadPoolExecutor _scheduler = (ScheduledThreadPoolExecutor) Executors.newScheduledThreadPool(1, new DaemonThreadFactory( + "controller.scheduler")); + + /** The _timeout handle. */ + volatile ScheduledFuture _timeoutHandle; + + Cycler _pingCheck; + ExecutorService workerExecutor = Executors.newCachedThreadPool(new DaemonThreadFactory( + "controller-worker")); + + Runnable _serviceStartupListener; + + float _heap = -1; + long _minGC = -1; + long _fullGC = -1; + long _heapInBytes = -1; + + + /** + * Instantiates a new controller. + * + * @param wrappedJavaProcess + * the wrapped java process + */ + public JVMController(WrappedProcess wrappedJavaProcess) + { + super(wrappedJavaProcess); + } + + public void init() + { + if (_pingCheck == null) + _pingCheck = new Cycler(_pingTimeout, _pingTimeout, _pingExecutor, new Runnable() + { + int r = 2; + public void run() + { + if (!_pingOK) + { + getLog().info("Missing wrapper ping within timeout of " + _pingTimeout); + // stop the process in a separate thread, otherwise + // conflict + executor.execute(new Runnable() + { + public void run() + { + stop(STATE_PING_TIMEOUT, "PING_TIMEOUT"); + } + }); + } + else + _pingOK = false; + } + }); + } + + /** + * Inits the. + */ + private void initInternal() + { + _acceptor = null; + _acceptor = new ServerBootstrap(new OioServerSocketChannelFactory(executor, executor)); + + // ???do not allow multiple servers to bind on the same port + _acceptor.setOption("reuseAddress", false); + _acceptor.setOption("tcpNoDelay", true); + _acceptor.setPipelineFactory(new ControllerPipelineFactory(this, _debug)); + + _init = true; + + } + + /** + * Start. + */ + public boolean start() + { + int myPort = -1; + + // in case of wrapper chaining: if we already have opened a port to our + // wrapper: do not use this port for a sub-process + try + { + myPort = Integer.parseInt((String) System.getProperties().get("wrapper.port")); + } + catch (Exception e) + { + } + if (myPort != -1) + _usedPorts.add(myPort); + + try + { + initInternal(); + setState(STATE_UNKNOWN); + // if we have kept the channel + if (_parentChannel != null && _parentChannel.isBound()) + { + setState(STATE_WAITING); + // beginWaitForStartup(); + if (isDebug()) + getLog().info("binding successfull"); + return true; + } + _port = _minPort; + while (getState() < STATE_WAITING && _port <= _maxPort) + { + if (_usedPorts.contains(_port)) + _port++; + else + try + { + _usedPorts.add(_port); + if (isDebug()) + getLog().info("binding to port " + _port); + _parentChannel = _acceptor.bind(new InetSocketAddress(_port)); + setState(STATE_WAITING); + // beginWaitForStartup(); + if (isDebug()) + getLog().info("binding successfull"); + return true; + } + catch (Exception ex) + { + if (_debug) + getLog().info("binding error: " + ex.getMessage() + " -> retry with another port"); + + _usedPorts.remove(_port); + try + { + Thread.sleep(500); + } + catch (InterruptedException e) + { + e.printStackTrace(); + getLog().info("sleep interrupted in JVMcontroller start"); + Thread.currentThread().interrupt(); + return false; + } + _port++; + } + } + getLog().severe("could not find a free port in the range " + _minPort + "..." + _maxPort); + return false; + } + catch (Exception ex) + { + getLog().severe("JVMController start " + ex); + return false; + } + } + + /** + * Begin wait for startup. + */ + public void beginWaitForStartup() + { + if (_startupTimeout <= 0) + return; + final Runnable timeOutAction = new Runnable() + { + int r = 1; + public void run() + { + if (isDebug()) + getLog().severe("WrapperManger did not log on within timeout of " + _startupTimeout); + stop(STATE_STARTUP_TIMEOUT, "STARTUP_TIMEOUT"); + } + }; + _timeoutHandle = _scheduler.schedule(timeOutAction, _startupTimeout, TimeUnit.MILLISECONDS); + } + + void schedulePingCheck() + { + _pingOK = false; + _pingCheck.start(); + } + + void stopPingCheck() + { + if (_pingCheck != null) + _pingCheck.stop(); + } + + void pingReceived() + { + _pingOK = true; + } + + void serviceStartup() + { + _wrappedProcess.setAppReportedReady(true); + if (_serviceStartupListener != null) + _serviceStartupListener.run(); + else + getLog().info("cannot report service startup: listener is null"); + } + + /** + * Stop. + * + * @param state + * the state + */ + public void stop(int state, String reason) + { + stopPingCheck(); + if (_timeoutHandle != null) + _timeoutHandle.cancel(true); + _scheduler.purge(); + setState(state); + if (_parentChannel != null) + { + int i = 0; + while (_channel != null && _channel.isConnected() && i < 3) + { + i++; + if (_debug) + getLog().info("controller sending a stop command"); + if (_channel != null) + { + String txt = null; + if (reason != null && reason.length() > 0) + txt = ":" + reason; + _channel.write(new Message(Constants.WRAPPER_MSG_STOP, txt)); + } + try + { + Thread.sleep(200); + } + catch (Exception ex) + { + } + } + if (_channel != null && _channel.isOpen()) + try + { + ChannelFuture cf = _channel.close(); + getLog().info("controller close session"); + cf.await(1000); + } + catch (InterruptedException e) + { + e.printStackTrace(); + getLog().info("session close wait interrupted in JVMController"); + } + + // undind and dispose all channels and ports. + // keep the same port until we shut down + /* + * log.info("unbind session"); if (_parentChannel != null && + * _parentChannel.isBound()) { try { + * _parentChannel.unbind().await(1000); } catch + * (InterruptedException e) { e.printStackTrace(); } _parentChannel + * = null; //_acceptor.releaseExternalResources(); } + * _usedPorts.remove(_port); + */ + } + } + + /** + * Startup ok. + */ + void startupOK() + { + if (_timeoutHandle == null) + return; + _timeoutHandle.cancel(false); + schedulePingCheck(); + _timeoutHandle = null; + } + + // test + /** + * The main method. + * + * @param args + * the arguments + */ + public static void main(String[] args) + { + JVMController c = new JVMController(null); + c.setDebug(true); + c.setKey("123"); + c.start(); + c.stop(0, null); + JVMController c1 = new JVMController(null); + c1.setDebug(true); + c1.setKey("123"); + c1.start(); + + } + + /** + * Gets the port. + * + * @return the port + */ + public int getPort() + { + return _port; + } + + /** + * Sets the port. + * + * @param port + * the new port + */ + public void setMinPort(int port) + { + if (port > 0 && port < 65536) + _minPort = port; + else + getLog().info("port out of range " + port); + } + + public void setMaxPort(int port) + { + if (port > 0 && port < 65536 && port >= _minPort) + _maxPort = port; + else + getLog().info("port out of range " + port); + } + + public void setPort(int port) + { + _port = port; + } + + /** + * Checks if is debug. + * + * @return true, if is debug + */ + boolean isDebug() + { + return _debug; + } + + /** + * Sets the debug. + * + * @param debug + * the new debug + */ + public void setDebug(boolean debug) + { + _debug = debug; + } + + /** + * Gets the key. + * + * @return the key + */ + public String getKey() + { + return _key; + } + + /** + * Sets the key. + * + * @param key + * the new key + */ + public void setKey(String key) + { + _key = key; + } + + /** + * Gets the startup timeout. + * + * @return the startup timeout + */ + int getStartupTimeout() + { + return _startupTimeout; + } + + /** + * Sets the startup timeout. + * + * @param startupTimeout + * the new startup timeout + */ + public void setStartupTimeout(int startupTimeout) + { + _startupTimeout = startupTimeout; + } + + /** + * Gets the ping timeout. + * + * @return the ping timeout + */ + int getPingTimeout() + { + return _pingTimeout; + } + + /** + * Sets the ping timeout. + * + * @param pingTimeout + * the new ping timeout + */ + public void setPingTimeout(int pingTimeout) + { + _pingTimeout = pingTimeout; + } + + /** + * Wait for. + * + * @return true, if successful + */ + public boolean waitFor(long timeout) + { + long end = System.currentTimeMillis() + 10000; + while (true) + { + if (_state == STATE_LOGGED_ON) + return true; + else if (_state == STATE_STARTUP_TIMEOUT) + return false; + else if (System.currentTimeMillis() > end) + return false; + try + { + Thread.sleep(250); + } + catch (InterruptedException e) + { + e.printStackTrace(); + getLog().info("sleep interrupted in JVMController.waitfor"); + return false; + } + } + } + + /** + * Request thread dump. + */ + public void requestThreadDump() + { + if (_channel != null) + _channel.write(new Message(Constants.WRAPPER_MSG_THREAD_DUMP, null)); + } + + /** + * Request thread dump. + */ + public void requestGc() + { + if (_channel != null) + _channel.write(new Message(Constants.WRAPPER_MSG_GC, null)); + } + + /** + * Request thread dump. + */ + public void requestDumpHeap(String fileName) + { + if (_channel != null) + _channel.write(new Message(Constants.WRAPPER_MSG_DUMP_HEAP, fileName)); + } + + public void reset() + { + stop(JVMController.STATE_UNKNOWN, "RESTART"); + _heap = -1; + _minGC = -1; + _fullGC = -1; + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#finalize() + */ + @Override + public void finalize() throws Throwable + { + try + { + reset(); + } + finally + { + super.finalize(); + } + } + + private volatile boolean _waitingForProcessTermination = false; + + private float _maxHeapRestart = -1; + + private long _maxFullGCTimeRestart = -1; + + + public void processStarted() + { + while (_waitingForProcessTermination) + try + { + getLog().info("should not happen: waiting for termination thread"); + Thread.sleep(200); + } + catch (InterruptedException e) + { + e.printStackTrace(); + } + + _waitingForProcessTermination = true; + executor.execute(new Runnable() + { + int r = 3; + public void run() + { + org.rzo.yajsw.os.Process osProcess; + try + { + osProcess = ((WrappedJavaProcess) _wrappedProcess)._osProcess; + if (_debug) + getLog().info("waiting for termination of process"); + if (osProcess != null) + osProcess.waitFor(); + if (_debug) + getLog().info("process terminated"); + } + finally + { + _waitingForProcessTermination = false; + } + _wrappedProcess.osProcessTerminated(); + if (_state == STATE_LOGGED_ON || _state == STATE_WAITING_CLOSED || osProcess == null || osProcess.isTerminated()) + { + stopPingCheck(); + executor.execute(new Runnable() + { + public void run() + { + setState(STATE_PROCESS_KILLED); + } + }); + } + } + }); + } + + public String stateAsStr(int state) + { + switch (state) + { + case STATE_UNKNOWN: + return "UNKNOWN"; + case STATE_WAITING: + return "WAITING"; + case STATE_ESTABLISHED: + return "ESTABLISHED"; + case STATE_LOGGED_ON: + return "LOGGED_ON"; + case STATE_STARTUP_TIMEOUT: + return "STARTUP_TIMEOUT"; + case STATE_WAITING_CLOSED: + return "WAITING_CLOSED"; + case STATE_USER_STOP: + return "USER_STOP"; + case STATE_PING_TIMEOUT: + return "PING_TIMEOUT"; + case STATE_PROCESS_KILLED: + return "PROCESS_KILLED"; + case STATE_THRESHOLD: + return "THRESHOLD"; + + default: + return "?"; + + } + } + + public void logStateChange(int state) + { + if (state == STATE_STARTUP_TIMEOUT) + getLog().warning("startup of java application timed out. if this is due to server overload consider increasing wrapper.startup.timeout"); + else if (state == STATE_PING_TIMEOUT) + getLog() + .warning( + "ping between java application and wrapper timed out. if this this is due to server overload consider increasing wrapper.ping.timeout"); + + } + + public void processFailed() + { + stop(STATE_PROCESS_KILLED, null); + } + + public void setServiceStartupListener(Runnable serviceStartupListener) + { + _serviceStartupListener = serviceStartupListener; + } + + private void restartProcess() + { + executor.execute(new Runnable() + { + int r = 4; + public void run() + { + stop(STATE_THRESHOLD, "THRESHOLD"); + } + }); + } + + public void setHeap(float heap, long minGC, long fullGC, long heapInBytes) + { + _heap = heap; + _minGC = minGC; + _fullGC = fullGC; + _heapInBytes = heapInBytes; + if (_heap > -1 && _heap > _maxHeapRestart && _maxHeapRestart > 0) + { + getLog().warning("restarting due to heap threshold : " + _heap + " > " + _maxHeapRestart); + restartProcess(); + } + else if (_fullGC > -1 && _fullGC > _maxFullGCTimeRestart && _maxFullGCTimeRestart > 0) + { + getLog().warning("restarting due to gc duration threshold : " + _fullGC + " > " + _maxFullGCTimeRestart); + restartProcess(); + } + } + + public float getHeap() + { + return _heap; + } + + public long getMinGC() + { + return _minGC; + } + + public long getFullGC() + { + return _fullGC; + } + + public long getHeapInBytes() + { + return _heapInBytes; + } + + public void setMaxHeapRestart(float maxHeapRestart) + { + _maxHeapRestart = maxHeapRestart; + } + + public void setMaxFullGCTimeRestart(long maxFullGCTimeRestart) + { + _maxFullGCTimeRestart = maxFullGCTimeRestart; + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/controller/jvm/MessageDecoder.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/controller/jvm/MessageDecoder.java new file mode 100644 index 0000000000..cf44a1c54a --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/controller/jvm/MessageDecoder.java @@ -0,0 +1,28 @@ +package org.rzo.yajsw.controller.jvm; + +import java.nio.charset.Charset; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.jboss.netty.channel.Channel; +import org.jboss.netty.channel.ChannelHandlerContext; +import org.jboss.netty.channel.ChannelPipelineCoverage; +import org.jboss.netty.handler.codec.oneone.OneToOneDecoder; +import org.rzo.yajsw.controller.Message; + +@ChannelPipelineCoverage("one") +public class MessageDecoder extends OneToOneDecoder +{ + @Override + protected Object decode(ChannelHandlerContext ctx, Channel channel, Object message) throws Exception + { + ChannelBuffer b = (ChannelBuffer) message; + + byte code = b.readByte(); + // TODO remove the nul + b.writerIndex(b.writerIndex()); + String msg = b.toString(Charset.defaultCharset().displayName()); + Message result = new Message(code, msg); + return result; + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/controller/jvm/MessageEncoder.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/controller/jvm/MessageEncoder.java new file mode 100644 index 0000000000..bcfcb70da6 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/controller/jvm/MessageEncoder.java @@ -0,0 +1,35 @@ +package org.rzo.yajsw.controller.jvm; + +import static org.jboss.netty.buffer.ChannelBuffers.buffer; + +import java.nio.CharBuffer; +import java.nio.charset.Charset; +import java.nio.charset.CharsetEncoder; + +import org.jboss.netty.buffer.ChannelBuffer; +import org.jboss.netty.channel.Channel; +import org.jboss.netty.channel.ChannelHandlerContext; +import org.jboss.netty.channel.ChannelPipelineCoverage; +import org.jboss.netty.handler.codec.oneone.OneToOneEncoder; +import org.rzo.yajsw.controller.Message; + +@ChannelPipelineCoverage("one") +public class MessageEncoder extends OneToOneEncoder +{ + + /** The Constant encoder. */ + static final CharsetEncoder encoder = Charset.defaultCharset().newEncoder(); + + @Override + protected Object encode(ChannelHandlerContext ctx, Channel channel, Object message) throws Exception + { + Message m = (Message) message; + ChannelBuffer buffer = buffer(m.getMessage().length() + 2); + buffer.writeByte(m.getCode()); + buffer.writeBytes(encoder.encode(CharBuffer.wrap(m.getMessage()))); + buffer.writeByte((byte) 0); + // System.out.println("encode " + buffer.toString("UTF-8")); + return buffer; + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/controller/runtime/RuntimeController.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/controller/runtime/RuntimeController.java new file mode 100644 index 0000000000..7e53f68863 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/controller/runtime/RuntimeController.java @@ -0,0 +1,106 @@ +package org.rzo.yajsw.controller.runtime; + +import java.util.Collections; + +import org.apache.commons.collections.MultiHashMap; +import org.rzo.yajsw.controller.AbstractController; +import org.rzo.yajsw.wrapper.WrappedProcess; +import org.rzo.yajsw.wrapper.WrappedRuntimeProcess; + +public class RuntimeController extends AbstractController +{ + static final int STATE_IDLE = 0; + static final int STATE_RUNNING = 1; + public static final int STATE_STOPPED = 2; + static final int STATE_USER_STOP_REQUEST = 3; + public static final int STATE_USER_STOPPED = 4; + static final int STATE_STARTUP_TIMEOUT = 5; + + public RuntimeController(WrappedProcess process) + { + super(process); + } + + public boolean start() + { + return true; + } + + public void processStarted() + { + getLog().info("process started"); + + executor.execute(new Runnable() + { + public void run() + { + getLog().info("process run started"); + _wrappedProcess.setAppReportedReady(true); + setState(STATE_RUNNING); + ((WrappedRuntimeProcess) _wrappedProcess)._osProcess.waitFor(); + getLog().info("process exited"); + if (_state == STATE_USER_STOP_REQUEST) + setState(STATE_USER_STOPPED); + else + setState(STATE_STOPPED); + getLog().info("all terminated"); + } + }); + + } + + public void stop(int state, String reason) + { + setState(state); + } + + public void reset() + { + _listeners = Collections.synchronizedMap(new MultiHashMap()); + setState(STATE_IDLE); + + } + + public String stateAsStr(int state) + { + switch (state) + { + case STATE_IDLE: + return "IDLE"; + case STATE_RUNNING: + return "RUNNING"; + case STATE_STOPPED: + return "STOPPED"; + case STATE_USER_STOP_REQUEST: + return "USER_STOP_REQUEST"; + case STATE_USER_STOPPED: + return "USER_STOPPED"; + case STATE_STARTUP_TIMEOUT: + return "STARTUP_TIMEOUT"; + + default: + return "?"; + + } + } + + public void logStateChange(int state) + { + if (state == STATE_STARTUP_TIMEOUT) + getLog().warning("startup of java application timed out. if this is due to server overload consider increasing wrapper.startup.timeout"); + + } + + public void processFailed() + { + stop(STATE_STOPPED, null); + } + + + public void beginWaitForStartup() + { + // TODO Auto-generated method stub + + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/groovy/WrapperBuilder.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/groovy/WrapperBuilder.java new file mode 100644 index 0000000000..b078c4edb9 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/groovy/WrapperBuilder.java @@ -0,0 +1,33 @@ +package org.rzo.yajsw.groovy; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Map; + +import org.rzo.yajsw.boot.WrapperLoader; + +public class WrapperBuilder extends HashMap // extends XHashMap +{ + static ClassLoader _wrapperClassLoader = WrapperLoader.getWrapperClassLoader(); + + public Object process() throws ClassNotFoundException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, + InvocationTargetException + { + Thread.currentThread().setContextClassLoader(_wrapperClassLoader); + Class cls = _wrapperClassLoader.loadClass("org.rzo.yajsw.wrapper.WrappedProcessFactory"); + + Method create = cls.getDeclaredMethod("createProcess", Map.class); + return create.invoke(null, this); + } + + public Object service() throws ClassNotFoundException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, + InvocationTargetException + { + Thread.currentThread().setContextClassLoader(_wrapperClassLoader); + Class cls = _wrapperClassLoader.loadClass("org.rzo.yajsw.wrapper.WrappedServiceFactory"); + Method create = cls.getDeclaredMethod("createService", Map.class); + return create.invoke(null, this); + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/groovy/XHashMap.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/groovy/XHashMap.java new file mode 100644 index 0000000000..be7818ca1a --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/groovy/XHashMap.java @@ -0,0 +1,60 @@ +package org.rzo.yajsw.groovy; + +import java.util.HashMap; +import java.util.Iterator; + +public class XHashMap extends HashMap +{ + public Object put(Object key, Object value) + { + // System.out.println("put "+key+" "+value); + XHashMap v = (XHashMap) super.get(key); + if (v == null) + v = new XHashMap(); + v.setValue(value); + return super.put(key, v); + } + + void setValue(Object value) + { + super.put("value", value); + } + + Object getValue() + { + return super.get("value"); + } + + public Object get(Object key) + { + Object value = super.get(key); + // System.out.println("get "+key+" "+value); + if (value == null) + { + value = new XHashMap(); + super.put(key, value); + } + return value; + } + + public HashMap toMap() + { + HashMap result = new HashMap(); + for (Iterator it = keySet().iterator(); it.hasNext();) + toMap("wrapper", (String) it.next(), result, this); + return result; + } + + private void toMap(String keyPrefix, String key, HashMap result, XHashMap map) + { + if ("value".equals(key)) + result.put(keyPrefix, map.getValue()); + else + { + XHashMap v = (XHashMap) map.get(key); + for (Iterator it = v.keySet().iterator(); it.hasNext();) + toMap(keyPrefix + "." + key, (String) it.next(), result, v); + } + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/io/CircularBuffer.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/io/CircularBuffer.java new file mode 100644 index 0000000000..73daa2c53b --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/io/CircularBuffer.java @@ -0,0 +1,437 @@ +/* This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + *

    + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ +package org.rzo.yajsw.io; + +import java.io.IOException; +import java.io.Reader; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +import org.rzo.yajsw.util.MyReentrantLock; + + +// TODO: Auto-generated Javadoc +/** + * A Synchronized circular byte buffer. This buffer orders elements FIFO + * (first-in-first-out). Writers block if the buffer is full. Attempts to + * retrieve an element from an empty buffer will block. + */ +public class CircularBuffer extends Reader +{ + + /** Default buffer size. */ + public final static int DEFAULT_BUFFER_SIZE = 512; + + /** The synchronization lock. */ + private Lock lock = new MyReentrantLock(); + + /** Sync condition indicating that buffer is not empty. */ + private Condition notEmpty = lock.newCondition(); + + /** The not full. */ + private Condition notFull = lock.newCondition(); + + /** The buffer. */ + private byte[] buffer; + + /** The number of valid elements in the buffe. */ + private int size = 0; + + /** The index to put next value. */ + private int putIndex = 0; + + /** The index to get next value. */ + private int getIndex = 0; + + /** The blocking. */ + boolean blocking = true; + + byte[] fullIndicator; + boolean fullIndocatorWritten = false; + boolean writeBlocking; + int fullIndicatorIndex = -1; + + /** + * Instantiates a new circular buffer. + * + * @param bufferSize + * the buffer size + * @param blocking + * the blocking + */ + public CircularBuffer(int bufferSize, boolean blocking) + { + buffer = new byte[bufferSize]; + this.blocking = blocking; + writeBlocking = blocking; + } + + public void setWriteBlocking(boolean blocking) + { + writeBlocking = blocking; + } + + /** + * Instantiates a new circular buffer with default size. + */ + public CircularBuffer() + { + buffer = new byte[DEFAULT_BUFFER_SIZE]; + } + + /** + * Put a value into the buffer. If buffer is full older values are + * overwritten. + * + * @param value + * the value + */ + public void put(byte value) + { + lock.lock(); // lock this object + // while no empty locations, place thread in waiting state + try + { + while (size == buffer.length) + { + if (writeBlocking) + { + // System.out.println("wait write"); + notFull.await();// await until a buffer element is free + } + else + { + lock.unlock(); + return; + } + } // end while + } + catch (Exception ex) + { + ex.printStackTrace(); + } + + buffer[putIndex] = value; // set new buffer value + + putByte(value); + + notEmpty.signal(); // signal threads waiting to read from buffer + lock.unlock(); // unlock this object + } // end method put + + private void writeFullIndicator() + { + fullIndocatorWritten = true; + } + + /** + * Put the values of a byte array into the buffer. In case of overflow put + * blocks + * + * @param buf + * the buf + * @param off + * the off + * @param len + * the len + */ + public void put(byte[] buf, int off, int len) + { + lock.lock(); // lock this object + for (int i = off; i < off + len - 1; i++) + putByte(buf[i]); + notEmpty.signal(); // signal threads waiting to read from buffer + lock.unlock(); // unlock this object + + } + + /** + * Put a single byte into the buffer. + * + * @param value + * the value + */ + private void putByte(byte value) + { + buffer[putIndex] = value; // set new buffer value + + // update circular write index + putIndex++; + if (putIndex >= buffer.length) + putIndex = putIndex - buffer.length; + + size++; // one more buffer element is full + + // if buffer overflow + if (size > buffer.length) + { + getIndex++; + if (getIndex >= buffer.length) + getIndex = getIndex - buffer.length; + size = buffer.length; + // System.out.println("overflow"); + if (fullIndicator != null && !fullIndocatorWritten) + writeFullIndicator(); + } + + } + + /** + * Get next value from the buffer. Blocks indefinitely if buffer is empty. + * + * @return the byte + */ + public byte get() + { + byte result = 0; // initialize value read from buffer + lock.lock(); // lock this object + + // wait until buffer has data, then read value + try + { + // while no data to read, place thread in waiting state + while (size == 0) + { + if (blocking) + notEmpty.await(); // await until a buffer element is + // filled + else + { + lock.unlock(); + return 0; + } + } // end while + + result = getByte(); + notFull.signal(); + } // end try + // if waiting thread interrupted, print stack trace + catch (InterruptedException exception) + { + exception.printStackTrace(); + Thread.currentThread().interrupt(); + } // end catch + finally + { + lock.unlock(); // unlock this object + } // end finally + + return result; + } // end method get + + /** + * Get bytes from the buffer and return these in an array. Blocks if the + * buffer is empty and no values have yet been added to the array. + * + * @param buf + * array to return bytes + * @param off + * the off + * @param len + * the len + * + * @return the number of bytes returned in the array + */ + public int get(byte[] buf, int off, int len) + { + lock.lock(); // lock this object + int i = 0; + + try + { + // while no data to read, place thread in waiting state + while (size == 0) + { + // System.out.println("read wait"); + notEmpty.await(); // await until a buffer element is + // filled + } // end while + } // end try + // if waiting thread interrupted, print stack trace + catch (Exception exception) + { + exception.printStackTrace(); + } // end catch + + for (; i < len && i < size; i++) + { + buf[off + i] = getByte(); + } + notFull.signal(); + lock.unlock(); // unlock this object + + return i; + + } + + /** + * Gets the byte. + * + * @return the byte + */ + private byte getByte() + { + if (fullIndocatorWritten) + { + fullIndicatorIndex++; + if (fullIndicatorIndex < fullIndicator.length) + { + return fullIndicator[fullIndicatorIndex]; + } + else + { + fullIndicatorIndex = -1; + fullIndocatorWritten = false; + } + } + if (size == 0) + return 0; + byte result; + result = buffer[getIndex]; // read value from buffer + + // update circular read index + getIndex++; + if (getIndex >= buffer.length) + getIndex = getIndex - buffer.length; + + size--; // one more buffer element is empty + return result; + } + + /** + * Size. + * + * @return the int + */ + public int size() + { + return size; + } + + /* + * (non-Javadoc) + * + * @see java.io.Reader#close() + */ + public void close() + { + lock.lock(); + notFull.signal(); + notEmpty.signal(); + size = 0; + putIndex = 0; + getIndex = 0; + lock.unlock(); + } + + /** + * The main method. + * + * @param args + * the arguments + */ + public static void main(String[] args) + { + CircularBuffer b = new CircularBuffer(2, true); + b.put((byte) 1); + b.put((byte) 2); + b.put((byte) 3); + System.out.println(b.get()); + System.out.println(b.get()); + b.put(new byte[] + { 1, 2, 3, 4 }, 0, 4); + System.out.println(b.get(new byte[10], 0, 10)); + System.out.println(b.get(new byte[10], 0, 10)); + } + + /* + * (non-Javadoc) + * + * @see java.io.Reader#read(char[], int, int) + */ + @Override + public int read(char[] cbuf, int off, int len) throws IOException + { + lock.lock(); // lock this object + int i = 0; + + try + { + // while no data to read, place thread in waiting state + while (size == 0) + { + if (blocking) + notEmpty.await(); // await until a buffer element is + // filled + else + { + lock.unlock(); + return -1; + } + } // end while + } // end try + // if waiting thread interrupted, print stack trace + catch (Exception exception) + { + exception.printStackTrace(); + } // end catch + + for (; i < len && i < size; i++) + { + cbuf[off + i] = (char) getByte(); + } + notFull.signal(); + lock.unlock(); // unlock this object + + return i; + } + + /** + * Write. + * + * @param cbuf + * the cbuf + * @param off + * the off + * @param len + * the len + */ + public void write(char[] cbuf, int off, int len) + { + lock.lock(); // lock this object + for (int i = off; i < off + len - 1; i++) + putByte((byte) cbuf[i]); + notEmpty.signal(); // signal threads waiting to read from buffer + lock.unlock(); // unlock this object + } + + /** + * Write. + * + * @param str + * the str + */ + public void write(String str) + { + char[] dst = new char[str.length() + 2]; + str.getChars(0, str.length(), dst, 0); + dst[str.length()] = '\r'; + dst[str.length()] = '\n'; + write(dst, 0, dst.length); + } + + public void setFullIndicator(String text) + { + fullIndicator = text.getBytes(); + } +} // end class CircularBuffer + diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/io/CyclicBufferFileInputStream.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/io/CyclicBufferFileInputStream.java new file mode 100644 index 0000000000..ced74a2dbe --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/io/CyclicBufferFileInputStream.java @@ -0,0 +1,282 @@ +/* This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + *

    + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ +package org.rzo.yajsw.io; + +import java.io.BufferedInputStream; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.RandomAccessFile; +import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +import org.rzo.yajsw.util.DaemonThreadFactory; +import org.rzo.yajsw.util.MyReentrantLock; + +// TODO: Auto-generated Javadoc +/** + * The Class CyclicBufferFileInputStream. + */ +public class CyclicBufferFileInputStream extends BufferedInputStream +{ + + /** + * Instantiates a new cyclic buffer file input stream. + * + * @param file + * the file + * + * @throws FileNotFoundException + * the file not found exception + * @throws IOException + * Signals that an I/O exception has occurred. + */ + public CyclicBufferFileInputStream(File file) throws FileNotFoundException, IOException + { + super(newInputStream(file)); + } + + /** + * New input stream. + * + * @param file + * the file + * + * @return the input stream + * + * @throws IOException + * Signals that an I/O exception has occurred. + */ + public static InputStream newInputStream(final File file) throws IOException + { + + return new InputStream() + { + + boolean closed = false; + boolean opened = false; + Lock lock = new MyReentrantLock(); + RandomAccessFile raf; + ByteBuffer buf; + ByteBuffer posBuf; + ByteBuffer lockBuf; + + synchronized void open() + { + if (opened) + return; + while (!opened && !closed) + { + lock.lock(); + if (file.exists()) + try + { + // System.out.println("open "+file); + if (raf == null) + raf = new RandomAccessFile(file, "r"); + buf = raf.getChannel().map(FileChannel.MapMode.READ_ONLY, 5, CyclicBufferFilePrintStream.length - 5); + posBuf = raf.getChannel().map(FileChannel.MapMode.READ_ONLY, 1, 4); + lockBuf = raf.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, 1); + opened = true; + } + catch (Exception ex) + { + System.out.println(ex.getMessage()); + } + lock.unlock(); + if (!opened) + try + { + Thread.sleep(200); + } + catch (InterruptedException e) + { + e.printStackTrace(); + Thread.currentThread().interrupt(); + } + } + + } + + @Override + public void close() + { + // lock.lock(); + closed = true; + try + { + if (raf != null) + raf.close(); + buf = null; + System.gc(); + Thread.yield(); + } + catch (IOException e) + { + // TODO Auto-generated catch block + e.printStackTrace(); + } + // lock.unlock(); + } + + @Override + public int read() throws IOException + { + if (!buf.hasRemaining()) + { + buf.position(0); + } + while (getPosition() == buf.position() && !closed) + { + try + { + Thread.sleep(100); + } + catch (InterruptedException e) + { + e.printStackTrace(); + return -1; + } + } + if (closed) + { + return -1; + } + return buf.get(); + } + + private int getPosition() + { + posBuf.position(0); + waitUnlocked(); + return posBuf.getInt(); + } + + private void waitUnlocked() + { + return; + /* + * lockBuf.position(0); boolean free = lockBuf.get() == 0; while + * (!free) { try { Thread.sleep(50); } catch + * (InterruptedException e) { // TODO Auto-generated catch block + * e.printStackTrace(); return; } lockBuf.position(0); free = + * lockBuf.get() == 0; } + */ + } + + @Override + public int read(byte[] bytes, int off, int len) throws IOException + { + // System.out.println("read "+buf.position()); + open(); + lock.lock(); + if (!buf.hasRemaining()) + { + buf.position(0); + } + while (!closed && getPosition() == buf.position()) + { + try + { + lock.unlock(); + Thread.sleep(100); + lock.lock(); + } + catch (InterruptedException e) + { + e.printStackTrace(); + lock.unlock(); + return -1; + } + } + if (closed) + { + lock.unlock(); + return -1; + } + int toRead = getPosition() - buf.position(); + if (toRead < 0) + toRead = buf.remaining(); + if (toRead > len) + toRead = len; + buf.get(bytes, off, toRead); + lock.unlock(); + return toRead; + } + }; + } + + /** + * The main method. + * + * @param args + * the arguments + * @throws Exception + * @throws FileNotFoundException + */ + public static void main(String[] args) + { + CyclicBufferFileInputStream reader = null; + try + { + reader = new CyclicBufferFileInputStream(new File("test.dat")); + } + catch (FileNotFoundException e) + { + // TODO Auto-generated catch block + e.printStackTrace(); + } + catch (IOException e) + { + e.printStackTrace(); + } + InputStreamReader isr = new InputStreamReader(reader); + final BufferedReader br = new BufferedReader(isr); + Executor executor = Executors.newCachedThreadPool(new DaemonThreadFactory("test")); + executor.execute(new Runnable() + { + + public void run() + { + String line = null; + try + { + while ((line = br.readLine()) != null) + System.out.println(line); + System.out.println(line); + System.out.flush(); + } + catch (Exception ex) + { + ex.printStackTrace(); + } + } + + }); + try + { + Thread.sleep(100000); + } + catch (InterruptedException e) + { + e.printStackTrace(); + Thread.currentThread().interrupt(); + } + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/io/CyclicBufferFilePrintStream.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/io/CyclicBufferFilePrintStream.java new file mode 100644 index 0000000000..dedb31d86f --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/io/CyclicBufferFilePrintStream.java @@ -0,0 +1,160 @@ +/* This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + *

    + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ +package org.rzo.yajsw.io; + +import java.io.File; +import java.io.IOException; +import java.io.OutputStream; +import java.io.PrintStream; +import java.io.RandomAccessFile; +import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; + +// TODO: Auto-generated Javadoc +/** + * The Class CyclicBufferFilePrintStream. + */ +public class CyclicBufferFilePrintStream extends PrintStream +{ + + /** The length. */ + public static int length = 1024 * 200; + + /** + * New output stream. + * + * @param raf + * the raf + * + * @return the output stream + * + * @throws IOException + * Signals that an I/O exception has occurred. + */ + public static OutputStream newOutputStream(final RandomAccessFile raf) throws IOException + { + return new OutputStream() + { + ByteBuffer buf = raf.getChannel().map(FileChannel.MapMode.READ_WRITE, 5, length - 5); + ByteBuffer posBuf = raf.getChannel().map(FileChannel.MapMode.READ_WRITE, 1, 4); + ByteBuffer lockBuf = raf.getChannel().map(FileChannel.MapMode.READ_WRITE, 0, 1); + + // RandomAccessFile _raf = raf; + + @Override + public synchronized void close() throws IOException + { + super.close(); + raf.close(); + } + + private void lock() + { + lockBuf.position(0); + lockBuf.put((byte) 1); + } + + private void unlock() + { + lockBuf.position(0); + lockBuf.put((byte) 0); + } + + private void setPosition(int pos) + { + posBuf.position(0); + posBuf.putInt(pos); + } + + @Override + public synchronized void write(int b) throws IOException + { + lock(); + if (buf.remaining() == 0) + { + buf.position(0); + // System.out.println("buffer overwrite"); + } + buf.put((byte) b); + setPosition(buf.position()); + unlock(); + } + + @Override + public synchronized void write(byte[] bytes, int off, int len) throws IOException + { + lock(); + int toWrite = buf.remaining() > len ? len : buf.remaining(); + // System.out.println("write "+buf.position() + + // " "+len+" "+buf.remaining()); + buf.put(bytes, off, toWrite); + + if (toWrite != len) + { + // System.out.println("buffer overwrite"); + buf.position(0); + buf.put(bytes, off + toWrite, len - toWrite); + } + setPosition(buf.position()); + unlock(); + } + }; + } + + /** + * Instantiates a new cyclic buffer file print stream. + * + * @param file + * the file + * + * @throws IOException + * Signals that an I/O exception has occurred. + */ + public CyclicBufferFilePrintStream(File file) throws IOException + { + super(newOutputStream(new RandomAccessFile(file, "rw"))); + } + + /** + * The main method. + * + * @param args + * the arguments + */ + public static void main(String[] args) + { + try + { + CyclicBufferFilePrintStream writer = new CyclicBufferFilePrintStream(new File("test.dat")); + for (int i = 0; i < 10000000; i++) + { + writer.println("test " + i); + System.out.println("test " + i); + if (i % 2000 == 0) + try + { + Thread.sleep(500); + } + catch (InterruptedException e) + { + e.printStackTrace(); + Thread.currentThread().interrupt(); + } + } + } + catch (IOException e) + { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/io/NonBlockingWriter.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/io/NonBlockingWriter.java new file mode 100644 index 0000000000..e0780811c2 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/io/NonBlockingWriter.java @@ -0,0 +1,96 @@ +package org.rzo.yajsw.io; + +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.io.Writer; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; + +import org.rzo.yajsw.util.DaemonThreadFactory; + +public class NonBlockingWriter extends Writer +{ + OutputStream _out; + CircularBuffer _buffer; + boolean _closed = false; + byte[] _writeBuffer; + + static final Executor executor = Executors.newCachedThreadPool(new DaemonThreadFactory("nonblockingwriter")); + + public NonBlockingWriter(OutputStream out, int size, String fullIndicator) + { + _out = out; + _buffer = new CircularBuffer(size, true); + _buffer.setFullIndicator(fullIndicator); + _buffer.setWriteBlocking(false); + int writeSize = size / 10; + if (writeSize > 1024) + writeSize = 1024; + else if (writeSize < 100) + writeSize = size / 2; + if (writeSize == 0) + writeSize = 1; + _writeBuffer = new byte[writeSize]; + executor.execute(new Runnable() + { + + public void run() + { + while (!_closed) + { + int len = _buffer.get(_writeBuffer, 0, _writeBuffer.length); + try + { + _out.write(_writeBuffer, 0, len); + } + catch (IOException e) + { + e.printStackTrace(); + } + } + } + + }); + } + + @Override + public void close() throws IOException + { + _closed = true; + _out.close(); + } + + @Override + public void flush() throws IOException + { + } + + @Override + public void write(char[] cbuf, int off, int len) throws IOException + { + _buffer.write(cbuf, off, len); + } + + public static void main(String[] args) throws IOException + { + OutputStream s = new FileOutputStream("c:/test.txt"); + NonBlockingWriter w = new NonBlockingWriter(s, 1024, "!!! BUFFER FULL !!!"); + int i = 0; + while (i < 10000) + { + w.write("12345678" + "\n\r"); + try + { + Thread.yield(); + } + catch (Exception e) + { + // TODO Auto-generated catch block + e.printStackTrace(); + } + i++; + } + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/io/TeeInputStream.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/io/TeeInputStream.java new file mode 100644 index 0000000000..5f16029b4e --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/io/TeeInputStream.java @@ -0,0 +1,313 @@ +/* This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + *

    + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ +package org.rzo.yajsw.io; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.ReentrantLock; + +import org.rzo.yajsw.util.DaemonThreadFactory; +import org.rzo.yajsw.util.MyReentrantLock; + +// TODO: Auto-generated Javadoc +/** + * The Class TeeInputStream. + */ +public class TeeInputStream extends InputStream +{ + + /** The sources. */ + Source[] sources = new Source[0]; + + /** The lock. */ + ReentrantLock lock = new MyReentrantLock(); + + /** The data available. */ + Condition dataAvailable = lock.newCondition(); + + /** The Constant executor. */ + static private final Executor executor = Executors.newCachedThreadPool(new DaemonThreadFactory("TeeInputStream")); + + /** + * Connect. + * + * @param source + * the source + */ + public synchronized void connect(InputStream source) + { + lock.lock(); + Source[] newsources = new Source[sources.length + 1]; + for (int i = 0; i < sources.length; i++) + { + if (source != sources[i].getInputStream()) + newsources[i] = sources[i]; + else + { + lock.unlock(); + return; + } + } + newsources[newsources.length - 1] = new Source(source, dataAvailable); + sources = newsources; + executor.execute(newsources[newsources.length - 1]); + lock.unlock(); + } + + /** + * Disconnect. + * + * @param source + * the source + */ + public synchronized void disconnect(InputStream source) + { + lock.lock(); + if (sources.length == 0) + { + lock.unlock(); + return; + } + Source[] newsources = new Source[sources.length - 1]; + int j = 0; + boolean removed = false; + for (int i = 0; i < sources.length && j < newsources.length; i++) + { + if (source != sources[i].getInputStream()) + { + newsources[j] = sources[i]; + j++; + } + else + removed = true; + } + if (removed) + sources = newsources; + lock.unlock(); + } + + /* + * (non-Javadoc) + * + * @see java.io.InputStream#read() + */ + @Override + public int read() throws IOException + { + lock.lock(); + while (true) + { + for (int i = 0; i < sources.length; i++) + if (!sources[i].isStop() && sources[i].getBuffer().size() > 0) + { + int result = sources[i].getBuffer().get(); + lock.unlock(); + return result; + } + try + { + dataAvailable.await(); + } + catch (InterruptedException e) + { + e.printStackTrace(); + Thread.currentThread().interrupt(); + } + } + } + + /* + * (non-Javadoc) + * + * @see java.io.InputStream#read(byte[], int, int) + */ + @Override + public int read(byte b[], int off, int len) throws IOException + { + lock.lock(); + try + { + while (true) + { + for (int i = 0; i < sources.length; i++) + if (!sources[i].isStop() && sources[i].getBuffer().size() > 0) + { + int result = sources[i].getBuffer().get(b, off, len); + lock.unlock(); + return result; + } + try + { + // System.out.println("+await"); + dataAvailable.await(); + // System.out.println("-await"); + } + catch (InterruptedException e) + { + e.printStackTrace(); + Thread.currentThread().interrupt(); + } + } + } + + finally + { + // lock.unlock(); + } + + } + + /** + * The Class Source. + */ + class Source implements Runnable + { + + /** The in. */ + InputStream in; + + /** The buffer. */ + CircularBuffer buffer = new CircularBuffer(512, true); + + /** The buff. */ + byte[] buff = new byte[512]; + + /** The stop. */ + boolean stop = false; + + /** The data available. */ + Condition dataAvailable; + + /** + * Instantiates a new source. + * + * @param in + * the in + * @param dataAvailable + * the data available + */ + Source(InputStream in, Condition dataAvailable) + { + this.in = in; + this.dataAvailable = dataAvailable; + } + + /* + * (non-Javadoc) + * + * @see java.lang.Runnable#run() + */ + public void run() + { + while (!stop) + { + int c; + try + { + // System.out.println("read"); + c = in.read(); + // System.out.println(c); + if (c != -1) + { + lock.lock(); + buffer.put((byte) c); + // System.out.println("put"); + try + { + // System.out.println("+signal"); + dataAvailable.signal(); + // System.out.println("-signal"); + } + catch (Exception ex) + { + //ex.printStackTrace(); + System.err.println("could not read from InputStream "+ex.getMessage()); + } + lock.unlock(); + } + else + stop = true; + } + catch (IOException e) + { + e.printStackTrace(); + stop = true; + } + } + + } + + /** + * Gets the buffer. + * + * @return the buffer + */ + CircularBuffer getBuffer() + { + return buffer; + } + + /** + * Checks if is stop. + * + * @return true, if is stop + */ + boolean isStop() + { + return stop; + } + + /** + * Close. + */ + void close() + { + try + { + in.close(); + buffer.close(); + } + catch (IOException e) + { + e.printStackTrace(); + } + stop = true; + } + + /** + * Gets the input stream. + * + * @return the input stream + */ + InputStream getInputStream() + { + return in; + } + } + + public static void main(String[] args) throws IOException + { + TeeInputStream in = new TeeInputStream(); + InputStream inp = System.in; + System.setIn(in); + in.connect(inp); + BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); + String line; + while ((line = reader.readLine()) != null) + System.out.println(">" + line); + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/io/TeeOutputStream.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/io/TeeOutputStream.java new file mode 100644 index 0000000000..28883f947a --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/io/TeeOutputStream.java @@ -0,0 +1,134 @@ +/* This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + *

    + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ +package org.rzo.yajsw.io; + +import java.io.IOException; +import java.io.OutputStream; + +// TODO: Auto-generated Javadoc +/** + * The Class TeeOutputStream. + */ +public class TeeOutputStream extends OutputStream +{ + + /** The sinks. */ + OutputStream[] sinks = new OutputStream[0]; + + /** + * Connect. + * + * @param sink + * the sink + */ + public synchronized void connect(OutputStream sink) + { + OutputStream[] newSinks = new OutputStream[sinks.length + 1]; + for (int i = 0; i < sinks.length; i++) + { + if (sink != sinks[i]) + newSinks[i] = sinks[i]; + else + return; + } + newSinks[newSinks.length - 1] = sink; + sinks = newSinks; + } + + /** + * Disconnect. + * + * @param sink + * the sink + */ + public synchronized void disconnect(OutputStream sink) + { + if (sinks.length == 0) + return; + OutputStream[] newSinks = new OutputStream[sinks.length - 1]; + int j = 0; + boolean removed = false; + for (int i = 0; i < sinks.length && j < newSinks.length; i++) + { + if (sink != sinks[i]) + { + newSinks[j] = sinks[i]; + j++; + } + else + removed = true; + } + if (removed) + sinks = newSinks; + } + + /* + * (non-Javadoc) + * + * @see java.io.OutputStream#write(int) + */ + @Override + public synchronized void write(int b) throws IOException + { + for (int i = 0; i < sinks.length; i++) + sinks[i].write(b); + } + + /* + * (non-Javadoc) + * + * @see java.io.OutputStream#write(byte[]) + */ + @Override + public synchronized void write(byte b[]) throws IOException + { + for (int i = 0; i < sinks.length; i++) + sinks[i].write(b); + } + + /* + * (non-Javadoc) + * + * @see java.io.OutputStream#write(byte[], int, int) + */ + @Override + public synchronized void write(byte b[], int off, int len) throws IOException + { + for (int i = 0; i < sinks.length; i++) + sinks[i].write(b, off, len); + + } + + /* + * (non-Javadoc) + * + * @see java.io.OutputStream#close() + */ + @Override + public synchronized void close() throws IOException + { + for (int i = 0; i < sinks.length; i++) + sinks[i].close(); + } + + /* + * (non-Javadoc) + * + * @see java.io.OutputStream#flush() + */ + @Override + public synchronized void flush() throws IOException + { + for (int i = 0; i < sinks.length; i++) + sinks[i].flush(); + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/io/Test.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/io/Test.java new file mode 100644 index 0000000000..96226cbece --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/io/Test.java @@ -0,0 +1,56 @@ +/* This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + *

    + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ +package org.rzo.yajsw.io; + +import java.io.File; +import java.io.RandomAccessFile; +import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; + +// TODO: Auto-generated Javadoc +/** + * The Class Test. + */ +public class Test +{ + + /** + * The main method. + * + * @param args + * the arguments + */ + public static void main(String[] args) + { + try + { + String name = "test" + System.currentTimeMillis(); + + RandomAccessFile in = new RandomAccessFile(new File(name), "rw"); + ByteBuffer buf = in.getChannel().map(FileChannel.MapMode.READ_WRITE, 0, 1024 * 40); + in.close(); + buf = null; + // in = null; + System.gc(); + Thread.yield(); + // Thread.sleep(5000); + // System.out.println( in.getChannel().isOpen()); + System.out.println(new File(name).delete()); + } + catch (Exception e) + { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/io/package.html b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/io/package.html new file mode 100644 index 0000000000..53486fbbb7 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/io/package.html @@ -0,0 +1,7 @@ + + + + + Provides... + + \ No newline at end of file diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/nettyutils/ChannelGroupFilter.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/nettyutils/ChannelGroupFilter.java new file mode 100644 index 0000000000..81c20b0563 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/nettyutils/ChannelGroupFilter.java @@ -0,0 +1,39 @@ +package org.rzo.yajsw.nettyutils; + +import org.jboss.netty.channel.ChannelHandlerContext; +import org.jboss.netty.channel.ChannelPipelineCoverage; +import org.jboss.netty.channel.ChannelStateEvent; +import org.jboss.netty.channel.MessageEvent; +import org.jboss.netty.channel.SimpleChannelHandler; +import org.jboss.netty.channel.group.ChannelGroup; +import org.jboss.netty.channel.group.DefaultChannelGroup; + +@ChannelPipelineCoverage("all") +public class ChannelGroupFilter extends SimpleChannelHandler +{ + ChannelGroup _channels = new DefaultChannelGroup(); + Condition _condition; + + public ChannelGroupFilter(Condition condition) + { + _condition = condition; + } + + @Override + public void channelOpen(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception + { + _channels.add(ctx.getChannel()); + } + + @Override + public void writeRequested(ChannelHandlerContext ctx, MessageEvent e) throws Exception + { + if (_condition.isOk(ctx, e)) + { + _channels.remove(ctx.getChannel()); + _channels.close(); + } + ctx.sendDownstream(e); + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/nettyutils/Condition.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/nettyutils/Condition.java new file mode 100644 index 0000000000..5fc853dd8f --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/nettyutils/Condition.java @@ -0,0 +1,9 @@ +package org.rzo.yajsw.nettyutils; + +import org.jboss.netty.channel.ChannelEvent; +import org.jboss.netty.channel.ChannelHandlerContext; + +public interface Condition +{ + public boolean isOk(ChannelHandlerContext ctx, ChannelEvent e); +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/nettyutils/ConditionFilter.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/nettyutils/ConditionFilter.java new file mode 100644 index 0000000000..f586373a10 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/nettyutils/ConditionFilter.java @@ -0,0 +1,33 @@ +package org.rzo.yajsw.nettyutils; + +import org.jboss.netty.channel.ChannelHandlerContext; +import org.jboss.netty.channel.ChannelPipelineCoverage; +import org.jboss.netty.channel.ChannelStateEvent; +import org.jboss.netty.channel.SimpleChannelUpstreamHandler; + +@ChannelPipelineCoverage("all") +public class ConditionFilter extends SimpleChannelUpstreamHandler +{ + + Condition _condition; + + public ConditionFilter(Condition condition) + { + _condition = condition; + } + + @Override + public void channelOpen(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception + { + if (_condition.isOk(ctx, e)) + { + // forward if condtion met + ctx.sendUpstream(e); + } + else + { + ctx.getChannel().close(); + } + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/nettyutils/LoggingFilter.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/nettyutils/LoggingFilter.java new file mode 100644 index 0000000000..8a0b1c07cf --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/nettyutils/LoggingFilter.java @@ -0,0 +1,44 @@ +package org.rzo.yajsw.nettyutils; + +import java.util.logging.Logger; + +import org.jboss.netty.channel.ChannelEvent; +import org.jboss.netty.channel.ChannelPipelineCoverage; +import org.jboss.netty.channel.MessageEvent; +import org.jboss.netty.handler.logging.LoggingHandler; + +@ChannelPipelineCoverage("one") +public class LoggingFilter extends LoggingHandler +{ + Logger _logger; + String _name; + + public LoggingFilter(Logger logger, String name) + { + _logger = logger; + _name = name; + } + + @Override + public void log(ChannelEvent e) + { + if (e instanceof MessageEvent) + { + MessageEvent msg = (MessageEvent) e; + log(msg.toString()); + } + if (e != null) + log(e.toString()); + else + log("null event !!"); + } + + private void log(String txt) + { + if (_logger == null) + System.out.println(txt); + else + _logger.fine("[" + _name + "]" + txt); + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/nettyutils/SystemOutLoggingFilter.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/nettyutils/SystemOutLoggingFilter.java new file mode 100644 index 0000000000..3c329ed0bd --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/nettyutils/SystemOutLoggingFilter.java @@ -0,0 +1,31 @@ +package org.rzo.yajsw.nettyutils; + +import org.jboss.netty.channel.ChannelEvent; +import org.jboss.netty.channel.ChannelPipelineCoverage; +import org.jboss.netty.handler.logging.LoggingHandler; + +@ChannelPipelineCoverage("one") +public class SystemOutLoggingFilter extends LoggingHandler +{ + String _name; + + public SystemOutLoggingFilter(String name) + { + _name = name; + } + + @Override + public void log(ChannelEvent e) + { + if (e != null) + log(e.toString()); + else + log("null event !!"); + } + + private void log(String txt) + { + System.out.println("[" + _name + "]" + txt); + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/nettyutils/WhitelistFilter.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/nettyutils/WhitelistFilter.java new file mode 100644 index 0000000000..1c4606d92b --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/nettyutils/WhitelistFilter.java @@ -0,0 +1,158 @@ +/* This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + *

    + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ +package org.rzo.yajsw.nettyutils; + +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; + +import org.jboss.netty.channel.Channel; +import org.jboss.netty.channel.ChannelHandlerContext; +import org.jboss.netty.channel.ChannelPipelineCoverage; +import org.jboss.netty.channel.ChannelStateEvent; +import org.jboss.netty.channel.SimpleChannelUpstreamHandler; + +/** + * The Class WhitelistFilter. + */ +@ChannelPipelineCoverage("all") +public class WhitelistFilter extends SimpleChannelUpstreamHandler +{ + + /** The whitelist. */ + private final List whitelist = new CopyOnWriteArrayList(); + + /** + * Sets the addresses to be whitelisted. + * + * NOTE: this call will remove any previously blacklisted addresses. + * + * @param addresses + * an array of addresses to be blacklisted. + */ + public void allowAll(InetAddress[] addresses) + { + if (addresses == null) + { + throw new NullPointerException("addresses"); + } + for (int i = 0; i < addresses.length; i++) + { + InetAddress addr = addresses[i]; + allow(addr); + } + } + + /** + * Sets the addresses to be blacklisted. + * + * NOTE: this call will remove any previously blacklisted addresses. + * + * @param addresses + * a collection of InetAddress objects representing the addresses + * to be blacklisted. + * + * @throws IllegalArgumentException + * if the specified collections contains non-{@link InetAddress} + * objects. + */ + public void allowAll(Iterable addresses) + { + if (addresses == null) + { + throw new NullPointerException("addresses"); + } + + for (InetAddress address : addresses) + { + allow(address); + } + } + + /** + * Blocks the specified endpoint. + * + * @param address + * the address + */ + public void allow(InetAddress address) + { + whitelist.add(address); + } + + /** + * Unblocks the specified endpoint. + * + * @param address + * the address + */ + public void remove(InetAddress address) + { + if (address == null) + { + throw new NullPointerException("address"); + } + whitelist.remove(address); + } + + @Override + public void channelOpen(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception + { + if (!isBlocked(ctx.getChannel())) + { + // forward if not blocked + ctx.sendUpstream(e); + } + else + { + System.out.println("connection refused : " + ctx.getChannel().getRemoteAddress()); + blockSession(ctx.getChannel()); + } + } + + /** + * Block session. + * + * @param session + * the session + */ + private void blockSession(Channel session) + { + SocketAddress remoteAddress = session.getRemoteAddress(); + // logger.warn("Remote address " + remoteAddress + + // " not in the whitelist; closing."); + session.close(); + } + + /** + * Checks if is blocked. + * + * @param session + * the session + * + * @return true, if is blocked + */ + private boolean isBlocked(Channel session) + { + SocketAddress remoteAddress = session.getRemoteAddress(); + if (remoteAddress instanceof InetSocketAddress) + { + if (whitelist.contains(((InetSocketAddress) remoteAddress).getAddress())) + { + return false; + } + } + + return true; + } +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/AbstractProcess.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/AbstractProcess.java new file mode 100644 index 0000000000..53ae03ff27 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/AbstractProcess.java @@ -0,0 +1,450 @@ +/* This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + *

    + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ +package org.rzo.yajsw.os; + +import java.io.FileDescriptor; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.logging.Logger; + +// TODO: Auto-generated Javadoc +/** + * The Class AbstractProcess. + */ +public abstract class AbstractProcess implements Process +{ + + /** The _cmd. */ + protected String _cmd; + + protected String[] _arrCmd; + + /** The _working dir. */ + protected String _workingDir; + + /** The _priority. */ + protected int _priority = PRIORITY_UNDEFINED; + + /** The _visible. */ + protected boolean _visible = true; + + /** The _pid. */ + volatile protected int _pid = -1; + + /** The _exit code. */ + volatile protected int _exitCode = -1; + + /** The _title. */ + protected String _title = ""; + + /** The _cpu affinity. */ + protected int _cpuAffinity = AFFINITY_UNDEFINED; + + /** The _pipe streams. */ + protected boolean _pipeStreams = false; + + /** The _redirect error stream. */ + protected boolean _redirectErrorStream = false; + + /** The _input stream. */ + protected InputStream _inputStream; + + /** The _output stream. */ + protected OutputStream _outputStream; + + /** The _error stream. */ + protected InputStream _errorStream; + + /** The in_fd. */ + protected final FileDescriptor in_fd = new FileDescriptor(); + + /** The out_fd. */ + protected final FileDescriptor out_fd = new FileDescriptor(); + + /** The err_fd. */ + protected final FileDescriptor err_fd = new FileDescriptor(); + + /** The _tee name. */ + protected String _teeName; + + /** The _tmp path. */ + protected String _tmpPath; + + protected String _user; + protected String _password; + protected Logger _logger; + protected List _environment = new ArrayList(); + protected boolean _debug = false; + protected boolean _minimized = false; + protected boolean _logonActiveSession = false; + protected String _desktop = null; + + /* + * (non-Javadoc) + * + * @see org.rzo.yajsw.os.Process#getCommand() + */ + public String getCommand() + { + return _cmd; + } + + /* + * (non-Javadoc) + * + * @see org.rzo.yajsw.os.Process#setCommand(java.lang.String) + */ + public void setCommand(String cmd) + { + _cmd = cmd; + } + + /** + * Sets the command. + * + * @param cmds + * the new command + */ + public void setCommand(String[] cmds) + { + _arrCmd = cmds; + _cmd = null; + } + + /* + * (non-Javadoc) + * + * @see org.rzo.yajsw.os.Process#getEnv() + */ + public List getEnvironment() + { + return _environment; + } + + public Map getEnvironmentAsMap() + { + Map result = new HashMap(); + for (String[] entry : _environment) + result.put(entry[0], entry[1]); + return result; + } + + /** + * Sets the env. + * + * @param env + * the new env + */ + public void setEnvironment(List environment) + { + if (environment == null) + environment = new ArrayList(); + _environment = environment; + } + + /* + * (non-Javadoc) + * + * @see org.rzo.yajsw.os.Process#getExitCode() + */ + public int getExitCode() + { + return _exitCode; + } + + /** + * Sets the exit code. + * + * @param exitCode + * the new exit code + */ + protected void setExitCode(int exitCode) + { + _exitCode = exitCode; + } + + /* + * (non-Javadoc) + * + * @see org.rzo.yajsw.os.Process#getPid() + */ + public int getPid() + { + return _pid; + } + + /** + * Sets the pid. + * + * @param pid + * the new pid + */ + public void setPid(int pid) + { + _pid = pid; + } + + /* + * (non-Javadoc) + * + * @see org.rzo.yajsw.os.Process#getPriority() + */ + public int getPriority() + { + return _priority; + } + + /* + * (non-Javadoc) + * + * @see org.rzo.yajsw.os.Process#setPriority(int) + */ + public void setPriority(int priority) + { + _priority = priority; + } + + /* + * (non-Javadoc) + * + * @see org.rzo.yajsw.os.Process#isVisible() + */ + public boolean isVisible() + { + return _visible; + } + + /* + * (non-Javadoc) + * + * @see org.rzo.yajsw.os.Process#setVisible(boolean) + */ + public void setVisible(boolean visible) + { + _visible = visible; + } + + /* + * (non-Javadoc) + * + * @see org.rzo.yajsw.os.Process#getWorkingDir() + */ + public String getWorkingDir() + { + return _workingDir; + } + + /* + * (non-Javadoc) + * + * @see org.rzo.yajsw.os.Process#setWorkingDir(java.lang.String) + */ + public void setWorkingDir(String workingDir) + { + _workingDir = workingDir; + } + + /** + * Gets the title. + * + * @return the title + */ + public String getTitle() + { + return _title; + } + + /* + * (non-Javadoc) + * + * @see org.rzo.yajsw.os.Process#setTitle(java.lang.String) + */ + public void setTitle(String title) + { + _title = title; + } + + /** + * Gets the cpu affinity. + * + * @return the cpu affinity + */ + public int getCpuAffinity() + { + return _cpuAffinity; + } + + /* + * (non-Javadoc) + * + * @see org.rzo.yajsw.os.Process#setCpuAffinity(int) + */ + public void setCpuAffinity(int cpuAffinity) + { + _cpuAffinity = cpuAffinity; + } + + /* + * (non-Javadoc) + * + * @see org.rzo.yajsw.os.Process#setPipeStreams(boolean, boolean) + */ + public void setPipeStreams(boolean pipeStreams, boolean redirectErrorStream) + { + _pipeStreams = pipeStreams; + _redirectErrorStream = redirectErrorStream; + + } + + /* + * (non-Javadoc) + * + * @see org.rzo.yajsw.os.Process#getInputStream() + */ + public InputStream getInputStream() + { + return _inputStream; + } + + /* + * (non-Javadoc) + * + * @see org.rzo.yajsw.os.Process#getErrorStream() + */ + public InputStream getErrorStream() + { + return _errorStream; + } + + /* + * (non-Javadoc) + * + * @see org.rzo.yajsw.os.Process#getOutputStream() + */ + public OutputStream getOutputStream() + { + return _outputStream; + } + + /** + * Gets the tee name. + * + * @return the tee name + */ + String getTeeName() + { + return _teeName; + } + + /** + * Sets the tee name. + * + * @param teeName + * the new tee name + */ + public void setTeeName(String teeName) + { + _teeName = teeName; + } + + /** + * Sets the tmp path. + * + * @param tmpPath + * the new tmp path + */ + public void setTmpPath(String tmpPath) + { + _tmpPath = tmpPath; + } + + public String getUser() + { + return _user; + } + + public void setUser(String user) + { + _user = user; + } + + public String getPassword() + { + return _password; + } + + public void setPassword(String password) + { + _password = password; + } + + public void setLogger(Logger logger) + { + _logger = logger; + } + + protected void log(String msg) + { + if (_logger != null) + _logger.info(msg); + else + System.out.println(msg); + } + + protected void throwing(String cls, String method, Throwable ex) + { + if (_logger != null) + _logger.throwing(cls, method, ex); + else + ex.printStackTrace(); + } + + public void setDebug(boolean debug) + { + _debug = debug; + } + + public boolean isMinimized() + { + return _minimized; + } + + public void setMinimized(boolean minimized) + { + _minimized = minimized; + } + + public boolean isLogonActiveSession() + { + return _logonActiveSession; + } + + public void setLogonActiveSession(boolean logonActiveSession) + { + _logonActiveSession = logonActiveSession; + } + + public void setDesktop(String desktop) + { + _desktop = desktop; + } + + public boolean isDebug() + { + return _debug; + } + + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/AbstractService.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/AbstractService.java new file mode 100644 index 0000000000..4a1b9d5c83 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/AbstractService.java @@ -0,0 +1,183 @@ +package org.rzo.yajsw.os; + +import java.util.logging.Logger; + +import org.apache.commons.configuration.Configuration; + +abstract public class AbstractService implements Service +{ + protected String _displayName; + protected String _description; + protected String[] _dependencies; + protected String _account; + protected String _password; + protected String _command[]; + protected String _name; + protected Configuration _config; + protected String _startType; + protected boolean _interactive; + protected Logger _logger; + protected Object _failureActions = null; + + public String getDisplayName() + { + return _displayName; + } + + public void setLogger(Logger logger) + { + _logger = logger; + } + + public void setDisplayName(String displayName) + { + _displayName = displayName; + } + + public String getDescription() + { + return _description; + } + + public void setDescription(String description) + { + _description = description; + } + + public String[] getDependencies() + { + return _dependencies; + } + + public void setDependencies(String[] dependencies) + { + _dependencies = dependencies; + } + + public String getAccount() + { + return _account; + } + + public void setAccount(String account) + { + _account = account; + } + + public String getPassword() + { + return _password; + } + + public void setPassword(String password) + { + _password = password; + } + + public String[] getCommand() + { + return _command; + } + + public void setCommand(String[] command) + { + _command = command; + } + + public String getName() + { + return _name; + } + + public void setName(String name) + { + _name = name; + } + + public Configuration getConfig() + { + return _config; + } + + public void setConfig(Configuration config) + { + _config = config; + } + + public boolean isAutomatic(int state) + { + return (state & STATE_AUTOMATIC) != 0; + } + + public boolean isDisabled(int state) + { + return (state & STATE_DISABLED) != 0; + } + + public boolean isInstalled(int state) + { + return (state & STATE_INSTALLED) != 0; + } + + public boolean isInteractive(int state) + { + return (state & STATE_INTERACTIVE) != 0; + } + + public boolean isManual(int state) + { + return (state & STATE_MANUAL) != 0; + } + + public boolean isPaused(int state) + { + return (state & STATE_PAUSED) != 0; + } + + public boolean isRunning(int state) + { + return (state & STATE_RUNNING) != 0; + } + + public boolean isStarting(int state) + { + return (state & STATE_STARTING) != 0; + } + + public boolean isStateUnknown(int state) + { + return (state & STATE_UNKNOWN) != 0; + } + + public String getStartType() + { + return _startType; + } + + public void setStartType(String startType) + { + _startType = startType; + } + + public boolean isInteractive() + { + return _interactive; + } + + public void setInteractive(boolean interactive) + { + _interactive = interactive; + } + + public void setFailureActions(Object failureActions) + { + _failureActions = failureActions; + } + + public Object getFailureActions() + { + return _failureActions; + } + + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/DummyWindow.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/DummyWindow.java new file mode 100644 index 0000000000..76d5fc5fc3 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/DummyWindow.java @@ -0,0 +1,705 @@ +/* This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + *

    + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ +package org.rzo.yajsw.os; + +import java.util.Map; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.Semaphore; + +import jnacontrib.jna.Options; + +import org.apache.commons.collections.BidiMap; +import org.apache.commons.collections.MultiHashMap; +import org.apache.commons.collections.bidimap.DualHashBidiMap; +import org.rzo.yajsw.os.ms.win.w32.DummyWindow.MyUser32.WNDCLASSEX; +import org.rzo.yajsw.os.ms.win.w32.DummyWindow.MyUser32.WNDPROC; + +import com.sun.jna.Callback; +import com.sun.jna.Native; +import com.sun.jna.Pointer; +import com.sun.jna.Structure; +import com.sun.jna.platform.win32.GDI32; +import com.sun.jna.platform.win32.Kernel32; +import com.sun.jna.platform.win32.User32; +import com.sun.jna.ptr.PointerByReference; + +// TODO: Auto-generated Javadoc +/** + * The Class DummyWindow. + */ +public class DummyWindow +{ + + /** + * The Interface MyKernel32. + */ + public interface MyKernel32 extends Kernel32 + { + + /** The INSTANCE. */ + MyKernel32 INSTANCE = (MyKernel32) Native.loadLibrary("Kernel32", MyKernel32.class); + + /* + * HMODULE WINAPI GetModuleHandle( __in LPCTSTR lpModuleName ); + */ + /** + * Gets the module handle a. + * + * @param lpModuleName + * the lp module name + * + * @return the pointer + */ + Pointer GetModuleHandleA(String lpModuleName); + + /* + * BOOL WINAPI GetModuleHandleEx( __in DWORD dwFlags, __in LPCTSTR + * lpModuleName, __out HMODULE phModule ); + */ + /** + * Gets the module handle ex a. + * + * @param dwFlags + * the dw flags + * @param lpModuleName + * the lp module name + * @param phModule + * the ph module + * + * @return true, if successful + */ + boolean GetModuleHandleExA(int dwFlags, String lpModuleName, PointerByReference phModule); + + /** + * Global add atom a. + * + * @param key + * the key + * + * @return the int + */ + int GlobalAddAtomA(String key); + + }// Kernel32 + + /** + * The Interface MyUser32. + */ + public interface MyUser32 extends User32 + { + // Method declarations, constant and structure definitions go here + + /** The INSTANCE. */ + MyUser32 INSTANCE = (MyUser32) Native.loadLibrary("User32", MyUser32.class); + + /** + * Register hot key. + * + * @param hWnd + * the h wnd + * @param id + * the id + * @param fsModifiers + * the fs modifiers + * @param vk + * the vk + * + * @return the int + */ + int RegisterHotKey(Pointer hWnd, int id, int fsModifiers, int vk); + + /* + * BOOL UnregisterHotKey( HWND hWnd, int id ); + */ + /** + * Unregister hot key. + * + * @param hWnd + * the h wnd + * @param id + * the id + * + * @return the int + */ + boolean UnregisterHotKey(Pointer hWnd, int id); + + /* + * LRESULT CALLBACK WindowProc( HWND hwnd, UINT uMsg, WPARAM wParam, + * LPARAM lParam ); + */ + /** + * The Interface WNDPROC. + */ + interface WNDPROC extends Callback + { + + /** + * Callback. + * + * @param hwnd + * the hwnd + * @param uMsg + * the u msg + * @param wParam + * the w param + * @param lParam + * the l param + * + * @return the int + */ + int callback(Pointer hwnd, int uMsg, int wParam, int lParam); + } + + /** The W m_ hotkey. */ + int WM_HOTKEY = 786; + + /* + * typedef struct { UINT cbSize; UINT style; WNDPROC lpfnWndProc; int + * cbClsExtra; int cbWndExtra; HINSTANCE hInstance; HICON hIcon; HCURSOR + * hCursor; HBRUSH hbrBackground; LPCTSTR lpszMenuName; LPCTSTR + * lpszClassName; HICON hIconSm; } WNDCLASSEX,PWNDCLASSEX; + */ + /** The C s_ hredraw. */ + int CS_HREDRAW = 2; + + /** The C s_ vredraw. */ + int CS_VREDRAW = 1; + + /** + * The Class WNDCLASSEX. + */ + static class WNDCLASSEX extends Structure + { + + /** The cb size. */ + public int cbSize = size(); + + /** The style. */ + public int style = 0; + + /** The lpfn wnd proc. */ + public WNDPROC lpfnWndProc; + + /** The cb cls extra. */ + public int cbClsExtra = 0; + + /** The cb wnd extra. */ + public int cbWndExtra = 0; + + /** The h instance. */ + public Pointer hInstance; + + /** The h icon. */ + public Pointer hIcon = null; + + /** The h cursor. */ + public Pointer hCursor = null; + + /** The hbr background. */ + public Pointer hbrBackground = null; + + /** The lpsz menu name. */ + public String lpszMenuName = null; + + /** The lpsz class name. */ + public String lpszClassName = "JavaDummyWnd"; + + /** The h icon sm. */ + public Pointer hIconSm = null; + } + + /* + * ATOM RegisterClassEx( CONST WNDCLASSEXlpwcx ); + */ + /** + * Register class ex a. + * + * @param lpwcx + * the lpwcx + * + * @return the int + */ + int RegisterClassExA(WNDCLASSEX lpwcx); + + /* + * BOOL UnregisterClass( LPCTSTR lpClassName, HINSTANCE hInstance ); + */ + /** + * Unregister class w. + * + * @param lpClassName + * the lp class name + * @param hInstance + * the h instance + * + * @return the int + */ + int UnregisterClassW(String lpClassName, Pointer hInstance); + + /* + * HWND CreateWindowEx( DWORD dwExStyle, LPCTSTR lpClassName, LPCTSTR + * lpWindowName, DWORD dwStyle, int x, int y, int nWidth, int nHeight, + * HWND hWndParent, HMENU hMenu, HINSTANCE hInstance, LPVOID lpParam ); + */ + + /** The W s_ overlappedwindow. */ + int WS_OVERLAPPEDWINDOW = 0xcf0000; + + /** The W s_ e x_ clientedge. */ + int WS_EX_CLIENTEDGE = 512; + + /** The W s_ overlapped. */ + int WS_OVERLAPPED = 0; + + /** The W s_ visible. */ + int WS_VISIBLE = 0x10000000; + + /** + * Creates the window ex a. + * + * @param dwExStyle + * the dw ex style + * @param lpClassName + * the lp class name + * @param lpWindowName + * the lp window name + * @param dwStyle + * the dw style + * @param x + * the x + * @param y + * the y + * @param nWidth + * the n width + * @param nHeight + * the n height + * @param hWndParent + * the h wnd parent + * @param hMenu + * the h menu + * @param hInstance + * the h instance + * @param lpParam + * the lp param + * + * @return the pointer + */ + Pointer CreateWindowExA(int dwExStyle, String lpClassName, String lpWindowName, int dwStyle, int x, int y, int nWidth, int nHeight, + Pointer hWndParent, Pointer hMenu, Pointer hInstance, Pointer lpParam); + + /* + * LONG_PTR SetWindowLongPtr( HWND hWnd, int nIndex, LONG_PTR dwNewLong + * ); + */ + /** + * Sets the window long a. + * + * @param hWnd + * the h wnd + * @param nIndex + * the n index + * @param dwNewLong + * the dw new long + * + * @return the pointer + */ + Pointer SetWindowLongA(Pointer hWnd, int nIndex, Pointer dwNewLong); + + /* + * LONG_PTR GetWindowLongPtr( HWND hWnd, int nIndex ); + */ + /** + * Gets the window long a. + * + * @param hWnd + * the h wnd + * @param nIndex + * the n index + * + * @return the pointer + */ + Pointer GetWindowLongA(Pointer hWnd, int nIndex); + + /** The GWL p_ userdata. */ + int GWLP_USERDATA = -21; + + /* + * LRESULT DefWindowProc( HWND hWnd, UINT Msg, WPARAM wParam, LPARAM + * lParam ); + */ + /** + * Def window proc a. + * + * @param hWnd + * the h wnd + * @param Msg + * the msg + * @param wParam + * the w param + * @param lParam + * the l param + * + * @return the int + */ + int DefWindowProcA(Pointer hWnd, int Msg, int wParam, int lParam); + + /* + * BOOL ShowWindow( HWND hWnd, int nCmdShow ); + */ + /** + * Show window. + * + * @param hWnd + * the h wnd + * @param nCmdShow + * the n cmd show + * + * @return the int + */ + int ShowWindow(Pointer hWnd, int nCmdShow); + + /** The S w_ hide. */ + int SW_HIDE = 0; + + /** The S w_ show. */ + int SW_SHOW = 5; + + /** The S w_ shownormal. */ + int SW_SHOWNORMAL = 1; + + /* + * BOOL UpdateWindow( HWND hWnd // handle to window ); + */ + /** + * Update window. + * + * @param hWnd + * the h wnd + * + * @return the int + */ + int UpdateWindow(Pointer hWnd // handle to window + ); + + /* + * typedef struct { HWND hwnd; UINT message; WPARAM wParam; LPARAM + * lParam; DWORD time; POINT pt; } MSG,PMSG; + */ + /** + * The Class MSG. + */ + class MSG extends Structure + { + + /** The hwnd. */ + public Pointer hwnd; + + /** The message. */ + public int message; + + /** The w param. */ + public int wParam; + + /** The l param. */ + public int lParam; + + /** The time. */ + public int time; + + /** The pt. */ + public POINT pt; + } + + /* + * BOOL GetMessage( LPMSG lpMsg, HWND hWnd, UINT wMsgFilterMin, UINT + * wMsgFilterMax ); + */ + /** + * Gets the message a. + * + * @param lpMsg + * the lp msg + * @param hWnd + * the h wnd + * @param wMsgFilterMin + * the w msg filter min + * @param wMsgFilterMax + * the w msg filter max + * + * @return the int + */ + int GetMessageA(MSG lpMsg, Pointer hWnd, int wMsgFilterMin, int wMsgFilterMax); + + /* + * LRESULT DispatchMessage( const MSGlpmsg ); + */ + /** + * Dispatch message a. + * + * @param lpmsg + * the lpmsg + * + * @return the int + */ + int DispatchMessageA(MSG lpmsg); + + } // user32 + + /** + * The Interface MyGdi32. + */ + public interface MyGdi32 extends GDI32 + { + + /** The INSTANCE. */ + MyGdi32 INSTANCE = (MyGdi32) Native.loadLibrary("gdi32", MyGdi32.class, Options.UNICODE_OPTIONS); + + /* + * HGDIOBJ GetStockObject( int fnObject // stock object type ); + */ + /** + * Gets the stock object. + * + * @param fnObject + * the fn object + * + * @return the pointer + */ + Pointer GetStockObject(int fnObject); + + /** The BLAC k_ brush. */ + int BLACK_BRUSH = 4; + + }// Gdi32 + + /** + * The Class CallbackMessage. + */ + static class CallbackMessage + { + + /** The _u msg. */ + int _uMsg; + + /** The _w param. */ + int _wParam; + + /** The _l param. */ + int _lParam; + + /** + * Instantiates a new callback message. + * + * @param uMsg + * the u msg + * @param wParam + * the w param + * @param lParam + * the l param + */ + CallbackMessage(int uMsg, int wParam, int lParam) + { + _uMsg = uMsg; + _wParam = wParam; + _lParam = lParam; + } + } + + /** The _instance. */ + static DummyWindow _instance; + + /** The _listners. */ + static Map _listners = new MultiHashMap(); + + /** The _wnd proc. */ + WndProc _wndProc = new WndProc(); + + /** The _hinstance. */ + Pointer _hinstance; + + /** The _h wnd. */ + Pointer _hWnd; + + /** The _wnd class. */ + WNDCLASSEX _wndClass = new WNDCLASSEX(); + + /** The _queue. */ + static LinkedBlockingQueue _queue = new LinkedBlockingQueue(); + + /** The _hot keys. */ + static BidiMap _hotKeys = new DualHashBidiMap(); + + /** The _semaphore. */ + Semaphore _semaphore = new Semaphore(0); + + /** + * The Class HotKey. + */ + public class HotKey + { + + /** + * Instantiates a new hot key. + * + * @param wParam + * the w param + * @param lParam + * the l param + */ + public HotKey(int wParam, int lParam) + { + _wParam = wParam; + _lParam = lParam; + } + + /** The _w param. */ + int _wParam; + + /** The _l param. */ + int _lParam; + + /* + * (non-Javadoc) + * + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() + { + return _wParam | _lParam; + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) + { + return (obj instanceof HotKey && ((HotKey) obj)._lParam == _lParam && ((HotKey) obj)._lParam == _lParam); + } + } + + /** + * Instance. + * + * @return the dummy window + */ + + /** + * The Class WndProc. + */ + static class WndProc extends Structure implements WNDPROC + { + + /* + * (non-Javadoc) + * + * @see + * org.rzo.yajsw.os.ms.win.xp.DummyWindow.MyUser32.WNDPROC#callback( + * com.sun.jna.Pointer, int, int, int) + */ + public int callback(Pointer hWnd, int uMsg, int wParam, int lParam) + { + // System.out.println("callback " + uMsg + " " + wParam + " " + + // lParam + " " + "ptr" + " "+ hWnd); + if (_listners.get(new Integer(uMsg)) != null) + { + CallbackMessage msg = new CallbackMessage(uMsg, wParam, lParam); + _queue.offer(msg); + } + int res = MyUser32.INSTANCE.DefWindowProcA(hWnd, uMsg, wParam, lParam); + // System.out.println(">" + res); + return res; + } + + /** + * Instantiates a new wnd proc. + */ + WndProc() + { + super(); + allocateMemory(4); + } + } + + /** + * The Interface WndListner. + */ + public interface WndListner + { + + /** + * Execute. + * + * @param uMsg + * the u msg + * @param wParam + * the w param + * @param lParam + * the l param + * + * @return the int + */ + int execute(int uMsg, int wParam, int lParam); + } + + /** + * Adds the listner. + * + * @param uMsg + * the u msg + * @param listner + * the listner + */ + public void addListner(Integer uMsg, WndListner listner) + { + synchronized (_listners) + { + _listners.put(uMsg, listner); + } + } + + /** + * Removes the listner. + * + * @param listner + * the listner + */ + public void removeListner(WndListner listner) + { + synchronized (_listners) + { + _listners.remove(listner); + } + + } + + /** + * Inits the. + */ + + /** + * Wait termination. + * + * @throws InterruptedException + * the interrupted exception + */ + void waitTermination() throws InterruptedException + { + // System.out.println("+ wait termination "+ + // System.currentTimeMillis()); + _semaphore.acquire(); + // System.out.println("- wait termination "+ + // System.currentTimeMillis()); + } +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/ErrorHandler.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/ErrorHandler.java new file mode 100644 index 0000000000..b38f58c910 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/ErrorHandler.java @@ -0,0 +1,40 @@ +/* This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + *

    + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ +package org.rzo.yajsw.os; + +// TODO: Auto-generated Javadoc +/** + * The Interface ErrorHandler. + */ +public interface ErrorHandler +{ + + /** + * To string. + * + * @param id + * the id + * + * @return the string + */ + public String toString(int id); + + /** + * Throw exception. + * + * @param id + * the id + * + * @throws OsException + * the os exception + */ + public void throwException(int id) throws OsException; +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/FileManager.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/FileManager.java new file mode 100644 index 0000000000..670e450770 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/FileManager.java @@ -0,0 +1,14 @@ +package org.rzo.yajsw.os; + +import org.rzo.yajsw.util.File; + +public interface FileManager +{ + + long created(File file); + + long freeSpace(File file); + + long totalSpace(File file); + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/JavaHome.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/JavaHome.java new file mode 100644 index 0000000000..eabe91d828 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/JavaHome.java @@ -0,0 +1,10 @@ +package org.rzo.yajsw.os; + +import org.jboss.netty.logging.InternalLogger; + +public interface JavaHome +{ + String findJava(String wrapperJava, String customProcessName); + void setLogger(InternalLogger logger); + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/Keyboard.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/Keyboard.java new file mode 100644 index 0000000000..9b2fa0af34 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/Keyboard.java @@ -0,0 +1,64 @@ +/* This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + *

    + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ +package org.rzo.yajsw.os; + +// TODO: Auto-generated Javadoc +/** + * The Interface Keyboard. + */ +public interface Keyboard +{ + + /** The Constant MOD_ALT. */ + static final int MOD_ALT = 1; + + /** The Constant MOD_CONTROL. */ + static final int MOD_CONTROL = 2; + + /** The Constant MOD_SHIFT. */ + static final int MOD_SHIFT = 4; + + /** The Constant MOD_WIN. */ + static final int MOD_WIN = 8; + + /** + * The Interface HotKeyListner. + */ + public interface HotKeyListner + { + + /** + * Key pressed. + */ + public void keyPressed(); + } + + /** + * Register hotkey. + * + * @param listner + * the listner + * @param mod + * the mod + * @param key + * the key + */ + public void registerHotkey(HotKeyListner listner, int mod, int key); + + /** + * Unregister hot key. + * + * @param listner + * the listner + */ + public void unregisterHotKey(HotKeyListner listner); + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/Mouse.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/Mouse.java new file mode 100644 index 0000000000..b3f781bb01 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/Mouse.java @@ -0,0 +1,11 @@ +package org.rzo.yajsw.os; + +import java.util.concurrent.Executor; + +public interface Mouse +{ + public void registerMouseUpListner(Runnable listner, Executor executor); + public void unregisterMouseUpListner(); + + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/OperatingSystem.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/OperatingSystem.java new file mode 100644 index 0000000000..94bca6d149 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/OperatingSystem.java @@ -0,0 +1,121 @@ +/* This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + *

    + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ +package org.rzo.yajsw.os; + +import org.apache.commons.configuration.Configuration; +import org.rzo.yajsw.os.ms.win.w32.OperatingSystemWindowsXP; +import org.rzo.yajsw.os.posix.bsd.OperatingSystemBSD; +import org.rzo.yajsw.os.posix.bsd.macosx.OperatingSystemMacOsX; +import org.rzo.yajsw.os.posix.linux.OperatingSystemLinux; +import org.rzo.yajsw.os.posix.solaris.OperatingSystemSolaris; + +// TODO: Auto-generated Javadoc +/** + * The Class OperatingSystem. + */ +public abstract class OperatingSystem +{ + + /** The _instance. */ + static OperatingSystem _instance; + + /** The _os name. */ + static String _osName; + + static boolean _isPosix = true; + + /** + * Instance. + * + * @return the operating system + */ + public static OperatingSystem instance() + { + if (_instance != null) + return _instance; + _osName = System.getProperty("os.name"); + if (_osName.toLowerCase().startsWith("windows")) + { + _instance = new OperatingSystemWindowsXP(); + _isPosix = false; + } + else if (_osName.toLowerCase().startsWith("mac os x")) + _instance = new OperatingSystemMacOsX(); + else if (_osName.contains("BSD")) + _instance = new OperatingSystemBSD(); + else if (_osName.contains("AIX")) + _instance = new OperatingSystemBSD(); + else if (_osName.toLowerCase().startsWith("linux")) + _instance = new OperatingSystemLinux(); + else if (_osName.toLowerCase().contains("sunos")) + _instance = new OperatingSystemSolaris(); + if (_instance == null) + System.out.println("OS not supported " + _osName); + return _instance; + + } + + /** + * Gets the operating system name. + * + * @return the operating system name + */ + public String getOperatingSystemName() + { + return _osName; + } + + public boolean isPosix() + { + return _isPosix; + } + + /** + * Keyboard instance. + * + * @return the keyboard + */ + public abstract Keyboard keyboardInstance(); + + public abstract Mouse mouseInstance(); + + /** + * Process manager instance. + * + * @return the process manager + */ + public abstract ProcessManager processManagerInstance(); + + public abstract FileManager fileManagerInstance(); + + /** + * Service manager instance. + * + * @return the process manager + */ + public abstract ServiceManager serviceManagerInstance(); + + /** + * Error handler instance. + * + * @return the error handler + */ + public abstract ErrorHandler errorHandlerInstance(); + + public abstract JavaHome getJavaHome(Configuration config); + + public abstract Object getServiceFailureActions(Configuration config); + + public abstract SystemInformation systemInformation(); + + public abstract boolean setWorkingDir(String name); + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/OsException.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/OsException.java new file mode 100644 index 0000000000..ac6eb882a5 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/OsException.java @@ -0,0 +1,19 @@ +/* This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + *

    + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ +package org.rzo.yajsw.os; + +/** + * The Class OsException. + */ +public class OsException extends Exception +{ + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/Process.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/Process.java new file mode 100644 index 0000000000..040ee03dee --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/Process.java @@ -0,0 +1,309 @@ +/* This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + *

    + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ +package org.rzo.yajsw.os; + +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.logging.Logger; + +// TODO: Auto-generated Javadoc +/** + * The Interface Process. + */ +public interface Process +{ + + /** + * Sets the command. + * + * @param cmd + * the new command + */ + public void setCommand(String cmd); + + public void setCommand(String[] cmd); + + /** + * Sets the working dir. + * + * @param dir + * the new working dir + */ + public void setWorkingDir(String dir); // + public boolean changeWorkingDir(String dir); // + + /** + * Sets the priority. + * + * @param priority + * the new priority + */ + public void setPriority(int priority); // + + /** + * Sets the cpu affinity. + * + * @param cpuAffinity + * the new cpu affinity + */ + public void setCpuAffinity(int cpuAffinity); + + /** + * Sets the visible. + * + * @param visible + * the new visible + */ + public void setVisible(boolean visible); + + /** + * Sets the title. + * + * @param title + * the new title + */ + public void setTitle(String title); + + /** + * Start. + * + * @return true, if successful + */ + public boolean start(); + + /** + * Wait for. + */ + public void waitFor(); + + /** + * Wait for. + * + * @param timeout + * the timeout + */ + public void waitFor(long timeout); + + /** + * Kill. + * + * @param code + * the code + * + * @return true, if successful + */ + public boolean kill(int code); + + /** + * Kill tree. + * + * @param code + * the code + * + * @return true, if successful + */ + public boolean killTree(int code); + + /** + * Checks if is running. + * + * @return true, if is running + */ + public boolean isRunning(); + + /** + * Gets the pid. + * + * @return the pid + */ + public int getPid(); + + /** + * Gets the exit code. + * + * @return the exit code + */ + public int getExitCode(); + + /** + * Gets the command. + * + * @return the command + */ + public String getCommand(); + + /** + * Gets the working dir. + * + * @return the working dir + */ + public String getWorkingDir(); + + /** + * Gets the priority. + * + * @return the priority + */ + public int getPriority(); + + /** + * Checks if is visible. + * + * @return true, if is visible + */ + public boolean isVisible(); + + /** + * Gets the children. + * + * @return the children + */ + public Collection getChildren(); + + /** + * Sets the pipe streams. + * + * @param pipeStreams + * the pipe streams + * @param redirectErrorStream + * the redirect error stream + */ + public void setPipeStreams(boolean pipeStreams, boolean redirectErrorStream); + + /** + * Gets the input stream. + * + * @return the input stream + */ + public InputStream getInputStream(); + + /** + * Gets the error stream. + * + * @return the error stream + */ + public InputStream getErrorStream(); + + /** + * Gets the output stream. + * + * @return the output stream + */ + public OutputStream getOutputStream(); + + /** + * Gets the current cpu. + * + * @return the current cpu + */ + public int getCurrentCpu(); + + /** + * Gets the current physical memory. + * + * @return the current physical memory + */ + public int getCurrentPhysicalMemory(); + + /** + * Gets the current virtual memory. + * + * @return the current virtual memory + */ + public int getCurrentVirtualMemory(); + + /** + * Gets the current page faults. + * + * @return the current page faults + */ + public int getCurrentPageFaults(); + + /** + * Destroy. + */ + public void destroy(); + + /** + * Stop. + * + * @param timeout + * the timeout + * @param code + * the code + * + * @return true, if successful + */ + public boolean stop(int timeout, int code); + + /** The Constant PRIORITY_NORMAL. */ + public static final int PRIORITY_NORMAL = 0; + + /** The Constant PRIORITY_BELOW_NORMAL. */ + public static final int PRIORITY_BELOW_NORMAL = -1; + + /** The Constant PRIORITY_LOW. */ + public static final int PRIORITY_LOW = -2; + + /** The Constant PRIORITY_ABOVE_NORMAL. */ + public static final int PRIORITY_ABOVE_NORMAL = 1; + + /** The Constant PRIORITY_HIGH. */ + public static final int PRIORITY_HIGH = 2; + + /** The Constant PRIORITY_UNDEFINED. */ + public static final int PRIORITY_UNDEFINED = -99; + + /** The Constant AFFINITY_UNDEFINED. */ + public static final int AFFINITY_UNDEFINED = -99; + + public void setTmpPath(String tmpPath); + + public boolean reconnectStreams(); + + public void setTeeName(String teeName); + + public String getTitle(); + + public void setUser(String name); + + public String getUser(); + + public void setPassword(String password); + + public int getCurrentThreads(); + + public int getCurrentHandles(); + + public boolean isTerminated(); + + public void setLogger(Logger logger); + + public List getEnvironment(); + + public Map getEnvironmentAsMap(); + + public void setEnvironment(List env); + + public void setDebug(boolean debug); + + public void setMinimized(boolean b); + + public boolean isLogonActiveSession(); + + public void setLogonActiveSession(boolean logonActiveSession); + + public void setDesktop(String desktop); + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/ProcessManager.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/ProcessManager.java new file mode 100644 index 0000000000..64a3221e19 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/ProcessManager.java @@ -0,0 +1,72 @@ +/* This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + *

    + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ +package org.rzo.yajsw.os; + +import java.util.List; + +// TODO: Auto-generated Javadoc +/** + * The Interface ProcessManager. + */ +public interface ProcessManager +{ + + /** + * Creates the process. + * + * @return the process + */ + public Process createProcess(); + + /** + * Gets the process. + * + * @param pid + * the pid + * + * @return the process + */ + public Process getProcess(int pid); + + /** + * Current process id. + * + * @return the int + */ + public int currentProcessId(); + + /** + * Process id of active window. + * + * @return the int + */ + public int processIdOfActiveWindow(); + + /** + * Gets the process tree. + * + * @param pid + * the pid + * + * @return the process tree + */ + public List getProcessTree(int pid); + + /** + * Task list instance. + * + * @return the task list + */ + public TaskList taskListInstance(); + + public List getProcessIds(); + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/Service.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/Service.java new file mode 100644 index 0000000000..20d6a3df22 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/Service.java @@ -0,0 +1,98 @@ +package org.rzo.yajsw.os; + +import java.util.logging.Logger; + +import org.apache.commons.configuration.Configuration; + +public interface Service +{ + public static final int STATE_INSTALLED = 1; + public static final int STATE_RUNNING = 2; + public static final int STATE_INTERACTIVE = 4; + public static final int STATE_AUTOMATIC = 8; + public static final int STATE_MANUAL = 16; + public static final int STATE_DISABLED = 32; + public static final int STATE_PAUSED = 64; + + public static final int STATE_STARTING = 48; + public static final int STATE_STOPPING = 40; + + public static final int STATE_UNKNOWN = 128; + + public boolean start(); + + public boolean stop(); + + public boolean install(); + + public boolean uninstall(); + + public boolean isInstalled(int state); + + public boolean isRunning(int state); + + public boolean isInteractive(int state); + + public boolean isAutomatic(int state); + + public boolean isManual(int state); + + public boolean isDisabled(int state); + + public boolean isPaused(int state); + + public boolean isStateUnknown(int state); + + public int state(); + + public String getName(); + + public void setName(String name); + + public Configuration getConfig(); + + public void setConfig(Configuration config); + + public String getDisplayName(); + + public void setDisplayName(String displayName); + + public String getDescription(); + + public void setDescription(String description); + + public String[] getDependencies(); + + public void setDependencies(String[] dependencies); + + public String getAccount(); + + public void setAccount(String account); + + public String getPassword(); + + public void setPassword(String password); + + public String[] getCommand(); + + public void setCommand(String[] command); + + public void init(); + + public void setStartType(String startType); + + public String getStartType(); + + public boolean isInteractive(); + + public void setInteractive(boolean interactive); + + public void setLogger(Logger log); + + public boolean isStarting(int state); + + public void setFailureActions(Object failureActions); + + public Object getFailureActions(); + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/ServiceInfo.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/ServiceInfo.java new file mode 100644 index 0000000000..0ddbb56e50 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/ServiceInfo.java @@ -0,0 +1,35 @@ +package org.rzo.yajsw.os; + +public interface ServiceInfo +{ + public String getName(); + + public String getDisplayName(); + + public String getDescription(); + + public String getAccount(); + + public String getCommand(); + + public String getStartType(); + + public String[] getDependencies(); + + public boolean isInteractive(); + + public int getState(); + + public int getPid(); + + public String getHost(); + + public String getWrapped(); + + public String getWrapperConfigurationPath(); + + public int getWrapperJmxPort(); + + public int getWrapperAppPid(); + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/ServiceInfoImpl.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/ServiceInfoImpl.java new file mode 100644 index 0000000000..b5cb482caa --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/ServiceInfoImpl.java @@ -0,0 +1,204 @@ +package org.rzo.yajsw.os; + +import java.io.Serializable; + +import org.rzo.yajsw.tools.JCLParser; + +public class ServiceInfoImpl implements ServiceInfo, Serializable, Comparable +{ + private String _account = "?"; + private String _command = "?"; + private String _description = "?"; + private String _name = "?"; + private int _pid = -1; + private String _startType = "?"; + private int _state = -1; + private int _wrapperAppPid = -1; + private String _wrapperConfigurationPath = "?"; + private int _wrapperJmxPort = -1; + private boolean _interactive = false; ; + private String _wrapped = "-"; + private String[] _dependencies; + private String _displayName; + private String _host = "localhost"; + + public String getAccount() + { + return _account; + } + + public String getCommand() + { + return _command; + } + + public String getDescription() + { + return _description; + } + + public String getName() + { + return _name; + } + + public int getPid() + { + return _pid; + } + + public String getStartType() + { + return _startType; + } + + public int getState() + { + return _state; + } + + public int getWrapperAppPid() + { + return _wrapperAppPid; + } + + public String getWrapperConfigurationPath() + { + return _wrapperConfigurationPath; + } + + public int getWrapperJmxPort() + { + return _wrapperJmxPort; + } + + public boolean isInteractive() + { + return _interactive; + } + + public String getWrapped() + { + return _wrapped; + } + + public void setAccount(String account) + { + _account = account; + } + + public void setCommand(String command) + { + _command = command.trim(); + JCLParser p = null; + try + { + p = JCLParser.parse(_command); + } + catch (Exception ex) + { + } + if (p == null) + return; + if ("org.rzo.yajsw.boot.WrapperServiceBooter".equals(p.getMainClass())) + { + _wrapped = "Service"; + for (String option : p.getVmOptions()) + if (option.startsWith("-Dwrapper.config=")) + _wrapperConfigurationPath = option.substring("-Dwrapper.config=".length()); + } + else if (p.getJar() != null && p.getJar().endsWith("wrapper.jar") && p.getArgs().size() > 1 && p.getArgs().get(0).equals("-c")) + { + _wrapped = "Console"; + _wrapperConfigurationPath = p.getArgs().get(1); + } + } + + public void setDescription(String description) + { + _description = description; + } + + public void setName(String name) + { + _name = name; + } + + public void setPid(int pid) + { + _pid = pid; + } + + public void setStartType(String startType) + { + _startType = startType; + } + + public void setState(int state) + { + _state = state; + } + + public void setWrapperAppPid(int wrapperAppPid) + { + _wrapperAppPid = wrapperAppPid; + } + + public void setWrapperConfigurationPath(String wrapperConfigurationPath) + { + _wrapperConfigurationPath = wrapperConfigurationPath; + } + + public void setWrapperJmxPort(int wrapperJmxPort) + { + _wrapperJmxPort = wrapperJmxPort; + } + + public void setIsInteractive(boolean istInteractive) + { + _interactive = istInteractive; + } + + public void setWrapped(String wrapped) + { + _wrapped = wrapped; + } + + public String[] getDependencies() + { + return _dependencies; + } + + public void setDependencies(String[] dependencies) + { + _dependencies = dependencies; + } + + public String getDisplayName() + { + return _displayName; + } + + public void setDisplayName(String displayName) + { + _displayName = displayName; + } + + public int compareTo(ServiceInfo o) + { + if (getDisplayName() != null) + return getDisplayName().compareTo(o.getDisplayName()); + return 0; + } + + public String getHost() + { + return _host; + } + + public void setHost(String host) + { + _host = host; + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/ServiceManager.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/ServiceManager.java new file mode 100644 index 0000000000..cc32e21d52 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/ServiceManager.java @@ -0,0 +1,14 @@ +package org.rzo.yajsw.os; + +import java.util.Map; + +public interface ServiceManager +{ + public Service createService(); + + public Service getService(String name); + + public ServiceInfo getServiceInfo(String name); + + public Map getServiceList(); +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/StopableService.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/StopableService.java new file mode 100644 index 0000000000..a9d1bdc035 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/StopableService.java @@ -0,0 +1,10 @@ +package org.rzo.yajsw.os; + +public interface StopableService +{ + + public void onStop(); + public void waitOnStop(); + public void signalStopping(long waitHint); + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/SystemInformation.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/SystemInformation.java new file mode 100644 index 0000000000..af50a8481b --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/SystemInformation.java @@ -0,0 +1,13 @@ +package org.rzo.yajsw.os; + +import java.util.logging.Logger; + +public interface SystemInformation +{ + public long totalRAM(); + + public long freeRAM(); + + public void setLogger(Logger logger); + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/TaskList.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/TaskList.java new file mode 100644 index 0000000000..41eadab687 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/TaskList.java @@ -0,0 +1,89 @@ +/* This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + *

    + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ +package org.rzo.yajsw.os; + +import java.util.Collection; +import java.util.Map; + +// TODO: Auto-generated Javadoc +/** + * The Interface TaskList. + */ +public interface TaskList +{ + + /** + * Adds the listner. + * + * @param listner + * the listner + */ + public void addListner(TaskListListner listner); + + /** + * Removes the listner. + * + * @param listner + * the listner + */ + public void removeListner(TaskListListner listner); + + /** + * Task list. + * + * @return the map + */ + public Map taskList(); + + /** + * The Interface TaskListListner. + */ + public interface TaskListListner + { + + /** + * Changed. + * + * @param event + * the event + */ + public void changed(TaskListEvent event); + } + + /** + * The Interface TaskListEvent. + */ + public interface TaskListEvent + { + + /** + * Gets the new tasks. + * + * @return the new tasks + */ + public Collection getNewTasks(); + + /** + * Gets the removed tasks. + * + * @return the removed tasks + */ + public Collection getRemovedTasks(); + + /** + * Gets the current tasks. + * + * @return the current tasks + */ + public Collection getCurrentTasks(); + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/ms/win/w32/Cluster.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/ms/win/w32/Cluster.java new file mode 100644 index 0000000000..14c6be2c05 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/ms/win/w32/Cluster.java @@ -0,0 +1,546 @@ +package org.rzo.yajsw.os.ms.win.w32; + +import java.net.UnknownHostException; +import java.util.ArrayList; +import java.util.LinkedHashSet; +import java.util.Set; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.apache.commons.lang.StringUtils; +import org.rzo.yajsw.os.ms.win.w32.Cluster.Clusapi.ClusterGroupState; +import org.rzo.yajsw.os.ms.win.w32.WindowsXPProcess.MyKernel32; + +import com.sun.jna.Memory; +import com.sun.jna.Native; +import com.sun.jna.Pointer; +import com.sun.jna.WString; +import com.sun.jna.ptr.IntByReference; + +public class Cluster +{ + static Logger _log = Logger.getLogger(Cluster.class.getCanonicalName()); + ExecutorService threadPool = Executors.newSingleThreadExecutor(); + + public interface Clusapi extends com.sun.jna.win32.StdCallLibrary + { + Clusapi INSTANCE = (Clusapi) Native.loadLibrary("ClusApi", Clusapi.class); + + /* + * HCLUSTER WINAPI OpenCluster( __in_opt LPCWSTR lpszClusterName ); + */ + Pointer OpenCluster(WString lpszClusterName); + + /* + * BOOL WINAPI CloseCluster( __in HCLUSTER hCluster ); + */ + boolean CloseCluster(Pointer hCluster); + + /* + * HCHANGE WINAPI CreateClusterNotifyPort( __in HCHANGE hChange, __in + * HCLUSTER hCluster, __in DWORD dwFilter, __in DWORD_PTR dwNotifyKey ); + */ + Pointer CreateClusterNotifyPort(Pointer hChange, Pointer hCluster, int dwFilter, IntByReference dwNotifyKey); + + final static int CLUSTER_CHANGE_GROUP_STATE = 0x00001000; + final static int CLUSTER_CHANGE_HANDLE_CLOSE = 0x80000000; + final static int CLUSTER_CHANGE_CLUSTER_RECONNECT = 0x00080000; + final static int CLUSTER_CHANGE_CLUSTER_STATE = 0x20000000; + final static int CLUSTER_CHANGE_GROUP_DELETED = 0x00002000; + final static int CLUSTER_CHANGE_GROUP_ADDED = 0x00004000; + + // Results + final static int WAIT_TIMEOUT = 0x102; + final static int ERROR_SUCCESS = 0x0; + final static int ERROR_NO_MORE_ITEMS = 0x103; + + // GetClusterGroupState results + final static int CLUSTER_GROUP_STATE_UNKNOWN = -1; + final static int CLUSTER_GROUP_ONLINE = 0; + final static int CLUSTER_GROUP_OFFLINE = 1; + final static int CLUSTER_GROUP_FAILED = 2; + final static int CLUSTER_GROUP_PARTIAL_ONLINE = 3; + final static int CLUSTER_GROUP_PENDING = 4; + + enum ClusterGroupState + { + Unknown, Online, Offline, Failed, PartialOnline, Pending; + public static ClusterGroupState parse(int val) + { + switch (val) + { + case CLUSTER_GROUP_STATE_UNKNOWN: + return Unknown; + case CLUSTER_GROUP_ONLINE: + return Online; + case CLUSTER_GROUP_OFFLINE: + return Offline; + case CLUSTER_GROUP_FAILED: + return Failed; + case CLUSTER_GROUP_PARTIAL_ONLINE: + return PartialOnline; + case CLUSTER_GROUP_PENDING: + return Pending; + default: + _log.severe("unknown cluster state: " + val); + return Unknown; + } + } + } + + /* + * DWORD WINAPI GetClusterNotify( __in HCHANGE hChange, __out DWORD_PTR + * *lpdwNotifyKey, __out LPDWORD lpdwFilterType, __out LPWSTR lpszName, + * __inout LPDWORD lpcchName, __in_opt DWORD dwMilliseconds ); + */ + int GetClusterNotify(Pointer hChange, IntByReference lpdwNotifyKey, IntByReference lpdwFilterType, Memory lpszName, IntByReference lpcchName, + int dwMilliseconds); + + /* + * BOOL WINAPI CloseClusterNotifyPort( __in HCHANGE hChange ); + */ + boolean CloseClusterNotifyPort(Pointer hChange); + + /* + * HNODE WINAPI OpenClusterNode( __in HCLUSTER hCluster, __in LPCWSTR + * lpszNodeName ); + */ + Pointer OpenClusterNode(Pointer hCluster, WString lpszNodeName); + + /* + * BOOL WINAPI CloseClusterNode( __in HNODE hNode ); + */ + boolean CloseClusterNode(Pointer hNode); + + /* + * CLUSTER_NODE_STATE WINAPI GetClusterNodeState( __in HNODE hNode ); + */ + int GetClusterNodeState(Pointer hNode); + + /* + * HCLUSENUM WINAPI ClusterOpenEnum( __in HCLUSTER hCluster, __in DWORD + * dwType ); + */ + Pointer ClusterOpenEnum(Pointer hCluster, int dwType); + + Pointer ClusterNodeOpenEnum(Pointer hNode, int dwType); + + static int CLUSTER_ENUM_NODE = 1; + static int CLUSTER_ENUM_RESOURCE = 4; + static int CLUSTER_ENUM_NETINTERFACE = 32; + static int CLUSTER_ENUM_GROUP = 8; + + /* + * DWORD WINAPI ClusterCloseEnum( __in HCLUSENUM hEnum ); + */ + int ClusterCloseEnum(Pointer hEnum); + + /* + * DWORD WINAPI ClusterEnum( __in HCLUSENUM hEnum, __in DWORD dwIndex, + * __out LPDWORD lpdwType, __out LPWSTR lpszName, __inout LPDWORD + * lpcchName ); + */ + int ClusterEnum(Pointer hEnum, int dwIndex, IntByReference lpdwType, Memory lpszName, IntByReference lpcchName); + + int ClusterNodeEnum(Pointer hEnum, int dwIndex, IntByReference lpdwType, Memory lpszName, IntByReference lpcchName); + + /* + * HRESOURCE WINAPI OpenClusterResource( __in HCLUSTER hCluster, __in + * LPCWSTR lpszResourceName ); + */ + Pointer OpenClusterResource(Pointer hCluster, WString lpszResourceName); + + /* + * HGROUP WINAPI OpenClusterGroup( __in HCLUSTER hCluster, __in LPCWSTR + * lpszGroupName ); + */ + Pointer OpenClusterGroup(Pointer hCluster, WString lpszGroupName); + + /* + * CLUSTER_GROUP_STATE WINAPI GetClusterGroupState( __in HGROUP hGroup, + * __out_opt LPWSTR lpszNodeName, __inout_opt LPDWORD lpcchNodeName ); + */ + int GetClusterGroupState(Pointer hGroup, Memory lpszNodeName, IntByReference lpcchNodeName); + } + + ArrayList _listeners = new ArrayList(); + boolean _stopped = true; + + public String getActiveNode() + { + String activeNode = null; + + try + { + Pointer cluster = Clusapi.INSTANCE.OpenCluster(null); + Pointer hEnum = Clusapi.INSTANCE.ClusterOpenEnum(cluster, Clusapi.CLUSTER_ENUM_GROUP); + int dwIndex = 0; + IntByReference lpdwType = new IntByReference(); + IntByReference lpcchName = new IntByReference(); + Memory lpszName = new Memory(256); + lpszName.clear(); + lpcchName.setValue(256); + int result = 0; + do + { + result = Clusapi.INSTANCE.ClusterEnum(hEnum, dwIndex, lpdwType, lpszName, lpcchName); + if (result == Clusapi.ERROR_SUCCESS) + { + String group = lpszName.getString(0, true); + ClusterGroupInfo info = getGroupNodeInfo(cluster, group); + if (info != null) + activeNode = info.getLocation(); + } + dwIndex++; + } + while (result == 0); + } + catch (Exception ex) + { + _log.log(Level.SEVERE, "Error getting cluster information", ex); + } + return activeNode; + } + + private ClusterGroupInfo getGroupNodeInfo(Pointer cluster, String groupName) + { + ClusterGroupInfo result = null; + try + { + Pointer hGroup = Clusapi.INSTANCE.OpenClusterGroup(cluster, new WString(groupName)); + + if (hGroup == null) + throw new RuntimeException("Clusapi call to OpenClusterGroup returned err code " + MyKernel32.INSTANCE.GetLastError()); + + IntByReference lpcchNodeName = new IntByReference(); + Memory lpszNodeName = new Memory(256); + lpszNodeName.clear(); + lpcchNodeName.setValue(256); + + int state = Clusapi.INSTANCE.GetClusterGroupState(hGroup, lpszNodeName, lpcchNodeName); + String location = lpszNodeName.getString(0, true); + + if (state == Clusapi.CLUSTER_GROUP_STATE_UNKNOWN) + _log.severe("unknown group state for group " + groupName + " err code " + MyKernel32.INSTANCE.GetLastError()); + + result = new ClusterGroupInfo(groupName, state, location); + + MyKernel32.INSTANCE.CloseHandle(hGroup); + } + catch (Exception e) + { + _log.log(Level.SEVERE, "Error while getting GroupActiveNode", e); + } + return result; + } + + public Set getGroupInfo() + { + Pointer hCluster = Clusapi.INSTANCE.OpenCluster(null); + if (hCluster == null) + throw new RuntimeException("Clusapi call to OpenClusterGroup returned err code " + MyKernel32.INSTANCE.GetLastError()); + + Pointer hEnum = Clusapi.INSTANCE.ClusterOpenEnum(hCluster, Clusapi.CLUSTER_ENUM_GROUP); + if (hEnum == null) + throw new RuntimeException("Clusapi call to ClusterOpenEnum returned err code " + MyKernel32.INSTANCE.GetLastError()); + + Set result = new LinkedHashSet(); + + try + { + IntByReference lpdwType = new IntByReference(); + IntByReference lpcchName = new IntByReference(0); + Memory lpszName = new Memory(256); + + int dwIndex = 0; + + int returnValue = 0; + do + { + lpdwType.setValue(0); + lpcchName.setValue(0); + lpszName.clear(); + lpcchName.setValue(256); + + returnValue = Clusapi.INSTANCE.ClusterEnum(hEnum, dwIndex, lpdwType, lpszName, lpcchName); + + if (returnValue == Clusapi.ERROR_SUCCESS) + { + String group = lpszName.getString(0, true); + ClusterGroupInfo info = getGroupNodeInfo(hCluster, group); + if (info != null) + result.add(info); + } + + if ((returnValue != Clusapi.ERROR_NO_MORE_ITEMS) && (returnValue != Clusapi.ERROR_SUCCESS)) + _log.log(Level.SEVERE, "strange returnValue from ClusApi" + returnValue); + + dwIndex++; + } + while (returnValue == 0); + } + catch (Exception e) + { + _log.log(Level.SEVERE, "Error while getting Cluster group information", e); + } + finally + { + MyKernel32.INSTANCE.CloseHandle(hEnum); + MyKernel32.INSTANCE.CloseHandle(hCluster); + } + return result; + } + + public void start() + { + Runnable check = null; + synchronized (this) + { + if (_stopped) + { + check = new Runnable() + { + public void run() + { + IntByReference lpdwNotifyKey = new IntByReference(); + IntByReference lpdwFilterType = new IntByReference(); + IntByReference lpcchName = new IntByReference(); + IntByReference dwNotifyKey = new IntByReference(); + Memory lpszName = new Memory(256); + Pointer minusOne = Pointer.createConstant(-1); + int dwMilliseconds = 300 * 1000; + final int dwFilter = Clusapi.CLUSTER_CHANGE_GROUP_STATE | Clusapi.CLUSTER_CHANGE_HANDLE_CLOSE + | Clusapi.CLUSTER_CHANGE_GROUP_DELETED | Clusapi.CLUSTER_CHANGE_CLUSTER_STATE + | Clusapi.CLUSTER_CHANGE_CLUSTER_RECONNECT | Clusapi.CLUSTER_CHANGE_GROUP_ADDED; + + while (!_stopped) + { + Pointer hCluster = null; + Pointer hChange = null; + + long started = System.currentTimeMillis(); + + try + { + lpdwNotifyKey.setValue(0); + lpdwFilterType.setValue(0); + lpcchName.setValue(0); + dwNotifyKey.setValue(0); + lpszName.clear(); + lpcchName.setValue(256); + + hCluster = Clusapi.INSTANCE.OpenCluster(null); + if (hCluster == null) + _log.severe("ClusApi.OpenCluster returned err code " + MyKernel32.INSTANCE.GetLastError()); + else + { + hChange = Clusapi.INSTANCE.CreateClusterNotifyPort(minusOne, hCluster, dwFilter, dwNotifyKey); + if (hChange == null) + _log.severe("ClusApi.CreateClusterNotifyPort returned err code " + MyKernel32.INSTANCE.GetLastError()); + } + + if (hCluster == null || hChange == null) + Thread.sleep(5000); + else + { + int result = Clusapi.INSTANCE.GetClusterNotify(hChange, lpdwNotifyKey, lpdwFilterType, lpszName, lpcchName, + dwMilliseconds); + + if (result == Clusapi.ERROR_SUCCESS) + doListeners(null, lpdwFilterType.getValue(), lpszName.getString(0, true)); + else if (result != Clusapi.WAIT_TIMEOUT) // 258 + // = + // Wait + // Time + // Out + _log.warning("ClusApi.GetClusterNotify result=" + result); + } + } + catch (Throwable e) + { + _log.log(Level.SEVERE, "Error getting ClusterInformation", e); + } + finally + { + _log.info("check cluster took " + (System.currentTimeMillis() - started) + " ms"); + if (hChange != null) + { + try + { + Clusapi.INSTANCE.CloseClusterNotifyPort(hChange); + } + catch (Throwable e2) + { + e2.printStackTrace(); + } + + MyKernel32.INSTANCE.CloseHandle(hChange); + } + + if (hCluster != null) + MyKernel32.INSTANCE.CloseHandle(hCluster); + } + } + } + }; + _stopped = false; + } + } + new Thread(check, "cluster listener thread").start(); + } + + private void doListeners(String activeNode, int lpdwFilterType, String lpszName) + { + // LOGGING + try + { + switch (lpdwFilterType) + { + case Clusapi.CLUSTER_CHANGE_GROUP_ADDED: + _log.severe("cluster group added: " + lpszName); + break; + + case Clusapi.CLUSTER_CHANGE_GROUP_DELETED: + _log.severe("cluster group deleted: " + lpszName); + break; + + case Clusapi.CLUSTER_CHANGE_GROUP_STATE: + _log.severe("cluster group state changed: " + lpszName); + break; + + case Clusapi.CLUSTER_CHANGE_HANDLE_CLOSE: + _log.severe("The queue receives a notification when a handle associated with a cluster object is closed. " + lpszName); + break; + + case Clusapi.CLUSTER_CHANGE_CLUSTER_RECONNECT: + _log.severe("The queue receives a notification when the connection to the cluster " + + "identified by hCluster is reestablished after a brief disconnect. Some events " + + "generated immediately before or after this event may have been lost. val=" + lpszName); + break; + + case Clusapi.CLUSTER_CHANGE_CLUSTER_STATE: + _log.severe("all attempts to communicate with the cluster failed, val=" + lpszName); + break; + + default: + _log.severe("unknown event id=" + Integer.toHexString(lpdwFilterType) + ", val=" + lpszName); + break; + } + } + catch (Throwable e) + { + _log.log(Level.SEVERE, "Error in Cluster Logging", e); + } + + threadPool.execute(new Runnable() + { + public void run() + { + ArrayList listeners = new ArrayList(); + synchronized (_listeners) + { + listeners.addAll(_listeners); + } + + for (ClusterNodeChangeListener l : listeners) + try + { + l.nodeChanged(); + } + catch (Throwable e) + { + _log.log(Level.SEVERE, "Error in ClusterNodeChangeListener.nodeChanged()", e); + } + } + }); + } + + public void addNodeChangeListener(ClusterNodeChangeListener listener) + { + synchronized (_listeners) + { + _listeners.add(listener); + } + } + + public void stop() + { + _stopped = true; + } + + public static void main(String[] args) throws UnknownHostException + { + final Cluster c = new Cluster(); + c.addNodeChangeListener(new ClusterNodeChangeListener() + { + public void nodeChanged() + { + try + { + System.out.println("new GroupInfo" + c.getGroupInfo()); + } + catch (Exception e) + { + e.printStackTrace(); + } + } + }); + c.start(); + try + { + Thread.sleep(Integer.MAX_VALUE); + } + catch (InterruptedException e) + { + e.printStackTrace(); + } + } + + public class ClusterGroupInfo + { + final private String _groupName; + final private String _location; + ClusterGroupState _state = ClusterGroupState.Unknown; + + public ClusterGroupInfo(String groupName, int state, String location) + { + _groupName = groupName; + _location = location; + _state = ClusterGroupState.parse(state); + } + + public String getGroupName() + { + return _groupName; + } + + public String getLocation() + { + return _location; + } + + public ClusterGroupState getState() + { + return _state; + } + + public boolean equals(ClusterGroupInfo info) + { + if (super.equals(info)) + return true; + + if (StringUtils.equals(_location, info._location) && StringUtils.equals(_groupName, info._groupName) && _state == info._state) + return true; + + return false; + } + + @Override + public String toString() + { + return _groupName + "(" + _state + ", " + _location + ")"; + } + } +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/ms/win/w32/Cluster.java.old b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/ms/win/w32/Cluster.java.old new file mode 100644 index 0000000000..57220ae384 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/ms/win/w32/Cluster.java.old @@ -0,0 +1,331 @@ +package org.rzo.yajsw.os.ms.win.w32; + +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import org.rzo.yajsw.os.ms.win.w32.WindowsXPProcess.MyKernel32; + +import com.sun.jna.Memory; +import com.sun.jna.Native; +import com.sun.jna.Pointer; +import com.sun.jna.PointerType; +import com.sun.jna.WString; +import com.sun.jna.ptr.IntByReference; +import com.sun.jna.win32.StdCallLibrary; + +public class Cluster +{ + public interface Clusapi extends StdCallLibrary + { + Clusapi INSTANCE = (Clusapi) Native.loadLibrary("ClusApi", Clusapi.class); + + /* + * HCLUSTER WINAPI OpenCluster( __in_opt LPCWSTR lpszClusterName ); + */ + Pointer OpenCluster(WString lpszClusterName); + + /* + * BOOL WINAPI CloseCluster( __in HCLUSTER hCluster ); + */ + boolean CloseCluster(Pointer hCluster); + + /* + * HCHANGE WINAPI CreateClusterNotifyPort( __in HCHANGE hChange, __in + * HCLUSTER hCluster, __in DWORD dwFilter, __in DWORD_PTR dwNotifyKey ); + */ + Pointer CreateClusterNotifyPort(Pointer hChange, Pointer hCluster, int dwFilter, IntByReference dwNotifyKey); + + static int CLUSTER_CHANGE_GROUP_STATE = 0x00001000; + + /* + * DWORD WINAPI GetClusterNotify( __in HCHANGE hChange, __out DWORD_PTR + * *lpdwNotifyKey, __out LPDWORD lpdwFilterType, __out LPWSTR lpszName, + * __inout LPDWORD lpcchName, __in_opt DWORD dwMilliseconds ); + */ + int GetClusterNotify(Pointer hChange, IntByReference lpdwNotifyKey, IntByReference lpdwFilterType, Memory lpszName, IntByReference lpcchName, + int dwMilliseconds); + + /* + * BOOL WINAPI CloseClusterNotifyPort( __in HCHANGE hChange ); + */ + boolean CloseClusterNotifyPort(Pointer hChange); + + /* + * HNODE WINAPI OpenClusterNode( __in HCLUSTER hCluster, __in LPCWSTR + * lpszNodeName ); + */ + Pointer OpenClusterNode(Pointer hCluster, WString lpszNodeName); + + /* + * BOOL WINAPI CloseClusterNode( __in HNODE hNode ); + */ + boolean CloseClusterNode(Pointer hNode); + + /* + * CLUSTER_NODE_STATE WINAPI GetClusterNodeState( __in HNODE hNode ); + */ + int GetClusterNodeState(Pointer hNode); + + /* + * HCLUSENUM WINAPI ClusterOpenEnum( __in HCLUSTER hCluster, __in DWORD + * dwType ); + */ + Pointer ClusterOpenEnum(Pointer hCluster, int dwType); + + Pointer ClusterNodeOpenEnum(Pointer hNode, int dwType); + + static int CLUSTER_ENUM_NODE = 1; + static int CLUSTER_ENUM_RESOURCE = 4; + static int CLUSTER_ENUM_NETINTERFACE = 32; + static int CLUSTER_ENUM_GROUP = 8; + + /* + * DWORD WINAPI ClusterCloseEnum( __in HCLUSENUM hEnum ); + */ + int ClusterCloseEnum(Pointer hEnum); + + /* + * DWORD WINAPI ClusterEnum( __in HCLUSENUM hEnum, __in DWORD dwIndex, + * __out LPDWORD lpdwType, __out LPWSTR lpszName, __inout LPDWORD + * lpcchName ); + */ + int ClusterEnum(Pointer hEnum, int dwIndex, IntByReference lpdwType, Memory lpszName, IntByReference lpcchName); + + int ClusterNodeEnum(Pointer hEnum, int dwIndex, IntByReference lpdwType, Memory lpszName, IntByReference lpcchName); + + /* + * HRESOURCE WINAPI OpenClusterResource( __in HCLUSTER hCluster, __in + * LPCWSTR lpszResourceName ); + */ + Pointer OpenClusterResource(Pointer hCluster, WString lpszResourceName); + + /* + * HGROUP WINAPI OpenClusterGroup( __in HCLUSTER hCluster, __in LPCWSTR + * lpszGroupName ); + */ + Pointer OpenClusterGroup(Pointer hCluster, WString lpszGroupName); + + /* + * CLUSTER_GROUP_STATE WINAPI GetClusterGroupState( __in HGROUP hGroup, + * __out_opt LPWSTR lpszNodeName, __inout_opt LPDWORD lpcchNodeName ); + */ + int GetClusterGroupState(Pointer hGroup, Memory lpszNodeName, IntByReference lpcchNodeName); + + } + + ArrayList _listeners = new ArrayList(); + boolean _stopped = false; + + public String getActiveNode() + { + String activeNode = null; + + try + { + Pointer cluster = Clusapi.INSTANCE.OpenCluster(null); + Pointer hEnum = Clusapi.INSTANCE.ClusterOpenEnum(cluster, Clusapi.CLUSTER_ENUM_GROUP); + int dwIndex = 0; + IntByReference lpdwType = new IntByReference(); + IntByReference lpcchName = new IntByReference(); + Memory lpszName = new Memory(256); + lpszName.clear(); + lpcchName.setValue(256); + int result = 0; + do + { + result = Clusapi.INSTANCE.ClusterEnum(hEnum, dwIndex, lpdwType, lpszName, lpcchName); + if (result == 0) + { + String group = lpszName.getString(0, true); + String node = getGroupActiveNode(cluster, group); + if (node != null) + { + activeNode = node; + } + } + dwIndex++; + } + while (result == 0); + } + catch (Exception ex) + { + ex.printStackTrace(); + } + return activeNode; + + } + + private String getGroupActiveNode(Pointer cluster, String groupName) + { + String activeNode = null; + try + { + Pointer hGroup = Clusapi.INSTANCE.OpenClusterGroup(cluster, new WString(groupName)); + + IntByReference lpcchNodeName = new IntByReference(); + Memory lpszNodeName = new Memory(256); + lpszNodeName.clear(); + lpcchNodeName.setValue(256); + + int result = Clusapi.INSTANCE.GetClusterGroupState(hGroup, lpszNodeName, lpcchNodeName); + if (result == 0) + activeNode = lpszNodeName.getString(0, true); + } + catch (Exception ex) + { + ex.printStackTrace(); + } + + return activeNode; + } + + public Map getGroupInfo() + { + Pointer hCluster = Clusapi.INSTANCE.OpenCluster(null); + if (hCluster == null) + throw new RuntimeException("Clusapi call to OpenClusterGroup returned err code " + Native.getLastError()); + + Pointer hEnum = Clusapi.INSTANCE.ClusterOpenEnum(hCluster, Clusapi.CLUSTER_ENUM_GROUP); + if (hEnum == null) + throw new RuntimeException("Clusapi call to ClusterOpenEnum returned err code " + Native.getLastError()); + + Map result = new HashMap(); + + try + { + IntByReference lpdwType = new IntByReference(); + IntByReference lpcchName = new IntByReference(0); + Memory lpszName = new Memory(256); + + int dwIndex = 0; + + int returnValue = 0; + do + { + lpdwType.setValue(0); + lpcchName.setValue(0); + lpszName.clear(); + lpcchName.setValue(256); + + returnValue = Clusapi.INSTANCE.ClusterEnum(hEnum, dwIndex, lpdwType, lpszName, lpcchName); + + if (returnValue == 0) + { + String group = lpszName.getString(0, true); + String node = getGroupActiveNode(hCluster, group); + if (node != null) + result.put(group, node); + } + + if (!(returnValue == 259 || returnValue == 0)) + System.out.println("strange returnValue from ClusApi " + returnValue); + + dwIndex++; + } + while (returnValue == 0); + } + catch (Exception ex) + { + System.out.println("Error while getting Cluster group information"); + ex.printStackTrace(); + + } + finally + { + MyKernel32.INSTANCE.CloseHandle(hEnum); + MyKernel32.INSTANCE.CloseHandle(hCluster); + } + return Collections.unmodifiableMap(result); + } + + public void start() + { + if (!_stopped) + return; + Pointer cluster = null; + Pointer hChange = null; + // the cluster may not be up yet -> sleep and try again. + while (cluster == null || hChange == null) + { + try + { + cluster = Clusapi.INSTANCE.OpenCluster(null); + IntByReference dwNotifyKey = new IntByReference(); + dwNotifyKey.setValue(0); + hChange = Clusapi.INSTANCE.CreateClusterNotifyPort(Pointer.createConstant(-1), cluster, Clusapi.CLUSTER_CHANGE_GROUP_STATE, + dwNotifyKey); + } + catch (Throwable ex) + { + ex.printStackTrace(); + } + if (cluster == null || hChange == null) + try + { + Thread.sleep(5000); + } + catch (InterruptedException e) + { + e.printStackTrace(); + return; + } + } + final Pointer change = hChange; + Runnable check = new Runnable() + { + public void run() + { + while (!_stopped) + { + IntByReference lpdwNotifyKey = new IntByReference(); + IntByReference lpdwFilterType = new IntByReference(); + IntByReference lpcchName = new IntByReference(); + Memory lpszName = new Memory(256); + lpcchName.setValue(256); + int dwMilliseconds = 10000; + int result = Clusapi.INSTANCE.GetClusterNotify(change, lpdwNotifyKey, lpdwFilterType, lpszName, lpcchName, dwMilliseconds); + if (result == 0) + { + String activeNode = getActiveNode(); + System.out.println("cluster change " + activeNode); + if (activeNode != null) + doListeners(activeNode); + } + } + } + }; + new Thread(check).start(); + } + + private void doListeners(String activeNode) + { + for (Iterator it = _listeners.iterator(); it.hasNext();) + { + ClusterNodeChangeListener l = (ClusterNodeChangeListener) it.next(); + l.nodeChanged(); + } + } + + public void addNodeChangeListener(ClusterNodeChangeListener listener) + { + _listeners.add(listener); + } + + public void stop() + { + _stopped = true; + } + + public static void main(String[] args) throws UnknownHostException + { + Cluster c = new Cluster(); + System.out.println(c.getActiveNode()); + c.start(); + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/ms/win/w32/ClusterNodeChangeListener.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/ms/win/w32/ClusterNodeChangeListener.java new file mode 100644 index 0000000000..01faaf733d --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/ms/win/w32/ClusterNodeChangeListener.java @@ -0,0 +1,7 @@ +package org.rzo.yajsw.os.ms.win.w32; + +public interface ClusterNodeChangeListener +{ + public void nodeChanged(); + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/ms/win/w32/DummyWindow.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/ms/win/w32/DummyWindow.java new file mode 100644 index 0000000000..2249676fc5 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/ms/win/w32/DummyWindow.java @@ -0,0 +1,800 @@ +/* This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + *

    + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ +package org.rzo.yajsw.os.ms.win.w32; + +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.Semaphore; + +import jnacontrib.jna.Options; + +import org.apache.commons.collections.BidiMap; +import org.apache.commons.collections.MultiHashMap; +import org.apache.commons.collections.bidimap.DualHashBidiMap; +import org.rzo.yajsw.os.ms.win.w32.DummyWindow.MyUser32.MSG; +import org.rzo.yajsw.os.ms.win.w32.DummyWindow.MyUser32.WNDCLASSEX; +import org.rzo.yajsw.os.ms.win.w32.DummyWindow.MyUser32.WNDPROC; + +import com.sun.jna.Callback; +import com.sun.jna.Native; +import com.sun.jna.Pointer; +import com.sun.jna.Structure; +import com.sun.jna.platform.win32.GDI32; +import com.sun.jna.platform.win32.Kernel32; +import com.sun.jna.ptr.PointerByReference; + +// TODO: Auto-generated Javadoc +/** + * The Class DummyWindow. + */ +public class DummyWindow +{ + + /** + * The Interface MyKernel32. + */ + public interface MyKernel32 extends Kernel32 + { + + /** The INSTANCE. */ + MyKernel32 INSTANCE = (MyKernel32) Native.loadLibrary("Kernel32", MyKernel32.class); + + /* + * HMODULE WINAPI GetModuleHandle( __in LPCTSTR lpModuleName ); + */ + /** + * Gets the module handle a. + * + * @param lpModuleName + * the lp module name + * + * @return the pointer + */ + Pointer GetModuleHandleA(String lpModuleName); + + /* + * BOOL WINAPI GetModuleHandleEx( __in DWORD dwFlags, __in LPCTSTR + * lpModuleName, __out HMODULE phModule ); + */ + /** + * Gets the module handle ex a. + * + * @param dwFlags + * the dw flags + * @param lpModuleName + * the lp module name + * @param phModule + * the ph module + * + * @return true, if successful + */ + boolean GetModuleHandleExA(int dwFlags, String lpModuleName, PointerByReference phModule); + + /** + * Global add atom a. + * + * @param key + * the key + * + * @return the int + */ + int GlobalAddAtomA(String key); + + }// Kernel32 + + /** + * The Interface MyUser32. + */ + public abstract interface MyUser32 extends com.sun.jna.platform.win32.User32 + { + // Method declarations, constant and structure definitions go here + + /** The INSTANCE. */ + MyUser32 INSTANCE = (MyUser32) Native.loadLibrary("User32", MyUser32.class); + + /** + * Register hot key. + * + * @param hWnd + * the h wnd + * @param id + * the id + * @param fsModifiers + * the fs modifiers + * @param vk + * the vk + * + * @return the int + */ + int RegisterHotKey(Pointer hWnd, int id, int fsModifiers, int vk); + + /* + * BOOL UnregisterHotKey( HWND hWnd, int id ); + */ + /** + * Unregister hot key. + * + * @param hWnd + * the h wnd + * @param id + * the id + * + * @return the int + */ + boolean UnregisterHotKey(Pointer hWnd, int id); + + /* + * LRESULT CALLBACK WindowProc( HWND hwnd, UINT uMsg, WPARAM wParam, + * LPARAM lParam ); + */ + /** + * The Interface WNDPROC. + */ + interface WNDPROC extends Callback + { + + /** + * Callback. + * + * @param hwnd + * the hwnd + * @param uMsg + * the u msg + * @param wParam + * the w param + * @param lParam + * the l param + * + * @return the int + */ + int callback(Pointer hwnd, int uMsg, int wParam, int lParam); + } + + /** The W m_ hotkey. */ + int WM_HOTKEY = 786; + + /* + * typedef struct { UINT cbSize; UINT style; WNDPROC lpfnWndProc; int + * cbClsExtra; int cbWndExtra; HINSTANCE hInstance; HICON hIcon; HCURSOR + * hCursor; HBRUSH hbrBackground; LPCTSTR lpszMenuName; LPCTSTR + * lpszClassName; HICON hIconSm; } WNDCLASSEX,PWNDCLASSEX; + */ + /** The C s_ hredraw. */ + int CS_HREDRAW = 2; + + /** The C s_ vredraw. */ + int CS_VREDRAW = 1; + + /** + * The Class WNDCLASSEX. + */ + static class WNDCLASSEX extends Structure + { + + /** The cb size. */ + public int cbSize = size(); + + /** The style. */ + public int style = 0; + + /** The lpfn wnd proc. */ + public WNDPROC lpfnWndProc; + + /** The cb cls extra. */ + public int cbClsExtra = 0; + + /** The cb wnd extra. */ + public int cbWndExtra = 0; + + /** The h instance. */ + public Pointer hInstance; + + /** The h icon. */ + public Pointer hIcon = null; + + /** The h cursor. */ + public Pointer hCursor = null; + + /** The hbr background. */ + public Pointer hbrBackground = null; + + /** The lpsz menu name. */ + public String lpszMenuName = null; + + /** The lpsz class name. */ + public String lpszClassName = "JavaDummyWnd"; + + /** The h icon sm. */ + public Pointer hIconSm = null; + } + + /* + * ATOM RegisterClassEx( CONST WNDCLASSEXlpwcx ); + */ + /** + * Register class ex a. + * + * @param lpwcx + * the lpwcx + * + * @return the int + */ + int RegisterClassExA(WNDCLASSEX lpwcx); + + /* + * BOOL UnregisterClass( LPCTSTR lpClassName, HINSTANCE hInstance ); + */ + /** + * Unregister class w. + * + * @param lpClassName + * the lp class name + * @param hInstance + * the h instance + * + * @return the int + */ + int UnregisterClassW(String lpClassName, Pointer hInstance); + + /* + * HWND CreateWindowEx( DWORD dwExStyle, LPCTSTR lpClassName, LPCTSTR + * lpWindowName, DWORD dwStyle, int x, int y, int nWidth, int nHeight, + * HWND hWndParent, HMENU hMenu, HINSTANCE hInstance, LPVOID lpParam ); + */ + + /** The W s_ overlappedwindow. */ + int WS_OVERLAPPEDWINDOW = 0xcf0000; + + /** The W s_ e x_ clientedge. */ + int WS_EX_CLIENTEDGE = 512; + + /** The W s_ overlapped. */ + int WS_OVERLAPPED = 0; + + /** The W s_ visible. */ + int WS_VISIBLE = 0x10000000; + + /** + * Creates the window ex a. + * + * @param dwExStyle + * the dw ex style + * @param lpClassName + * the lp class name + * @param lpWindowName + * the lp window name + * @param dwStyle + * the dw style + * @param x + * the x + * @param y + * the y + * @param nWidth + * the n width + * @param nHeight + * the n height + * @param hWndParent + * the h wnd parent + * @param hMenu + * the h menu + * @param hInstance + * the h instance + * @param lpParam + * the lp param + * + * @return the pointer + */ + Pointer CreateWindowExA(int dwExStyle, String lpClassName, String lpWindowName, int dwStyle, int x, int y, int nWidth, int nHeight, + Pointer hWndParent, Pointer hMenu, Pointer hInstance, Pointer lpParam); + + /* + * LONG_PTR SetWindowLongPtr( HWND hWnd, int nIndex, LONG_PTR dwNewLong + * ); + */ + /** + * Sets the window long a. + * + * @param hWnd + * the h wnd + * @param nIndex + * the n index + * @param dwNewLong + * the dw new long + * + * @return the pointer + */ + Pointer SetWindowLongA(Pointer hWnd, int nIndex, Pointer dwNewLong); + + /* + * LONG_PTR GetWindowLongPtr( HWND hWnd, int nIndex ); + */ + /** + * Gets the window long a. + * + * @param hWnd + * the h wnd + * @param nIndex + * the n index + * + * @return the pointer + */ + Pointer GetWindowLongA(Pointer hWnd, int nIndex); + + /** The GWL p_ userdata. */ + int GWLP_USERDATA = -21; + + /* + * LRESULT DefWindowProc( HWND hWnd, UINT Msg, WPARAM wParam, LPARAM + * lParam ); + */ + /** + * Def window proc a. + * + * @param hWnd + * the h wnd + * @param Msg + * the msg + * @param wParam + * the w param + * @param lParam + * the l param + * + * @return the int + */ + int DefWindowProcA(Pointer hWnd, int Msg, int wParam, int lParam); + + /* + * BOOL ShowWindow( HWND hWnd, int nCmdShow ); + */ + /** + * Show window. + * + * @param hWnd + * the h wnd + * @param nCmdShow + * the n cmd show + * + * @return the int + */ + int ShowWindow(Pointer hWnd, int nCmdShow); + + /** The S w_ hide. */ + int SW_HIDE = 0; + + /** The S w_ show. */ + int SW_SHOW = 5; + + /** The S w_ shownormal. */ + int SW_SHOWNORMAL = 1; + + /* + * BOOL UpdateWindow( HWND hWnd // handle to window ); + */ + /** + * Update window. + * + * @param hWnd + * the h wnd + * + * @return the int + */ + int UpdateWindow(Pointer hWnd // handle to window + ); + + /* + * typedef struct { HWND hwnd; UINT message; WPARAM wParam; LPARAM + * lParam; DWORD time; POINT pt; } MSG,PMSG; + */ + /** + * The Class MSG. + */ + class MSG extends Structure + { + + /** The hwnd. */ + public Pointer hwnd; + + /** The message. */ + public int message; + + /** The w param. */ + public int wParam; + + /** The l param. */ + public int lParam; + + /** The time. */ + public int time; + + /** The pt. */ + public POINT pt; + } + + /* + * BOOL GetMessage( LPMSG lpMsg, HWND hWnd, UINT wMsgFilterMin, UINT + * wMsgFilterMax ); + */ + /** + * Gets the message a. + * + * @param lpMsg + * the lp msg + * @param hWnd + * the h wnd + * @param wMsgFilterMin + * the w msg filter min + * @param wMsgFilterMax + * the w msg filter max + * + * @return the int + */ + int GetMessageA(MSG lpMsg, Pointer hWnd, int wMsgFilterMin, int wMsgFilterMax); + + /* + * LRESULT DispatchMessage( const MSGlpmsg ); + */ + /** + * Dispatch message a. + * + * @param lpmsg + * the lpmsg + * + * @return the int + */ + int DispatchMessageA(MSG lpmsg); + + } // user32 + + /** + * The Interface MyGdi32. + */ + public interface MyGdi32 extends GDI32 + { + + /** The INSTANCE. */ + MyGdi32 INSTANCE = (MyGdi32) Native.loadLibrary("gdi32", MyGdi32.class, Options.UNICODE_OPTIONS); + + /* + * HGDIOBJ GetStockObject( int fnObject // stock object type ); + */ + /** + * Gets the stock object. + * + * @param fnObject + * the fn object + * + * @return the pointer + */ + Pointer GetStockObject(int fnObject); + + /** The BLAC k_ brush. */ + int BLACK_BRUSH = 4; + + }// Gdi32 + + /** + * The Class CallbackMessage. + */ + static class CallbackMessage + { + + /** The _u msg. */ + int _uMsg; + + /** The _w param. */ + int _wParam; + + /** The _l param. */ + int _lParam; + + /** + * Instantiates a new callback message. + * + * @param uMsg + * the u msg + * @param wParam + * the w param + * @param lParam + * the l param + */ + CallbackMessage(int uMsg, int wParam, int lParam) + { + _uMsg = uMsg; + _wParam = wParam; + _lParam = lParam; + } + } + + /** The _instance. */ + static DummyWindow _instance; + + /** The _listners. */ + static Map _listners = new MultiHashMap(); + + /** The _wnd proc. */ + WndProc _wndProc = new WndProc(); + + /** The _hinstance. */ + Pointer _hinstance; + + /** The _h wnd. */ + Pointer _hWnd; + + /** The _wnd class. */ + WNDCLASSEX _wndClass = new WNDCLASSEX(); + + /** The _queue. */ + static LinkedBlockingQueue _queue = new LinkedBlockingQueue(); + + /** The _hot keys. */ + static BidiMap _hotKeys = new DualHashBidiMap(); + + /** The _semaphore. */ + Semaphore _semaphore = new Semaphore(0); + + /** + * The Class HotKey. + */ + public class HotKey + { + + /** + * Instantiates a new hot key. + * + * @param wParam + * the w param + * @param lParam + * the l param + */ + public HotKey(int wParam, int lParam) + { + _wParam = wParam; + _lParam = lParam; + } + + /** The _w param. */ + int _wParam; + + /** The _l param. */ + int _lParam; + + /* + * (non-Javadoc) + * + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() + { + return _wParam | _lParam; + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) + { + return (obj instanceof HotKey && ((HotKey) obj)._lParam == _lParam && ((HotKey) obj)._lParam == _lParam); + } + } + + /** + * Instance. + * + * @return the dummy window + */ + public static synchronized DummyWindow instance() + { + if (_instance == null) + { + _instance = new DummyWindow(); + _instance.init(); + } + return _instance; + } + + /** + * The Class WndProc. + */ + static class WndProc extends Structure implements WNDPROC + { + + /* + * (non-Javadoc) + * + * @see + * org.rzo.yajsw.os.ms.win.xp.DummyWindow.MyUser32.WNDPROC#callback( + * com.sun.jna.Pointer, int, int, int) + */ + public int callback(Pointer hWnd, int uMsg, int wParam, int lParam) + { + // System.out.println("callback " + uMsg + " " + wParam + " " + + // lParam + " " + "ptr" + " "+ hWnd); + if (_listners.get(new Integer(uMsg)) != null) + { + CallbackMessage msg = new CallbackMessage(uMsg, wParam, lParam); + _queue.offer(msg); + } + int res = MyUser32.INSTANCE.DefWindowProcA(hWnd, uMsg, wParam, lParam); + // System.out.println(">" + res); + return res; + } + + /** + * Instantiates a new wnd proc. + */ + WndProc() + { + super(); + allocateMemory(4); + } + } + + /** + * The Interface WndListner. + */ + public interface WndListner + { + + /** + * Execute. + * + * @param uMsg + * the u msg + * @param wParam + * the w param + * @param lParam + * the l param + * + * @return the int + */ + int execute(int uMsg, int wParam, int lParam); + } + + /** + * Adds the listner. + * + * @param uMsg + * the u msg + * @param listner + * the listner + */ + public void addListner(Integer uMsg, WndListner listner) + { + synchronized (_listners) + { + _listners.put(uMsg, listner); + } + } + + /** + * Removes the listner. + * + * @param listner + * the listner + */ + public void removeListner(WndListner listner) + { + synchronized (_listners) + { + _listners.remove(listner); + } + + } + + /** + * Inits the. + */ + private void init() + { + Runnable r = new Runnable() + { + public void run() + { + _hinstance = MyKernel32.INSTANCE.GetModuleHandleA(null); + + _wndClass.lpfnWndProc = _wndProc; + _wndClass.hInstance = _hinstance; + if (MyUser32.INSTANCE.RegisterClassExA(_wndClass) == 0) + return; + _hWnd = MyUser32.INSTANCE.CreateWindowExA(0, _wndClass.lpszClassName, _wndClass.lpszClassName, 0, 0, 0, 0, 0, null, null, _hinstance, + null); + MyUser32.INSTANCE.ShowWindow(_hWnd, MyUser32.SW_HIDE); + MyUser32.INSTANCE.UpdateWindow(_hWnd); + + MSG msg = new MSG(); + msg.size(); + int ret; + do + { + ret = MyUser32.INSTANCE.GetMessageA(msg, _hWnd, 0, 0); + // System.out.println("get message "+msg); + if (msg.message == WindowsXPKeyboard.WND_REGISTER_HOTKEY) + { + int id = MyKernel32.INSTANCE.GlobalAddAtomA("JHOTKEY-" + msg.wParam + "-" + msg.lParam); + int ret2 = MyUser32.INSTANCE.RegisterHotKey(_hWnd, id, msg.wParam, msg.lParam); + // System.out.println("register: "+id); + if (ret2 != 0) + _hotKeys.put(new Integer(id), new HotKey(msg.wParam, msg.lParam)); + // System.out.println("register "+ret2); + _semaphore.release(); + } + else if (msg.message == WindowsXPKeyboard.WND_UNREGISTER_HOTKEY) + { + HotKey k = new HotKey(msg.wParam, msg.lParam); + Integer id = (Integer) _hotKeys.getKey(k); + // System.out.println("unregister: "+id); + if (id != null) + if (MyUser32.INSTANCE.UnregisterHotKey(_hWnd, id.intValue())) + _hotKeys.remove(id); + _semaphore.release(); + + } + else + MyUser32.INSTANCE.DispatchMessageA(msg); + } + while (ret != 0); + } + }; + Runnable r2 = new Runnable() + { + public void run() + { + CallbackMessage msg; + try + { + while (true) + { + msg = (CallbackMessage) _queue.take(); + // System.out.println("Message queue: "+msg._uMsg); + Collection listners = (Collection) _listners.get(new Integer(msg._uMsg)); + if (listners != null) + for (Iterator it = listners.iterator(); it.hasNext();) + { + WndListner listner = (WndListner) it.next(); + listner.execute(msg._uMsg, msg._wParam, msg._lParam); + } + } + } + catch (Exception e) + { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + } + }; + new Thread(r2).start(); + new Thread(r).start(); + _semaphore.release(); + + } + + /** + * Wait termination. + * + * @throws InterruptedException + * the interrupted exception + */ + void waitTermination() throws InterruptedException + { + // System.out.println("+ wait termination "+ + // System.currentTimeMillis()); + _semaphore.acquire(); + // System.out.println("- wait termination "+ + // System.currentTimeMillis()); + } +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/ms/win/w32/FileUtils.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/ms/win/w32/FileUtils.java new file mode 100644 index 0000000000..933cb88149 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/ms/win/w32/FileUtils.java @@ -0,0 +1,265 @@ +/* This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + *

    + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ +package org.rzo.yajsw.os.ms.win.w32; + +import java.io.File; +import java.io.FileFilter; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; +import java.util.logging.Logger; + +import org.apache.commons.io.filefilter.WildcardFileFilter; + +import com.sun.jna.platform.win32.Kernel32; +import com.sun.jna.platform.win32.Kernel32Util; +import com.sun.jna.platform.win32.WinNT; + +// TODO: Auto-generated Javadoc +/** + * The Class FileUtils. + */ +public class FileUtils +{ + static Logger log = Logger.getLogger(FileUtils.class.getName()); + + public static long created(File file) + { + + throw new UnsupportedOperationException("Sorry, not implemented in this verson"); + // long result = -1; + // SYSTEMTIME sysTime = new SYSTEMTIME(); + // sysTime.size(); + // Pointer hFile = + // MyKernel32.INSTANCE.CreateFile(file.getAbsolutePath(), + // MyKernel32..GENERIC_READ, 0, null, MyKernel32.OPEN_EXISTING, 0, + // null); + // if (hFile != null) + // { + // LongByReference created = new LongByReference(); + // if (MyKernel32.INSTANCE.GetFileTime(hFile, created, null, null)) + // { + // // if (MyKernel32.INSTANCE.FileTimeToSystemTime(created, sysTime)) + // // { + // // Calendar c = Calendar.getInstance(); + // // c.setTimeZone(TimeZone.getTimeZone("UTC")); + // // c.set(sysTime.wYear, sysTime.wMonth-1, sysTime.wDay, + // sysTime.wHour, sysTime.wMinute, sysTime.wSecond); + // // result = c.getTimeInMillis(); + // // System.out.println(created.getValue() + " "+ result); + // // } + // // found in /org/apache/poi/hpsf/Util.java + // final long ms_since_16010101 = created.getValue() / (1000 * 10); + // final long ms_since_19700101 = ms_since_16010101 - EPOCH_DIFF; + // result = ms_since_19700101; + // } + // + // MyKernel32.INSTANCE.CloseHandle(hFile); + // + // } + // + // + // return result; + } + + public static long freeSpace(File file) + { + if (!file.isDirectory() || !file.exists()) + return -2; + + WinNT.LARGE_INTEGER.ByReference lpTotalNumberOfFreeBytes = new WinNT.LARGE_INTEGER.ByReference(); + lpTotalNumberOfFreeBytes.clear(); + + boolean ret = Kernel32.INSTANCE.GetDiskFreeSpaceEx(file.getPath(), null, null, lpTotalNumberOfFreeBytes); + if (ret) + return lpTotalNumberOfFreeBytes.getValue(); + else + { + String s = Kernel32Util.formatMessageFromLastErrorCode(Kernel32.INSTANCE.GetLastError()); + log.severe("error in File.freeSpace getting for \"" + file.getPath() + "\" " + s); + } + + return -1; + } + + public static long totalSpace(File file) + { + if (!file.isDirectory() || !file.exists()) + return -2; + + WinNT.LARGE_INTEGER.ByReference lpTotalNumberOfBytes = new WinNT.LARGE_INTEGER.ByReference(); + lpTotalNumberOfBytes.clear(); + + boolean ret = Kernel32.INSTANCE.GetDiskFreeSpaceEx(file.getPath(), null, lpTotalNumberOfBytes, null); + if (ret) + return lpTotalNumberOfBytes.getValue(); + else + { + String s = Kernel32Util.formatMessageFromLastErrorCode(Kernel32.INSTANCE.GetLastError()); + log.severe("error in File.totalSpace getting for \"" + file.getPath() + "\" " + s); + } + + return -1; + + } + + /** + * Gets the files. + * + * @param workingDir + * the working dir + * @param pattern + * the pattern + * + * @return the files + */ + public static Collection getFiles(String workingDir, String pattern) + { + ArrayList result = new ArrayList(); + + // check if we have a non patterned file name + + // check if we have an absolute file + File res = new File(pattern); + if (res.exists() && res.isAbsolute()) + { + result.add(res); + return result; + } + + // check if we have a file relative working dir + if (!res.isAbsolute()) + { + res = new File(workingDir, pattern); + if (res.exists()) + { + result.add(res); + return result; + } + } + + // so this must be a pattern try to figure out the files + String[] s = pattern.split("[" + File.separator + "|/]"); + String[] sh; + if (s.length == 1) + { + sh = new String[2]; + sh[0] = "."; + sh[1] = s[0]; + } + else + sh = s; + + if (pattern.startsWith("/") && "".equals(sh[0])) + sh[0] = "/"; + + Collection paths = new HashSet(); + paths.add(sh[0]); + for (int i = 1; i < sh.length; i++) + { + String file = sh[i]; + Collection newPaths = new HashSet(); + for (Iterator it = paths.iterator(); it.hasNext();) + { + String pathStr = (String) it.next(); + if (pathStr.endsWith(":")) + pathStr += "/"; + File path = new File(pathStr); + if (!path.isDirectory() || !path.exists() || !path.isAbsolute()) + path = new File(workingDir, pathStr); + Collection files = getWildcardFiles(path.getAbsolutePath(), file); + for (Iterator it2 = files.iterator(); it2.hasNext();) + { + File f = (File) it2.next(); + if (f.isDirectory()) + newPaths.add(f.getPath()); + else if (f.isFile()) + result.add(f); + } + } + paths = newPaths; + } + + /* + * String file = s[s.length-1]; String path = pattern.substring(0, + * pattern.lastIndexOf(file)); + * + * if (path == null || path.equals("")) path = "."; File fPath = null; + * try { fPath = new File(path); if (!fPath.isDirectory()) { + * log.warning("classpath directory "+fPath.getCanonicalPath()+" not + * found"); return result; } } catch (Exception ex) { + * log.warning("classpath directory "+path+" error" + ex.getMessage()); + * return result; } FileFilter fileFilter = new + * WildcardFileFilter(file); File[] thisFiles = + * fPath.listFiles(fileFilter); for (int i=0; i< thisFiles.length; i++) + * { File f = thisFiles[i]; if (f.exists()) result.add(f); else + * log.warning("classpath file "+f.getName() +"not found"); } + */ + if (result.size() == 0) + log.warning("No files found for " + pattern); + return result; + } + + /** + * Gets the wildcard files. + * + * @param path + * the path + * @param file + * the file + * + * @return the wildcard files + */ + private static Collection getWildcardFiles(String path, String file) + { + ArrayList result = new ArrayList(); + File fPath = new File(path); + try + { + if (!fPath.isDirectory()) + { + log.warning("classpath directory " + fPath.getCanonicalPath() + " not found"); + return result; + } + } + catch (Exception ex) + { + log.warning("classpath directory " + path + " error" + ex.getMessage()); + return result; + } + FileFilter fileFilter = new WildcardFileFilter(file); + File[] thisFiles = fPath.listFiles(fileFilter); + for (int i = 0; i < thisFiles.length; i++) + { + File f = thisFiles[i]; + if (f.exists()) + result.add(f); + else + log.warning("classpath file " + f.getName() + "not found"); + } + return result; + } + + /** + * The main method. + * + * @param args + * the arguments + */ + public static void main(String[] args) + { + System.out.println(FileUtils.freeSpace(new File("C:\\"))); + System.out.println(FileUtils.totalSpace(new File("C:\\"))); + for (Iterator it = getFiles(".", "lib/*/*/*jar").iterator(); it.hasNext();) + System.out.println(it.next()); + } +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/ms/win/w32/OperatingSystemWindowsXP.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/ms/win/w32/OperatingSystemWindowsXP.java new file mode 100644 index 0000000000..e5c0be87a2 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/ms/win/w32/OperatingSystemWindowsXP.java @@ -0,0 +1,130 @@ +/* This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + *

    + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ +package org.rzo.yajsw.os.ms.win.w32; + +import org.apache.commons.configuration.Configuration; +import org.rzo.yajsw.os.ErrorHandler; +import org.rzo.yajsw.os.FileManager; +import org.rzo.yajsw.os.JavaHome; +import org.rzo.yajsw.os.Keyboard; +import org.rzo.yajsw.os.Mouse; +import org.rzo.yajsw.os.OperatingSystem; +import org.rzo.yajsw.os.ProcessManager; +import org.rzo.yajsw.os.ServiceManager; +import org.rzo.yajsw.os.SystemInformation; + +// TODO: Auto-generated Javadoc +/** + * The Class OperatingSystemWindowsXP. + */ +public class OperatingSystemWindowsXP extends OperatingSystem +{ + + /** The _keyboard instance. */ + static Keyboard _keyboardInstance; + static Mouse _mouseInstance; + + /** The _process manager. */ + static ProcessManager _processManager; + static FileManager _fileManager; + + /** The _process manager. */ + static ServiceManager _serviceManager; + + /** The _error handler. */ + static ErrorHandler _errorHandler = new WindowsXPErrorHandler(); + + static SystemInformation _systemInformation = new WindowsXPSystemInformation(); + + /* + * (non-Javadoc) + * + * @see org.rzo.yajsw.os.OperatingSystem#keyboardInstance() + */ + @Override + public Keyboard keyboardInstance() + { + if (_keyboardInstance == null) + _keyboardInstance = WindowsXPKeyboard.instance(); + return _keyboardInstance; + } + + public Mouse mouseInstance() + { + if (_mouseInstance == null) + _mouseInstance = WindowsXPMouse.instance(); + return _mouseInstance; + } + + /* + * (non-Javadoc) + * + * @see org.rzo.yajsw.os.OperatingSystem#processManagerInstance() + */ + @Override + public ProcessManager processManagerInstance() + { + if (_processManager == null) + _processManager = WindowsXPProcessManager.instance(); + return _processManager; + } + + public FileManager fileManagerInstance() + { + if (_fileManager == null) + _fileManager = WindowsXPFileManager.instance(); + return _fileManager; + } + + /* + * (non-Javadoc) + * + * @see org.rzo.yajsw.os.OperatingSystem#errorHandlerInstance() + */ + @Override + public ErrorHandler errorHandlerInstance() + { + // TODO Auto-generated method stub + return _errorHandler; + } + + public JavaHome getJavaHome(Configuration config) + { + return new WindowsJavaHome(config); + } + + @Override + public ServiceManager serviceManagerInstance() + { + if (_serviceManager == null) + _serviceManager = WindowsXPServiceManager.instance(); + return _serviceManager; + } + + @Override + public SystemInformation systemInformation() + { + return _systemInformation; + } + + @Override + public boolean setWorkingDir(String name) + { + return new WindowsXPProcess().changeWorkingDir(name); + } + + @Override + public Object getServiceFailureActions(Configuration config) + { + return WindowsXPService.getServiceFailureActions(config); + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/ms/win/w32/Pdh.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/ms/win/w32/Pdh.java new file mode 100644 index 0000000000..62b3c47966 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/ms/win/w32/Pdh.java @@ -0,0 +1,1005 @@ +/* This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + *

    + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ +package org.rzo.yajsw.os.ms.win.w32; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.apache.commons.collections.Bag; +import org.apache.commons.collections.bag.HashBag; +import org.apache.commons.lang.StringUtils; +import org.rzo.yajsw.util.File; + +import com.sun.jna.Memory; +import com.sun.jna.Native; +import com.sun.jna.Pointer; +import com.sun.jna.Structure; +import com.sun.jna.Union; +import com.sun.jna.WString; +import com.sun.jna.ptr.IntByReference; +import com.sun.jna.ptr.PointerByReference; +import com.sun.jna.win32.StdCallLibrary; + +// TODO: Auto-generated Javadoc +/** + * The Class Pdh. + */ +public class Pdh +{ + + /** + * The Interface Pdhdll. + */ + interface Pdhdll extends StdCallLibrary + { + + /** The INSTANCE. */ + Pdhdll INSTANCE = (Pdhdll) Native.loadLibrary("pdh", Pdhdll.class); + + /* + * PDH_STATUS PdhOpenQuery( __in LPCTSTR szDataSource, __in DWORD_PTR + * dwUserData, __out PDH_HQUERY phQuery ); + */ + /** + * Pdh open query. + * + * @param szDataSource + * the sz data source + * @param dwUserData + * the dw user data + * @param phQuery + * the ph query + * + * @return the int + */ + int PdhOpenQuery(Pointer szDataSource, Pointer dwUserData, PointerByReference phQuery); + + /* + * PDH_STATUS PdhValidatePath( __in LPCTSTR szFullCounterPath ); + */ + /** + * Pdh validate path a. + * + * @param szFullCounterPath + * the sz full counter path + * + * @return the int + */ + int PdhValidatePathA(String szFullCounterPath); + + /* + * PDH_STATUS PdhCollectQueryData( __in_out PDH_HQUERY hQuery ); + */ + /** + * Pdh collect query data. + * + * @param hQuery + * the h query + * + * @return the int + */ + int PdhCollectQueryData(Pointer hQuery); + + /* + * PDH_STATUS PdhGetFormattedCounterValue( __in PDH_HCOUNTER hCounter, + * __in DWORD dwFormat, __out LPDWORD lpdwType, __out + * PPDH_FMT_COUNTERVALUE pValue ); + */ + /** + * Pdh get formatted counter value. + * + * @param hCounter + * the h counter + * @param dwFormat + * the dw format + * @param lpdwType + * the lpdw type + * @param pValue + * the value + * + * @return the int + */ + int PdhGetFormattedCounterValue(Pointer hCounter, int dwFormat, IntByReference lpdwType, Pointer pValue); + + /* + * typedef struct _PDH_FMT_COUNTERVALUE { DWORD CStatus; union { LONG + * longValue; double doubleValue; LONGLONG largeValue; LPCSTR + * AnsiStringValue; LPCWSTR WideStringValue; }; } PDH_FMT_COUNTERVALUE, + * PPDH_FMT_COUNTERVALUE; + */ + + /* + * PDH_STATUS PdhAddCounter( __in PDH_HQUERY hQuery, __in LPCTSTR + * szFullCounterPath, __in DWORD_PTR dwUserData, __out PDH_HCOUNTER + * phCounter ); + */ + /** + * Pdh add counter a. + * + * @param hQuery + * the h query + * @param szFullCounterPath + * the sz full counter path + * @param dwUserData + * the dw user data + * @param phCounter + * the ph counter + * + * @return the int + */ + int PdhAddCounterA(Pointer hQuery, String szFullCounterPath, int dwUserData, PointerByReference phCounter); + + /* + * PDH_STATUS PdhAddEnglishCounter( __in PDH_HQUERY hQuery, __in LPCTSTR + * szFullCounterPath, __in DWORD_PTR dwUserData, __out PDH_HCOUNTER + * phCounter ); + */ + /** + * Pdh add009 counter a. + * + * @param hQuery + * the h query + * @param szFullCounterPath + * the sz full counter path + * @param dwUserData + * the dw user data + * @param phCounter + * the ph counter + * + * @return the int + */ + int PdhAdd009CounterA(Pointer hQuery, String szFullCounterPath, int dwUserData, PointerByReference phCounter); + + /** The ERRO r_ success. */ + int ERROR_SUCCESS = 0; + + /** The PD h_ fm t_ double. */ + int PDH_FMT_DOUBLE = 0x00000200; + + /** The PD h_ fm t_ long. */ + int PDH_FMT_LONG = 0x00000100; + + /* + * PDH_STATUS PdhRemoveCounter( __in PDH_HCOUNTER hCounter ); + */ + /** + * Pdh remove counter. + * + * @param hCounter + * the h counter + * + * @return the int + */ + int PdhRemoveCounter(Pointer hCounter); + + /* + * PDH_STATUS PdhCloseQuery( __in PDH_HQUERY hQuery ); + */ + /** + * Pdh close query. + * + * @param hQuery + * the h query + * + * @return the int + */ + int PdhCloseQuery(Pointer hQuery); + + /* + * PDH_STATUS PdhEnumObjectItems( __in LPCTSTR szDataSource, __in + * LPCTSTR szMachineName, __in LPCTSTR szObjectName, __out LPTSTR + * mszCounterList, __in_out LPDWORD pcchCounterListLength, __out LPTSTR + * mszInstanceList, __in_out LPDWORD pcchInstanceListLength, __in DWORD + * dwDetailLevel, DWORD dwFlags ); + */ + /** + * Pdh enum object items a. + * + * @param szDataSource + * the sz data source + * @param szMachineName + * the sz machine name + * @param szObjectName + * the sz object name + * @param mszCounterList + * the msz counter list + * @param pcchCounterListLength + * the pcch counter list length + * @param mszInstanceList + * the msz instance list + * @param pcchInstanceListLength + * the pcch instance list length + * @param dwDetailLevel + * the dw detail level + * @param dwFlags + * the dw flags + * + * @return the int + */ + int PdhEnumObjectItemsA(String szDataSource, String szMachineName, String szObjectName, Memory mszCounterList, + IntByReference pcchCounterListLength, Memory mszInstanceList, IntByReference pcchInstanceListLength, int dwDetailLevel, int dwFlags); + + /** The PER f_ detai l_ wizard. */ + int PERF_DETAIL_WIZARD = 400; + + /** The PD h_ mor e_ data. */ + int PDH_MORE_DATA = 0x800007D2; + + /* + * DH_STATUS PdhLookupPerfNameByIndex( __in LPCTSTR szMachineName, __in + * DWORD dwNameIndex, __out LPTSTR szNameBuffer, __in LPDWORD + * pcchNameBufferSize ); + */ + /** + * Pdh lookup perf name by index a. + * + * @param szMachineName + * the sz machine name + * @param dwNameIndex + * the dw name index + * @param szNameBuffer + * the sz name buffer + * @param pcchNameBufferSize + * the pcch name buffer size + * + * @return the int + */ + int PdhLookupPerfNameByIndexA(String szMachineName, int dwNameIndex, Memory szNameBuffer, IntByReference pcchNameBufferSize); + + /* + * PDH_STATUS PdhParseCounterPath( __in LPCTSTR szFullPathBuffer, __out + * PDH_COUNTER_PATH_ELEMENTS pCounterPathElements, __in_out LPDWORD + * pdwBufferSize, DWORD dwFlags ); + */ + /** + * Pdh parse counter path a. + * + * @param szFullPathBuffer + * the sz full path buffer + * @param pCounterPathElements + * the counter path elements + * @param pdwBufferSize + * the pdw buffer size + * @param dwFlags + * the dw flags + * + * @return the int + */ + int PdhParseCounterPathA(String szFullPathBuffer, Pointer pCounterPathElements, IntByReference pdwBufferSize, int dwFlags); + + }// Pdhdll + + /** + * The Interface Advapi32. + */ + interface Advapi32 extends StdCallLibrary + { + + /** The INSTANCE. */ + Advapi32 INSTANCE = (Advapi32) Native.loadLibrary("Advapi32", Advapi32.class); // , + // Options.UNICODE_OPTIONS); + + /* + * LONG WINAPI RegOpenKeyEx( HKEY hKey, LPCTSTR lpSubKey, DWORD + * ulOptions, REGSAM samDesired, PHKEY phkResult ); + */ + /** + * Reg open key ex a. + * + * @param hKey + * the h key + * @param lpSubKey + * the lp sub key + * @param ulOptions + * the ul options + * @param samDesired + * the sam desired + * @param phkResult + * the phk result + * + * @return the int + */ + int RegOpenKeyExA(int hKey, String lpSubKey, int ulOptions, int samDesired, IntByReference phkResult); + + /** The HKE y_ loca l_ machine. */ + int HKEY_LOCAL_MACHINE = 0x80000002; + + /** The KE y_ read. */ + int KEY_READ = 0x20019; + + /* + * LONG WINAPI RegCloseKey( HKEY hKey ); + */ + /** + * Reg close key. + * + * @param hKey + * the h key + * + * @return the int + */ + public int RegCloseKey(int hKey); + + /* + * LONG WINAPI RegQueryValueEx( __in HKEY hKey, __in LPCTSTR + * lpValueName, LPDWORD lpReserved, __out LPDWORD lpType, __out LPBYTE + * lpData, __in_out LPDWORD lpcbData ); + */ + /** + * Reg query value ex a. + * + * @param hKey + * the h key + * @param lpValueName + * the lp value name + * @param lpReserved + * the lp reserved + * @param lpType + * the lp type + * @param lpData + * the lp data + * @param lpcbData + * the lpcb data + * + * @return the int + */ + int RegQueryValueExA(int hKey, String lpValueName, Pointer lpReserved, IntByReference lpType, Pointer lpData, IntByReference lpcbData); + + /** The HKE y_ performanc e_ data. */ + int HKEY_PERFORMANCE_DATA = 0x80000004; + + /** The ERRO r_ mor e_ data. */ + int ERROR_MORE_DATA = 234; + + } + + /** + * The Class PDH_FMT_COUNTERVALUE. + */ + public static class PDH_FMT_COUNTERVALUE extends Structure + { + + /** The C status. */ + public int CStatus; + + /** The Value. */ + public ValueUnion Value; + } + + /** + * The Class ValueUnion. + */ + public static class ValueUnion extends Union + { + + /** The long value. */ + public int longValue; + + /** The double value. */ + public double doubleValue; + + /** The large value. */ + public long largeValue; + + /** The Ansi string value. */ + public String AnsiStringValue; + + /** The Wide string value. */ + public WString WideStringValue; + } + + /** + * The Class COUNTER_PATH_ELEMENTS. + */ + public static class COUNTER_PATH_ELEMENTS + { + + /** The sz machine name. */ + public String szMachineName; + + /** The sz object name. */ + public String szObjectName; + + /** The sz instance name. */ + public String szInstanceName; + + /** The dw instance index. */ + public int dwInstanceIndex; + + /** The sz counter name. */ + public String szCounterName; + + } + + /** The MA x_ counte r_ path. */ + static int MAX_COUNTER_PATH = 256; // Maximum counter path length + + /** The PD h_ ma x_ counte r_ name. */ + static int PDH_MAX_COUNTER_NAME = 1024; // Maximum counter name length. + + /** The PD h_ ma x_ instanc e_ name. */ + static int PDH_MAX_INSTANCE_NAME = 1024; // Maximum counter instance name + // length. + /** The PD h_ ma x_ counte r_ path. */ + static int PDH_MAX_COUNTER_PATH = 2048; // Maximum full counter path + + // length. + + /** + * The Class HddCounter. + */ + static public class HddCounter implements PdhCounter + { + /** The _file. */ + private String _drive; + + private enum HDDInfoType + { + FreeSpaceInPercent, FreeSpaceInBytes, UsedSpaceInBytes, TotalSpaceinBytes, UnknownInfoType + } + + private HDDInfoType _infoType = HDDInfoType.UnknownInfoType; + + /** + * Instantiates a new hdd counter. + * + * @param counter + * the counter + */ + public HddCounter(String counter) + { + _drive = counter.substring(counter.indexOf('(') + 1, counter.indexOf(')')); + + if (counter.endsWith("\\% free space")) + _infoType = HDDInfoType.FreeSpaceInPercent; + else if (counter.endsWith("\\free space")) + _infoType = HDDInfoType.FreeSpaceInBytes; + else if (counter.endsWith("\\used space")) + _infoType = HDDInfoType.UsedSpaceInBytes; + else if (counter.endsWith("\\total space")) + _infoType = HDDInfoType.TotalSpaceinBytes; + } + + /* + * (non-Javadoc) + * + * @see org.rzo.yajsw.os.ms.win.xp.PdhCounter#close() + */ + public void close() + { + + } + + /* + * (non-Javadoc) + * + * @see org.rzo.yajsw.os.ms.win.xp.PdhCounter#getDoubleValue() + */ + public double getDoubleValue() + { + if (!isValid()) + throw new RuntimeException("Cannot find Harddisk drive " + _drive); + File file = new File(_drive); + + switch (_infoType) + { + case FreeSpaceInPercent: + return ((double) file.getFreeSpace() / (double) file.getTotalSpace()) * 100; + case FreeSpaceInBytes: + return file.getFreeSpace(); + case TotalSpaceinBytes: + return file.getTotalSpace(); + case UsedSpaceInBytes: + return file.getTotalSpace() - file.getFreeSpace(); + default: + return -1; + } + } + + /* + * (non-Javadoc) + * + * @see org.rzo.yajsw.os.ms.win.xp.PdhCounter#getIntValue() + */ + public int getIntValue() + { + return (int) getDoubleValue(); + } + + /* + * (non-Javadoc) + * + * @see org.rzo.yajsw.os.ms.win.xp.PdhCounter#isValid() + */ + public boolean isValid() + { + return new File(_drive).exists() && !_infoType.equals(HDDInfoType.UnknownInfoType); + } + + public static void main(String[] args) + { + PdhCounter cc = getEnglishCounter("\\Process(MY_JAVA_VM_PROCESS)\\Handle Count"); + System.out.println("Handle Count: " + cc.getDoubleValue()); + cc = getEnglishCounter("\\Process(MY_JAVA_VM_PROCESS)\\Thread Count"); + System.out.println("Thread Count: " + cc.getDoubleValue()); + cc = getEnglishCounter("\\Process(MY_JAVA_VM_PROCESS)\\Private Bytes"); + System.out.println("Private Bytes: " + cc.getDoubleValue()); + + java.io.File[] drives = File.listRoots(); + PdhCounter c; + for (java.io.File file : drives) + { + String drive = file.getPath(); + drive = StringUtils.remove(drive, '\\'); + System.out.println("Drive " + drive); + try + { + c = getEnglishCounter("\\HddCounter(" + drive + ")\\used space"); + System.out.println("\t UsedSpace " + ((long) c.getDoubleValue()) / (1024 * 1024) + " MB"); + } + catch (Exception e) + { + } + try + { + c = getEnglishCounter("\\HddCounter(" + drive + ")\\free space"); + System.out.println("\t FreeSpace " + ((long) c.getDoubleValue()) / (1024 * 1024) + " MB"); + } + catch (Exception e) + { + } + try + { + c = getEnglishCounter("\\HddCounter(" + drive + ")\\total space"); + System.out.println("\t TotalSpace " + ((long) c.getDoubleValue()) / (1024 * 1024) + " MB"); + } + catch (Exception e) + { + } + try + { + c = getEnglishCounter("\\HddCounter(" + drive + ")\\% free space"); + System.out.println("\t FreeSpace " + ((long) c.getDoubleValue()) + " %"); + } + catch (Exception e) + { + } + } + } + } + + /** + * The Class Counter. + */ + static public class Counter implements PdhCounter + { + + /** The _h query. */ + PointerByReference _hQuery = new PointerByReference(); + + /** The _h counter. */ + PointerByReference _hCounter = new PointerByReference(); + + /** The _counter. */ + String _counter; + + /** + * Instantiates a new counter. + * + * @param counter + * the counter + * @param english + * the english + */ + Counter(String counter, boolean english) + { + int ret = Pdhdll.INSTANCE.PdhOpenQuery(null, null, _hQuery); + _counter = counter; + if (ret != Pdhdll.ERROR_SUCCESS) + System.out.println("Error in PdhOpenQuery " + counter + ": "+ Integer.toHexString(ret)); + + if (english) + ret = Pdhdll.INSTANCE.PdhAdd009CounterA(_hQuery.getValue(), counter, 0, _hCounter); + else + ret = Pdhdll.INSTANCE.PdhAddCounterA(_hQuery.getValue(), counter, 0, _hCounter); + if (ret != Pdhdll.ERROR_SUCCESS) + throw new IllegalArgumentException("PdhCounter: " + counter + " " + ret); + // System.out.println("created counter: "+ _counter + " "+ + // _hQuery.getPointer() + " " +_hCounter.getPointer()+ " "+ + // _hQuery.getValue() + " " +_hCounter.getValue()); + getIntValue(); + } + + /* + * (non-Javadoc) + * + * @see org.rzo.yajsw.os.ms.win.xp.PdhCounter#isValid() + */ + public boolean isValid() + { + return !(_hCounter == null || _hQuery == null || _hQuery.getValue().equals(null) || _hCounter.getValue().equals(null)); + } + + /* + * (non-Javadoc) + * + * @see org.rzo.yajsw.os.ms.win.xp.PdhCounter#getDoubleValue() + */ + public double getDoubleValue() + { + if (!isValid()) + return -1; + PDH_FMT_COUNTERVALUE pdhCounterValue = new PDH_FMT_COUNTERVALUE(); + pdhCounterValue.size(); + int ret = Pdhdll.INSTANCE.PdhCollectQueryData(_hQuery.getValue()); + if (ret != Pdhdll.ERROR_SUCCESS) + System.out.println("Error in PdhCollectQueryData " + _counter + ": " + Integer.toHexString(ret)); + PointerByReference result = new PointerByReference(); + ret = Pdhdll.INSTANCE.PdhGetFormattedCounterValue(_hCounter.getValue(), Pdhdll.PDH_FMT_DOUBLE, null, pdhCounterValue.getPointer()); + if (ret != Pdhdll.ERROR_SUCCESS) + System.out.println("Error in PdhGetFormattedCounterValue " + _counter + ": " + Integer.toHexString(ret)); + else + { + pdhCounterValue.read(); + return pdhCounterValue.Value.doubleValue; + } + return -1; + } + + /* + * (non-Javadoc) + * + * @see org.rzo.yajsw.os.ms.win.xp.PdhCounter#getIntValue() + */ + public int getIntValue() + { + if (!isValid()) + return -1; + PDH_FMT_COUNTERVALUE pdhCounterValue = new PDH_FMT_COUNTERVALUE(); + pdhCounterValue.size(); + int ret = Pdhdll.INSTANCE.PdhCollectQueryData(_hQuery.getValue()); + if (ret != Pdhdll.ERROR_SUCCESS) + System.out.println("Error in PdhCollectQueryData " + _counter + ": "+ Integer.toHexString(ret)); + PointerByReference result = new PointerByReference(); + ret = Pdhdll.INSTANCE.PdhGetFormattedCounterValue(_hCounter.getValue(), Pdhdll.PDH_FMT_LONG, null, pdhCounterValue.getPointer()); + if (ret != Pdhdll.ERROR_SUCCESS) + System.out.println("Error in PdhGetFormattedCounterValue " + _counter + ": " + Integer.toHexString(ret)); + else + { + pdhCounterValue.read(); + return pdhCounterValue.Value.longValue; + } + return -1; + } + + // call close to free sources. this is not done in finalize, because the + // pointers may have already been finalized + /* + * (non-Javadoc) + * + * @see org.rzo.yajsw.os.ms.win.xp.PdhCounter#close() + */ + public void close() + { + // System.out.println("closing counter: "+_counter + " " + + // _hCounter.getValue() + " " + _hQuery.getValue()); + if (!isValid()) + return; + if (_hCounter != null) + Pdhdll.INSTANCE.PdhRemoveCounter(_hCounter.getValue()); + + if (_hQuery != null) + Pdhdll.INSTANCE.PdhCloseQuery(_hQuery.getValue()); + + _hQuery = null; + _hCounter = null; + } + + } // Counter + + /** + * Enum object items. + * + * @param objectName + * the object name + * @param english + * the english + * + * @return the list + */ + public static List enumObjectItems(String objectName, boolean english) + { + if (english) + objectName = translate(objectName); + Bag bag = new HashBag(); + int pdhStatus = Pdhdll.ERROR_SUCCESS; + Memory szCounterListBuffer = null; + IntByReference dwCounterListSize = new IntByReference(); + Memory szInstanceListBuffer = null; + IntByReference dwInstanceListSize = new IntByReference(); + String szThisInstance = null; + + // Determine the required buffer size for the data. + pdhStatus = Pdhdll.INSTANCE.PdhEnumObjectItemsA(null, // real time + // source + null, // local machine + objectName, // object to enumerate + szCounterListBuffer, // pass NULL and 0 + dwCounterListSize, // to get length required + szInstanceListBuffer, // buffer size + dwInstanceListSize, // + Pdhdll.PERF_DETAIL_WIZARD, // counter detail level + 0); + + if (pdhStatus == Pdhdll.PDH_MORE_DATA) + { + // Allocate the buffers and try the call again. + szCounterListBuffer = new Memory(dwCounterListSize.getValue() * 4); + szInstanceListBuffer = new Memory((dwInstanceListSize.getValue() * 4)); + + if ((szCounterListBuffer != null) && (szInstanceListBuffer != null)) + { + pdhStatus = Pdhdll.INSTANCE.PdhEnumObjectItemsA(null, // real + // time + // source + null, // local machine + objectName, // object to enumerate + szCounterListBuffer, // buffer to receive counter + // list + dwCounterListSize, szInstanceListBuffer, // buffer to + // receive + // instance + // list + dwInstanceListSize, Pdhdll.PERF_DETAIL_WIZARD, // counter + // detail + // level + 0); + + if (pdhStatus == Pdhdll.ERROR_SUCCESS) + { + // System.out.println ("Enumerating Processes:"); + + // Walk the instance list. The list can contain one + // or more null-terminated strings. The last string + // is followed by a second null-terminator. + int i = 0; + for (szThisInstance = szInstanceListBuffer.getString(0); szThisInstance != null && szThisInstance.length() > 0; i += szThisInstance + .length() + 1, szThisInstance = szInstanceListBuffer.getString(i)) + { + // System.out.println( szThisInstance); + bag.add(szThisInstance); + + } + } + else + { + System.out.println("PdhEnumObjectItems failed with " + Integer.toHexString(pdhStatus)); + } + } + else + { + System.out.println("Unable to allocate buffers"); + // pdhStatus = ERROR_OUTOFMEMORY; + } + + } + else + { + System.out.println("PdhEnumObjectItems failed with " + Integer.toHexString(pdhStatus)); + } + + List result = new ArrayList(); + for (Iterator it = bag.uniqueSet().iterator(); it.hasNext();) + { + String str = (String) it.next(); + result.add(str); + // System.out.println(str); + for (int i = 1; i < bag.getCount(str); i++) + { + result.add(str + "#" + i); + // System.out.println(str+"#"+i); + } + } + + return result; + } + + /** The _indexes. */ + static Map _indexes = null; + + /** + * Read index map. + */ + static void readIndexMap() + { + if (_indexes != null) + return; + _indexes = new HashMap(); + int ret = 0; + IntByReference hkey = new IntByReference(); + + ret = Advapi32.INSTANCE.RegOpenKeyExA(Advapi32.HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Perflib\\009", 0, + Advapi32.KEY_READ, hkey); + // System.out.println(">> "+ret + " " + + // Integer.toHexString(hkey.getValue())); + + int BufferSize = 1; + int BYTEINCREMENT = 1024; + Memory PerfData = new Memory(BufferSize); + PerfData.clear(); + IntByReference PBufferSize = new IntByReference(); + + PBufferSize.setValue(BufferSize); + + // System.out.println("Allocating memory..."); + for (ret = Advapi32.INSTANCE.RegQueryValueExA(hkey.getValue(), "Counter", null, null, PerfData, PBufferSize); ret == Advapi32.ERROR_MORE_DATA; ret = Advapi32.INSTANCE + .RegQueryValueExA(hkey.getValue(), "Counter", null, null, PerfData, PBufferSize)) + { + + // Get a buffer that is big enough. + + BufferSize += BYTEINCREMENT; + PBufferSize = new IntByReference(); + PBufferSize.setValue(BufferSize); + PerfData = new Memory(BufferSize); + PerfData.clear(); + } + // System.out.println("Final buffer size is " +PBufferSize.getValue()); + if (ret != Pdhdll.ERROR_SUCCESS) + System.out.println("Error reading Registry entry SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Perflib\\009: " + Integer.toHexString(ret)); + else + { + String key; + String counter; + int i = 0; + while (i < PBufferSize.getValue()) + { + key = PerfData.getString(i); + i += key.length() + 1; + counter = PerfData.getString(i); + i += counter.length() + 1; + // System.out.println(counter+":"+key); + if (counter.length() > 0 && key.length() > 0) + try + { + _indexes.put(counter, new Integer(Integer.parseInt(key))); + } + catch (Exception ex) + { + ex.printStackTrace(); + } + } + + } + Advapi32.INSTANCE.RegCloseKey(hkey.getValue()); + + } + + /** + * Translate. + * + * @param name + * the name + * + * @return the string + */ + static String translate(String name) + { + readIndexMap(); + Integer index = (Integer) _indexes.get(name); + if (index == null) + return name; + Memory buff = new Memory(256); + IntByReference buffSize = new IntByReference(); + buffSize.setValue(256); + if (Pdhdll.INSTANCE.PdhLookupPerfNameByIndexA(null, index.intValue(), buff, buffSize) == Pdhdll.ERROR_SUCCESS) + return buff.getString(0); + return name; + } + + /** + * Read process map. + * + * @return the map + */ + static Map readProcessMap() + { + Map result = new HashMap(); + List processes = enumObjectItems("Process", true); + for (Iterator it = processes.iterator(); it.hasNext();) + { + String process = (String) it.next(); + PdhCounter c = getEnglishCounter("\\Process(" + process + ")\\ID Process"); + int pid = c.getIntValue(); + c.close(); + result.put(new Integer(pid), process); + } + + return result; + + } + + /** + * Gets the english counter. + * + * @param counter + * the counter + * + * @return the english counter + */ + static public PdhCounter getEnglishCounter(String counter) + { + if (counter.startsWith("\\HddCounter")) + return new HddCounter(counter); + if (counter.startsWith("\\ScriptCounter")) + return new ScriptCounter(counter); + + return new Counter(counter, true); + } + + /** + * Gets the locale counter. + * + * @param counter + * the counter + * + * @return the locale counter + */ + static public PdhCounter getLocaleCounter(String counter) + { + return new Counter(counter, false); + } + + /** + * Gets the process english counter. + * + * @param pid + * the pid + * @param counter + * the counter + * + * @return the process english counter + */ + static public PdhCounter getProcessEnglishCounter(int pid, String counter) + { + Map m = readProcessMap(); + String process = (String) m.get(new Integer(pid)); + if (process == null) + return null; + //System.out.println("creating PdhCounter " + counter + " for Process " + process + " with pid " + pid); + return getEnglishCounter("\\Process(" + process + ")\\" + counter); + } + + /** + * Gets the process locale counter. + * + * @param pid + * the pid + * @param counter + * the counter + * + * @return the process locale counter + */ + static public PdhCounter getProcessLocaleCounter(int pid, String counter) + { + Map m = readProcessMap(); + String processObject = translate("Process"); + String process = (String) m.get(new Integer(pid)); + if (process == null) + return null; + //System.out.println("creating PdhCounter " + counter + " for Process " + process + " with pid " + pid); + return getLocaleCounter("\\" + processObject + "(" + process + ")\\" + counter); + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/ms/win/w32/Pdh.java.old b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/ms/win/w32/Pdh.java.old new file mode 100644 index 0000000000..7260ef21d1 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/ms/win/w32/Pdh.java.old @@ -0,0 +1,991 @@ +/* This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + *

    + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ +package org.rzo.yajsw.os.ms.win.w32; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import jnacontrib.jna.Options; + +import org.apache.commons.collections.Bag; +import org.apache.commons.collections.bag.HashBag; +import org.rzo.yajsw.os.OperatingSystem; +import org.rzo.yajsw.util.File; + +import com.sun.jna.Memory; +import com.sun.jna.Native; +import com.sun.jna.Pointer; +import com.sun.jna.Structure; +import com.sun.jna.Union; +import com.sun.jna.WString; +import com.sun.jna.ptr.IntByReference; +import com.sun.jna.ptr.PointerByReference; +import com.sun.jna.win32.StdCallLibrary; + +// TODO: Auto-generated Javadoc +/** + * The Class Pdh. + */ +public class Pdh +{ + + /** + * The Interface Pdhdll. + */ + interface Pdhdll extends StdCallLibrary + { + + /** The INSTANCE. */ + Pdhdll INSTANCE = (Pdhdll) Native.loadLibrary("C:/WINDOWS/system32/pdh.dll", Pdhdll.class); + + /* + * PDH_STATUS PdhOpenQuery( __in LPCTSTR szDataSource, __in DWORD_PTR + * dwUserData, __out PDH_HQUERY phQuery ); + */ + /** + * Pdh open query. + * + * @param szDataSource + * the sz data source + * @param dwUserData + * the dw user data + * @param phQuery + * the ph query + * + * @return the int + */ + int PdhOpenQuery(Pointer szDataSource, Pointer dwUserData, PointerByReference phQuery); + + /* + * PDH_STATUS PdhValidatePath( __in LPCTSTR szFullCounterPath ); + */ + /** + * Pdh validate path a. + * + * @param szFullCounterPath + * the sz full counter path + * + * @return the int + */ + int PdhValidatePathA(String szFullCounterPath); + + /* + * PDH_STATUS PdhCollectQueryData( __in_out PDH_HQUERY hQuery ); + */ + /** + * Pdh collect query data. + * + * @param hQuery + * the h query + * + * @return the int + */ + int PdhCollectQueryData(Pointer hQuery); + + /* + * PDH_STATUS PdhGetFormattedCounterValue( __in PDH_HCOUNTER hCounter, + * __in DWORD dwFormat, __out LPDWORD lpdwType, __out + * PPDH_FMT_COUNTERVALUE pValue ); + */ + /** + * Pdh get formatted counter value. + * + * @param hCounter + * the h counter + * @param dwFormat + * the dw format + * @param lpdwType + * the lpdw type + * @param pValue + * the value + * + * @return the int + */ + int PdhGetFormattedCounterValue(Pointer hCounter, int dwFormat, IntByReference lpdwType, Pointer pValue); + + /* + * typedef struct _PDH_FMT_COUNTERVALUE { DWORD CStatus; union { LONG + * longValue; double doubleValue; LONGLONG largeValue; LPCSTR + * AnsiStringValue; LPCWSTR WideStringValue; }; } PDH_FMT_COUNTERVALUE, + * PPDH_FMT_COUNTERVALUE; + */ + + /* + * PDH_STATUS PdhAddCounter( __in PDH_HQUERY hQuery, __in LPCTSTR + * szFullCounterPath, __in DWORD_PTR dwUserData, __out PDH_HCOUNTER + * phCounter ); + */ + /** + * Pdh add counter a. + * + * @param hQuery + * the h query + * @param szFullCounterPath + * the sz full counter path + * @param dwUserData + * the dw user data + * @param phCounter + * the ph counter + * + * @return the int + */ + int PdhAddCounterA(Pointer hQuery, String szFullCounterPath, int dwUserData, PointerByReference phCounter); + + /* + * PDH_STATUS PdhAddEnglishCounter( __in PDH_HQUERY hQuery, __in LPCTSTR + * szFullCounterPath, __in DWORD_PTR dwUserData, __out PDH_HCOUNTER + * phCounter ); + */ + /** + * Pdh add009 counter a. + * + * @param hQuery + * the h query + * @param szFullCounterPath + * the sz full counter path + * @param dwUserData + * the dw user data + * @param phCounter + * the ph counter + * + * @return the int + */ + int PdhAdd009CounterA(Pointer hQuery, String szFullCounterPath, int dwUserData, PointerByReference phCounter); + + /** The ERRO r_ success. */ + int ERROR_SUCCESS = 0; + + /** The PD h_ fm t_ double. */ + int PDH_FMT_DOUBLE = 0x00000200; + + /** The PD h_ fm t_ long. */ + int PDH_FMT_LONG = 0x00000100; + + /* + * PDH_STATUS PdhRemoveCounter( __in PDH_HCOUNTER hCounter ); + */ + /** + * Pdh remove counter. + * + * @param hCounter + * the h counter + * + * @return the int + */ + int PdhRemoveCounter(Pointer hCounter); + + /* + * PDH_STATUS PdhCloseQuery( __in PDH_HQUERY hQuery ); + */ + /** + * Pdh close query. + * + * @param hQuery + * the h query + * + * @return the int + */ + int PdhCloseQuery(Pointer hQuery); + + /* + * PDH_STATUS PdhEnumObjectItems( __in LPCTSTR szDataSource, __in + * LPCTSTR szMachineName, __in LPCTSTR szObjectName, __out LPTSTR + * mszCounterList, __in_out LPDWORD pcchCounterListLength, __out LPTSTR + * mszInstanceList, __in_out LPDWORD pcchInstanceListLength, __in DWORD + * dwDetailLevel, DWORD dwFlags ); + */ + /** + * Pdh enum object items a. + * + * @param szDataSource + * the sz data source + * @param szMachineName + * the sz machine name + * @param szObjectName + * the sz object name + * @param mszCounterList + * the msz counter list + * @param pcchCounterListLength + * the pcch counter list length + * @param mszInstanceList + * the msz instance list + * @param pcchInstanceListLength + * the pcch instance list length + * @param dwDetailLevel + * the dw detail level + * @param dwFlags + * the dw flags + * + * @return the int + */ + int PdhEnumObjectItemsA(String szDataSource, String szMachineName, String szObjectName, Memory mszCounterList, + IntByReference pcchCounterListLength, Memory mszInstanceList, IntByReference pcchInstanceListLength, int dwDetailLevel, int dwFlags); + + /** The PER f_ detai l_ wizard. */ + int PERF_DETAIL_WIZARD = 400; + + /** The PD h_ mor e_ data. */ + int PDH_MORE_DATA = 0x800007D2; + + /* + * DH_STATUS PdhLookupPerfNameByIndex( __in LPCTSTR szMachineName, __in + * DWORD dwNameIndex, __out LPTSTR szNameBuffer, __in LPDWORD + * pcchNameBufferSize ); + */ + /** + * Pdh lookup perf name by index a. + * + * @param szMachineName + * the sz machine name + * @param dwNameIndex + * the dw name index + * @param szNameBuffer + * the sz name buffer + * @param pcchNameBufferSize + * the pcch name buffer size + * + * @return the int + */ + int PdhLookupPerfNameByIndexA(String szMachineName, int dwNameIndex, Memory szNameBuffer, IntByReference pcchNameBufferSize); + + /* + * PDH_STATUS PdhParseCounterPath( __in LPCTSTR szFullPathBuffer, __out + * PDH_COUNTER_PATH_ELEMENTS pCounterPathElements, __in_out LPDWORD + * pdwBufferSize, DWORD dwFlags ); + */ + /** + * Pdh parse counter path a. + * + * @param szFullPathBuffer + * the sz full path buffer + * @param pCounterPathElements + * the counter path elements + * @param pdwBufferSize + * the pdw buffer size + * @param dwFlags + * the dw flags + * + * @return the int + */ + int PdhParseCounterPathA(String szFullPathBuffer, Pointer pCounterPathElements, IntByReference pdwBufferSize, int dwFlags); + + }// Pdhdll + + /** + * The Interface Advapi32. + */ + interface Advapi32 extends StdCallLibrary + { + + /** The INSTANCE. */ + Advapi32 INSTANCE = (Advapi32) Native.loadLibrary("Advapi32", Advapi32.class);//, Options.UNICODE_OPTIONS); + + /* + * LONG WINAPI RegOpenKeyEx( HKEY hKey, LPCTSTR lpSubKey, DWORD + * ulOptions, REGSAM samDesired, PHKEY phkResult ); + */ + /** + * Reg open key ex a. + * + * @param hKey + * the h key + * @param lpSubKey + * the lp sub key + * @param ulOptions + * the ul options + * @param samDesired + * the sam desired + * @param phkResult + * the phk result + * + * @return the int + */ + int RegOpenKeyExA(int hKey, String lpSubKey, int ulOptions, int samDesired, IntByReference phkResult); + + /** The HKE y_ loca l_ machine. */ + int HKEY_LOCAL_MACHINE = 0x80000002; + + /** The KE y_ read. */ + int KEY_READ = 0x20019; + + /* + * LONG WINAPI RegCloseKey( HKEY hKey ); + */ + /** + * Reg close key. + * + * @param hKey + * the h key + * + * @return the int + */ + public int RegCloseKey(int hKey); + + /* + * LONG WINAPI RegQueryValueEx( __in HKEY hKey, __in LPCTSTR + * lpValueName, LPDWORD lpReserved, __out LPDWORD lpType, __out LPBYTE + * lpData, __in_out LPDWORD lpcbData ); + */ + /** + * Reg query value ex a. + * + * @param hKey + * the h key + * @param lpValueName + * the lp value name + * @param lpReserved + * the lp reserved + * @param lpType + * the lp type + * @param lpData + * the lp data + * @param lpcbData + * the lpcb data + * + * @return the int + */ + int RegQueryValueExA(int hKey, String lpValueName, Pointer lpReserved, IntByReference lpType, Pointer lpData, IntByReference lpcbData); + + /** The HKE y_ performanc e_ data. */ + int HKEY_PERFORMANCE_DATA = 0x80000004; + + /** The ERRO r_ mor e_ data. */ + int ERROR_MORE_DATA = 234; + + } + + /** + * The Class PDH_FMT_COUNTERVALUE. + */ + public static class PDH_FMT_COUNTERVALUE extends Structure + { + + /** The C status. */ + public int CStatus; + + /** The Value. */ + public ValueUnion Value; + } + + /** + * The Class ValueUnion. + */ + public static class ValueUnion extends Union + { + + /** The long value. */ + public int longValue; + + /** The double value. */ + public double doubleValue; + + /** The large value. */ + public long largeValue; + + /** The Ansi string value. */ + public String AnsiStringValue; + + /** The Wide string value. */ + public WString WideStringValue; + } + + /** + * The Class COUNTER_PATH_ELEMENTS. + */ + public static class COUNTER_PATH_ELEMENTS + { + + /** The sz machine name. */ + public String szMachineName; + + /** The sz object name. */ + public String szObjectName; + + /** The sz instance name. */ + public String szInstanceName; + + /** The dw instance index. */ + public int dwInstanceIndex; + + /** The sz counter name. */ + public String szCounterName; + + } + + /** The MA x_ counte r_ path. */ + static int MAX_COUNTER_PATH = 256; // Maximum counter path length + + /** The PD h_ ma x_ counte r_ name. */ + static int PDH_MAX_COUNTER_NAME = 1024; // Maximum counter name length. + + /** The PD h_ ma x_ instanc e_ name. */ + static int PDH_MAX_INSTANCE_NAME = 1024; // Maximum counter instance name + // length. + /** The PD h_ ma x_ counte r_ path. */ + static int PDH_MAX_COUNTER_PATH = 2048; // Maximum full counter path + + // length. + + /** + * The Class HddCounter. + */ + static public class HddCounter implements PdhCounter + { + + /** The _file. */ + private File _file; + + /** The _free space percent. */ + private boolean _freeSpacePercent = false; + + /** The _free space. */ + private boolean _freeSpace = false; + + /** The _used space. */ + private boolean _usedSpace = false; + + /** The _total space. */ + private boolean _totalSpace = false; + + /** + * Instantiates a new hdd counter. + * + * @param counter + * the counter + */ + public HddCounter(String counter) + { + + String hdd = counter.substring(counter.indexOf('(') + 1, counter.indexOf(')')); + String counterHdd = new File(hdd).getPath(); + /* + * int i=0; do { counterHdd = getHddFileName(hdd); if (counterHdd == + * null) try { Thread.sleep(100); } catch (InterruptedException e) { + * // TODO Auto-generated catch block e.printStackTrace(); } i++; } + * while (i<5 && counterHdd == null); + */ + + _file = new File(counterHdd); + + if (_file == null || !_file.exists()) + System.out.println("could not open file " + counterHdd + " " + hdd); + + if (counter.endsWith("\\% free space")) + _freeSpacePercent = true; + else if (counter.endsWith("\\free space")) + _freeSpace = true; + else if (counter.endsWith("\\used space")) + _usedSpace = true; + else if (counter.endsWith("\\total space")) + _totalSpace = true; + } + + /** + * Gets the hdd file name. + * + * @param hdd + * the hdd + * + * @return the hdd file name + */ + private String getHddFileName(String hdd) + { + List hdds = enumObjectItems("PhysicalDisk", true); + String counterHdd = null; + for (Iterator it = hdds.iterator(); it.hasNext();) + { + String s = (String) it.next(); + if (s.contains(hdd)) + { + counterHdd = s.substring(2); + break; + } + } + return counterHdd; + } + + /* + * (non-Javadoc) + * + * @see org.rzo.yajsw.os.ms.win.xp.PdhCounter#close() + */ + public void close() + { + _file = null; + } + + /* + * (non-Javadoc) + * + * @see org.rzo.yajsw.os.ms.win.xp.PdhCounter#getDoubleValue() + */ + public double getDoubleValue() + { + if (!isValid()) + return -1; + if (_freeSpacePercent) + return ((double) _file.getFreeSpace() / (double) _file.getTotalSpace()) * 100; + if (_freeSpace) + return _file.getFreeSpace(); + if (_usedSpace) + return _file.getTotalSpace() - _file.getFreeSpace(); + if (_totalSpace) + return _file.getTotalSpace(); + return -1; + } + + /* + * (non-Javadoc) + * + * @see org.rzo.yajsw.os.ms.win.xp.PdhCounter#getIntValue() + */ + public int getIntValue() + { + return (int) getDoubleValue(); + } + + /* + * (non-Javadoc) + * + * @see org.rzo.yajsw.os.ms.win.xp.PdhCounter#isValid() + */ + public boolean isValid() + { + return _file != null && _file.exists(); + } + + } + + /** + * The Class Counter. + */ + static public class Counter implements PdhCounter + { + + /** The _h query. */ + PointerByReference _hQuery = new PointerByReference(); + + /** The _h counter. */ + PointerByReference _hCounter = new PointerByReference(); + + /** The _counter. */ + String _counter; + + /** + * Instantiates a new counter. + * + * @param counter + * the counter + * @param english + * the english + */ + Counter(String counter, boolean english) + { + int ret = Pdhdll.INSTANCE.PdhOpenQuery(null, null, _hQuery); + _counter = counter; + if (ret != Pdhdll.ERROR_SUCCESS) + System.out.println("PdhOpenQuery " + Integer.toHexString(ret)); + + if (english) + ret = Pdhdll.INSTANCE.PdhAdd009CounterA(_hQuery.getValue(), counter, 0, _hCounter); + else + ret = Pdhdll.INSTANCE.PdhAddCounterA(_hQuery.getValue(), counter, 0, _hCounter); + if (ret != Pdhdll.ERROR_SUCCESS) + System.out.println(Integer.toHexString(ret)); + // System.out.println("created counter: "+ _counter + " "+ + // _hQuery.getPointer() + " " +_hCounter.getPointer()+ " "+ + // _hQuery.getValue() + " " +_hCounter.getValue()); + getIntValue(); + } + + /* + * (non-Javadoc) + * + * @see org.rzo.yajsw.os.ms.win.xp.PdhCounter#isValid() + */ + public boolean isValid() + { + return !(_hCounter == null || _hQuery == null || _hQuery.getValue().equals(null) || _hCounter.getValue().equals(null)); + } + + /* + * (non-Javadoc) + * + * @see org.rzo.yajsw.os.ms.win.xp.PdhCounter#getDoubleValue() + */ + public double getDoubleValue() + { + if (!isValid()) + return -1; + PDH_FMT_COUNTERVALUE pdhCounterValue = new PDH_FMT_COUNTERVALUE(); + pdhCounterValue.size(); + int ret = Pdhdll.INSTANCE.PdhCollectQueryData(_hQuery.getValue()); + if (ret != Pdhdll.ERROR_SUCCESS) + System.out.println(Integer.toHexString(ret)); + PointerByReference result = new PointerByReference(); + ret = Pdhdll.INSTANCE.PdhGetFormattedCounterValue(_hCounter.getValue(), Pdhdll.PDH_FMT_DOUBLE, null, pdhCounterValue.getPointer()); + if (ret != Pdhdll.ERROR_SUCCESS) + System.out.println(Integer.toHexString(ret)); + else + { + pdhCounterValue.read(); + return pdhCounterValue.Value.doubleValue; + } + return -1; + } + + /* + * (non-Javadoc) + * + * @see org.rzo.yajsw.os.ms.win.xp.PdhCounter#getIntValue() + */ + public int getIntValue() + { + if (!isValid()) + return -1; + PDH_FMT_COUNTERVALUE pdhCounterValue = new PDH_FMT_COUNTERVALUE(); + pdhCounterValue.size(); + int ret = Pdhdll.INSTANCE.PdhCollectQueryData(_hQuery.getValue()); + if (ret != Pdhdll.ERROR_SUCCESS) + System.out.println(Integer.toHexString(ret)); + PointerByReference result = new PointerByReference(); + ret = Pdhdll.INSTANCE.PdhGetFormattedCounterValue(_hCounter.getValue(), Pdhdll.PDH_FMT_LONG, null, pdhCounterValue.getPointer()); + if (ret != Pdhdll.ERROR_SUCCESS) + System.out.println(Integer.toHexString(ret)); + else + { + pdhCounterValue.read(); + return pdhCounterValue.Value.longValue; + } + return -1; + } + + // call close to free sources. this is not done in finalize, because the + // pointers may have already been finalized + /* + * (non-Javadoc) + * + * @see org.rzo.yajsw.os.ms.win.xp.PdhCounter#close() + */ + public void close() + { + // System.out.println("closing counter: "+_counter + " " + + // _hCounter.getValue() + " " + _hQuery.getValue()); + if (!isValid()) + return; + if (_hCounter != null) + Pdhdll.INSTANCE.PdhRemoveCounter(_hCounter.getValue()); + + if (_hQuery != null) + Pdhdll.INSTANCE.PdhCloseQuery(_hQuery.getValue()); + + _hQuery = null; + _hCounter = null; + } + + } // Counter + + /** + * Enum object items. + * + * @param objectName + * the object name + * @param english + * the english + * + * @return the list + */ + public static List enumObjectItems(String objectName, boolean english) + { + if (english) + objectName = translate(objectName); + Bag bag = new HashBag(); + int pdhStatus = Pdhdll.ERROR_SUCCESS; + Memory szCounterListBuffer = null; + IntByReference dwCounterListSize = new IntByReference(); + Memory szInstanceListBuffer = null; + IntByReference dwInstanceListSize = new IntByReference(); + String szThisInstance = null; + + // Determine the required buffer size for the data. + pdhStatus = Pdhdll.INSTANCE.PdhEnumObjectItemsA(null, // real time + // source + null, // local machine + objectName, // object to enumerate + szCounterListBuffer, // pass NULL and 0 + dwCounterListSize, // to get length required + szInstanceListBuffer, // buffer size + dwInstanceListSize, // + Pdhdll.PERF_DETAIL_WIZARD, // counter detail level + 0); + + if (pdhStatus == Pdhdll.PDH_MORE_DATA) + { + // Allocate the buffers and try the call again. + szCounterListBuffer = new Memory(dwCounterListSize.getValue() * 4); + szInstanceListBuffer = new Memory((dwInstanceListSize.getValue() * 4)); + + if ((szCounterListBuffer != null) && (szInstanceListBuffer != null)) + { + pdhStatus = Pdhdll.INSTANCE.PdhEnumObjectItemsA(null, // real + // time + // source + null, // local machine + objectName, // object to enumerate + szCounterListBuffer, // buffer to receive counter + // list + dwCounterListSize, szInstanceListBuffer, // buffer to + // receive + // instance + // list + dwInstanceListSize, Pdhdll.PERF_DETAIL_WIZARD, // counter + // detail + // level + 0); + + if (pdhStatus == Pdhdll.ERROR_SUCCESS) + { + // System.out.println ("Enumerating Processes:"); + + // Walk the instance list. The list can contain one + // or more null-terminated strings. The last string + // is followed by a second null-terminator. + int i = 0; + for (szThisInstance = szInstanceListBuffer.getString(0); szThisInstance != null && szThisInstance.length() > 0; i += szThisInstance + .length() + 1, szThisInstance = szInstanceListBuffer.getString(i)) + { + // System.out.println( szThisInstance); + bag.add(szThisInstance); + + } + } + else + { + System.out.println("PdhEnumObjectItems failed with " + Integer.toHexString(pdhStatus)); + } + } + else + { + System.out.println("Unable to allocate buffers"); + // pdhStatus = ERROR_OUTOFMEMORY; + } + + } + else + { + System.out.println("PdhEnumObjectItems failed with " + Integer.toHexString(pdhStatus)); + } + + List result = new ArrayList(); + for (Iterator it = bag.uniqueSet().iterator(); it.hasNext();) + { + String str = (String) it.next(); + result.add(str); + // System.out.println(str); + for (int i = 1; i < bag.getCount(str); i++) + { + result.add(str + "#" + i); + // System.out.println(str+"#"+i); + } + } + + return result; + } + + /** The _indexes. */ + static Map _indexes = null; + + /** + * Read index map. + */ + static void readIndexMap() + { + if (_indexes != null) + return; + _indexes = new HashMap(); + int ret = 0; + IntByReference hkey = new IntByReference(); + + ret = Advapi32.INSTANCE.RegOpenKeyExA(Advapi32.HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Perflib\\009", 0, + Advapi32.KEY_READ, hkey); + // System.out.println(">> "+ret + " " + + // Integer.toHexString(hkey.getValue())); + + int BufferSize = 1; + int BYTEINCREMENT = 1024; + Memory PerfData = new Memory(BufferSize); + PerfData.clear(); + IntByReference PBufferSize = new IntByReference(); + + PBufferSize.setValue(BufferSize); + + // System.out.println("Allocating memory..."); + for (ret = Advapi32.INSTANCE.RegQueryValueExA(hkey.getValue(), "Counter", null, null, PerfData, PBufferSize); ret == Advapi32.ERROR_MORE_DATA; ret = Advapi32.INSTANCE + .RegQueryValueExA(hkey.getValue(), "Counter", null, null, PerfData, PBufferSize)) + { + + // Get a buffer that is big enough. + + BufferSize += BYTEINCREMENT; + PBufferSize = new IntByReference(); + PBufferSize.setValue(BufferSize); + PerfData = new Memory(BufferSize); + PerfData.clear(); + } + // System.out.println("Final buffer size is " +PBufferSize.getValue()); + if (ret != Pdhdll.ERROR_SUCCESS) + System.out.println(Integer.toHexString(ret)); + else + { + String key; + String counter; + int i = 0; + while (i < PBufferSize.getValue()) + { + key = PerfData.getString(i); + i += key.length() + 1; + counter = PerfData.getString(i); + i += counter.length() + 1; + // System.out.println(counter+":"+key); + if (counter.length() > 0 && key.length() > 0) + try + { + System.out.println(counter); + _indexes.put(counter, new Integer(Integer.parseInt(key))); + } + catch (Exception ex) + { + ex.printStackTrace(); + } + } + + } + Advapi32.INSTANCE.RegCloseKey(hkey.getValue()); + + } + + /** + * Translate. + * + * @param name + * the name + * + * @return the string + */ + static String translate(String name) + { + readIndexMap(); + Integer index = (Integer) _indexes.get(name); + if (index == null) + return name; + Memory buff = new Memory(256); + IntByReference buffSize = new IntByReference(); + buffSize.setValue(256); + if (Pdhdll.INSTANCE.PdhLookupPerfNameByIndexA(null, index.intValue(), buff, buffSize) == Pdhdll.ERROR_SUCCESS) + return buff.getString(0); + return name; + } + + /** + * Read process map. + * + * @return the map + */ + static Map readProcessMap() + { + Map result = new HashMap(); + List processes = enumObjectItems("Process", true); + for (Iterator it = processes.iterator(); it.hasNext();) + { + String process = (String) it.next(); + PdhCounter c = getEnglishCounter("\\Process(" + process + ")\\ID Process"); + int pid = c.getIntValue(); + c.close(); + result.put(new Integer(pid), process); + } + + return result; + + } + + /** + * Gets the english counter. + * + * @param counter + * the counter + * + * @return the english counter + */ + static public PdhCounter getEnglishCounter(String counter) + { + if (counter.startsWith("\\PhysicalDisk")) + if (counter.endsWith("\\% free space") || counter.endsWith("\\free space") || counter.endsWith("\\used space") + || counter.endsWith("\\total space")) + return new HddCounter(counter); + return new Counter(counter, true); + } + + /** + * Gets the locale counter. + * + * @param counter + * the counter + * + * @return the locale counter + */ + static public PdhCounter getLocaleCounter(String counter) + { + return new Counter(counter, false); + } + + /** + * Gets the process english counter. + * + * @param pid + * the pid + * @param counter + * the counter + * + * @return the process english counter + */ + static public PdhCounter getProcessEnglishCounter(int pid, String counter) + { + Map m = readProcessMap(); + String process = (String) m.get(new Integer(pid)); + if (process == null) + return null; + return getEnglishCounter("\\Process(" + process + ")\\" + counter); + } + + /** + * Gets the process locale counter. + * + * @param pid + * the pid + * @param counter + * the counter + * + * @return the process locale counter + */ + static public PdhCounter getProcessLocaleCounter(int pid, String counter) + { + Map m = readProcessMap(); + String processObject = translate("Process"); + String process = (String) m.get(new Integer(pid)); + if (process == null) + return null; + return getLocaleCounter("\\" + processObject + "(" + process + ")\\" + counter); + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/ms/win/w32/PdhBufferedCounter.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/ms/win/w32/PdhBufferedCounter.java new file mode 100644 index 0000000000..7fbfd73c40 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/ms/win/w32/PdhBufferedCounter.java @@ -0,0 +1,229 @@ +/* This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + *

    + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ +package org.rzo.yajsw.os.ms.win.w32; + +// TODO: Auto-generated Javadoc +/** + * The Class PdhBufferedCounter. + */ +public class PdhBufferedCounter implements PdhCounter +{ + + /** The _counter. */ + PdhCounter _counter; + + /** The i buff. */ + int[] iBuff; + + /** The d buff. */ + double[] dBuff; + + /** The _frequency. */ + long _frequency; + + /** The _last add. */ + long _lastAdd; + + /** The _next add. */ + long _nextAdd; + + /** The _head. */ + int _head = -1; + + /** The _tail. */ + int _tail = -1; + + /** The _count. */ + int _count = 0; + + /** The _i avg. */ + int _iAvg; + + /** The _d avg. */ + double _dAvg; + + /** + * Instantiates a new pdh buffered counter. + * + * @param counter + * the counter + * @param buffSize + * the buff size + * @param frequency + * the frequency + * @param valueType + * the value type + */ + public PdhBufferedCounter(PdhCounter counter, int buffSize, long frequency, Class valueType) + { + _counter = counter; + _frequency = frequency; + if (valueType.equals(int.class)) + { + iBuff = new int[buffSize]; + } + else if (valueType.equals(double.class)) + { + dBuff = new double[buffSize]; + } + } + + /* + * (non-Javadoc) + * + * @see org.rzo.yajsw.os.ms.win.xp.PdhCounter#getDoubleValue() + */ + public double getDoubleValue() + { + return _counter.getDoubleValue(); + } + + /* + * (non-Javadoc) + * + * @see org.rzo.yajsw.os.ms.win.xp.PdhCounter#getIntValue() + */ + public int getIntValue() + { + return _counter.getIntValue(); + } + + /* + * (non-Javadoc) + * + * @see org.rzo.yajsw.os.ms.win.xp.PdhCounter#isValid() + */ + public boolean isValid() + { + return _counter.isValid(); + } + + /* + * (non-Javadoc) + * + * @see org.rzo.yajsw.os.ms.win.xp.PdhCounter#close() + */ + public void close() + { + _counter.close(); + } + + /** + * Adds the. + */ + private void add() + { + if (iBuff != null) + { + if (_count < iBuff.length) + _count++; + _head = (_head + 1) % iBuff.length; + if (_head == _tail || _tail == -1) + _tail = (_tail + 1) % iBuff.length; + iBuff[_head] = _counter.getIntValue(); + _iAvg = (_iAvg * (_count - 1) + iBuff[_head]) / _count; + } + else if (dBuff != null) + { + if (_count < dBuff.length) + _count++; + _head = (_head + 1) % dBuff.length; + if (_head == _tail || _tail == -1) + _tail = (_tail + 1) % dBuff.length; + dBuff[_head] = _counter.getDoubleValue(); + _dAvg = (_dAvg * (_count - 1) + dBuff[_head]) / _count; + } + } + + /** + * Tick. + */ + public void tick() + { + long t = System.currentTimeMillis(); + if (t >= _nextAdd) + { + add(); + _lastAdd = t; + _nextAdd = _lastAdd + _frequency; + } + // System.out.println(t-System.currentTimeMillis()); + } + + /** + * Gets the int avg. + * + * @return the int avg + */ + public int getIntAvg() + { + return _iAvg; + } + + /** + * Gets the double avg. + * + * @return the double avg + */ + public double getDoubleAvg() + { + return _dAvg; + } + + /** + * Gets the int head. + * + * @return the int head + */ + public int getIntHead() + { + if (iBuff != null && _head >= 0) + return iBuff[_head]; + else + return -1; + + } + + /** + * Gets the double head. + * + * @return the double head + */ + public double getDoubleHead() + { + if (dBuff != null && _head >= 0) + return dBuff[_head]; + else + return -1; + + } + + /** + * Gets the int buffer. + * + * @return the int buffer + */ + public int[] getIntBuffer() + { + return iBuff; + } + + /** + * Gets the double buffer. + * + * @return the double buffer + */ + public double[] getDoubleBuffer() + { + return dBuff; + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/ms/win/w32/PdhCounter.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/ms/win/w32/PdhCounter.java new file mode 100644 index 0000000000..cdad5fd415 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/ms/win/w32/PdhCounter.java @@ -0,0 +1,46 @@ +/* This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + *

    + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ +package org.rzo.yajsw.os.ms.win.w32; + +// TODO: Auto-generated Javadoc +/** + * The Interface PdhCounter. + */ +public interface PdhCounter +{ + + /** + * Gets the double value. + * + * @return the double value + */ + public double getDoubleValue(); + + /** + * Gets the int value. + * + * @return the int value + */ + public int getIntValue(); + + /** + * Checks if is valid. + * + * @return true, if is valid + */ + public boolean isValid(); + + /** + * Close. + */ + public void close(); + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/ms/win/w32/ScriptCounter.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/ms/win/w32/ScriptCounter.java new file mode 100644 index 0000000000..a39985e4b2 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/ms/win/w32/ScriptCounter.java @@ -0,0 +1,107 @@ +package org.rzo.yajsw.os.ms.win.w32; + +import org.apache.commons.lang.StringUtils; +import org.apache.commons.vfs2.FileSystemException; +import org.jboss.netty.logging.InternalLogger; +import org.jboss.netty.logging.InternalLoggerFactory; +import org.rzo.yajsw.script.Script; +import org.rzo.yajsw.script.ScriptFactory; +import org.rzo.yajsw.util.VFSUtils; + +public class ScriptCounter implements PdhCounter +{ + InternalLogger _log = InternalLoggerFactory.getInstance(getClass().getName()); + Script _script; + private String _scriptFile; + String _counterString; + private String[] _args; + private long _lastModified; + + /** + * Format "\ScriptCounter(abc.groovy)\arg1,arg2,..." + */ + public ScriptCounter(String counterString) + { + init(counterString); + checkScript(); + + } + + private void init(String counterString) + { + _counterString = counterString; + _scriptFile = StringUtils.substringBetween(counterString, "(", ")"); + String argsString = StringUtils.substringAfter(counterString, ")\\"); + if (argsString != null) + _args = StringUtils.split(argsString, ","); + } + + private void checkScript() + { + long lastModified; + try + { + lastModified = VFSUtils.resolveFile(".", _scriptFile).getContent().getLastModifiedTime(); + } + catch (FileSystemException e) + { + throw new IllegalArgumentException("Cannot find script " + _scriptFile + " ex=" + e.getMessage()); + } + if (_lastModified == lastModified) + return; + else + { + _lastModified = lastModified; + _script = ScriptFactory.createScript(_scriptFile, _counterString, null, _args, _log, 0, null, false, false); + if (_script == null) + throw new IllegalArgumentException("Cannot find script " + _scriptFile); + } + } + + public void close() + { + // nothing + } + + public double getDoubleValue() + { + checkScript(); + Object result = _script.execute(); + + if (result instanceof Number) + return ((Number) result).doubleValue(); + else + return Double.parseDouble((String) result); + } + + public int getIntValue() + { + checkScript(); + Object result = _script.execute(); + if (result instanceof Number) + return ((Number) result).intValue(); + else + return Integer.parseInt((String) result); + } + + public boolean isValid() + { + try + { + getIntValue(); + return true; + } + catch (Exception e) + { + try + { + getDoubleValue(); + return true; + } + catch (Exception e2) + { + return false; + } + } + } +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/ms/win/w32/WindowsJavaHome.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/ms/win/w32/WindowsJavaHome.java new file mode 100644 index 0000000000..71e2d493af --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/ms/win/w32/WindowsJavaHome.java @@ -0,0 +1,405 @@ +/* This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + *

    + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ +package org.rzo.yajsw.os.ms.win.w32; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.channels.FileChannel; + +import jnacontrib.win32.Registry; +import jnacontrib.win32.Registry.REGISTRY_ROOT_KEY; + +import org.apache.commons.configuration.BaseConfiguration; +import org.apache.commons.configuration.Configuration; +import org.jboss.netty.logging.InternalLogger; +import org.rzo.yajsw.boot.WrapperLoader; +import org.rzo.yajsw.os.JavaHome; + +// TODO: Auto-generated Javadoc +/** + * The Class JavaHome. + */ +public class WindowsJavaHome implements JavaHome +{ + + /** The _config. */ + Configuration _config; + InternalLogger _logger; + + public void setLogger(InternalLogger logger) + { + _logger = logger; + } + + /** + * Instantiates a new java home. + * + * @param config + * the config + */ + WindowsJavaHome(Configuration config) + { + _config = config; + } + + /** + * Find java. + * + * @return the string + */ + public String findJava(String wrapperJava, String customProcName) + { + String java = null; + // String wrapperJava = _config.getString("wrapper.java.command"); + if (wrapperJava != null && !wrapperJava.endsWith(".exe") && !wrapperJava.endsWith("}")) + wrapperJava = wrapperJava + ".exe"; + try + { + // check if this is relative to wrapper home + if (wrapperJava != null) + { + File f = new File(wrapperJava); + if (!f.isAbsolute()) + { + File f2 = new File(WrapperLoader.getWrapperHome(), wrapperJava); + if (f2.exists()) + try + { + wrapperJava = f2.getCanonicalPath(); + } + catch (IOException e) + { + _logger.warn("Exception in findJava()", e); + } + } + else + { + wrapperJava = new File(wrapperJava).getCanonicalPath(); + if (!new File(wrapperJava).exists()) + { + _logger.error("java file does not exist: " + wrapperJava); + return null; + } + } + } + } + catch (Exception e) + { + _logger.warn("Error in JavaHome.findJava(): ", e); + } + java = wrapperJava; + // String customProcName = + // _config.getString("wrapper.java.customProcName"); + boolean useJavaw = _config.getBoolean("wrapper.java.command.javaw", (wrapperJava != null) && (wrapperJava.endsWith("javaw.exe"))); + + if (java == null) + { + String minVersion = _config.getString("wrapper.java.command.minVersion"); + String maxVersion = _config.getString("wrapper.java.command.maxVersion"); + boolean b64bit = _config.getBoolean("wrapper.java.command.64bit", false); + boolean jreOnly = _config.getBoolean("wrapper.java.command.jreOnly", false); + boolean preferJdk = _config.getBoolean("wrapper.java.command.preferJdk", false); + boolean preferJre = _config.getBoolean("wrapper.java.command.preferJre", true && !preferJdk); + boolean jdkOnly = _config.getBoolean("wrapper.java.command.jdkOnly", false); + if (!jdkOnly && (jreOnly || preferJre)) + { + java = findJavaInRegistry(new String[] + { "SOFTWARE\\JavaSoft\\Java Runtime Environment", "SOFTWARE\\IBM\\Java2 Runtime Environment" }, minVersion, maxVersion, b64bit); + } + if (java == null && !jreOnly) + java = findJavaInRegistry(new String[] + { "SOFTWARE\\JavaSoft\\Java Development Kit", "SOFTWARE\\IBM\\Java Development Kit" }, minVersion, maxVersion, b64bit); + } + else if (customProcName != null) + { + String h = java; + File f = new File(h); + if (f.exists()) + java = f.getParentFile().getParentFile().getAbsolutePath(); + else + // if we have to copy java, we need to find the path to java + java = findJavaFromPath(java); + } + else + // user has given us wrapper.java and we need not rename the exe -> + // return user input + return java; + + // we have no java path -> abort + if (java == null) + return null; + + // java is now set to a path, it must exist + File javaFile = new File(java); + if (!javaFile.exists()) + return null; + + java = javaFile.getAbsolutePath() + "/bin"; + if (!useJavaw) + { + java += "/java.exe"; + } + else + java += "/javaw.exe"; + if (customProcName != null) + { + java = copyTotmp(java, customProcName); + } + + if (_logger == null) + System.out.println("using java: " + java); + else + _logger.info("using java: " + java); + + return java; + + } + + /** + * Find java from path. + * + * @param java + * the java + * + * @return the string + */ + private String findJavaFromPath(String java) + { + if (java != null) + { + File javaFile = new File(java); + if (javaFile.exists()) + return java; + } + + // search java in environment path + String[] paths = System.getenv("path").split(";"); + for (String path : paths) + { + if (path.contains("jdk") || path.contains("jre")) + { + File javaFile = new File(path); + if (javaFile.exists()) + { + return path.substring(0, path.length() - 4); + } + } + } + return null; + + } + + /** + * Copy totmp. + * + * @param java + * the java + * @param customProcName + * the custom proc name + * + * @return the string + */ + private String copyTotmp(String java, String customProcName) + { + try + { + boolean isTmp = true; + File javaFile = new File(java); + File tmpJavaFile = null; + try + { + File fc = new File(customProcName); + if (fc.isAbsolute() && fc.getParentFile().exists()) + { + tmpJavaFile = fc; + isTmp = false; + } + } + catch (Exception ex) + { + // ignore + } + + // Create temp file. + customProcName = customProcName.endsWith(".exe") ? customProcName.substring(0, customProcName.length() - 4) : customProcName; + String exeName = "java_" + customProcName + "_"; + if (tmpJavaFile == null) + try + { + tmpJavaFile = File.createTempFile(exeName, ".exe"); + copyFile(javaFile, tmpJavaFile); + } + catch (Exception ex) + { + _logger.error("error creating tmp file: " + exeName, ex); + } + // Delete temp file when program exits. + if (!tmpJavaFile.exists()) + copyFile(javaFile, tmpJavaFile); + if (isTmp) + tmpJavaFile.deleteOnExit(); + return tmpJavaFile.getAbsolutePath(); + } + catch (Throwable e) + { + _logger.error("error copying java: " + java + " -> " + customProcName, e); + } + return null; + } + + /** + * Copy file. + * + * @param in + * the in + * @param out + * the out + * + * @throws IOException + * Signals that an I/O exception has occurred. + */ + void copyFile(File in, File out) throws IOException + { + _logger.info("copying java: " + in.getAbsolutePath() + " -> " + out.getAbsolutePath()); + FileChannel inChannel = new FileInputStream(in).getChannel(); + FileChannel outChannel = new FileOutputStream(out).getChannel(); + try + { + inChannel.transferTo(0, inChannel.size(), outChannel); + } + catch (IOException e) + { + throw e; + } + finally + { + if (inChannel != null) + inChannel.close(); + if (outChannel != null) + outChannel.close(); + } + } + + /** + * Find java from java home env. + * + * @return the string + */ + private String findJavaFromJavaHomeEnv() + { + return System.getenv("JAVA_HOME"); + } + + /** + * Find java in registry. + * + * @param keys + * the keys + * @param minVersion + * the min version + * @param maxVersion + * the max version + * @param b64bit + * the b64bit + * + * @return the string + */ + private String findJavaInRegistry(String[] keys, String minVersion, String maxVersion, boolean b64bit) + { + String[] values = null; + String result = null; + String resultKey = null; + String resultDir = null; + minVersion = minVersion == null ? "1.1.0" : minVersion; + maxVersion = maxVersion == null ? "99.99.99" : maxVersion; + for (String key : keys) + { + try + { + values = Registry.getSubKeys(REGISTRY_ROOT_KEY.LOCAL_MACHINE, key); + for (String value : values) + { + String dir = Registry.getStringValue(REGISTRY_ROOT_KEY.LOCAL_MACHINE, key + "\\" + value, "JavaHome"); + boolean exists = false; + try + { + exists = dir != null && new File(dir).exists(); + } + catch (Exception ex) + { + System.out.println("wrong registry key value: " + dir); + } + if (exists && value.compareTo(maxVersion) <= 0 && value.compareTo(minVersion) >= 0) + { + if (result == null) + { + result = value; + resultKey = key; + resultDir = dir; + } + else if (value.compareTo(result) >= 0) + { + result = value; + resultKey = key; + resultDir = dir; + } + } + + } + } + catch (Exception e) + { + e.printStackTrace(); + } + } + return resultDir; + } + + /** + * The main method. + * + * @param args + * the arguments + */ + public static void main(String[] args) + { + Configuration conf = new BaseConfiguration(); + conf.setProperty("wrapper.java.command", "java"); + WindowsJavaHome javaHome = new WindowsJavaHome(conf); + System.out.println(javaHome.findJava(conf.getString("wrapper.java.command"), conf.getString("wrapper.java.command"))); + + conf.setProperty("wrapper.java.customProcName", "test"); + javaHome = new WindowsJavaHome(conf); + System.out.println(javaHome.findJava(conf.getString("wrapper.java.command"), conf.getString("wrapper.java.command"))); + + conf.setProperty("wrapper.java.command", "javaw"); + javaHome = new WindowsJavaHome(conf); + System.out.println(javaHome.findJava(conf.getString("wrapper.java.command"), conf.getString("wrapper.java.command"))); + + conf.clear(); + conf.setProperty("wrapper.java.minversion", "1.5.0"); + conf.setProperty("wrapper.java.maxversion", "1.5.99"); + conf.setProperty("wrapper.java.customProcName", "test"); + javaHome = new WindowsJavaHome(conf); + System.out.println(javaHome.findJava(conf.getString("wrapper.java.command"), conf.getString("wrapper.java.command"))); + + conf.clear(); + conf.setProperty("wrapper.java.minversion", "1.6.0"); + conf.setProperty("wrapper.java.customProcName", "test"); + conf.setProperty("wrapper.java.preferJdk", true); + javaHome = new WindowsJavaHome(conf); + System.out.println(javaHome.findJava(conf.getString("wrapper.java.command"), conf.getString("wrapper.java.command"))); + + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/ms/win/w32/WindowsXPErrorHandler.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/ms/win/w32/WindowsXPErrorHandler.java new file mode 100644 index 0000000000..1eacb1a54d --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/ms/win/w32/WindowsXPErrorHandler.java @@ -0,0 +1,43 @@ +/* This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + *

    + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ +package org.rzo.yajsw.os.ms.win.w32; + +import org.rzo.yajsw.os.ErrorHandler; +import org.rzo.yajsw.os.OsException; + +// TODO: Auto-generated Javadoc +/** + * The Class WindowsXPErrorHandler. + */ +public class WindowsXPErrorHandler implements ErrorHandler +{ + + /* + * (non-Javadoc) + * + * @see org.rzo.yajsw.os.ErrorHandler#toString(int) + */ + public String toString(int id) + { + return null; + } + + /* + * (non-Javadoc) + * + * @see org.rzo.yajsw.os.ErrorHandler#throwException(int) + */ + public void throwException(int id) throws OsException + { + throw new OsException(); + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/ms/win/w32/WindowsXPFileManager.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/ms/win/w32/WindowsXPFileManager.java new file mode 100644 index 0000000000..a751419120 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/ms/win/w32/WindowsXPFileManager.java @@ -0,0 +1,39 @@ +package org.rzo.yajsw.os.ms.win.w32; + +import org.rzo.yajsw.os.FileManager; +import org.rzo.yajsw.util.File; + +public class WindowsXPFileManager implements FileManager +{ + + /** The _instance. */ + static FileManager _instance; + + /** + * Instance. + * + * @return the process manager + */ + public static FileManager instance() + { + if (_instance == null) + _instance = new WindowsXPFileManager(); + return _instance; + } + + public long created(File file) + { + return FileUtils.created(file); + } + + public long freeSpace(File file) + { + return FileUtils.freeSpace(file); + } + + public long totalSpace(File file) + { + return FileUtils.totalSpace(file); + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/ms/win/w32/WindowsXPKeyboard.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/ms/win/w32/WindowsXPKeyboard.java new file mode 100644 index 0000000000..9151c3c805 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/ms/win/w32/WindowsXPKeyboard.java @@ -0,0 +1,306 @@ +/* This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + *

    + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ +package org.rzo.yajsw.os.ms.win.w32; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import org.apache.commons.collections.MultiHashMap; +import org.rzo.yajsw.os.Keyboard; +import org.rzo.yajsw.os.ms.win.w32.DummyWindow.HotKey; +import org.rzo.yajsw.os.ms.win.w32.DummyWindow.WndListner; + +import com.sun.jna.Native; +import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.User32; + +// TODO: Auto-generated Javadoc +/** + * The Class WindowsXPKeyboard. + */ +public class WindowsXPKeyboard implements Keyboard +{ + + /** The _instance. */ + static Keyboard _instance; + + /** + * Instance. + * + * @return the keyboard + */ + static synchronized public Keyboard instance() + { + if (_instance == null) + _instance = new WindowsXPKeyboard(); + return _instance; + } + + /** + * The Interface MyUser32. + */ + public interface MyUser32 extends User32 + { + + /** The INSTANCE. */ + MyUser32 INSTANCE = (MyUser32) Native.loadLibrary("User32", MyUser32.class); + + /* + * LRESULT CALLBACK KeyboardProc( int code, WPARAM wParam, LPARAM lParam + * ); + */ + /** + * The Interface KeyboardProc. + */ + interface KeyboardProc extends StdCallCallback + { + + /** + * Callback. + * + * @param code + * the code + * @param wParam + * the w param + * @param lParam + * the l param + * + * @return the int + */ + int callback(int code, int wParam, int lParam); + } + + /* + * LRESULT CallNextHookEx( HHOOK hhk, int nCode, WPARAM wParam, LPARAM + * lParam ); + */ + /** + * Call next hook ex. + * + * @param hhk + * the hhk + * @param nCode + * the n code + * @param wParam + * the w param + * @param lParam + * the l param + * + * @return the int + */ + int CallNextHookEx(Pointer hhk, int nCode, int wParam, int lParam); + + /* + * HHOOK SetWindowsHookEx( int idHook, HOOKPROC lpfn, HINSTANCE hMod, + * DWORD dwThreadId ); + */ + /** + * Sets the windows hook ex a. + * + * @param idHook + * the id hook + * @param lpfn + * the lpfn + * @param hMod + * the h mod + * @param dwThreadId + * the dw thread id + * + * @return the pointer + */ + Pointer SetWindowsHookExA(int idHook, KeyboardProc lpfn, Pointer hMod, int dwThreadId); + + /** The W h_ keyboard. */ + int WH_KEYBOARD = 2; + + /* + * BOOL UnhookWindowsHookEx( HHOOK hhk ); + */ + /** + * Unhook windows hook ex. + * + * @param hhk + * the hhk + * + * @return true, if successful + */ + boolean UnhookWindowsHookEx(Pointer hhk); + + /* + * BOOL PostMessage( HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam + * ); + */ + /** + * Post message a. + * + * @param hWnd + * the h wnd + * @param uMsg + * the u msg + * @param wParam + * the w param + * @param lParam + * the l param + * + * @return the int + */ + int PostMessageA(Pointer hWnd, int uMsg, int wParam, int lParam); + + } + + /* + * class KeyboardListnerClass implements KeyboardProc { KeyboardListner + * _listner; KeyboardListnerClass(KeyboardListner listner) { _listner = + * listner; } + * + * public int callback(int code, int wParam, int lParam) { + * //_listner.keyRead(null); System.out.println("keyboard callback code: " + * +code + " wParam "+ wParam + " lParam "+lParam ); return + * MyUser32.INSTANCE.CallNextHookEx(null, code, wParam, lParam); } } + * + * public boolean addListner(KeyboardListner listner) { if + * (_listners.get(listner) != null) return false; Pointer hinst = null; + * PointerByReference hhinst = new PointerByReference(); //if + * (MyKernel32.INSTANCE.GetModuleHandleExA(0, null, hhinst)) // hinst = + * hhinst.getPointer(); / if (hinst == null) hinst = + * MyKernel32.INSTANCE.GetModuleHandleA(null); Pointer handle = null; + * KeyboardListnerClass iListner = new KeyboardListnerClass(listner); if + * (hinst != null) handle = + * MyUser32.INSTANCE.SetWindowsHookExA(MyUser32.WH_KEYBOARD, iListner, + * hinst, 0); if (handle != null) { _listners.put(listner, handle); return + * true; } else { int er = MyKernel32.INSTANCE.GetLastError(); + * System.out.println("error "+ Integer.toHexString(er)); } + * + * return false; } + * + * public boolean removeListner(KeyboardListner listner) { Pointer handle = + * (Pointer) _listners.get(listner); if (handle != null) { if + * (MyUser32.INSTANCE.UnhookWindowsHookEx(handle)) + * _listners.remove(listner); return true; } return false; } + */ + + /** The Constant WND_REGISTER_HOTKEY. */ + public static final int WND_REGISTER_HOTKEY = 999; + + /** The Constant WND_UNREGISTER_HOTKEY. */ + public static final int WND_UNREGISTER_HOTKEY = 998; + + /** The _dummy window. */ + DummyWindow _dummyWindow = DummyWindow.instance(); + + /** The _keys. */ + MultiHashMap _keys = new MultiHashMap(); + + /** The _listners. */ + Map _listners = new HashMap(); + + /** + * Instantiates a new windows xp keyboard. + */ + public WindowsXPKeyboard() + { + try + { + _dummyWindow.waitTermination(); + } + catch (InterruptedException e) + { + // TODO Auto-generated catch block + e.printStackTrace(); + } + WndListner hotKeyHandler = new WndListner() + { + + public int execute(int uMsg, int wParam, int lParam) + { + HotKey k = (HotKey) DummyWindow._hotKeys.get(new Integer(wParam)); + if (k == null) + return 0; + Collection listners = _keys.getCollection(k); + if (listners == null) + return 0; + for (Iterator it = listners.iterator(); it.hasNext();) + { + HotKeyListner listner = (HotKeyListner) it.next(); + try + { + listner.keyPressed(); + } + catch (Exception ex) + { + ex.printStackTrace(); + } + } + return 0; + } + + }; + _dummyWindow.addListner(new Integer(DummyWindow.MyUser32.WM_HOTKEY), hotKeyHandler); + + } + + /* + * (non-Javadoc) + * + * @seeorg.rzo.yajsw.os.Keyboard#registerHotkey(org.rzo.yajsw.os.Keyboard. + * HotKeyListner, int, int) + */ + public synchronized void registerHotkey(HotKeyListner listner, int mod, int key) + { + MyUser32.INSTANCE.PostMessageA(_dummyWindow._hWnd, WND_REGISTER_HOTKEY, mod, key); + try + { + _dummyWindow.waitTermination(); + } + catch (InterruptedException e) + { + // TODO Auto-generated catch block + e.printStackTrace(); + } + HotKey k = _dummyWindow.new HotKey(mod, key); + _listners.put(listner, k); + _keys.put(k, listner); + } + + /* + * (non-Javadoc) + * + * @see + * org.rzo.yajsw.os.Keyboard#unregisterHotKey(org.rzo.yajsw.os.Keyboard. + * HotKeyListner) + */ + public synchronized void unregisterHotKey(HotKeyListner listner) + { + HotKey k = (HotKey) _listners.get(listner); + if (k == null) + return; + _listners.remove(listner); + _keys.remove(k, listner); + Collection listners = _keys.getCollection(k); + + if (listners == null || listners.isEmpty()) + { + MyUser32.INSTANCE.PostMessageA(_dummyWindow._hWnd, WND_UNREGISTER_HOTKEY, k._wParam, k._lParam); + try + { + _dummyWindow.waitTermination(); + } + catch (InterruptedException e) + { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/ms/win/w32/WindowsXPMouse.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/ms/win/w32/WindowsXPMouse.java new file mode 100644 index 0000000000..c21562bcd6 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/ms/win/w32/WindowsXPMouse.java @@ -0,0 +1,195 @@ +/* This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + *

    + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ +package org.rzo.yajsw.os.ms.win.w32; + +import java.util.concurrent.Executor; + +import org.rzo.yajsw.os.Mouse; + +import com.sun.jna.Native; +import com.sun.jna.Platform; +import com.sun.jna.Structure; +import com.sun.jna.platform.win32.Kernel32; +import com.sun.jna.platform.win32.User32; +import com.sun.jna.platform.win32.BaseTSD.ULONG_PTR; +import com.sun.jna.platform.win32.WinDef.HWND; +import com.sun.jna.platform.win32.WinDef.LRESULT; +import com.sun.jna.platform.win32.WinDef.WPARAM; +import com.sun.jna.platform.win32.WinUser.HHOOK; +import com.sun.jna.platform.win32.WinUser.HOOKPROC; +import com.sun.jna.platform.win32.WinUser.MSG; +import com.sun.jna.platform.win32.WinUser.POINT; + +// TODO: Auto-generated Javadoc +/** + * The Class WindowsXPKeyboard. + */ +public class WindowsXPMouse implements Mouse +{ + + + public final User32 USER32INST; + public final Kernel32 KERNEL32INST; + public WindowsXPMouse() + { + if(!Platform.isWindows()) + { + throw new UnsupportedOperationException("Not supported on this platform."); + } + USER32INST = User32.INSTANCE; + KERNEL32INST = Kernel32.INSTANCE; + mouseHook=hookTheMouse(); + Native.setProtected(true); + + } + public static LowLevelMouseProc mouseHook; + public static Runnable action; + public static Mouse instance; + public HHOOK hhk; + public Thread thrd; + public boolean threadFinish = true; + public boolean isHooked = false; + public static final int WM_MOUSEMOVE = 512; + public static final int WM_LBUTTONDOWN = 513; + public static final int WM_LBUTTONUP = 514; + public static final int WM_RBUTTONDOWN = 516; + public static final int WM_RBUTTONUP = 517; + public static final int WM_MBUTTONDOWN = 519; + public static final int WM_MBUTTONUP = 520; + + public static Mouse instance() + { + if (instance == null) + instance = new WindowsXPMouse(); + return instance; + } + + + public synchronized void unregisterMouseUpListner() + { + if (thrd == null) + return; + //System.out.println("unregister "); + threadFinish = true; + if (thrd.isAlive()) + { + thrd.interrupt(); + thrd = null; + } + isHooked = false; + } + public boolean isIsHooked() + { + return isHooked; + } + public synchronized void registerMouseUpListner(Runnable action, Executor executor) + { + if (thrd != null) + return; + this.action = action; + thrd = new Thread(new Runnable() { + + public void run() + { + try + { + if(!isHooked) + { + hhk = USER32INST.SetWindowsHookEx(14, (HOOKPROC) mouseHook,KERNEL32INST.GetModuleHandle(null),0); + isHooked = true; + MSG msg = new MSG(); + while ((USER32INST.GetMessage(msg, null, 0, 0)) != 0) + { + System.out.println("got message"); + USER32INST.TranslateMessage(msg); + USER32INST.DispatchMessage(msg); + System.out.print(isHooked); + if (!isHooked) + break; + } + } + else + System.out.println("The Hook is already installed."); + } + catch (Exception e) + { System.err.println(e.getMessage()); + System.err.println("Caught exception in MouseHook!"); + } + //System.out.println("terminated "); + USER32INST.UnhookWindowsHookEx(hhk); + hhk = null; + } + + },"Named thread"); + threadFinish = false; + thrd.start(); + + } + private interface LowLevelMouseProc extends HOOKPROC + { + LRESULT callback(int nCode, WPARAM wParam, MOUSEHOOKSTRUCT lParam); + } + public LowLevelMouseProc hookTheMouse() { + return new LowLevelMouseProc() + { + + public LRESULT callback(int nCode, WPARAM wParam, MOUSEHOOKSTRUCT info) { + LRESULT result = USER32INST.CallNextHookEx(hhk, nCode, wParam, info.getPointer()); + if (nCode >= 0) + { + int action = wParam.intValue(); + //System.out.println(action); + switch(action) + { + case WM_LBUTTONDOWN: + // do stuff + break; + case WM_RBUTTONDOWN: + WindowsXPMouse.action.run(); + break; + case WM_MBUTTONDOWN: + //do other stuff + break; + case WM_LBUTTONUP: + WindowsXPMouse.action.run(); + break; + case WM_MOUSEMOVE: + + break; + default: + break; + } + /****************************DO NOT CHANGE, this code unhooks mouse *********************************/ + if (threadFinish == true) + { + //System.out.println("post quit"); + USER32INST.PostQuitMessage(0); + } + /***************************END OF UNCHANGABLE *******************************************************/ + } + return result; + } + }; + } + public static class MOUSEHOOKSTRUCT extends Structure + { + public static class ByReference extends MOUSEHOOKSTRUCT implements Structure.ByReference {}; + public POINT pt; + public HWND hwnd; + public int wHitTestCode; + public ULONG_PTR dwExtraInfo; + } + + public static void main(String[] args) + { + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/ms/win/w32/WindowsXPProcess.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/ms/win/w32/WindowsXPProcess.java new file mode 100644 index 0000000000..bcdb972642 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/ms/win/w32/WindowsXPProcess.java @@ -0,0 +1,3934 @@ +/* This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + *

    + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ +package org.rzo.yajsw.os.ms.win.w32; + + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileDescriptor; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.FileReader; +import java.io.IOException; +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import jnacontrib.jna.Advapi32; +import jnacontrib.jna.Options; + +import org.apache.commons.collections.MultiHashMap; +import org.rzo.yajsw.io.CyclicBufferFileInputStream; +import org.rzo.yajsw.io.CyclicBufferFilePrintStream; +import org.rzo.yajsw.os.AbstractProcess; +import org.rzo.yajsw.os.Process; +import org.rzo.yajsw.os.ms.win.w32.WindowsXPProcess.MyAdvapi.TOKEN_PRIVILEGES; +import org.rzo.yajsw.os.ms.win.w32.WindowsXPProcess.MyKernel32.MEMORY_BASIC_INFORMATION; +import org.rzo.yajsw.os.ms.win.w32.WindowsXPProcess.MyKernel32.PROCESSENTRY32; +import org.rzo.yajsw.os.ms.win.w32.WindowsXPProcess.MyKernel32.PROCESS_INFORMATION; +import org.rzo.yajsw.os.ms.win.w32.WindowsXPProcess.MyKernel32.SECURITY_ATTRIBUTES; +import org.rzo.yajsw.os.ms.win.w32.WindowsXPProcess.MyKernel32.STARTUPINFO; +import org.rzo.yajsw.os.ms.win.w32.WindowsXPProcess.Ntdll.PEB; +import org.rzo.yajsw.os.ms.win.w32.WindowsXPProcess.Ntdll.PEB64; +import org.rzo.yajsw.os.ms.win.w32.WindowsXPProcess.Ntdll.PROCESS_BASIC_INFORMATION; +import org.rzo.yajsw.os.ms.win.w32.WindowsXPProcess.Ntdll.RTL_USER_PROCESS_PARAMETERS; +import org.rzo.yajsw.os.ms.win.w32.WindowsXPProcess.Shell32.SHELLEXECUTEINFO; + +import com.sun.jna.Memory; +import com.sun.jna.Native; +import com.sun.jna.NativeLong; +import com.sun.jna.PlatformEx; +import com.sun.jna.Pointer; +import com.sun.jna.StringBlock; +import com.sun.jna.Structure; +import com.sun.jna.WString; +import com.sun.jna.platform.win32.Kernel32Util; +import com.sun.jna.platform.win32.User32; +import com.sun.jna.platform.win32.WinDef.HWND; +import com.sun.jna.platform.win32.WinNT.HANDLE; +import com.sun.jna.platform.win32.WinUser.WINDOWINFO; +import com.sun.jna.ptr.IntByReference; +import com.sun.jna.ptr.LongByReference; +import com.sun.jna.ptr.NativeLongByReference; +import com.sun.jna.ptr.PointerByReference; +import com.sun.jna.win32.StdCallLibrary; + +// TODO: Auto-generated Javadoc +/** + * The Class WindowsXPProcess. + */ +public class WindowsXPProcess extends AbstractProcess +{ + public interface Shell32 extends com.sun.jna.win32.StdCallLibrary + { + // Method declarations, constant and structure definitions go here + + /** The INSTANCE. */ + Shell32 INSTANCE = (Shell32) Native.loadLibrary("Shell32", Shell32.class); + + /* + * BOOL ShellExecuteEx( + __inout LPSHELLEXECUTEINFO lpExecInfo +); + */ + boolean ShellExecuteEx( + SHELLEXECUTEINFO lpExecInfo + ); + + /* + * typedef struct _SHELLEXECUTEINFO { + DWORD cbSize; + ULONG fMask; + HWND hwnd; + LPCTSTR lpVerb; + LPCTSTR lpFile; + LPCTSTR lpParameters; + LPCTSTR lpDirectory; + int nShow; + HINSTANCE hInstApp; + LPVOID lpIDList; + LPCTSTR lpClass; + HKEY hkeyClass; + DWORD dwHotKey; + union { + HANDLE hIcon; + HANDLE hMonitor; + } DUMMYUNIONNAME; + HANDLE hProcess; +} SHELLEXECUTEINFO, *LPSHELLEXECUTEINFO; + */ + public static class SHELLEXECUTEINFO extends Structure + { + public int cbSize; + public int fMask; + public Pointer hwnd; + public String lpVerb; + public String lpFile; + public String lpParameters; + public String lpDirectory; + public int nShow; + public IntByReference hInstApp; + public Pointer lpIDList; + public String lpClass; + public Pointer hkeyClass; + public int dwHotKey; + public Pointer hMonitor; + public HANDLE hProcess; + } + + public static final int SEE_MASK_DEFAULT = 0x00000000; + public static final int SEE_MASK_CLASSNAME = 0x00000001; + public static final int SEE_MASK_CLASSKEY = 0x00000003; + public static final int SEE_MASK_IDLIST = 0x00000004; + public static final int SEE_MASK_INVOKEIDLIST = 0x0000000C; + public static final int SEE_MASK_ICON = 0x00000010; + public static final int SEE_MASK_HOTKEY = 0x00000020; + public static final int SEE_MASK_NOCLOSEPROCESS = 0x00000040; + public static final int SEE_MASK_CONNECTNETDRV = 0x00000080; + public static final int SEE_MASK_NOASYNC = 0x00000100; + public static final int SEE_MASK_FLAG_DDEWAIT = 0x00000100; + public static final int SEE_MASK_DOENVSUBST = 0x00000200; + public static final int SEE_MASK_FLAG_NO_UI = 0x00000400; + public static final int SEE_MASK_UNICODE = 0x00004000; + public static final int SEE_MASK_NO_CONSOLE = 0x00008000; + public static final int SEE_MASK_ASYNCOK = 0x00100000; + public static final int SEE_MASK_NOQUERYCLASSSTORE = 0x01000000; + public static final int SEE_MASK_HMONITOR = 0x00200000; + public static final int SEE_MASK_NOZONECHECKS = 0x00800000; + public static final int SEE_MASK_WAITFORINPUTIDLE = 0x02000000; + public static final int SEE_MASK_FLAG_LOG_USAGE = 0x04000000; + + public static final String VERB_EDIT = "edit"; + public static final String VERB_EXPLORE = "explore"; + public static final String VERB_FIND = "find"; + public static final String VERB_OPEN = "open"; + public static final String VERB_PRINT = "print"; + public static final String VERB_PROPERTIES = "properties"; + public static final String VERB_RUNAS = "runas"; + + public static final int SW_HIDE = 0; + public static final int SW_MAXIMIZE = 3; + public static final int SW_MINIMIZE = 6; + public static final int SW_RESTORE = 9; + public static final int SW_SHOW = 5; + public static final int SW_SHOWDEFAULT = 10; + public static final int SW_SHOWMAXIMIZED = 3; + public static final int SW_SHOWMINIMIZED = 2; + public static final int SW_SHOWMINNOACTIVE = 7; + public static final int SW_SHOWNA = 8; + public static final int SW_SHOWNOACTIVATE = 4; + public static final int SW_SHOWNORMAL = 1; + + + } + + + + /** + * The Interface MyUser32. + */ + public interface MyUser32 extends User32 + { + // Method declarations, constant and structure definitions go here + + /** The INSTANCE. */ + MyUser32 INSTANCE = (MyUser32) Native.loadLibrary("User32", MyUser32.class); + + /* + * HWND GetForegroundWindow(VOID); + */ + /** + * Gets the foreground window. + * + * @return the pointer + */ + HWND GetForegroundWindow(); + + /* + * DWORD GetWindowThreadProcessId( HWND hWnd, LPDWORD lpdwProcessId ); + */ + /** + * Gets the window thread process id. + * + * @param hWnd + * the h wnd + * @param lpdwProcessId + * the lpdw process id + * + * @return the int + */ + int GetWindowThreadProcessId(Pointer hWnd, IntByReference lpdwProcessId); + + /** The W m_ close. */ + int WM_CLOSE = 16; + int WM_QUIT = 18; + int WM_DESTROY = 2; + int WM_KEYDOWN = 256; + int WM_KEYUP = 257; + int WM_CHAR = 258; + int WM_SETFOCUS = 7; + + int VK_CONTROL = 17; + + int GetWindowTextA(HWND hWnd, byte[] lpString, int nMaxCount); + + + /* + * BOOL WINAPI GetWindowInfo( + __in HWND hwnd, + __inout PWINDOWINFO pwi +); + */ + boolean GetWindowInfo( + HWND hwnd, + WINDOWINFO pwi + ); + + /* + * BOOL PostThreadMessage( DWORD idThread, UINT Msg, WPARAM wParam, + * LPARAM lParam ); + */ + /** + * Post thread message a. + * + * @param idThread + * the id thread + * @param Msg + * the msg + * @param wParam + * the w param + * @param lParam + * the l param + * + * @return true, if successful + */ + boolean PostThreadMessageA(int idThread, int Msg, int wParam, int lParam); + + /* + * DWORD WINAPI WaitForInputIdle( __in HANDLE hProcess, __in DWORD + * dwMilliseconds ); + */ + /** + * Wait for input idle. + * + * @param hProcess + * the h process + * @param dwMilliseconds + * the dw milliseconds + * + * @return the int + */ + int WaitForInputIdle(HANDLE hProcess, int dwMilliseconds); + + void PostMessageA(HWND hWnd, int msg, Pointer wParam, Pointer lParam); + void SendMessageW(HWND hWnd, int msg, long wParam, int lParam); + public interface WNDENUMPROC extends StdCallCallback + { + /** Return whether to continue enumeration. */ + boolean callback(HWND hWnd, int data); + } + + boolean EnumWindows(WNDENUMPROC lpEnumFunc, int data); + + } + + /** + * The Interface MyKernel32. + */ + public interface MyKernel32 extends com.sun.jna.platform.win32.Kernel32 + { + + // Method declarations, constant and structure definitions go here + + /** The INSTANCE. */ + MyKernel32 INSTANCE = (MyKernel32) Native.loadLibrary("kernel32", MyKernel32.class); + + /* + * BOOL WINAPI ReadFile( __in HANDLE hFile, __out LPVOID lpBuffer, __in + * DWORD nNumberOfBytesToRead, __out_opt LPDWORD lpNumberOfBytesRead, + * __inout_opt LPOVERLAPPED lpOverlapped ); + */ + + boolean ReadFile(Pointer hFile, Memory lpBuffer, int nNumberOfBytesToRead, IntByReference lpNumberOfBytesRead, Structure lpOverlapped); + + /* + * DWORD WINAPI GetCurrentProcessId(void); + */ + /* + * (non-Javadoc) + * + * @see com.sun.jna.examples.win32.Kernel32#GetCurrentProcessId() + */ + int GetCurrentProcessId(); + + /* + * DWORD WINAPI GetProcessIdOfThread( __in HANDLE Thread ); + */ + /** + * Gets the process id of thread. + * + * @param Thread + * the thread + * + * @return the int + */ + int GetProcessIdOfThread(Pointer Thread); + + /* + * BOOL WINAPI CreateProcess( LPCTSTR lpApplicationName, LPTSTR + * lpCommandLine, LPSECURITY_ATTRIBUTES lpProcessAttributes, + * LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles, DWORD + * dwCreationFlags, LPVOID lpEnvironment, LPCTSTR lpCurrentDirectory, + * LPSTARTUPINFO lpStartupInfo, LPPROCESS_INFORMATION + * lpProcessInformation ); + */ + /** + * Creates the process a. + * + * @param lpApplicationName + * the lp application name + * @param lpCommandLine + * the lp command line + * @param lpProcessAttributes + * the lp process attributes + * @param lpThreadAttributes + * the lp thread attributes + * @param bInheritHandles + * the b inherit handles + * @param dwCreationFlags + * the dw creation flags + * @param lpEnvironment + * the lp environment + * @param lpCurrentDirectory + * the lp current directory + * @param lpStartupInfo + * the lp startup info + * @param lpProcessInformation + * the lp process information + * + * @return true, if successful + */ + boolean CreateProcessA(String lpApplicationName, String lpCommandLine, Structure lpProcessAttributes, Structure lpThreadAttributes, + boolean bInheritHandles, int dwCreationFlags, Structure lpEnvironment, String lpCurrentDirectory, Structure lpStartupInfo, + Structure lpProcessInformation); + + boolean CreateProcessW(WString lpApplicationName, WString lpCommandLine, Structure lpProcessAttributes, Structure lpThreadAttributes, + boolean bInheritHandles, int dwCreationFlags, Memory lpEnvironment, WString lpCurrentDirectory, Structure lpStartupInfo, + Structure lpProcessInformation); + + /** The CREAT e_ n o_ window. */ + int CREATE_NO_WINDOW = 0x08000000; + + /** The CREAT e_ unicod e_ environment. */ + int CREATE_UNICODE_ENVIRONMENT = 0x00000400; + + /** The CREAT e_ ne w_ console. */ + int CREATE_NEW_CONSOLE = 0x00000010; + + int DETACHED_PROCESS = 0x00000008; + + /* + * typedef struct _PROCESS_INFORMATION { HANDLE hProcess; HANDLE + * hThread; DWORD dwProcessId; DWORD dwThreadId; } + */ + + /** + * The Class PROCESS_INFORMATION. + */ + public static class PROCESS_INFORMATION extends Structure + { + + /** The h process. */ + public HANDLE hProcess = null; + + /** The h thread. */ + public Pointer hThread = null; + + /** The dw process id. */ + public int dwProcessId = -1; + + /** The dw thread id. */ + public int dwThreadId = -1; + + @Override + public void finalize() + { + try + { + super.finalize(); + } + catch (Throwable e) + { + e.printStackTrace(); + } + } + + } + + /* + * typedef struct _STARTUPINFO { DWORD cb; LPTSTR lpReserved; LPTSTR + * lpDesktop; LPTSTR lpTitle; DWORD dwX; DWORD dwY; DWORD dwXSize; DWORD + * dwYSize; DWORD dwXCountChars; DWORD dwYCountChars; DWORD + * dwFillAttribute; DWORD dwFlags; WORD wShowWindow; WORD cbReserved2; + * LPBYTE lpReserved2; HANDLE hStdInput; HANDLE hStdOutput; HANDLE + * hStdError; } + */ + + /** + * The Class STARTUPINFO. + */ + public static class STARTUPINFO extends Structure + { + + /** The cb. */ + public int cb; + + /** The lp reserved. */ + public WString lpReserved; + + /** The lp desktop. */ + public WString lpDesktop; + + /** The lp title. */ + public WString lpTitle; + + /** The dw x. */ + public int dwX; + + /** The dw y. */ + public int dwY; + + /** The dw x size. */ + public int dwXSize; + + /** The dw y size. */ + public int dwYSize; + + /** The dw x count chars. */ + public int dwXCountChars; + + /** The dw y count chars. */ + public int dwYCountChars; + + /** The dw fill attribute. */ + public int dwFillAttribute; + + /** The dw flags. */ + public int dwFlags; + + /** The w show window. */ + public short wShowWindow; + + /** The cb reserved2. */ + public short cbReserved2; + + /** The lp reserved2. */ + public Pointer lpReserved2; + + /** The h std input. */ + public Pointer hStdInput; + + /** The h std output. */ + public Pointer hStdOutput; + + /** The h std error. */ + public Pointer hStdError; + + @Override + public void finalize() + { + try + { + super.finalize(); + } + catch (Throwable e) + { + e.printStackTrace(); + } + } + + @Override + public String toString() + { + return ""+hStdError+"/"+hStdOutput+"/"+hStdInput; + } + + } + + int SW_SHOWMINIMIZED = 2; + int SW_SHOWNOACTIVATE = 4; + + int STARTF_USESHOWWINDOW = 0x00000001; + + /** The START f_ usestdhandles. */ + int STARTF_USESTDHANDLES = 256; + + /** The IDL e_ priorit y_ class. */ + int IDLE_PRIORITY_CLASS = 0x00000040; + + /** The BELO w_ norma l_ priorit y_ class. */ + int BELOW_NORMAL_PRIORITY_CLASS = 0x00004000; + + /** The NORMA l_ priorit y_ class. */ + int NORMAL_PRIORITY_CLASS = 0x00000020; + + /** The ABOV e_ norma l_ priorit y_ class. */ + int ABOVE_NORMAL_PRIORITY_CLASS = 0x00008000; + + /** The HIG h_ priorit y_ class. */ + int HIGH_PRIORITY_CLASS = 0x00000080; + + /** The REALTIM e_ priorit y_ class. */ + int REALTIME_PRIORITY_CLASS = 0x00000100; + + /* + * DWORD WINAPI WaitForSingleObject( HANDLE hHandle, DWORD + * dwMilliseconds ); + */ + /** + * Wait for single object. + * + * @param handle + * the handle + * @param dwMilliseconds + * the dw milliseconds + * + * @return the int + */ + int WaitForSingleObject(Pointer handle, int dwMilliseconds); + + /** The INFINITE. */ + int INFINITE = 0xFFFFFFFF; + + /* + * BOOL WINAPI GetExitCodeProcess( HANDLE hProcess, LPDWORD lpExitCode + * ); + */ + /** + * Gets the exit code process. + * + * @param handle + * the h process + * @param lpExitCode + * the lp exit code + * + * @return true, if successful + */ + boolean GetExitCodeProcess(HANDLE handle, IntByReference lpExitCode); + + /** The STIL l_ active. */ + int STILL_ACTIVE = 0x103; + + /* + * BOOL WINAPI TerminateProcess( HANDLE hProcess, UINT uExitCode ); + */ + + /** + * Terminate process. + * + * @param handle + * the h process + * @param uExitCode + * the u exit code + * + * @return true, if successful + */ + boolean TerminateProcess(HANDLE handle, int uExitCode); + + /* + * BOOL WINAPI GetProcessAffinityMask( __in HANDLE hProcess, __out + * PDWORD_PTR lpProcessAffinityMask, __out PDWORD_PTR + * lpSystemAffinityMask ); + */ + /** + * Gets the process affinity mask. + * + * @param handle + * the h process + * @param lpProcessAffinityMask + * the lp process affinity mask + * @param lpSystemAffinityMask + * the lp system affinity mask + * + * @return true, if successful + */ + boolean GetProcessAffinityMask(HANDLE handle, IntByReference lpProcessAffinityMask, IntByReference lpSystemAffinityMask); + + /* + * BOOL WINAPI SetProcessAffinityMask( __in HANDLE hProcess, __in + * DWORD_PTR dwProcessAffinityMask ); + */ + /** + * Sets the process affinity mask. + * + * @param handle + * the h process + * @param dwProcessAffinityMask + * the dw process affinity mask + * + * @return true, if successful + */ + boolean SetProcessAffinityMask(HANDLE handle, int dwProcessAffinityMask); + + /* + * BOOL WINAPI CloseHandle( HANDLE hObject ); + */ + /** + * Close handle. + * + * @param hObject + * the h object + * + * @return true, if successful + */ + public boolean CloseHandle(Pointer hObject); + + /* + * HANDLE WINAPI CreateToolhelp32Snapshot( DWORD dwFlags, DWORD + * th32ProcessID ); + */ + /** + * Creates the toolhelp32 snapshot. + * + * @param dwFlags + * the dw flags + * @param th32ProcessID + * the th32 process id + * + * @return the pointer + */ + Pointer CreateToolhelp32Snapshot(int dwFlags, int th32ProcessID); + + /** The T h32 c s_ snapprocess. */ + int TH32CS_SNAPPROCESS = 0x2; + + int WAIT_FAILED = 0xFFFFFFFF; + int WAIT_TIMEOUT = 0x00000102; + int WAIT_OBJECT_0 = 0x00000000; + int WAIT_ABANDONED = 0x00000080; + + /* + * BOOL WINAPI Process32First( HANDLE hSnapshot, LPPROCESSENTRY32 lppe + * ); + */ + /** + * Process32 first. + * + * @param hSnapshot + * the h snapshot + * @param lppe + * the lppe + * + * @return true, if successful + */ + boolean Process32First(Pointer hSnapshot, Structure lppe); + + /* + * typedef struct tagPROCESSENTRY32 { DWORD dwSize; DWORD cntUsage; + * DWORD th32ProcessID; ULONG_PTR th32DefaultHeapID; DWORD th32ModuleID; + * DWORD cntThreads; DWORD th32ParentProcessID; LONG pcPriClassBase; + * DWORD dwFlags; TCHAR szExeFile[MAX_PATH]; } PROCESSENTRY32, + * PPROCESSENTRY32; + */ + + /** + * The Class PROCESSENTRY32. + */ + public static class PROCESSENTRY32 extends Structure + { + + /** The dw size. */ + public int dwSize; + + /** The cnt usage. */ + public int cntUsage; + + /** The th32 process id. */ + public int th32ProcessID; + + /** The th32 default heap id. */ + public int th32DefaultHeapID; + + /** The th32 module id. */ + public int th32ModuleID; + + /** The cnt threads. */ + public int cntThreads; + + /** The th32 parent process id. */ + public int th32ParentProcessID; + + /** The pc pri class base. */ + public int pcPriClassBase; + + /** The dw flags. */ + public int dwFlags; + + /** The sz exe file. */ + public char[] szExeFile; + } + + /** The MA x_ path. */ + int MAX_PATH = 260; + + /* + * BOOL WINAPI Process32Next( HANDLE hSnapshot, LPPROCESSENTRY32 lppe ); + */ + /** + * Process32 next. + * + * @param hSnapshot + * the h snapshot + * @param lppe + * the lppe + * + * @return true, if successful + */ + boolean Process32Next(Pointer hSnapshot, Structure lppe); + + /* + * HANDLE WINAPI OpenProcess( DWORD dwDesiredAccess, BOOL + * bInheritHandle, DWORD dwProcessId ); + */ + /** + * Open process. + * + * @param dwDesiredAccess + * the dw desired access + * @param bInheritHandle + * the b inherit handle + * @param dwProcessId + * the dw process id + * + * @return the pointer + */ + HANDLE OpenProcess(int dwDesiredAccess, boolean bInheritHandle, int dwProcessId); + + /** The PROCES s_ terminate. */ + int PROCESS_TERMINATE = 1; + + /** The PROCES s_ quer y_ information. */ + int PROCESS_QUERY_INFORMATION = 1024; + + /** The STANDAR d_ right s_ required. */ + int STANDARD_RIGHTS_REQUIRED = 0xF0000; + + /** The SYNCHRONIZE. */ + int SYNCHRONIZE = 0x100000; + + /** The PROCES s_ al l_ access. */ + int PROCESS_ALL_ACCESS = STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0xFFF; + + /* + * BOOL WINAPI GetProcessTimes( __in HANDLE hProcess, __out LPFILETIME + * lpCreationTime, __out LPFILETIME lpExitTime, __out LPFILETIME + * lpKernelTime, __out LPFILETIME lpUserTime ); + */ + /** + * Gets the process times. + * + * @param handle + * the h process + * @param lpCreationTime + * the lp creation time + * @param lpExitTime + * the lp exit time + * @param lpKernelTime + * the lp kernel time + * @param lpUserTime + * the lp user time + * + * @return true, if successful + */ + boolean GetProcessTimes(HANDLE handle, LongByReference lpCreationTime, LongByReference lpExitTime, LongByReference lpKernelTime, + LongByReference lpUserTime); + + /* + * BOOL WINAPI CreatePipe( __out PHANDLE hReadPipe, __out PHANDLE + * hWritePipe, __in LPSECURITY_ATTRIBUTES lpPipeAttributes, __in DWORD + * nSize ); + */ + /** + * Creates the pipe. + * + * @param hReadPipe + * the h read pipe + * @param hWritePipe + * the h write pipe + * @param lpPipeAttributes + * the lp pipe attributes + * @param nSize + * the n size + * + * @return the int + */ + int CreatePipe(PointerByReference hReadPipe, PointerByReference hWritePipe, Structure lpPipeAttributes, int nSize); + + /* + * typedef struct _SECURITY_ATTRIBUTES { DWORD nLength; LPVOID + * lpSecurityDescriptor; BOOL bInheritHandle; } SECURITY_ATTRIBUTES, + * PSECURITY_ATTRIBUTES, LPSECURITY_ATTRIBUTES; + */ + + /** + * The Class SECURITY_ATTRIBUTES. + */ + public static class SECURITY_ATTRIBUTES extends Structure + { + + /** The n length. */ + public int nLength; + + /** The lp security descriptor. */ + public Pointer lpSecurityDescriptor; + + /** The b inherit handle. */ + public boolean bInheritHandle; + } + + /* + * BOOL WINAPI SetHandleInformation( __in HANDLE hObject, __in DWORD + * dwMask, __in DWORD dwFlags ); + */ + /** + * Sets the handle information. + * + * @param hObject + * the h object + * @param dwMask + * the dw mask + * @param dwFlags + * the dw flags + * + * @return true, if successful + */ + boolean SetHandleInformation(Pointer hObject, int dwMask, int dwFlags); + + /** The HANDL e_ fla g_ inherit. */ + int HANDLE_FLAG_INHERIT = 0x00000001; + int HANDLE_FLAG_PROTECT_FROM_CLOSE = 0x00000002; + + /* + * HANDLE WINAPI CreateNamedPipe( __in LPCTSTR lpName, __in DWORD + * dwOpenMode, __in DWORD dwPipeMode, __in DWORD nMaxInstances, __in + * DWORD nOutBufferSize, __in DWORD nInBufferSize, __in DWORD + * nDefaultTimeOut, __in_opt LPSECURITY_ATTRIBUTES lpSecurityAttributes + * ); + */ + /** + * Creates the named pipe a. + * + * @param lpName + * the lp name + * @param dwOpenMode + * the dw open mode + * @param dwPipeMode + * the dw pipe mode + * @param nMaxInstances + * the n max instances + * @param nOutBufferSize + * the n out buffer size + * @param nInBufferSize + * the n in buffer size + * @param nDefaultTimeOut + * the n default time out + * @param lpSecurityAttributes + * the lp security attributes + * + * @return the pointer + */ + Pointer CreateNamedPipeA(String lpName, int dwOpenMode, int dwPipeMode, int nMaxInstances, int nOutBufferSize, int nInBufferSize, + int nDefaultTimeOut, SECURITY_ATTRIBUTES lpSecurityAttributes); + + /** The PIP e_ acces s_ outbound. */ + int PIPE_ACCESS_OUTBOUND = 0x00000002; + + /** The PIP e_ acces s_ inbound. */ + int PIPE_ACCESS_INBOUND = 0x00000001; + + /** The PIP e_ wait. */ + int PIPE_WAIT = 0x00000000; + + /** The PIP e_ nowait. */ + int PIPE_NOWAIT = 0x00000001; + + /** The GENERI c_ read. */ + int GENERIC_READ = 0x80000000; + + /** + * Creates the file a. + * + * @param lpFileName + * the lp file name + * @param dwDesiredAccess + * the dw desired access + * @param dwShareMode + * the dw share mode + * @param lpSecurityAttributes + * the lp security attributes + * @param dwCreationDisposition + * the dw creation disposition + * @param dwFlagsAndAttributes + * the dw flags and attributes + * @param hTemplateFile + * the h template file + * + * @return the pointer + */ + Pointer CreateFileA(String lpFileName, int dwDesiredAccess, int dwShareMode, SECURITY_ATTRIBUTES lpSecurityAttributes, + int dwCreationDisposition, int dwFlagsAndAttributes, Pointer hTemplateFile); + + /* + * BOOL WINAPI ConnectNamedPipe( __in HANDLE hNamedPipe, __inout_opt + * LPOVERLAPPED lpOverlapped ); + */ + /** + * Connect named pipe. + * + * @param hNamedPipe + * the h named pipe + * @param lpOverlapped + * the lp overlapped + * + * @return true, if successful + */ + boolean ConnectNamedPipe(Pointer hNamedPipe, PointerByReference lpOverlapped); + + /** The INVALI d_ handl e_ value. */ + Pointer INVALID_HANDLE_VALUE = Pointer.createConstant(-1); + + /* + * BOOL WINAPI WaitNamedPipe( __in LPCTSTR lpNamedPipeName, __in DWORD + * nTimeOut ); + */ + /** + * Wait named pipe a. + * + * @param lpNamedPipeName + * the lp named pipe name + * @param nTimeOut + * the n time out + * + * @return true, if successful + */ + boolean WaitNamedPipeA(String lpNamedPipeName, int nTimeOut); + + /** The NMPWAI t_ us e_ defaul t_ wait. */ + int NMPWAIT_USE_DEFAULT_WAIT = 0; + + /** The NMPWAI t_ wai t_ forever. */ + int NMPWAIT_WAIT_FOREVER = 0xffffffff; + + /* + * BOOL WINAPI SetCurrentDirectory( __in LPCTSTR lpPathName ); + */ + boolean SetCurrentDirectoryA(String lpPathName); + + /* + * typedef struct _MEMORY_BASIC_INFORMATION { PVOID BaseAddress; PVOID + * AllocationBase; DWORD AllocationProtect; SIZE_T RegionSize; DWORD + * State; DWORD Protect; DWORD Type; } MEMORY_BASIC_INFORMATION, + * *PMEMORY_BASIC_INFORMATION; + */ + public static class MEMORY_BASIC_INFORMATION extends Structure + { + public Pointer BaseAddress; + public Pointer AllocationBase; + public int AllocationProtect; + public Pointer RegionSize; + public int State; + public int Protect; + public int Type; + } + + public static int PAGE_NOACCESS = 0x01; + public static int PAGE_EXECUTE = 0x10; + + /* + * SIZE_T WINAPI VirtualQueryEx( __in HANDLE hProcess, __in_opt LPCVOID + * lpAddress, __out PMEMORY_BASIC_INFORMATION lpBuffer, __in SIZE_T + * dwLength ); + */ + int VirtualQueryEx(Pointer hProcess, Pointer lpAddress, Pointer lpBuffer, int dwLength); + + boolean ReadProcessMemory(Pointer hProcess, Pointer lpBaseAddress, Pointer lpBuffer, NativeLong nSize, NativeLongByReference lpNumberOfBytesRead); + + /* + * DWORD WTSGetActiveConsoleSessionId(void); + */ + int WTSGetActiveConsoleSessionId(); + + } + + /** + * The Interface Ntdll. + */ + public interface Ntdll extends com.sun.jna.win32.StdCallLibrary + { + + /** The INSTANCE. */ + Ntdll INSTANCE = (Ntdll) Native.loadLibrary("Ntdll", Ntdll.class); + + /* + * NTOSAPI NTSTATUS NTAPI ZwReadVirtualMemory( /IN/ HANDLE + * ProcessHandle, /IN/ PVOID BaseAddress, /OUT/ PVOID Buffer, /IN/ ULONG + * BufferLength, /OUT/ PULONG ReturnLength /OPTIONAL/); + */ + + /** + * Zw read virtual memory. + * + * @param ProcessHandle + * the process handle + * @param BaseAddress + * the base address + * @param Buffer + * the buffer + * @param BufferLength + * the buffer length + * @param ReturnLength + * the return length + * + * @return the int + */ + int ZwReadVirtualMemory(Pointer ProcessHandle, Pointer BaseAddress, Pointer Buffer, int BufferLength, IntByReference ReturnLength); + + /* + * NTSTATUS WINAPI ZwQueryInformationProcess( __in HANDLE ProcessHandle, + * __in PROCESSINFOCLASS ProcessInformationClass, __out PVOID + * ProcessInformation, __in ULONG ProcessInformationLength, __out_opt + * PULONG ReturnLength ); + */ + /** + * Zw query information process. + * + * @param process + * the process handle + * @param ProcessInformationClass + * the process information class + * @param ProcessInformation + * the process information + * @param ProcessInformationLength + * the process information length + * @param ReturnLength + * the return length + * + * @return the int + */ + int ZwQueryInformationProcess(HANDLE process, int ProcessInformationClass, Pointer ProcessInformation, int ProcessInformationLength, + IntByReference ReturnLength); + + /* + * typedef struct _PROCESS_BASIC_INFORMATION { PVOID Reserved1; PPEB + * PebBaseAddress; PVOID Reserved2[2]; ULONG_PTR UniqueProcessId; PVOID + * Reserved3; } PROCESS_BASIC_INFORMATION; + */ + /** + * The Class PROCESS_BASIC_INFORMATION. + */ + class PROCESS_BASIC_INFORMATION extends Structure + { + + /** The Reserved1. */ + public Pointer Reserved1; + + /** The Peb base address. */ + public Pointer PebBaseAddress; + + /** The Reserved2. */ + public Pointer[] Reserved2 = new Pointer[2]; + + /** The Unique process id. */ + public Pointer UniqueProcessId; + + /** The Reserved3. */ + public Pointer Reserved3; + } + + /* + * typedef struct _PEB { BYTE Reserved1[2]; BYTE BeingDebugged; BYTE + * Reserved2[1]; PVOID Reserved3[2]; PPEB_LDR_DATA Ldr; + * PRTL_USER_PROCESS_PARAMETERS ProcessParameters; BYTE Reserved4[104]; + * PVOID Reserved5[52]; PPS_POST_PROCESS_INIT_ROUTINE + * PostProcessInitRoutine; BYTE Reserved6[128]; PVOID Reserved7[1]; + * ULONG SessionId; } PEB, PPEB; + */ + /** + * The Class PEB. + */ + class PEB extends Structure + { + + /** The Reserved1. */ + public byte[] Reserved1 = new byte[2]; + + /** The Being debugged. */ + public byte BeingDebugged; + + /** The Reserved2. */ + public byte Reserved2; + + /** The Reserved3. */ + public Pointer[] Reserved3 = new Pointer[2]; + + /** The Ldr. */ + public Pointer Ldr; + + /** The Process parameters. */ + public Pointer ProcessParameters; + + /** The Reserved4. */ + public byte[] Reserved4 = new byte[104]; + + /** The Reserved5. */ + public Pointer[] Reserved5 = new Pointer[52]; + + /** The Post process init routine. */ + public Pointer PostProcessInitRoutine; + + /** The Reserved6. */ + public byte[] Reserved6 = new byte[128]; + + /** The Reserved7. */ + public Pointer Reserved7; + + /** The Session id. */ + public int SessionId; + + } + + /* + * typedef struct _PEB { BYTE Reserved1[2]; BYTE BeingDebugged; BYTE + * Reserved2[21]; PPEB_LDR_DATA LoaderData; PRTL_USER_PROCESS_PARAMETERS + * ProcessParameters; BYTE Reserved3[520]; PPS_POST_PROCESS_INIT_ROUTINE + * PostProcessInitRoutine; BYTE Reserved4[136]; ULONG SessionId; } PEB; + */ + class PEB64 extends Structure + { + public byte[] Reserved1 = new byte[2]; + public byte BeingDebugged; + public byte[] Reserved2 = new byte[21]; ; + public Pointer Ldr; + public Pointer ProcessParameters; + public byte[] Reserved3 = new byte[520]; + public Pointer PostProcessInitRoutine; + public byte[] Reserved4 = new byte[136]; + public int SessionId; + } + + /* + * typedef struct _RTL_USER_PROCESS_PARAMETERS { BYTE Reserved1[16]; 16 + * PVOID Reserved2[10]; 40 UNICODE_STRING ImagePathName; UNICODE_STRING + * CommandLine; } RTL_USER_PROCESS_PARAMETERS, + * PRTL_USER_PROCESS_PARAMETERS; + * + * typedef struct _RTL_USER_PROCESS_PARAMETERS { ULONG MaximumLength; 4 + * ULONG Length; 8 ULONG Flags; 12 ULONG DebugFlags; 16 PVOID + * ConsoleHandle; 4 ULONG ConsoleFlags; 8 HANDLE StdInputHandle; 12 + * HANDLE StdOutputHandle; 16 HANDLE StdErrorHandle; 20 UNICODE_STRING + * CurrentDirectoryPath; 28 HANDLE CurrentDirectoryHandle; 32 + * UNICODE_STRING DllPath; 40 UNICODE_STRING ImagePathName; 48 + * UNICODE_STRING CommandLine; 56 PVOID Environment; 4 ULONG + * StartingPositionLeft; 8 ULONG StartingPositionTop; 12 ULONG Width; 16 + * ULONG Height; 20 ULONG CharWidth; 24 ULONG CharHeight; 28 ULONG + * ConsoleTextAttributes; 32 ULONG WindowFlags; 36 ULONG + * ShowWindowFlags; 40 UNICODE_STRING WindowTitle; 48 UNICODE_STRING + * DesktopName; UNICODE_STRING ShellInfo; UNICODE_STRING RuntimeData; + * RTL_DRIVE_LETTER_CURDIR DLCurrentDirectory[0x20]; } + * RTL_USER_PROCESS_PARAMETERS,PRTL_USER_PROCESS_PARAMETERS; + */ + + /** + * The Class RTL_USER_PROCESS_PARAMETERS. + */ + class RTL_USER_PROCESS_PARAMETERS extends Structure + { + public int AllocationSize; + public int Size; + public int Flags; + public int DebugFlags; + + public HANDLE hConsole; + + public int ProcessGroup; + + public HANDLE hStdInput; + public HANDLE hStdOutput; + public HANDLE hStdError; + + public UNICODE_STRING CurrentDirectoryPath; + public Pointer CurrentDirectoryHandle; + public UNICODE_STRING DllPath; + + /** The Image path name. */ + public UNICODE_STRING ImagePathName; + + /** The Command line. */ + public UNICODE_STRING CommandLine; + public Pointer Environment; // new + + public int dwX; + public int dwY; + public int dwXSize; + public int dwYSize; + public int dwXCountChars; + public int dwYCountChars; + public int dwFillAttribute; + public int dwFlags; + public int wShowWindow; + + public UNICODE_STRING WindowTitle; + public UNICODE_STRING Desktop; + + public UNICODE_STRING ShellInfo; + public UNICODE_STRING RuntimeInfo; + public RTL_DRIVE_LETTER_CURDIR[] DLCurrentDirectory = new RTL_DRIVE_LETTER_CURDIR[0x20]; + } + + class RTL_DRIVE_LETTER_CURDIR extends Structure + { + public int Flags; + public int Length; + public int TimeStamp; + public UNICODE_STRING DosPath; + }; + + /* + * typedef struct _LSA_UNICODE_STRING { USHORT Length; USHORT + * MaximumLength; PWSTR Buffer; } LSA_UNICODE_STRING, + * PLSA_UNICODE_STRING, UNICODE_STRING, PUNICODE_STRING; + */ + /** + * The Class UNICODE_STRING. + */ + class UNICODE_STRING extends Structure + { + + /** The Length. */ + public short Length = 0; + + /** The Maximum length. */ + public short MaximumLength; + + /** The Buffer. */ + public Pointer Buffer; + } + } + + public interface MyAdvapi extends Advapi32 + { + MyAdvapi INSTANCE = (MyAdvapi) Native.loadLibrary("Advapi32", MyAdvapi.class, Options.UNICODE_OPTIONS); + + /* + * BOOL WINAPI LookupAccountSid( __in_opt LPCTSTR lpSystemName, __in + * PSID lpSid, __out_opt LPTSTR lpName, __inout LPDWORD cchName, + * __out_opt LPTSTR lpReferencedDomainName, __inout LPDWORD + * cchReferencedDomainName, __out PSID_NAME_USE peUse ); + */ + boolean LookupAccountSidW(String lpSystemName, Pointer lpSid, Memory lpName, IntByReference cchName, Memory lpReferencedDomainName, + IntByReference cchReferencedDomainName, IntByReference peUse); + + /* + * typedef struct _SID_AND_ATTRIBUTES { PSID Sid; DWORD Attributes; } + * SID_AND_ATTRIBUTES,PSID_AND_ATTRIBUTES; + */ + static class SID_AND_ATTRIBUTES extends Structure + { + public Pointer Sid; + public int Attributes; + } + + /* + * typedef struct _TOKEN_USER { SID_AND_ATTRIBUTES User; } TOKEN_USER, + * PTOKEN_USER; + */ + static class TOKEN_USER extends Structure + { + public SID_AND_ATTRIBUTES User; + + public TOKEN_USER(Pointer p) + { + super(); + this.useMemory(p); + this.read(); + } + } + + public static final int TokenPrivileges = 3; + public static final int TokenUser = 1; + public static final int TokenElevation = 20; + + /* + * typedef struct _TOKEN_ELEVATION { + DWORD TokenIsElevated; +} TOKEN_ELEVATION, *PTOKEN_ELEVATION; + */ + static class TOKEN_ELEVATION extends Structure + { + public int TokenIsElevated = 0; + + public TOKEN_ELEVATION(Pointer p) + { + super(); + this.useMemory(p); + this.read(); + } + + public boolean isElevated() + { + return TokenIsElevated != 0; + } + + } + + /* + * BOOL WINAPI GetTokenInformation( __in HANDLE TokenHandle, __in + * TOKEN_INFORMATION_CLASS TokenInformationClass, __out_opt LPVOID + * TokenInformation, __in DWORD TokenInformationLength, __out PDWORD + * ReturnLength ); + */ + boolean GetTokenInformation(Pointer TokenHandle, int TokenInformationClass, Memory TokenInformation, int TokenInformationLength, + IntByReference ReturnLength); + + /* + * BOOL WINAPI InitializeSecurityDescriptor( __out PSECURITY_DESCRIPTOR + * pSecurityDescriptor, __in DWORD dwRevision ); + */ + boolean InitializeSecurityDescriptor(Memory pSecurityDescriptor, int dwRevision); + + public static final int SECURITY_DESCRIPTOR_MIN_LENGTH = 20; + public static final int SECURITY_DESCRIPTOR_REVISION = 1; + + /* + * BOOL WINAPI SetSecurityDescriptorSacl( __inout PSECURITY_DESCRIPTOR + * pSecurityDescriptor, __in BOOL bSaclPresent, __in_opt PACL pSacl, + * __in BOOL bSaclDefaulted ); + */ + boolean SetSecurityDescriptorDacl(Pointer pSecurityDescriptor, boolean bSaclPresent, Pointer pSacl, boolean bSaclDefaulted); + + public static int SE_PRIVILEGE_ENABLED = 2; + + /* + * typedef struct _LUID { DWORD LowPart; LONG HighPart; } LUID,PLUID; + */ + static class LUID extends Structure + { + public int LowPart; + public int HighPart; + } + + /* + * typedef struct _LUID_AND_ATTRIBUTES { LUID Luid; DWORD Attributes; } + * LUID_AND_ATTRIBUTES,PLUID_AND_ATTRIBUTES; + */ + static class LUID_AND_ATTRIBUTES extends Structure + { + public LUID Luid; + public int Attributes; + } + + /* + * typedef struct _TOKEN_PRIVILEGES { DWORD PrivilegeCount; + * LUID_AND_ATTRIBUTES Privileges[ANYSIZE_ARRAY]; } TOKEN_PRIVILEGES, + * PTOKEN_PRIVILEGES; + */ + static class TOKEN_PRIVILEGES extends Structure + { + public int PrivilegeCount = 1; + public LUID_AND_ATTRIBUTES[] Privileges = new LUID_AND_ATTRIBUTES[1]; + + public TOKEN_PRIVILEGES() + { + super(); + Privileges[0] = new LUID_AND_ATTRIBUTES(); + } + + public TOKEN_PRIVILEGES(Pointer p) + { + super(); + PrivilegeCount = p.getInt(0); + Privileges = new LUID_AND_ATTRIBUTES[PrivilegeCount]; + this.useMemory(p); + this.read(); + } + + } + + /* + * BOOL WINAPI AdjustTokenPrivileges( __in HANDLE TokenHandle, __in BOOL + * DisableAllPrivileges, __in_opt PTOKEN_PRIVILEGES NewState, __in DWORD + * BufferLength, __out_opt PTOKEN_PRIVILEGES PreviousState, __out_opt + * PDWORD ReturnLength ); + */ + boolean AdjustTokenPrivileges(Pointer TokenHandle, boolean DisableAllPrivileges, TOKEN_PRIVILEGES NewState, int BufferLength, + PointerByReference PreviousState, IntByReference ReturnLength); + + /* + * BOOL WINAPI LookupPrivilegeValue( __in_opt LPCTSTR lpSystemName, __in + * LPCTSTR lpName, __out PLUID lpLuid ); + */ + boolean LookupPrivilegeValueA(String lpSystemName, String lpName, LUID lpLuid); + + public static final String SE_ASSIGNPRIMARYTOKEN_NAME = "SeAssignPrimaryTokenPrivilege"; + public static final String SE_INCREASE_QUOTA_NAME = "SeIncreaseQuotaPrivilege"; + public static final String SE_DEBUG_NAME = "SeDebugPrivilege"; + public static final String SE_TCB_NAME = "SeTcbPrivilege"; + + /* + * BOOL WINAPI OpenProcessToken( __in HANDLE ProcessHandle, __in DWORD + * DesiredAccess, __out PHANDLE TokenHandle ); + */ + boolean OpenProcessToken(HANDLE ProcessHandle, int DesiredAccess, PointerByReference TokenHandle); + + public static final int STANDARD_RIGHTS_READ = 0x20000; + public static final int STANDARD_RIGHTS_WRITE = 0x20000; + public static final int TOKEN_QUERY = 0x0008; + public static final int TOKEN_ADJUST_PRIVILEGES = 0x0020; + public static final int TOKEN_ADJUST_GROUPS = 0x0040; + public static final int TOKEN_ADJUST_DEFAULT = 0x0080; + public static final int TOKEN_DUPLICATE = 0x0002; + public static final int TOKEN_IMPERSONATE = 0x0004; + + public static final int TOKEN_READ = STANDARD_RIGHTS_READ | TOKEN_QUERY; + + public static final int TOKEN_WRITE = STANDARD_RIGHTS_WRITE | TOKEN_ADJUST_PRIVILEGES | TOKEN_ADJUST_GROUPS + | TOKEN_ADJUST_DEFAULT; + + /* + * BOOL WINAPI CreateProcessWithLogonW( __in LPCWSTR lpUsername, + * __in_opt LPCWSTR lpDomain, __in LPCWSTR lpPassword, __in DWORD + * dwLogonFlags, __in_opt LPCWSTR lpApplicationName, __inout_opt LPWSTR + * lpCommandLine, __in DWORD dwCreationFlags, __in_opt LPVOID + * lpEnvironment, __in_opt LPCWSTR lpCurrentDirectory, __in + * LPSTARTUPINFOW lpStartupInfo, __out LPPROCESS_INFORMATION + * lpProcessInfo ); + */ + boolean CreateProcessWithLogonW(WString lpUsername, WString lpDomain, WString lpPassword, int dwLogonFlags, WString lpApplicationName, + WString lpCommandLine, int dwCreationFlags, Pointer lpEnvironment, WString lpCurrentDirectory, Structure lpStartupInfo, + Structure lpProcessInfo); + + public static final int LOGON_WITH_PROFILE = 0x00000001; + public static final int LOGON_NETCREDENTIALS_ONLY = 0x00000002; + + /* + * BOOL LogonUser( __in LPTSTR lpszUsername, __in_opt LPTSTR lpszDomain, + * __in LPTSTR lpszPassword, __in DWORD dwLogonType, __in DWORD + * dwLogonProvider, __out PHANDLE phToken ); + */ + boolean LogonUserA(String lpszUsername, String lpszDomain, String lpszPassword, int dwLogonType, int dwLogonProvider, + PointerByReference phToken); + + boolean LogonUserW(WString lpszUsername, WString lpszDomain, WString lpszPassword, int dwLogonType, int dwLogonProvider, + PointerByReference phToken); + + public static final int LOGON32_LOGON_INTERACTIVE = 2; + public static final int LOGON32_LOGON_NETWORK = 3; + public static final int LOGON32_LOGON_BATCH = 4; + public static final int LOGON32_LOGON_SERVICE = 5; + public static final int LOGON32_LOGON_UNLOCK = 7; + public static final int LOGON32_LOGON_NETWORK_CLEARTEXT = 8; + public static final int LOGON32_LOGON_NEW_CREDENTIALS = 9; + + public static final int LOGON32_PROVIDER_DEFAULT = 0; + public static final int LOGON32_PROVIDER_WINNT35 = 1; + public static final int LOGON32_PROVIDER_WINNT40 = 2; + public static final int LOGON32_PROVIDER_WINNT50 = 3; + + /* + * BOOL WINAPI ImpersonateLoggedOnUser( __in HANDLE hToken ); + */ + boolean ImpersonateLoggedOnUser(Pointer hToken); + + /* + * BOOL WINAPI CreateProcessAsUser( __in_opt HANDLE hToken, __in_opt + * LPCTSTR lpApplicationName, __inout_opt LPTSTR lpCommandLine, __in_opt + * LPSECURITY_ATTRIBUTES lpProcessAttributes, __in_opt + * LPSECURITY_ATTRIBUTES lpThreadAttributes, __in BOOL bInheritHandles, + * __in DWORD dwCreationFlags, __in_opt LPVOID lpEnvironment, __in_opt + * LPCTSTR lpCurrentDirectory, __in LPSTARTUPINFO lpStartupInfo, __out + * LPPROCESS_INFORMATION lpProcessInformation ); + */ + boolean CreateProcessAsUserW(Pointer hToken, WString lpApplicationName, WString lpCommandLine, Structure lpProcessAttributes, + Structure lpThreadAttributes, boolean bInheritHandles, int dwCreationFlags, Structure lpEnvironment, WString lpCurrentDirectory, + Structure lpStartupInfo, Structure lpProcessInformation); + + static class SECURITY_ATTRIBUTES + { + public int nLength; + public Pointer lpSecurityDescriptor; + boolean bInheritHandle; + } + + /* + * BOOL WINAPI DuplicateTokenEx( __in HANDLE hExistingToken, __in DWORD + * dwDesiredAccess, __in_opt LPSECURITY_ATTRIBUTES lpTokenAttributes, + * __in SECURITY_IMPERSONATION_LEVEL ImpersonationLevel, __in TOKEN_TYPE + * TokenType, __out PHANDLE phNewToken ); + */ + boolean DuplicateTokenEx(Pointer hExistingToken, int dwDesiredAccess, Pointer lpTokenAttributes, int ImpersonationLevel, int TokenType, + PointerByReference phNewToken); + + } + + public interface Secur32 extends StdCallLibrary + { + + /** The INSTANCE. */ + Secur32 INSTANCE = (Secur32) Native.loadLibrary("Secur32", Secur32.class); + + /* + * BOOLEAN WINAPI GetUserNameEx( __in EXTENDED_NAME_FORMAT NameFormat, + * __out LPTSTR lpNameBuffer, __inout PULONG lpnSize ); + */ + boolean GetUserNameEx(int NameFormat, Memory lpNameBuffer, IntByReference lpnSize); + + } + + public interface MyWtsapi32 extends StdCallLibrary + { + + // Method declarations, constant and structure definitions go here + + /** The INSTANCE. */ + MyWtsapi32 INSTANCE = (MyWtsapi32) Native.loadLibrary("Wtsapi32", MyWtsapi32.class); + + /* + * BOOL WTSQueryUserToken( __in ULONG SessionId, __out PHANDLE phToken); + */ + boolean WTSQueryUserToken(int SessionId, PointerByReference phToken); + + } + + /** The _startup info. */ + volatile STARTUPINFO _startupInfo; + + /** The _process information. */ + volatile PROCESS_INFORMATION _processInformation; + + /** The in read. */ + volatile PointerByReference inRead = null; + + /** The in write. */ + volatile PointerByReference inWrite = null; + + /** The out read. */ + volatile PointerByReference outRead = null; + + /** The out write. */ + volatile PointerByReference outWrite = null; + + /** The err read. */ + volatile PointerByReference errRead = null; + + /** The err write. */ + volatile PointerByReference errWrite = null; + + /** The sa. */ + volatile SECURITY_ATTRIBUTES sa; + + /** The m_h out pipe. */ + volatile Pointer m_hOutPipe = null; + + /** The m_h err pipe. */ + volatile Pointer m_hErrPipe = null; + + /** The m_h in pipe. */ + volatile Pointer m_hInPipe = null; + + /** The in write pipe. */ + volatile Pointer inWritePipe = null; + + /** The out read pipe. */ + volatile Pointer outReadPipe = null; + + /** The err read pipe. */ + volatile Pointer errReadPipe = null; + + volatile int _isElevated = -1; // 1 = true, 0 = false; + + /** + * Gets the process. + * + * @param pid + * the pid + * + * @return the process + */ + public static Process getProcess(int pid) + { + WindowsXPProcess result = new WindowsXPProcess(); + HANDLE hProcess = MyKernel32.INSTANCE.OpenProcess(MyKernel32.PROCESS_ALL_ACCESS, false, pid); + if (hProcess == null) + hProcess = MyKernel32.INSTANCE.OpenProcess(MyKernel32.PROCESS_QUERY_INFORMATION, false, pid); + if (hProcess == null) + return null; + + result._pid = pid; + result._processInformation = new PROCESS_INFORMATION(); + result._processInformation.dwProcessId = pid; + result._processInformation.hProcess = hProcess; + result._cmd = result.getCommandLineInternal(); + // this does not always work (why ??), if so try again, then this + // normally does + // on win64 PEB of 64 bit cannot be accessed from wow -> use wmi + if (result._cmd.equals("?")) + result._cmd = result.getCommandLineInternalWMI(); + if ("?".equals(result._cmd)) + { + System.err.println("Could not get commandline"); + } + //else + // System.out.println("Command line of " + pid + ": " + result._cmd); + PointerByReference hToken = new PointerByReference(); + HANDLE hp = new HANDLE(); + hp.setPointer(hProcess.getPointer()); + if (MyAdvapi.INSTANCE.OpenProcessToken(hp, MyAdvapi.TOKEN_READ, hToken)) + { + IntByReference dwSize = new IntByReference(); + MyAdvapi.INSTANCE.GetTokenInformation(hToken.getValue(), MyAdvapi.TokenUser, null, 0, dwSize); + { + Memory pTokenUser = new Memory(dwSize.getValue()); + if (MyAdvapi.INSTANCE.GetTokenInformation(hToken.getValue(), MyAdvapi.TokenUser, pTokenUser, dwSize.getValue(), dwSize)) + { + MyAdvapi.TOKEN_USER tokenUser = new MyAdvapi.TOKEN_USER(pTokenUser); + Pointer lpSid = tokenUser.User.Sid; + Memory lpName = new Memory(256); + IntByReference cchName = new IntByReference(); + cchName.setValue(256); + Memory lpReferencedDomainName = new Memory(256); + IntByReference cchReferencedDomainName = new IntByReference(); + cchReferencedDomainName.setValue(256); + IntByReference peUse = new IntByReference(); + if (MyAdvapi.INSTANCE.LookupAccountSidW(null, lpSid, lpName, cchName, lpReferencedDomainName, cchReferencedDomainName, peUse)) + + result._user = lpReferencedDomainName.getString(0, true) + "\\" + lpName.getString(0, true); + ; + // System.out.println(result._user); + } + } + if (result._user == null) + System.out.println("could not get user name OS error #" + MyKernel32.INSTANCE.GetLastError()); + MyKernel32.INSTANCE.CloseHandle(hToken.getValue()); + } + return result; + } + + private boolean setPrivilege(Pointer hToken, String lpszPrivilege, boolean bEnablePrivilege) + { + TOKEN_PRIVILEGES tp = new TOKEN_PRIVILEGES(); + MyAdvapi.LUID luid = new MyAdvapi.LUID(); + luid.size(); + + if (!MyAdvapi.INSTANCE.LookupPrivilegeValueA(null, lpszPrivilege, luid)) + return false; + + tp.Privileges[0].Luid = luid; + tp.write(); + + if (bEnablePrivilege) + tp.Privileges[0].Attributes = MyAdvapi.SE_PRIVILEGE_ENABLED; + else + tp.Privileges[0].Attributes = 0; + + int size = tp.size(); + boolean result = MyAdvapi.INSTANCE.AdjustTokenPrivileges(hToken, false, tp, 0, null, null); + // return GetLastError() == ERROR_SUCCESS; + if (!result) + { + int errNr = MyKernel32.INSTANCE.GetLastError(); + log("error setting privliges OS error #" + errNr + "/" + Integer.toHexString(errNr)); + } + + return result; + } + + /* + * (non-Javadoc) + * + * @see org.rzo.yajsw.os.Process#start() + */ + public boolean start() + { + boolean result = false; + + if (isRunning()) + { + log("process already running -> abort start"); + return false; + } + else + { + setPid(-1); + setExitCode(-1); + } + _started = false; + + int PIPE_SIZE = 1024; // buffer size for pipes + int PIPE_TIMEOUT = 12000; // time to wait for pipe + + if (_arrCmd == null && _cmd == null) + return false; + if (_cmd == null) + { + _cmd = ""; + for (String cmd : _arrCmd) + { + if (cmd == null || cmd.length() == 0) + continue; + if (cmd.contains(" ") && !cmd.endsWith("\"")) + _cmd += '"' + cmd + "\" "; + else + _cmd += cmd + " "; + } + // _cmd += cmd + " "; + } + if (_debug) + log("exec: " + _cmd); + if (_processInformation != null) + { + log("process not correctly disposed -> abort start"); + return false; + } + try + { + destroyed = false; + _startupInfo = new STARTUPINFO(); + _startupInfo.clear(); + _processInformation = new PROCESS_INFORMATION(); + _processInformation.clear(); + if (_pipeStreams) + { + if (sa == null) + { + sa = new SECURITY_ATTRIBUTES(); + sa.clear(); + sa.nLength = sa.size(); + sa.lpSecurityDescriptor = null; + sa.bInheritHandle = true;// 1; // true otherwise streams are + // not piped + } + inRead = new PointerByReference(); + inWrite = new PointerByReference(); + outRead = new PointerByReference(); + outWrite = new PointerByReference(); + errRead = new PointerByReference(); + errWrite = new PointerByReference(); + + + _startupInfo.dwFlags = MyKernel32.STARTF_USESTDHANDLES; + + if (MyKernel32.INSTANCE.CreatePipe(inRead, inWrite, sa, 0) == 0) + { + log("Error in CreatePipe inWrite " + Integer.toHexString(MyKernel32.INSTANCE.GetLastError())); + return false; + } + if (!MyKernel32.INSTANCE.SetHandleInformation(inWrite.getValue(), MyKernel32.HANDLE_FLAG_INHERIT, 0)) + { + log("error in set handle inWrite -> abort start"); + return false; + } + _startupInfo.hStdInput = inRead.getValue(); + + if (MyKernel32.INSTANCE.CreatePipe(outRead, outWrite, sa, 0) == 0) + { + log("Error in CreatePipe outWrite " + Integer.toHexString(MyKernel32.INSTANCE.GetLastError())); + return false; + } + if (!MyKernel32.INSTANCE.SetHandleInformation(outRead.getValue(), MyKernel32.HANDLE_FLAG_INHERIT, 0)) + { + log("error in set handle outRead -> abort start"); + return false; + } + _startupInfo.hStdOutput = outWrite.getValue(); + + if (MyKernel32.INSTANCE.CreatePipe(errRead, errWrite, sa, 0) == 0) + { + log("Error in CreatePipe errWrite " + Integer.toHexString(MyKernel32.INSTANCE.GetLastError())); + return false; + } + if (!MyKernel32.INSTANCE.SetHandleInformation(errRead.getValue(), MyKernel32.HANDLE_FLAG_INHERIT, 0)) + { + log("error in set handle inWrite -> abort start"); + return false; + } + _startupInfo.hStdError = errWrite.getValue(); + + // for some unknown reason: if we add the following + // lines we do not get "operation on non socket" error + // in mina + /*|| !MyKernel32.INSTANCE.SetHandleInformation(inWrite.getValue(), MyKernel32.HANDLE_FLAG_PROTECT_FROM_CLOSE, + MyKernel32.HANDLE_FLAG_PROTECT_FROM_CLOSE) + || !MyKernel32.INSTANCE.SetHandleInformation(outRead.getValue(), MyKernel32.HANDLE_FLAG_PROTECT_FROM_CLOSE, + MyKernel32.HANDLE_FLAG_PROTECT_FROM_CLOSE) + || !MyKernel32.INSTANCE.SetHandleInformation(errRead.getValue(), MyKernel32.HANDLE_FLAG_PROTECT_FROM_CLOSE, + MyKernel32.HANDLE_FLAG_PROTECT_FROM_CLOSE) + */ + + if (this._redirectErrorStream) + MyKernel32.INSTANCE.SetHandleInformation(errRead.getValue(), MyKernel32.HANDLE_FLAG_INHERIT, 0); + + } + + int creationFlag = 0; + if (!_visible) + { + creationFlag |= MyKernel32.CREATE_NO_WINDOW | MyKernel32.CREATE_UNICODE_ENVIRONMENT; + _startupInfo.lpTitle = null; + } + else + { + creationFlag |= MyKernel32.CREATE_NEW_CONSOLE | MyKernel32.CREATE_UNICODE_ENVIRONMENT; + _startupInfo.lpTitle = new WString(_title); + + if (_minimized) + { + _startupInfo.wShowWindow |= MyKernel32.SW_SHOWMINIMIZED | MyKernel32.SW_SHOWNOACTIVATE; + _startupInfo.dwFlags |= MyKernel32.STARTF_USESHOWWINDOW; + } + + } + + creationFlag |= getPriorityFlag(); + + // do not inherit handles. otherwise resources are not freed if + // parent is killed + // inherit only when we need to pipe the streams + _startupInfo.write(); + WString cmd = new WString(_cmd); + WString wDir = getWorkingDir() == null ? null : new WString(getWorkingDir()); + String stdUser = standardizeUser(_user); + StringBlock environment = null; + WString[] env = null; + if (_environment.size() != 0) + { + env = new WString[_environment.size()]; + int i = 0; + for (String[] entry : _environment) + { + env[i++] = new WString(entry[0] + "=" + entry[1]); + } + environment = new StringBlock(env); + } + if (_desktop != null) + { + _startupInfo.lpDesktop = new WString(_desktop); + log("setting desktop "+_desktop); + } + + if (_logonActiveSession) + { + log("start process in active session"); + int session = 0xFFFFFFFF; + // wait until we have an active session + while (session == 0xFFFFFFFF) + { + session = MyKernel32.INSTANCE.WTSGetActiveConsoleSessionId(); + if (session == 0xFFFFFFFF) + Thread.sleep(1000); + log("active session: "+session); + } + + + PointerByReference phToken = new PointerByReference(); + + // wait for a user to log on to the session + boolean userLoggedOn = false; + int retries = 0; + while (!userLoggedOn) + { + result = MyWtsapi32.INSTANCE.WTSQueryUserToken(session, phToken); + userLoggedOn = result || MyKernel32.INSTANCE.GetLastError() != 1008; + if (!userLoggedOn) + { + Thread.sleep(1000); + retries++; + } + } + // if user just logged on: wait for the desktop to get up. + // TODO evntl. add a configuration property for the time to wait. + if (retries > 0) + Thread.sleep(10000); + + if (!doesUserHavePrivilege(MyAdvapi.SE_TCB_NAME)) + log("WARNING: Process does not have the SE_TCB_NAME privilege !!"); + + // start the application + if (result) + { + log("got session token: "+phToken.getValue()); + PointerByReference phNewToken = new PointerByReference(); + result = MyAdvapi.INSTANCE.DuplicateTokenEx(phToken.getValue(), 0x2000000, null, 0, 1, phNewToken); + if (result) + { + log("duplicated token: "+phNewToken.getValue()); + //_startupInfo.lpDesktop = new WString("winsta0\\default"); + creationFlag = 0; + creationFlag |= MyKernel32.CREATE_NO_WINDOW | MyKernel32.CREATE_UNICODE_ENVIRONMENT; + creationFlag |= getPriorityFlag(); + result = MyAdvapi.INSTANCE.CreateProcessAsUserW(phNewToken.getValue(), null, cmd, null, null, _pipeStreams, creationFlag, + null, new WString(getWorkingDir()), _startupInfo, _processInformation); + log("started "+result); + } + } + + } + else + if (stdUser == null || stdUser.equals(currentUser())) + { + result = MyKernel32.INSTANCE.CreateProcessW(null, cmd, null, null, _pipeStreams, creationFlag, environment, wDir, _startupInfo, + _processInformation); + } + else + { + WString user = null; + + WString domain = null; + + int i = _user.lastIndexOf("\\"); + if (i > 0) + { + user = new WString(_user.substring(_user.lastIndexOf("\\") + 1)); + domain = new WString(_user.substring(0, _user.lastIndexOf("\\"))); + } + else + user = new WString(_user); + WString password = null; + if (_password != null) + password = new WString(_password); + + log("current user :: requested user: " + currentUserName() + " :: " + stdUser); + // in windows 2008: system user seems to be $ + // could not find documentation on this. + if (!("SYSTEM".equals(currentUserName()) || currentUserName().endsWith("$"))) + { + // createProcessWithLogon : cmd line is only 1024 char long + // parent process is not current process. + // -> use CreateProcessAsUser + // result = MyAdvapi.INSTANCE.CreateProcessWithLogonW(user, + // domain, password, MyAdvapi.LOGON_WITH_PROFILE, null, cmd, + // creationFlag, null, wDir, _startupInfo, + // _processInformation); + + /**/ + PointerByReference phToken = new PointerByReference(); + + String stUser = user.toString(); + String stDomain = domain == null ? null : domain.toString(); + String stPassword = password == null ? "" : password.toString(); + result = true; + // result = MyAdvapi.INSTANCE.LogonUserA(stUser, stDomain, + // stPassword, MyAdvapi.LOGON32_LOGON_NEW_CREDENTIALS, + // MyAdvapi.LOGON32_PROVIDER_WINNT50, phToken); + if (result) + { + // HANDLE hCurrentProcess = + // MyKernel32.INSTANCE.GetCurrentProcess(); + // PointerByReference hTokenSelf = new + // PointerByReference(); + // result = + // MyAdvapi.INSTANCE.OpenProcessToken(hCurrentProcess, + // MyAdvapi.TOKEN_READ | MyAdvapi.TOKEN_WRITE | + // MyAdvapi.TOKEN_DUPLICATE | + // MyAdvapi.TOKEN_IMPERSONATE, hTokenSelf ); + /* + * Memory pSD = new + * Memory(MyAdvapi.SECURITY_DESCRIPTOR_MIN_LENGTH); + * pSD.clear(); if (result) result = + * MyAdvapi.INSTANCE.InitializeSecurityDescriptor(pSD, + * MyAdvapi.SECURITY_DESCRIPTOR_REVISION); + * + * if (result) result = + * MyAdvapi.INSTANCE.SetSecurityDescriptorDacl(pSD, + * true, null, false); + */ + if (result) + { + + /* + * SECURITY_ATTRIBUTES sap = new + * SECURITY_ATTRIBUTES(); sap.clear(); sap.nLength = + * sap.size(); sap.lpSecurityDescriptor = pSD; + * sap.bInheritHandle = false; + */ + // result = + // MyAdvapi.INSTANCE.ImpersonateLoggedOnUser(phToken.getValue()); + /**/// System.out.println(MyAdvapi.SE_ASSIGNPRIMARYTOKEN_NAME+" "+this.doesUserHavePrivilege(MyAdvapi.SE_ASSIGNPRIMARYTOKEN_NAME)); + // System.out.println(MyAdvapi.SE_INCREASE_QUOTA_NAME+" "+this.doesUserHavePrivilege(MyAdvapi.SE_INCREASE_QUOTA_NAME)); + // System.out.println(MyAdvapi.SE_DEBUG_NAME+" "+this.doesUserHavePrivilege(MyAdvapi.SE_DEBUG_NAME)); + // System.out.println(MyAdvapi.SE_TCB_NAME+" "+this.doesUserHavePrivilege(MyAdvapi.SE_TCB_NAME)); + // */ + /**/if (result) + { + // result = + // setPrivilege(hTokenSelf.getValue(), + // MyAdvapi.SE_ASSIGNPRIMARYTOKEN_NAME, true) + // && setPrivilege(hTokenSelf.getValue(), + // MyAdvapi.SE_INCREASE_QUOTA_NAME, true) + // && setPrivilege(hTokenSelf.getValue(), + // MyAdvapi.SE_DEBUG_NAME, true) + // && setPrivilege(hTokenSelf.getValue(), + // MyAdvapi.SE_TCB_NAME, true) + ; + } + // System.out.println(MyAdvapi.SE_ASSIGNPRIMARYTOKEN_NAME+" "+this.doesUserHavePrivilege(MyAdvapi.SE_ASSIGNPRIMARYTOKEN_NAME)); + // System.out.println(MyAdvapi.SE_INCREASE_QUOTA_NAME+" "+this.doesUserHavePrivilege(MyAdvapi.SE_INCREASE_QUOTA_NAME)); + // System.out.println(MyAdvapi.SE_DEBUG_NAME+" "+this.doesUserHavePrivilege(MyAdvapi.SE_DEBUG_NAME)); + // System.out.println(MyAdvapi.SE_TCB_NAME+" "+this.doesUserHavePrivilege(MyAdvapi.SE_TCB_NAME)); + // */ + // MyKernel32.INSTANCE.CloseHandle(hTokenSelf.getValue()); + if (!doesUserHavePrivilege(MyAdvapi.SE_ASSIGNPRIMARYTOKEN_NAME)) + log("Process does not have the SE_ASSIGNPRIMARYTOKEN_NAME privilege !!"); + + if (!doesUserHavePrivilege(MyAdvapi.SE_INCREASE_QUOTA_NAME)) + log("Process does not have the SE_INCREASE_QUOTA_NAME privilege !!"); + + result = MyAdvapi.INSTANCE.LogonUserA(stUser, stDomain, stPassword, MyAdvapi.LOGON32_LOGON_INTERACTIVE, + MyAdvapi.LOGON32_PROVIDER_DEFAULT, phToken); + if (result) + // result = + // MyAdvapi.INSTANCE.CreateProcessWithLogonW(user, + // domain, password, + // MyAdvapi.LOGON_NETCREDENTIALS_ONLY, null, + // cmd, creationFlag, null, wDir, _startupInfo, + // _processInformation); + result = MyAdvapi.INSTANCE.CreateProcessAsUserW(phToken.getValue(), null, cmd, null, // sap, + null, true,// _pipeStreams, + creationFlag, null, null,// getWorkingDir(), + _startupInfo, _processInformation); + } + + } + /**/ + + } + else + { + /* + * _startupInfo = new STARTUPINFO(); _startupInfo.clear(); + * _processInformation = new PROCESS_INFORMATION(); + * _processInformation.clear(); + */ + PointerByReference phToken = new PointerByReference(); + + String stUser = user.toString(); + String stDomain = domain == null ? null : domain.toString(); + String stPassword = password == null ? "" : password.toString(); + + result = MyAdvapi.INSTANCE.LogonUserW(user, domain, password, MyAdvapi.LOGON32_LOGON_INTERACTIVE, + MyAdvapi.LOGON32_PROVIDER_DEFAULT, phToken); + log("logonUserA " + result); + if (result) + { + // result = + // MyAdvapi.INSTANCE.ImpersonateLoggedOnUser(phToken.getValue()); + if (result) + // result = + // MyAdvapi.INSTANCE.CreateProcessWithLogonW(user, + // domain, password, + // MyAdvapi.LOGON_NETCREDENTIALS_ONLY, null, cmd, + // creationFlag, null, wDir, _startupInfo, + // _processInformation); + result = MyAdvapi.INSTANCE.CreateProcessAsUserW(phToken.getValue(), null, cmd, null, null, _pipeStreams, creationFlag, + null, new WString(getWorkingDir()), _startupInfo, _processInformation); + + } + + } + } + + if (!result) + { + int err = MyKernel32.INSTANCE.GetLastError(); + log("could not start process " + Integer.toHexString(err)); + log(Kernel32Util.formatMessageFromLastErrorCode(err)); + log(_startupInfo.toString()); + return result; + } + _started = true; + + // Thread.sleep(1000); + + int res = MyUser32.INSTANCE.WaitForInputIdle(_processInformation.hProcess, 2000); + if (res > 0) + { + log("Warning: WaitForInputIdle returned " + res); + // return false; + } + + int affinity = getProcessAffinity(); + if (affinity > 0) + { + if (!MyKernel32.INSTANCE.SetProcessAffinityMask(_processInformation.hProcess, affinity)) + log("could not set process affinity"); + } + + if (_pipeStreams) + { + + // Thread.sleep(15000); + /* + * Memory buf = new Memory(1); IntByReference rres = new + * IntByReference(); System.out.println("readddd"); boolean x = + * MyKernel32.INSTANCE.ReadFile(outRead.getValue(), buf, (int) + * buf.getSize(), rres, null); if (!x) + * System.out.println("read error"); else + * System.out.println(buf.getByte(0)); + */ + + writefd(in_fd, inWrite.getValue()); + writefd(out_fd, outRead.getValue()); + writefd(err_fd, errRead.getValue()); + + _outputStream = new BufferedOutputStream(new FileOutputStream(in_fd)); + _inputStream = new BufferedInputStream(new FileInputStream(out_fd)); + _errorStream = new BufferedInputStream(new FileInputStream(err_fd)); + + MyKernel32.INSTANCE.CloseHandle(inRead.getValue()); + MyKernel32.INSTANCE.CloseHandle(outWrite.getValue()); + MyKernel32.INSTANCE.CloseHandle(errWrite.getValue()); + + } + else if (_teeName != null && _tmpPath != null) + { + File f = new File(_tmpPath); + if (!f.exists()) + f.mkdir(); + _outputStream = new CyclicBufferFilePrintStream(new File(_tmpPath, "in_" + _teeName)); + _inputStream = new CyclicBufferFileInputStream(new File(_tmpPath, "out_" + _teeName)); + _errorStream = new CyclicBufferFileInputStream(new File(_tmpPath, "err_" + _teeName)); + new File(_tmpPath, "in_" + _teeName).deleteOnExit(); + new File(_tmpPath, "out_" + _teeName).deleteOnExit(); + new File(_tmpPath, "err_" + _teeName).deleteOnExit(); + } + + _pid = _processInformation.dwProcessId; + } + catch (Exception ex) + { + log("exception in process start: " + ex); + ex.printStackTrace(); + } + + return result; + } + + /** + * Gets the process affinity. + * + * @return the process affinity + */ + private int getProcessAffinity() + { + if (_cpuAffinity <= 0) + return 0; + IntByReference lpProcessAffinityMask = new IntByReference(); + IntByReference lpSystemAffinityMask = new IntByReference(); + if (MyKernel32.INSTANCE.GetProcessAffinityMask(_processInformation.hProcess, lpProcessAffinityMask, lpSystemAffinityMask)) + return lpSystemAffinityMask.getValue() & _cpuAffinity; + else + { + log("could not get process affinity mask -> not setting"); + return 0; + } + } + + /** + * Gets the priority flag. + * + * @return the priority flag + */ + private int getPriorityFlag() + { + switch (_priority) + { + case PRIORITY_NORMAL: + return MyKernel32.NORMAL_PRIORITY_CLASS; + case PRIORITY_ABOVE_NORMAL: + return MyKernel32.ABOVE_NORMAL_PRIORITY_CLASS; + case PRIORITY_HIGH: + return MyKernel32.HIGH_PRIORITY_CLASS; + case PRIORITY_BELOW_NORMAL: + return MyKernel32.BELOW_NORMAL_PRIORITY_CLASS; + case PRIORITY_LOW: + return MyKernel32.IDLE_PRIORITY_CLASS; + default: + return 0; + } + } + + // fd.handle = pointer.peer, using reflection, since both are private + /* + * (non-Javadoc) + * + * @see org.rzo.yajsw.os.Process#waitFor() + */ + public void waitFor() + { + if (!isRunning()) + return; + waitFor(MyKernel32.INFINITE); + } + + /** + * Gets the exit code internal. + * + * @return the exit code internal + */ + private int getExitCodeInternal() + { + IntByReference code = new IntByReference(); + if (_processInformation == null) + return -1; + boolean result = MyKernel32.INSTANCE.GetExitCodeProcess(_processInformation.hProcess, code); + try + { + // if server overloaded windows may need some time to set the exit + // code. + Thread.sleep(100); + } + catch (InterruptedException e) + { + // TODO Auto-generated catch block + e.printStackTrace(); + } + // System.out.println("get exit code internal " + result + " " + + // code.getValue()); + if (result) + { + if (_debug) + log("GetExitCodeProcess returned " + code.getValue()); + return code.getValue(); + } + else + { + log("Error in GetExitCodeProcess OS Error #" + MyKernel32.INSTANCE.GetLastError()); + return -3; + } + } + + /* + * (non-Javadoc) + * + * @see org.rzo.yajsw.os.Process#waitFor(int) + */ + public void waitFor(long timeout) + { + if (_debug) + log("waitFor "+timeout); + try + { + if (!isRunning()) + return; + if (_debug) + log("1waitFor "); + if (timeout > Integer.MAX_VALUE) + timeout = Integer.MAX_VALUE; + if (_processInformation == null) + return; + long start = System.currentTimeMillis(); + if (_debug) + log("2waitFor "); + while (_processInformation != null && (timeout == -1 || (start+timeout) > System.currentTimeMillis()) && isRunning()) + { + if (_debug) + log("WaitForSingleObject +"); + int result = MyKernel32.INSTANCE.WaitForSingleObject(_processInformation.hProcess, (int) timeout); + if (_debug) + log("WaitForSingleObject -"); + if (_debug) + log("WaitForSingleObject terminated PID: " + getPid()); + if (result == MyKernel32.WAIT_FAILED) + { + int errNr = MyKernel32.INSTANCE.GetLastError(); + log("Error in Process.waitFor OS Error #" + errNr + " " + Kernel32Util.formatMessageFromLastErrorCode(errNr)); + } + else if (result != MyKernel32.WAIT_OBJECT_0) + { + log("Error in Process.waitFor OS result #" + result + " " + Kernel32Util.formatMessageFromLastErrorCode(result)); + } + } + } + catch (Throwable ex) + { + log("Exception in Process.waitFor: "+ ex); + } + + } + + /* + * (non-Javadoc) + * + * @see org.rzo.yajsw.os.Process#stop(int, int) + */ + public boolean stop(int timeout, int code) + { + if (_pid <= 0) + { + log("cannot kill process with negative pid " + _pid); + return false; + } + // first try polite kill + // e.g. post WM_CLOSE to all windows whose PID + // matches our process. + MyUser32.WNDENUMPROC closeWindow = new MyUser32.WNDENUMPROC() + { + // lParam is the pid of our process + public boolean callback(HWND wnd, int lParam) + { + // get the pid of the window + IntByReference dwID = new IntByReference(); + MyUser32.INSTANCE.GetWindowThreadProcessId(wnd, dwID); + // if this windows belongs to our process + if (dwID.getValue() == lParam) + { + // System.out.println("post message a: " + wnd); + MyUser32.INSTANCE.PostMessageA(wnd, MyUser32.WM_CLOSE, null, null); + // MyUser32.INSTANCE.PostMessageA(wnd, MyUser32.WM_QUIT, + // null, null) ; + // MyUser32.INSTANCE.PostMessageA(wnd, MyUser32.WM_DESTROY, + // null, null) ; + } + // continue with next window + return true; + } + }; + // execute closeWindow on all windows + MyUser32.INSTANCE.EnumWindows(closeWindow, _pid); + try + { + Thread.sleep(100); + } + catch (InterruptedException e) + { + e.printStackTrace(); + Thread.currentThread().interrupt(); + } + + // Wait for process to terminate + + if (timeout > 0) + waitFor(timeout); + + // give system time to put exit code + try + { + Thread.sleep(100); + } + catch (InterruptedException e) + { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + // If still running -> hard kill + if (isRunning()) + { + log("process is not polite -> hard kill"); + return kill(code); + } + else + { + // _processInformation = null; + _pid = -1; + setExitCode(code); + return true; + } + } + + // public boolean cleanKill(int code, int timeout) + // { + // // first try polite kill + // // e.g. post WM_CLOSE to all windows whose PID + // // matches our process. + // MyUser32.WNDENUMPROC closeWindow = new MyUser32.WNDENUMPROC() + // { + // // lParam is the pid of our process + // public boolean callback(Pointer wnd, int lParam) + // { + // // get the pid of the window + // IntByReference dwID = new IntByReference(); + // MyUser32.INSTANCE.GetWindowThreadProcessId(wnd, dwID) ; + // // if this windows belongs to our process + // if(dwID.getValue() == lParam) + // { + // MyUser32.INSTANCE.PostMessageA(wnd, MyUser32.WM_CLOSE, null, null) ; + // } + // // continue with next window + // return true; + // } + // }; + // // execute closeWindow on all windows + // MyUser32.INSTANCE.EnumWindows(closeWindow , + // _pid) ; + // + // // Wait for process to terminate + // waitFor(timeout); + // // If still running -> hard kill + // if (isRunning()) + // return kill(code); + // return false; + // + // } + + /* + * (non-Javadoc) + * + * @see org.rzo.yajsw.os.Process#kill(int) + */ + public boolean kill(int code) + { + boolean result = false; + try + { + if (_pid <= 0) + { + log("cannot kill process with pid " + _pid); + return false; + } + + if (!isRunning()) + { + // _processInformation = null; + _pid = -1; + return false; + } + int i = 0; + if (_processInformation != null && _processInformation.hProcess != null) + while (!result && i < 10) + { + if (_processInformation != null && _processInformation.hProcess != null) + { + result = MyKernel32.INSTANCE.TerminateProcess(_processInformation.hProcess, code); + if (!result) + { + log("kill of process with PID " + _pid + " failed: OS Error #" + MyKernel32.INSTANCE.GetLastError()); + i++; + try + { + Thread.sleep(500); + } + catch (InterruptedException e) + { + e.printStackTrace(); + Thread.currentThread().interrupt(); + } + + } + } + else + { + Thread.sleep(1000); + result = !isRunning(); + } + + } + Thread.sleep(100); + if (!isRunning()) + _pid = -1; + } + catch (Exception ex) + { + ex.printStackTrace(); + result = true; + } + + if (!result) + log("kill failed: " + _pid + " process still running"); + + return result; + } + + /** + * Kill. + * + * @param pid + * the pid + * @param code + * the code + * + * @return true, if successful + */ + public static boolean kill(int pid, int code) + { + if (pid <= 0) + return false; + HANDLE hProcess = MyKernel32.INSTANCE.OpenProcess(MyKernel32.PROCESS_TERMINATE, false, pid); + boolean result = MyKernel32.INSTANCE.TerminateProcess(hProcess, code); + Thread.yield(); + if (!result) + System.out.println("process kill failed: " + pid + " code=" + code); + MyKernel32.INSTANCE.CloseHandle(hProcess); + return result; + } + + /** + * Gets the process maps. + * + * @param pid + * the pid + * + * @return the process maps + */ + public static Map[] getProcessMaps(int pid) + { + Map processMap = new HashMap(); + Map childrenMap = new MultiHashMap(); + Map[] result = new Map[] + { processMap, childrenMap }; + + Pointer processes = MyKernel32.INSTANCE.CreateToolhelp32Snapshot(MyKernel32.TH32CS_SNAPPROCESS, 0); + if (processes == null) + { + System.out.println("note: task list is empty "); + return result; + } + + PROCESSENTRY32 me = new PROCESSENTRY32(); + me.szExeFile = new char[MyKernel32.MAX_PATH]; + int size = me.size(); + // System.out.println("size: " + size); + me.dwSize = size; + if (MyKernel32.INSTANCE.Process32First(processes, me)) + { + System.out.println("ProcessList:"); + do + { + // System.out.println(/* new String(next.szExeFile) + */" " + + // me.th32ModuleID + " " + me.th32DefaultHeapID + " " + + // me.th32ProcessID + // + " -> " + me.th32ParentProcessID); + if (me.th32ProcessID > 0) + processMap.put(new Integer(me.th32ProcessID), me); + if (me.th32ParentProcessID > 0 && processMap.get(new Integer(me.th32ParentProcessID)) != null) + { + childrenMap.put(new Integer(me.th32ParentProcessID), new Integer(me.th32ProcessID)); + } + System.out.println("\tProcessID=" + me.th32ProcessID + "\t\t -> ParentProcessID=" + me.th32ParentProcessID); + + // else + // System.out.println("not added"); + + } + while (MyKernel32.INSTANCE.Process32Next(processes, me)); + } + else + System.out.println("get process list: cannot access first process in list "); + + MyKernel32.INSTANCE.CloseHandle(processes); + + return result; + } + + /** The levels. */ + int levels; + + /** The _pf counter. */ + private PdhCounter _pfCounter; + + /** The _v mem counter. */ + private PdhCounter _vMemCounter; + + /** The _cpu counter. */ + private PdhCounter _cpuCounter; + + /** The _p mem counter. */ + private PdhCounter _pMemCounter; + + private PdhCounter _threadCounter; + private PdhCounter _handleCounter; + + private boolean _started = false; + + /** + * Gets the process tree. + * + * @param pid + * the pid + * + * @return the process tree + */ + static public List getProcessTree(int pid) + { + Map[] maps = getProcessMaps(pid); + Map processMap = maps[0]; + Map childrenMap = maps[1]; + Collection pids = new ArrayList(); + pids.add(new Integer(pid)); + return getProcessTree(childrenMap, pids); + } + + /** + * Gets the process tree. + * + * @param childrenMap + * the children map + * @param pids + * the pids + * + * @return the process tree + */ + static List getProcessTree(Map childrenMap, Collection pids) + { + List result = new ArrayList(); + if (pids == null) + return result; + if (pids.isEmpty()) + return result; + for (Iterator it = pids.iterator(); it.hasNext();) + { + Integer i = (Integer) it.next(); + // System.out.println(i); + result.addAll(getProcessTree(childrenMap, (Collection) childrenMap.get(i))); + } + result.addAll(pids); + return result; + } + + /* + * (non-Javadoc) + * + * @see org.rzo.yajsw.os.Process#killTree(int) + */ + public boolean killTree(int code) + { + if (_pid <= 0) + { + log("cannot kill process with pid " + _pid); + return false; + } + + if (!isRunning()) + return false; + boolean result = true; + List tree = getProcessTree(_pid); + int retry = 0; + while (tree.size() < 2 && retry < 20) + { + if (_debug) + log("killTree: getProcessTree error: retrying "); + tree = getProcessTree(_pid); + retry++; + } + for (Iterator it = tree.iterator(); it.hasNext();) + { + int pid = ((Integer) it.next()).intValue(); + if (pid != _pid) + result = result && kill(pid, code); + + } + + result = result && kill(code); + + return result; + } + + /* + * (non-Javadoc) + * + * @see org.rzo.yajsw.os.AbstractProcess#getExitCode() + */ + @Override + public int getExitCode() + { + int result = 0; + if (_exitCode < 0 && _processInformation != null) + { + result = getExitCodeInternal(); + if (result != MyKernel32.STILL_ACTIVE) + setExitCode(result); + else + setExitCode(-2); + } + else + { + // log("getExitCode "+_exitCode + " "+_processInformation); + } + if (isDebug()) + log("getExitCode " + _exitCode + " processINFO==null=" + (_processInformation == null)); + // System.out.println("get exit code "+_exitCode); + return _exitCode; + } + + /* + * (non-Javadoc) + * + * @see org.rzo.yajsw.os.Process#isRunning() + */ + public boolean isRunning() + { + if (_pid <= 0) + { + if (isDebug()) + log("is running: false pid=(" + _pid + "<=0)"); + // log("is running: false "+_pid); + return false; + } + if (_processInformation == null) + { + if (isDebug()) + log("is running: _processInformation == null pid=" + _pid); + return false; + } + // return _processInformation != null && getExitCode() < 0 && _pid > 0; + boolean result = getExitCode() == -2 && _pid >= 0; + // log("is running: "+result +" "+_pid + " "+ _exitCode); + if (isDebug()) + log("is running: " + result + " " + _pid + " " + _exitCode); + return result; + /* + * Pointer process = + * MyKernel32.INSTANCE.OpenProcess(MyKernel32.PROCESS_QUERY_INFORMATION, + * false, _pid); if (process == Pointer.NULL) { + * log("is running: false "+_pid); return false; } + * MyKernel32.INSTANCE.CloseHandle(process); + * log("is running: true "+_pid); return true; + */ + + } + + // if you use counters: you will have to destroy before finalze is called. + // Otherwise the JVM may crash + /* + * (non-Javadoc) + * + * @see org.rzo.yajsw.os.Process#destroy() + */ + volatile boolean destroyed = false; + + public void destroy() + { + if (destroyed) + return; + destroyed = true; + if (_processInformation != null) + { + if (_teeName != null && _inputStream != null) + { + try + { + _inputStream.close(); + } + catch (Exception e) + { + e.printStackTrace(); + } + try + { + _outputStream.close(); + } + catch (Exception e) + { + e.printStackTrace(); + } + try + { + _errorStream.close(); + } + catch (Exception e) + { + e.printStackTrace(); + } + _inputStream = null; + _outputStream = null; + _errorStream = null; + new File(_tmpPath, "in_" + _teeName).delete(); + new File(_tmpPath, "out_" + _teeName).delete(); + new File(_tmpPath, "err_" + _teeName).delete(); + + } + // else + // System.out.println("no streams to destroy"); + + if (outRead != null && outRead.getValue() != Pointer.NULL) + { + //MyKernel32.INSTANCE.SetHandleInformation(outRead.getValue(), 2, 0); + //MyKernel32.INSTANCE.CloseHandle(outRead.getValue()); + outRead = null; + } + + if (errRead != null && errRead.getValue() != Pointer.NULL) + { + //MyKernel32.INSTANCE.SetHandleInformation(errRead.getValue(), 2, 0); + //MyKernel32.INSTANCE.CloseHandle(errRead.getValue()); + errRead = null; + } + + if (inWrite != null && inWrite.getValue() != Pointer.NULL) + { + //MyKernel32.INSTANCE.SetHandleInformation(inWrite.getValue(), 2, 0); + //MyKernel32.INSTANCE.CloseHandle(inWrite.getValue()); + inWrite = null; + } + + if (_processInformation.hThread != null) + if (!_processInformation.hThread.equals(Pointer.NULL)) + MyKernel32.INSTANCE.CloseHandle(_processInformation.hThread); + if (_processInformation.hProcess != null) + if (!_processInformation.hProcess.equals(Pointer.NULL)) + MyKernel32.INSTANCE.CloseHandle(_processInformation.hProcess); + if (_cpuCounter != null) + { + _cpuCounter.close(); + _cpuCounter = null; + } + if (_vMemCounter != null) + { + _vMemCounter.close(); + _vMemCounter = null; + } + if (_pMemCounter != null) + { + _pMemCounter.close(); + _pMemCounter = null; + } + + if (_pfCounter != null) + { + _pfCounter.close(); + _pfCounter = null; + } + if (_threadCounter != null) + { + _threadCounter.close(); + _threadCounter = null; + } + if (_handleCounter != null) + { + _handleCounter.close(); + _handleCounter = null; + } + } + if (_debug) + log("process handles destroyed " + _pid); + //if (_processInformation != null) + //_processInformation.finalize(); + _processInformation = null; + + //if (_startupInfo != null) + //_startupInfo.finalize(); + _startupInfo = null; + try + { + Thread.sleep(100); + } + catch (InterruptedException e) + { + // TODO Auto-generated catch block + e.printStackTrace(); + } + /* + * if (_pipeStreams) { if (inRead != null) if (inRead.getValue() != + * null) MyKernel32.INSTANCE.CloseHandle(inRead.getValue()); if + * (outWrite != null) if (outWrite.getValue() != null) + * MyKernel32.INSTANCE.CloseHandle(outWrite.getValue()); if (errWrite != + * null) if (errWrite.getValue() != null) + * MyKernel32.INSTANCE.CloseHandle(errWrite.getValue()); } + * + * if (_outputStream != null) { try { _outputStream.close(); } catch + * (IOException e) { } _outputStream = null; } + * + * if (_errorStream != null) { try { _errorStream.close(); } catch + * (IOException e) { } _errorStream = null; } + * + * if (_inputStream != null) { try { _inputStream.close(); } catch + * (IOException e) { } _inputStream = null; } + */ + + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#finalize() + */ + @Override + public void finalize() throws Throwable + { + try + { + // this may cause jvm crash when java shuts down -> TODO + // destroy(); + } + finally + { + super.finalize(); + } + } + + /** + * Read virtual memory to structure. + * + * @param baseAddress + * the base address + * @param goal + * the goal + * + * @return true, if successful + */ + boolean readVirtualMemoryToStructure(Pointer baseAddress, Structure goal) + { + int size = goal.size(); + // System.out.println("readVirtualMemoryToStructure "+size); + int ret = Ntdll.INSTANCE.ZwReadVirtualMemory(_processInformation.hProcess.getPointer(), baseAddress, goal.getPointer(), size, null); + if (ret != 0) + log("pid " + _pid + " ZwReadVirtualMemory returns " + Integer.toHexString(ret)); + + goal.read(); + return ret == 0; + + } + + /** + * Read virtual memory to memory. + * + * @param baseAddress + * the base address + * @param goal + * the goal + * + * @return true, if successful + */ + boolean readVirtualMemoryToMemory(Pointer baseAddress, Memory goal) + { + int size = (int) goal.getSize(); + // System.out.println("readVirtualMemoryToMemory "+size); + int ret = Ntdll.INSTANCE.ZwReadVirtualMemory(_processInformation.hProcess.getPointer(), baseAddress, goal, size, null); + if (ret != 0) + { + if (ret == 0x8000000d) // see more http://nologs.com/ntstatus.html + log("pid " + _pid + " ZwReadVirtualMemory returns " + Integer.toHexString(ret)+ " partial copy "); + else + log("pid " + _pid + " ZwReadVirtualMemory returns " + Integer.toHexString(ret)); + } + + return ret == 0; + + } + + /** + * readProcessMemory to memory. + */ + long readProcessMemory(Pointer baseAddress, Memory goal) + { + NativeLong sizeAvalaible = new NativeLong(goal.size()); + NativeLongByReference bytesReadRefernce = new NativeLongByReference(); + boolean ret = MyKernel32.INSTANCE.ReadProcessMemory(_processInformation.hProcess.getPointer(), baseAddress, goal, + sizeAvalaible, bytesReadRefernce); + if (!ret) + log("pid " + _pid + " ReadProcessMemory returns " + ret); + long bytesRead = bytesReadRefernce.getValue().longValue(); + return bytesRead; + + } + + /** + * Gets the command line internal. this works only for 32 bit processes + * + * @return the command line internal + */ + String getCommandLineInternal() + { + // System.out.println("get command internal "+getPid()); + String result = "?"; + PROCESS_BASIC_INFORMATION pbi = null; + + pbi = new PROCESS_BASIC_INFORMATION(); + IntByReference returnLength = new IntByReference(); + HANDLE hProcess = _processInformation.hProcess; + int pbiSize = pbi.size(); // x64 = 48 bytes, x32 = 24 + int ret = Ntdll.INSTANCE.ZwQueryInformationProcess(hProcess, (byte) 0, pbi.getPointer(), pbiSize, returnLength); + if (ret == 0) + { + pbi.read(); + if (pbi.PebBaseAddress != null) + { + PEB peb = new PEB(); + // System.out.println(""+1); + if (readVirtualMemoryToStructure(pbi.PebBaseAddress, peb)) + if (peb.ProcessParameters != null) + { + RTL_USER_PROCESS_PARAMETERS userParams = new RTL_USER_PROCESS_PARAMETERS(); + int userParamsSize = userParams.size(); //x32 = 784, x64 = 1264 + // System.out.println(""+2); + if (readVirtualMemoryToStructure(peb.ProcessParameters, userParams)) + { + // System.out.println("MaximumLength "+userParams.CommandLine.MaximumLength); + if (userParams.CommandLine.MaximumLength > 0) + { + Memory stringBuffer = new Memory(userParams.CommandLine.MaximumLength); + // System.out.println(""+3); + if (readVirtualMemoryToMemory(userParams.CommandLine.Buffer, stringBuffer)) + result = stringBuffer.getString(0, true); + } + + if (userParams.CurrentDirectoryPath.MaximumLength > 0) + { + Memory stringBuffer = new Memory(userParams.CurrentDirectoryPath.MaximumLength); + if (readVirtualMemoryToMemory(userParams.CurrentDirectoryPath.Buffer, stringBuffer)) + _workingDir = stringBuffer.getString(0, true); + } + if (userParams.WindowTitle.MaximumLength > 0) + { + Memory stringBuffer = new Memory(userParams.WindowTitle.MaximumLength); + if (readVirtualMemoryToMemory(userParams.WindowTitle.Buffer, stringBuffer)) + _title = stringBuffer.getString(0, true); + } + if (userParams.Environment != null) + { + // get size of environment strings + MEMORY_BASIC_INFORMATION memInfo = new MEMORY_BASIC_INFORMATION(); + int memInfoSize = memInfo.size(); //x64 = 48, x32 = 28 + int bytesRead = MyKernel32.INSTANCE.VirtualQueryEx(hProcess.getPointer(), userParams.Environment, memInfo.getPointer(), + memInfoSize); + memInfo.read(); + if (bytesRead == 0) + { + _logger.warning("error getting environment in VirtualQueryEx " + Native.getLastError()); + } + else if (MyKernel32.PAGE_NOACCESS == memInfo.Protect || MyKernel32.PAGE_EXECUTE == memInfo.Protect) + { + _logger.warning("error getting environment in VirtualQueryEx no access right"); + } + else + { + long envSize = Math.min(Pointer.nativeValue(memInfo.RegionSize), 32767); //Max Size http://msdn.microsoft.com/en-us/library/ms682653%28v=vs.85%29.aspx + + Memory mem = new Memory(envSize); + readProcessMemory(userParams.Environment, mem); + + List envStrings = new ArrayList(); + String env = null; + int l = 0; + while (!"".equals(env)) + { + env = mem.getString(l, true); + if (env != null && env.length() != 0) + { + envStrings.add(env); + l += env.length() * 2 + 2; + } + if (env == null) + break; + } + + parseEnvString(envStrings); + } + } + } + + } + } + } + // else + // System.out.println("3 pid " + _pid + + // " ZwQueryInformationProcess returns " + Integer.toHexString(ret)); + if (result != null) + result = result.trim(); + return result; + + } + + private void parseEnvString(List envStrings) + { + if (envStrings == null || envStrings.size() == 0) + return; + for (String str : envStrings) + { + String[] var = str.split("="); + if (var.length == 2) + _environment.add(new String[] + { var[0], var[1] }); + } + + } + + // this will work only if we run on 64. + // if we run on wow64 (eg 32 bit), ZwQueryInformationProcess returns 0, but + // PEB64.ProcessParameters is empty + String getCommandLineInternal64() + { + log("get command internal 64 " + getPid()); + String result = "?"; + PROCESS_BASIC_INFORMATION pbi = null; + + pbi = new PROCESS_BASIC_INFORMATION(); + IntByReference returnLength = new IntByReference(); + HANDLE hProcess = _processInformation.hProcess; + int size = pbi.size(); + int ret = Ntdll.INSTANCE.ZwQueryInformationProcess(hProcess, (byte) 0, pbi.getPointer(), size, returnLength); + if (ret == 0) + { + pbi.read(); + if (pbi.PebBaseAddress != null) + { + PEB64 peb = new PEB64(); + // System.out.println("64 " + 1); + if (readVirtualMemoryToStructure(pbi.PebBaseAddress, peb)) + if (peb.ProcessParameters != null) + { + RTL_USER_PROCESS_PARAMETERS userParams = new RTL_USER_PROCESS_PARAMETERS(); + // System.out.println("64 " + 2); + if (readVirtualMemoryToStructure(peb.ProcessParameters, userParams)) + { + // System.out.println("MaximumLength " + + // userParams.CommandLine.MaximumLength); + // System.out.println("Length " + + // userParams.CommandLine.Length); + Memory stringBuffer = new Memory(userParams.CommandLine.Length); + // System.out.println("64 " + 3); + if (readVirtualMemoryToMemory(userParams.CommandLine.Buffer, stringBuffer)) + result = stringBuffer.getString(0, true); + } + + } + } + } + else + log("pid " + _pid + " ZwQueryInformationProcess returns " + Integer.toHexString(ret)); + return result; + + } + + // this should run on all platforms + // TODO optimize by calling windows methods for WMI + // note: Runtime.exec("cmd /C wmic") hangs + // note: we cannot use p.getInputStream() since the result stream contains + // unexpeced characters + // note: when we write the result to file we have to convert the string. + public String getCommandLineInternalWMI() + { + String result = "?"; + WindowsXPProcess p = null; + // if the server is overloaded we may not get an answer -> try 3 times + for (int k = 0; k < 3 && "?".equals(result); k++) + try + { + + p = new WindowsXPProcess(); + new File("wmic.tmp").delete(); + p.setCommand("cmd /C wmic process where processid=" + getPid() + " get commandline > wmic.tmp"); + p.setVisible(false); + p.start(); + p.waitFor(30000); + if (p.isRunning()) + p.kill(99); + int ec = p.getExitCode(); + if (ec != 0) + log("unexptected exit code in getCommandLineInternalWMI: "+ec); + BufferedReader br = new BufferedReader(new FileReader("wmic.tmp")); + String l = "?"; + try + { + br.readLine(); + br.readLine(); + l = br.readLine(); + if (l.codePointAt(0) == 0) + { + StringBuffer s = new StringBuffer(); + for (int i = 0; i < l.length(); i++) + if (l.codePointAt(i) != 0) + s.append(l.charAt(i)); + l = s.toString(); + } + result = l; + } + catch (Exception ex) + { + ex.printStackTrace(); + } + br.close(); + p.destroy(); + } + catch (Exception e) + { + if (_debug) + log("Error in getCommandLineInternalWMI"); + e.printStackTrace(); + try + { + Thread.sleep(10000); + } + catch (InterruptedException e1) + { + e1.printStackTrace(); + return result; + } + if (p != null) + p.destroy(); + } + return result; + + } + + /** + * Gets the total cpu. + * + * @return the total cpu + */ + public long getTotalCPU() + { + long result = -1; + if (!isRunning()) + return -1; + LongByReference lpCreationTime = new LongByReference(); + LongByReference lpExitTime = new LongByReference(); + LongByReference lpKernelTime = new LongByReference(); + LongByReference lpUserTime = new LongByReference(); + + if (MyKernel32.INSTANCE.GetProcessTimes(_processInformation.hProcess, lpCreationTime, lpExitTime, lpKernelTime, lpUserTime)) + result = lpUserTime.getValue() + lpKernelTime.getValue(); + return result; + } + + /** + * Current process id. + * + * @return the int + */ + public static int currentProcessId() + { + return MyKernel32.INSTANCE.GetCurrentProcessId(); + } + + /** + * Process id of active window. + * + * @return the int + */ + public static int processIdOfActiveWindow() + { + HWND w = MyUser32.INSTANCE.GetForegroundWindow(); + IntByReference result = new IntByReference(); + MyUser32.INSTANCE.GetWindowThreadProcessId(w, result); + return result.getValue(); + } + + /* + * (non-Javadoc) + * + * @see org.rzo.yajsw.os.Process#getCurrentCpu() + */ + public int getCurrentCpu() + { + if (!isRunning() || getCpuCounter() == null) + return -1; + PdhCounter c = getCpuCounter(); + return c.getIntValue(); + } + + /** + * Gets the cpu counter. + * + * @return the cpu counter + */ + private PdhCounter getCpuCounter() + { + if (_cpuCounter == null) + _cpuCounter = Pdh.getProcessEnglishCounter(_pid, "% Processor Time"); + return _cpuCounter; + } + + /* + * (non-Javadoc) + * + * @see org.rzo.yajsw.os.Process#getCurrentPhysicalMemory() + */ + public int getCurrentPhysicalMemory() + { + if (!isRunning()) + return -1; + PdhCounter c = getPMemCounter(); + return c.getIntValue(); + } + + /** + * Gets the p mem counter. + * + * @return the p mem counter + */ + private PdhCounter getPMemCounter() + { + if (_pMemCounter == null) + _pMemCounter = Pdh.getProcessEnglishCounter(_pid, "Private Bytes"); + return _pMemCounter; + } + + /* + * (non-Javadoc) + * + * @see org.rzo.yajsw.os.Process#getCurrentVirtualMemory() + */ + public int getCurrentVirtualMemory() + { + if (!isRunning() || getVMemCounter() == null) + return -1; + PdhCounter c = getVMemCounter(); + return c.getIntValue(); + } + + /** + * Gets the v mem counter. + * + * @return the v mem counter + */ + private PdhCounter getVMemCounter() + { + if (_vMemCounter == null) + _vMemCounter = Pdh.getProcessEnglishCounter(_pid, "Virtual Bytes"); + return _vMemCounter; + } + + /* + * (non-Javadoc) + * + * @see org.rzo.yajsw.os.Process#getCurrentPageFaults() + */ + public int getCurrentPageFaults() + { + if (!isRunning()) + return -1; + PdhCounter c = getPfCounter(); + return c.getIntValue(); + } + + /** + * Gets the pf counter. + * + * @return the pf counter + */ + private PdhCounter getPfCounter() + { + if (_pfCounter == null) + _pfCounter = Pdh.getProcessEnglishCounter(_pid, "Page Faults/sec"); + return _pfCounter; + } + + /* + * (non-Javadoc) + * + * @see org.rzo.yajsw.os.Process#getChildren() + */ + public Collection getChildren() + { + return getProcessTree(_pid); + } + + // test + /** + * The main method. + * + * @param args + * the arguments + */ + public static void main(String[] args) + { + /* + * WindowsXPProcess[] p = new WindowsXPProcess[1]; for (int i = 0; i < + * p.length; i++) { p[i] = new WindowsXPProcess(); // + * p[i].setPipeStreams(true, false); + * p[i].setCommand("ping 127.0.0.1 -t");// "c:/driwin/dripc.exe");// // + * "java -cp yajsw.jar // org.rzo.yajsw.HelloWorld > // t.log"); // + * p[i].setWorkingDir("c:/driwin"); p[i].setVisible(false); + * p[i].setPipeStreams(true, false); } boolean done = false; while + * (!done) { done = true; System.out.println("START"); + * + * for (int i = 0; i < p.length; i++) { + * + * p[i].start(); // + * System.out.println(p[i].getCommandLineInternalWMI()); + * + * / String line = null; int k = 0; try { InputStreamReader isr = new + * InputStreamReader(p[i].getInputStream()); BufferedReader br = new + * BufferedReader(isr); + * + * line = br.readLine(); System.out.println(line); while (k < 30 && line + * != null) { System.out.println(line); line = br.readLine(); k++; } + * + * } catch (Exception e) { // TODO Auto-generated catch block + * e.printStackTrace(); } / + * + * System.out.println("sleep"); p[i].waitFor(5000); } + * + * System.out.println("KILL"); for (int i = 0; i < p.length; i++) { // + * p[i].killTree(999); ((WindowsXPProcess) p[i]).stop(5000, 999); + * System.out.println(p[i].getExitCode()); // p[i].finalize(); } try { + * Thread.sleep(1000); } catch (InterruptedException e) { // TODO + * Auto-generated catch block e.printStackTrace(); } } + * + * // p.setCommand("java -classpath z:\dev\yajsw\wrapper.jar org.rzo." ) + */ + /* + * WindowsXPProcess p = new WindowsXPProcess(); p.setCommand("notepad"); + * p.setUser("test\\yajsw"); p.setPassword("yajsw"); p.start(); + */ + // getProcess(3332); + /* + Process p = new WindowsXPProcess(); + // p.setCommand("ping 127.0.0.1"); + p.setCommand("set.bat"); + List env = OperatingSystem.instance().processManagerInstance().getProcess( + OperatingSystem.instance().processManagerInstance().currentProcessId()).getEnvironment(); + p.setEnvironment(env); + System.out.println(p.getEnvironmentAsMap().get("Path")); + System.out.println(env.get(0)[0]); + p.setPipeStreams(true, false); + p.start(); + String line = null; + int k = 0; + try + { + InputStreamReader isr = new InputStreamReader(p.getInputStream()); + BufferedReader br = new BufferedReader(isr); + line = br.readLine(); + System.out.println(line); + while (k < 30 && line != null) + { + System.out.println(line); + line = br.readLine(); + k++; + } + } + catch (Exception ex) + { + ex.printStackTrace(); + } + */ + + /* + System.out.println("start -----------------"); + WindowsXPProcess p2 = new WindowsXPProcess(); + elevating command line java -jar "Z:\dev\yajsw\bat\/..\wrapper.jar" -t conf/wr + apper.helloworld.conf + p2.setCommand("java -jar wrapper.jar -t Z:\\dev\\yajsw\\conf\\wrapper.helloworld.conf" ); + p2.setDebug(true); + if (p2.start()) + { + System.out.println(p2.isElevated()); + p2.waitFor(); + System.out.println(p2.getPid()); + System.out.println(p2.getExitCode()); + } + */ + + /* + System.out.println("stop -----------------"); + WindowsXPProcess p = new WindowsXPProcess(); + p.setCommand("\"java\" -Xmx5m \"-Dtest=Ttest 1\" org.Test abc"); + p.setCommand("\"java\" -version"); + p.setCommand("java -jar \"Z:\\dev\\yajsw\\bat\\/..\\wrapper.jar\" -t conf/wrapper.helloworld.conf" ); + p.setDebug(true); + if (p.startElevated()) + { + System.out.println(p.isElevated()); + p.waitFor(); + System.out.println(p.getPid()); + System.out.println(p.getExitCode()); + } + */ + WindowsXPProcess p = (WindowsXPProcess) getProcess(4664); + for (int i=0; i<4; i++) + { + try + { + p.sendKey('D'); + Thread.sleep(10000); + } + catch (InterruptedException e) + { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + + //p.sendKey('B'); + //p.sendKey('C'); + //p.sendKey('D'); + + + } + + /** + * Reconnect streams. + * + * @return true, if successful + */ + public boolean reconnectStreams() + { + if (_teeName != null) + try + { + _inputStream = new CyclicBufferFileInputStream(new File(_tmpPath, "out_" + _teeName)); + _errorStream = new CyclicBufferFileInputStream(new File(_tmpPath, "err_" + _teeName)); + _outputStream = new CyclicBufferFilePrintStream(new File(_tmpPath, "in_" + _teeName)); + return true; + } + catch (Exception ex) + { + ex.printStackTrace(); + } + + return false; + + } + + /** + * Writefd. + * + * @param fd + * the fd + * @param pointer + * the pointer + */ + private void writefd(FileDescriptor fd, Pointer pointer) + { + try + { + // Field[] fields = FileDescriptor.class.getDeclaredFields(); + // System.out.println("fields"); + // for (Field field : fields){ + // System.out.println(field.getName()); + // } + // System.out.println("writefd"); + Field handleField = FileDescriptor.class.getDeclaredField("handle"); + handleField.setAccessible(true); + Field peerField = Pointer.class.getDeclaredField("peer"); + peerField.setAccessible(true); + long value = peerField.getLong(pointer); + // System.out.println(value); + // System.out.flush(); + handleField.setLong(fd, value); + } + catch (Exception e) + { + e.printStackTrace(); + } + + } + + public String currentUser() + { + String result = System.getenv("USERDOMAIN") + "\\" + System.getenv("USERNAME"); + result = result.toUpperCase(); + return result; + } + + public String currentUserName() + { + String result = System.getProperty("user.name"); + if (result == null) + return ""; + result = result.toUpperCase(); + return result; + } + + public String currentUserDomain() + { + String result = System.getenv("USERDOMAIN"); + if (result == null) + return ""; + return result.toUpperCase(); + } + + public String standardizeUser(String user) + { + if (user == null) + return null; + if (user.indexOf("\\") == -1) + return currentUserDomain() + "\\" + user.toUpperCase(); + return user.toUpperCase(); + } + + boolean doesUserHavePrivilege(String lpPrivilegeName) + + { + PointerByReference hToken = new PointerByReference(); + IntByReference dwSize = new IntByReference(); + Memory lpPrivileges; + MyAdvapi.LUID PrivilegeLuid = new MyAdvapi.LUID(); + int i; + boolean bResult = false; + + if (!MyAdvapi.INSTANCE.OpenProcessToken(MyKernel32.INSTANCE.GetCurrentProcess(), MyAdvapi.INSTANCE.TOKEN_QUERY, hToken)) + return false; + + MyAdvapi.INSTANCE.GetTokenInformation(hToken.getValue(), MyAdvapi.TokenPrivileges, null, 0, dwSize); + + lpPrivileges = new Memory(dwSize.getValue()); + + if (!MyAdvapi.INSTANCE.GetTokenInformation(hToken.getValue(), MyAdvapi.TokenPrivileges, lpPrivileges, dwSize.getValue(), dwSize)) + { + return false; + } + + MyKernel32.INSTANCE.CloseHandle(hToken.getValue()); + + if (!MyAdvapi.INSTANCE.LookupPrivilegeValueA(null, lpPrivilegeName, PrivilegeLuid)) + { + return false; + } + + MyAdvapi.TOKEN_PRIVILEGES privileges = new MyAdvapi.TOKEN_PRIVILEGES(lpPrivileges); + for (i = 0; i < privileges.PrivilegeCount; i++) + { + if (privileges.Privileges[i].Luid.HighPart == PrivilegeLuid.HighPart && privileges.Privileges[i].Luid.LowPart == PrivilegeLuid.LowPart) + { + return true; + } + } + return false; + } + + public int getCurrentHandles() + { + if (!isRunning() || getHandlesCounter() == null) + return -1; + PdhCounter c = getHandlesCounter(); + return c.getIntValue(); + } + + private PdhCounter getHandlesCounter() + { + if (_handleCounter == null) + _handleCounter = Pdh.getProcessEnglishCounter(_pid, "Handle Count"); + return _handleCounter; + } + + public int getCurrentThreads() + { + if (!isRunning() || getThreadsCounter() == null) + return -1; + PdhCounter c = getThreadsCounter(); + return c.getIntValue(); + } + + private PdhCounter getThreadsCounter() + { + if (_threadCounter == null) + _threadCounter = Pdh.getProcessEnglishCounter(_pid, "Thread Count"); + return _threadCounter; + } + + public boolean isTerminated() + { + return (_started && !isRunning()); + } + + public boolean changeWorkingDir(String name) + { + File f = new File(name); + String dir; + if (!f.exists() || !f.isDirectory()) + { + System.out.println("setWorkingDirectory failed. file not found " + name); + return false; + } + else + try + { + dir = f.getCanonicalPath(); + } + catch (IOException e) + { + e.printStackTrace(); + return false; + } + boolean result = MyKernel32.INSTANCE.SetCurrentDirectoryA(dir); + if (result) + System.setProperty("user.dir", dir); + return result; + } + + public boolean startElevated() + { + + String file = getCmdFile(); + if (file == null) + { + log("startElevated: Error: error in command"); + return false; + } + String parameters = getCmdParameters(); + if (_debug) + log("elevated exec: " + file + " "+ parameters); + + SHELLEXECUTEINFO lpExecInfo = new SHELLEXECUTEINFO(); + lpExecInfo.fMask = Shell32.SEE_MASK_NOCLOSEPROCESS; + lpExecInfo.hwnd = null; + lpExecInfo.lpFile = file; + lpExecInfo.lpVerb = Shell32.VERB_RUNAS; + lpExecInfo.nShow = Shell32.SW_SHOWMAXIMIZED; + lpExecInfo.lpParameters = parameters; + lpExecInfo.cbSize = lpExecInfo.size(); + + boolean result = Shell32.INSTANCE.ShellExecuteEx(lpExecInfo); + if (!result) + { + int err = Native.getLastError(); + System.out.println("Error: "+err+" "+Kernel32Util.formatMessageFromLastErrorCode(err)); + } + else + { + _pid = MyKernel32.INSTANCE.GetProcessId(lpExecInfo.hProcess); + _processInformation = new PROCESS_INFORMATION(); + _processInformation.dwProcessId = _pid; + _processInformation.hProcess = lpExecInfo.hProcess; + + } + return result; + } + + private String getCmdParameters() + { + String result = ""; + int i = 0; + if (_arrCmd != null) + { + for (String cmd : _arrCmd) + { + if (i != 0) + { + if (cmd.startsWith("\"")) + result += cmd + " "; + else + result += '"' + cmd + "\" "; + } + i++; + } + } + else + { + if (_cmd.startsWith("\"")) + { + result = _cmd.substring(_cmd.indexOf("\" ", 1)+2); + } + else + { + result = _cmd.substring(_cmd.indexOf(" ")); + } + } + if ("".equals(result)) + result = null; + return result; + } + + private String getCmdFile() + { + if (_arrCmd != null) + { + return _arrCmd[0]; + } + if (_cmd != null) + { + if (_cmd.startsWith("\"")) + { + return _cmd.substring(1, _cmd.indexOf("\" ", 1)); + } + return _cmd.substring(0, _cmd.indexOf(" ")); + } + return null; + } + + public boolean isElevated() + { + if (_isElevated > -1) + return _isElevated == 1; + _isElevated = isElevatedInternal(); + return _isElevated == 1; + } + + private int isElevatedInternal() + { + try + { + PointerByReference hToken = new PointerByReference(); + IntByReference dwSize = new IntByReference(); + Memory lpElevation; + + if (!MyAdvapi.INSTANCE.OpenProcessToken(MyKernel32.INSTANCE.GetCurrentProcess(), MyAdvapi.INSTANCE.TOKEN_QUERY, hToken)) + return -1; + + MyAdvapi.INSTANCE.GetTokenInformation(hToken.getValue(), MyAdvapi.TokenElevation, null, 0, dwSize); + + lpElevation = new Memory(dwSize.getValue()); + + if (!MyAdvapi.INSTANCE.GetTokenInformation(hToken.getValue(), MyAdvapi.TokenElevation, lpElevation, dwSize.getValue(), dwSize)) + { + return -1; + } + + MyKernel32.INSTANCE.CloseHandle(hToken.getValue()); + + MyAdvapi.TOKEN_ELEVATION elevation = new MyAdvapi.TOKEN_ELEVATION(lpElevation); + return elevation.isElevated() ? 1 : 0; + } + catch (Throwable ex) + { + ex.printStackTrace(); + } + return -1; + } + + public static boolean elevateMe() + { + WindowsXPProcess me = (WindowsXPProcess) getProcess(currentProcessId()); + System.out.println("elevating command line " + me.getCommand()); + if (PlatformEx.isWinVista() && !me.isElevated()) + { + WindowsXPProcess elevatedMe = new WindowsXPProcess(); + elevatedMe.setCommand(me.getCommand()); + if (me._arrCmd != null) + elevatedMe.setCommand(me._arrCmd); + elevatedMe.setDebug(me._debug); + elevatedMe.setLogger(me._logger); + me.destroy(); + boolean result = elevatedMe.startElevated(); + if (result) + { + elevatedMe.waitFor(); + elevatedMe.destroy(); + return true; + } + } + return false; + } + + HWND lastActiveWindow = null; + + private boolean isActiveWindow(HWND wnd) + { + WINDOWINFO pwi = new WINDOWINFO(); + pwi.size(); + if (MyUser32.INSTANCE.GetWindowInfo(wnd, pwi)) + { + pwi.read(); + return pwi.dwWindowStatus == 1; + } + return false; + + } + + public HWND getActiveWindow() + { + if (lastActiveWindow != null && isActiveWindow(lastActiveWindow)) + return lastActiveWindow; + else + { + MyUser32.WNDENUMPROC findActiveWindow = new MyUser32.WNDENUMPROC() + { + + // lParam is the pid of our process + public boolean callback(HWND wnd, int lParam) + { + + // remember first window + //System.out.println("callback "); + // get the pid of the window + IntByReference dwID = new IntByReference(); + MyUser32.INSTANCE.GetWindowThreadProcessId(wnd, dwID); + // if this windows belongs to our process + if (dwID.getValue() == lParam) + { + // if we have no window, try the first one with a name. + if (lastActiveWindow == null) + { + byte[] windowText = new byte[512]; + MyUser32.INSTANCE.GetWindowTextA(wnd, windowText, 512); + String wText = Native.toString(windowText); + System.out.println(wText); + if (!wText.isEmpty()) + lastActiveWindow = wnd; + } + if (isActiveWindow(wnd)) + lastActiveWindow = wnd; + } + // continue with next window + return true; + } + }; + MyUser32.INSTANCE.EnumWindows(findActiveWindow, _pid); + + } + byte[] windowText = new byte[512]; + + + return lastActiveWindow; + + + } + + public void sendKey(final char key) + { + if (_pid <= 0) + return; + if (getActiveWindow() != null) + { + byte[] windowText = new byte[512]; + MyUser32.INSTANCE.GetWindowTextA(lastActiveWindow, windowText, 512); + String wText = Native.toString(windowText); + wText = (wText.isEmpty()) ? "" : "; text: " + wText; + System.out.println("sending key "+key+" to "+wText); + + MyUser32.INSTANCE.SendMessageW(lastActiveWindow, MyUser32.WM_KEYDOWN, key, 0); + MyUser32.INSTANCE.SendMessageW(lastActiveWindow, MyUser32.WM_KEYUP, key, 0); + } + else + System.out.println("no window found"); + + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/ms/win/w32/WindowsXPProcessManager.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/ms/win/w32/WindowsXPProcessManager.java new file mode 100644 index 0000000000..209354954a --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/ms/win/w32/WindowsXPProcessManager.java @@ -0,0 +1,119 @@ +/* This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + *

    + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ +package org.rzo.yajsw.os.ms.win.w32; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import org.rzo.yajsw.os.Process; +import org.rzo.yajsw.os.ProcessManager; +import org.rzo.yajsw.os.TaskList; + +// TODO: Auto-generated Javadoc +/** + * The Class WindowsXPProcessManager. + */ +public class WindowsXPProcessManager implements ProcessManager +{ + + /** The _instance. */ + static ProcessManager _instance; + + /** + * Instance. + * + * @return the process manager + */ + public static ProcessManager instance() + { + if (_instance == null) + _instance = new WindowsXPProcessManager(); + return _instance; + } + + /* + * (non-Javadoc) + * + * @see org.rzo.yajsw.os.ProcessManager#createProcess() + */ + public Process createProcess() + { + return new WindowsXPProcess(); + } + + /* + * (non-Javadoc) + * + * @see org.rzo.yajsw.os.ProcessManager#getProcess(int) + */ + public Process getProcess(int pid) + { + return WindowsXPProcess.getProcess(pid); + } + + /* + * (non-Javadoc) + * + * @see org.rzo.yajsw.os.ProcessManager#currentProcessId() + */ + public int currentProcessId() + { + return WindowsXPProcess.currentProcessId(); + } + + /* + * (non-Javadoc) + * + * @see org.rzo.yajsw.os.ProcessManager#processIdOfActiveWindow() + */ + public int processIdOfActiveWindow() + { + return WindowsXPProcess.processIdOfActiveWindow(); + } + + /* + * (non-Javadoc) + * + * @see org.rzo.yajsw.os.ProcessManager#getProcessTree(int) + */ + public List getProcessTree(int pid) + { + return WindowsXPProcess.getProcessTree(pid); + } + + /* + * (non-Javadoc) + * + * @see org.rzo.yajsw.os.ProcessManager#taskListInstance() + */ + public TaskList taskListInstance() + { + return WindowsXPTaskList.instance(); + } + + public List getProcessIds() + { + Map[] maps = WindowsXPProcess.getProcessMaps(0); + if (maps[0].size() > 0) + { + System.out.println("getids " + maps[0].keySet().size()); + return new ArrayList(maps[0].keySet()); + } + else + { + System.out.println("getids " + maps[1].keySet().size()); + return new ArrayList(maps[1].keySet()); + } + + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/ms/win/w32/WindowsXPService.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/ms/win/w32/WindowsXPService.java new file mode 100644 index 0000000000..dc54fb2b19 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/ms/win/w32/WindowsXPService.java @@ -0,0 +1,203 @@ +package org.rzo.yajsw.os.ms.win.w32; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import jnacontrib.jna.Advapi32; +import jnacontrib.jna.Advapi32.ENUM_SERVICE_STATUS_PROCESS; +import jnacontrib.jna.Advapi32.SC_ACTION; +import jnacontrib.jna.Advapi32.SERVICE_FAILURE_ACTIONS; +import jnacontrib.win32.Win32Service; + +import org.apache.commons.configuration.Configuration; +import org.rzo.yajsw.os.AbstractService; +import org.rzo.yajsw.os.Service; +import org.rzo.yajsw.os.ServiceInfo; + +public class WindowsXPService extends AbstractService +{ + + class MyWin32Service extends Win32Service + { + + public MyWin32Service(String name) + { + super(name); + } + + @Override + public void onStart() + { + } + + @Override + public void onStop() + { + } + + @Override + public void log(String txt) + { + if (_debug) + System.out.println(txt); + } + + } + + MyWin32Service _service; + + public boolean install() + { + String command = ""; + for (int i = 0; i < _command.length; i++) + if (_command[i].startsWith("\"")) + command += _command[i] + " "; + else + command += '"' + _command[i] + "\" "; + + return _service.install(_displayName, _description, _dependencies, _account, _password, command, _startType, _interactive, _failureActions); + } + + public boolean start() + { + return _service.start(); + } + + public boolean stop() + { + try + { + return _service.stop(); + } + catch (Exception e) + { + e.printStackTrace(); + return false; + } + } + + // TODO add further data from Info + protected static Service getService(String name) + { + WindowsXPService result = new WindowsXPService(); + result.setName(name); + result.init(); + return result; + } + + public void init() + { + if (_service == null) + { + _service = new MyWin32Service(_name); + } + if (_config != null && _config.getBoolean("wrapper.ntservice.interactive", false)) + _interactive = true; + } + + public boolean uninstall() + { + return _service.uninstall(); + } + + public int state() + { + if (_service == null) + return STATE_UNKNOWN; + return _service.state(); + + } + + public static Map getServiceList() + { + Map result = new HashMap(); + Map services = Win32Service.enumerateServices(null); + for (String name : services.keySet()) + { + result.put(name, getServiceInfo(name)); + } + return result; + } + + public static ServiceInfo getServiceInfo(String name) + { + return Win32Service.serviceInfo(name); + } + + public boolean requestElevation() + { + if (_service != null) + return _service.requestElevation(); + return false; + } + + public static Object getServiceFailureActions(Configuration config) + { + String cmd = config.getString("wrapper.ntservice.failure_actions.command", null); + List actions = config.getList("wrapper.ntservice.failure_actions.actions", null); + List actionsDelay = config.getList("wrapper.ntservice.failure_actions.actions_delay", new ArrayList()); + if (actions == null) + return null; + + SC_ACTION[] scActions = (SC_ACTION[]) new SC_ACTION().toArray(actions.size()); + int i = 0; + int lastDelay = 0; + for (Object action : actions) + { + //scActions[i] = new SC_ACTION(); + if ("NONE".equals(action)) + { + scActions[i].Type = Advapi32.SC_ACTION_NONE; + } + else if ("REBOOT".equals(action)) + { + scActions[i].Type = Advapi32.SC_ACTION_REBOOT; + } + else if ("RESTART".equals(action)) + { + scActions[i].Type = Advapi32.SC_ACTION_RESTART; + } + else if ("COMMAND".equals(action)) + { + scActions[i].Type = Advapi32.SC_ACTION_RUN_COMMAND; + } + else + { + System.out.println("ERROR: unknown failure action : " + action); + System.out.println("Aborting setting failure actions"); + return null; + } + if (actionsDelay.size() > i) + try + { + Object d = actionsDelay.get(i); + lastDelay = Integer.parseInt((String) d); + } + catch (Exception ex) + { + System.out.println("Error: failure actions delay is not a number."); + } + scActions[i].Delay = lastDelay; + i++; + } + + SERVICE_FAILURE_ACTIONS result = new SERVICE_FAILURE_ACTIONS(); + result.dwResetPeriod = config.getInt("wrapper.ntservice.failure_actions.reset_period", 0); + result.lpCommand = config.getString("wrapper.ntservice.failure_actions.command", ""); + result.lpRebootMsg = config.getString("wrapper.ntservice.failure_actions.reboot_msg", null); + result.cActions = scActions.length; + scActions[0].autoWrite(); + result.lpsaActions = scActions[0].getPointer(); + result.write(); +// for (int z = 0; z getServiceList() + { + return WindowsXPService.getServiceList(); + } + + public ServiceInfo getServiceInfo(String name) + { + // TODO Auto-generated method stub + return null; + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/ms/win/w32/WindowsXPSystemInformation.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/ms/win/w32/WindowsXPSystemInformation.java new file mode 100644 index 0000000000..feab311353 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/ms/win/w32/WindowsXPSystemInformation.java @@ -0,0 +1,70 @@ +package org.rzo.yajsw.os.ms.win.w32; + +import java.util.logging.Logger; + +import org.rzo.yajsw.os.SystemInformation; + +import com.sun.jna.platform.win32.Kernel32; +import com.sun.jna.platform.win32.WinBase; +import com.sun.jna.platform.win32.WinDef.DWORD; + +public class WindowsXPSystemInformation implements SystemInformation +{ + + public Logger _logger; + private long _freeRAM = 0; + private long _totalRAM = 0; + private long _lastCall = 0; + + private void calc() + { + if (System.currentTimeMillis()- _lastCall < 500) + return; + WinBase.MEMORYSTATUSEX lpBuffer = new WinBase.MEMORYSTATUSEX(); + lpBuffer.dwLength = new DWORD(lpBuffer.size()); + if (Kernel32.INSTANCE.GlobalMemoryStatusEx(lpBuffer)) + { + lpBuffer.read(); + _freeRAM = lpBuffer.ullAvailPhys.longValue(); + _totalRAM = lpBuffer.ullTotalPhys.longValue(); + _lastCall = System.currentTimeMillis(); + } + else + { + if (_logger != null) + _logger.severe("ERROR: could not read free/total RAM"); + else + System.out.println("ERROR: could not read free/total RAM"); + } + + } + + + public long freeRAM() + { + calc(); + return _freeRAM; + } + + public long totalRAM() + { + calc(); + return _totalRAM; + } + + public void setLogger(Logger logger) + { + _logger = logger; + } + + public static void main(String[] args) + { + while (true) + { + System.out.println(new WindowsXPSystemInformation().totalRAM()); + System.out.println(new WindowsXPSystemInformation().freeRAM()); + } + + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/ms/win/w32/WindowsXPTaskList.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/ms/win/w32/WindowsXPTaskList.java new file mode 100644 index 0000000000..d96b69414b --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/ms/win/w32/WindowsXPTaskList.java @@ -0,0 +1,228 @@ +/* This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + *

    + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ +package org.rzo.yajsw.os.ms.win.w32; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.rzo.yajsw.os.TaskList; + +// TODO: Auto-generated Javadoc +/** + * The Class WindowsXPTaskList. + */ +public class WindowsXPTaskList implements TaskList +{ + + /** The _listners. */ + List _listners = new ArrayList(); + + /** The _maps. */ + Map[] _maps; + + /** The _maps lock. */ + Map _mapsLock = new HashMap(); + + /** The _instance. */ + static WindowsXPTaskList _instance; + + /** The _worker. */ + Worker _worker = new Worker(); + + /** + * Instance. + * + * @return the windows xp task list + */ + public static WindowsXPTaskList instance() + { + if (_instance == null) + { + _instance = new WindowsXPTaskList(); + } + return _instance; + } + + /** + * Instantiates a new windows xp task list. + */ + private WindowsXPTaskList() + { + synchronized (_mapsLock) + { + _maps = WindowsXPProcess.getProcessMaps(0); + } + + new Thread(_worker).start(); + } + + /* + * (non-Javadoc) + * + * @seeorg.rzo.yajsw.os.TaskList#addListner(org.rzo.yajsw.os.TaskList. + * TaskListListner) + */ + public synchronized void addListner(TaskListListner listner) + { + _listners.add(listner); + } + + /* + * (non-Javadoc) + * + * @seeorg.rzo.yajsw.os.TaskList#removeListner(org.rzo.yajsw.os.TaskList. + * TaskListListner) + */ + public synchronized void removeListner(TaskListListner listner) + { + _listners.remove(listner); + } + + /** + * The Class TaskListEvent. + */ + public class TaskListEvent implements org.rzo.yajsw.os.TaskList.TaskListEvent + { + + /** The _new tasks. */ + Collection _newTasks; + + /** The _removed tasks. */ + Collection _removedTasks; + + /** The _current tasks. */ + Collection _currentTasks; + + /** + * Instantiates a new task list event. + * + * @param newTasks + * the new tasks + * @param removedTasks + * the removed tasks + * @param currentTasks + * the current tasks + */ + TaskListEvent(Collection newTasks, Collection removedTasks, Collection currentTasks) + { + _newTasks = newTasks; + _removedTasks = removedTasks; + _currentTasks = currentTasks; + } + + /* + * (non-Javadoc) + * + * @see org.rzo.yajsw.os.TaskList.TaskListEvent#getNewTasks() + */ + public Collection getNewTasks() + { + return _newTasks; + } + + /* + * (non-Javadoc) + * + * @see org.rzo.yajsw.os.TaskList.TaskListEvent#getRemovedTasks() + */ + public Collection getRemovedTasks() + { + return _removedTasks; + } + + /* + * (non-Javadoc) + * + * @see org.rzo.yajsw.os.TaskList.TaskListEvent#getCurrentTasks() + */ + public Collection getCurrentTasks() + { + return _currentTasks; + } + } + + /* + * (non-Javadoc) + * + * @see org.rzo.yajsw.os.TaskList#taskList() + */ + public Map taskList() + { + synchronized (_mapsLock) + { + if (_maps == null) + return null; + else + return new HashMap(_maps[0]); + } + } + + /** + * The Class Worker. + */ + class Worker implements Runnable + { + + /* + * (non-Javadoc) + * + * @see java.lang.Runnable#run() + */ + public void run() + { + while (true) + { + try + { + Thread.sleep(250); + } + catch (InterruptedException e) + { + e.printStackTrace(); + } + synchronized (_mapsLock) + { + synchronized (_listners) + { + Map[] maps2 = WindowsXPProcess.getProcessMaps(0); + Set newSet = new HashSet(maps2[0].keySet()); + newSet.removeAll(_maps[0].keySet()); + Set deleteSet = new HashSet(_maps[0].keySet()); + deleteSet.removeAll(maps2[0].keySet()); + _maps = maps2; + if ((newSet.size() > 0 || deleteSet.size() > 0) && _listners.size() > 0) + { + TaskListEvent event = new TaskListEvent(newSet, deleteSet, _maps[0].keySet()); + for (Iterator it = _listners.iterator(); it.hasNext();) + try + { + ((TaskListListner) it.next()).changed(event); + } + catch (Exception ex) + { + ex.printStackTrace(); + } + } + } + } + + } + + } + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/ms/win/w32/package.html b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/ms/win/w32/package.html new file mode 100644 index 0000000000..53486fbbb7 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/ms/win/w32/package.html @@ -0,0 +1,7 @@ + + + + + Provides... + + \ No newline at end of file diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/package.html b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/package.html new file mode 100644 index 0000000000..53486fbbb7 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/package.html @@ -0,0 +1,7 @@ + + + + + Provides... + + \ No newline at end of file diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/OperatingSystemPosix.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/OperatingSystemPosix.java new file mode 100644 index 0000000000..ea29d8fa0f --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/OperatingSystemPosix.java @@ -0,0 +1,30 @@ +package org.rzo.yajsw.os.posix; + +import org.apache.commons.configuration.Configuration; +import org.rzo.yajsw.os.Mouse; +import org.rzo.yajsw.os.OperatingSystem; + +public abstract class OperatingSystemPosix extends OperatingSystem +{ + @Override + public boolean setWorkingDir(String name) + { + return new PosixProcess().changeWorkingDir(name); + } + @Override + public Mouse mouseInstance() + { + return null;//PosixMouse.instance(); + } + + @Override + public Object getServiceFailureActions(Configuration config) + { + return null; + } + + + + + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/PosixErrorHandler.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/PosixErrorHandler.java new file mode 100644 index 0000000000..0c0dc2b939 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/PosixErrorHandler.java @@ -0,0 +1,21 @@ +package org.rzo.yajsw.os.posix; + +import org.rzo.yajsw.os.ErrorHandler; +import org.rzo.yajsw.os.OsException; + +public class PosixErrorHandler implements ErrorHandler +{ + + public void throwException(int id) throws OsException + { + // TODO Auto-generated method stub + + } + + public String toString(int id) + { + // TODO Auto-generated method stub + return null; + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/PosixFileManager.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/PosixFileManager.java new file mode 100644 index 0000000000..9d284badab --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/PosixFileManager.java @@ -0,0 +1,35 @@ +package org.rzo.yajsw.os.posix; + +import org.rzo.yajsw.os.FileManager; +import org.rzo.yajsw.util.File; + +public class PosixFileManager implements FileManager +{ + private static FileManager _instance; + + public static synchronized FileManager instance() + { + if (_instance == null) + _instance = new PosixFileManager(); + return _instance; + } + + public long created(File file) + { + // TODO Auto-generated method stub + return -1; + } + + public long freeSpace(File file) + { + // TODO Auto-generated method stub + return -1; + } + + public long totalSpace(File file) + { + // TODO Auto-generated method stub + return -1; + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/PosixJavaHome.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/PosixJavaHome.java new file mode 100644 index 0000000000..ea92be1320 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/PosixJavaHome.java @@ -0,0 +1,146 @@ +package org.rzo.yajsw.os.posix; + +import java.io.File; + +import org.apache.commons.configuration.BaseConfiguration; +import org.apache.commons.configuration.Configuration; +import org.jboss.netty.logging.InternalLogger; +import org.rzo.yajsw.os.JavaHome; + +public class PosixJavaHome implements JavaHome +{ + Configuration _config; + InternalLogger _logger; + + public PosixJavaHome(Configuration config) + { + if (config != null) + _config = config; + else + _config = new BaseConfiguration(); + } + + public String findJava( String wrapperJava, String customProcessName ) + { + File customProc = null; + File wrapJava = null; + + // Search for JAVA if necessary ( nothing supplied ) + if ( wrapperJava == null && customProcessName == null ) + return findJava(); + + customProc = ((customProcessName != null) ? new File( customProcessName ) : null); + wrapJava = ((wrapperJava != null) ? new File( wrapperJava ) : null); + + // customProcessName takes precedences over wrapperJava + if ( customProc != null && customProc.exists() && customProc.canExecute() ){ + return customProcessName; + } + else if ( wrapJava != null && wrapJava.exists() && wrapJava.canExecute() ){ + return wrapperJava; + } + else + return findJava(); + // -old return wrapperJava == null ? "java" : wrapperJava; + } + + private String findJava() + { + // Posix Version does not use wrapper.java.command like Win version does. ( whatever ) + // Find working java and equate to both + File fJava = null; + String java = null; + + // Find Path to Regular Java + String javaFiles[] = new String[3]; + javaFiles[0] = _config.getString( "wrapper.java.command" ); + javaFiles[1] = _config.getString( "wrapper.ntservice.java.command" ); + javaFiles[2] = "java"; + + for ( int idx = 0; (fJava == null && idx < javaFiles.length); idx++ ) + { + String javaName; + for ( int loop = 0; loop < 2; loop++ ) + { + if ( javaFiles[idx] != null ) + { + javaName = ((loop == 0) ? javaFiles[idx] : System.setProperty( "JAVA_HOME", "" ) + File.separator + "bin" + + File.separator + javaFiles[idx]); + File fJavaTmp = new File( javaName ); + if ( fJavaTmp.exists() && fJavaTmp.canExecute() ) + { + fJava = fJavaTmp; + break; + } + } + } + } + + // if Regular java not found.... Search Path for JAVA's HOME + if ( fJava == null ) + { + // Check path for JAVA's HOME + String home = findJavaHomeFromPath( null ); + if ( home != null ) + { + String javaName; + javaName = home + File.separator + "bin" + File.separator + "java"; + File fJavaTmp = new File( javaName ); + if ( fJavaTmp.exists() && fJavaTmp.canExecute() ) + { + fJava = fJavaTmp; + } + } + } + + // if Regular java still not found.... bummer were done + if ( fJava != null ) + { + java = fJava.getAbsolutePath(); + + // Posix Version does not use wrapper.java.command like Win version does. Update both + _config.setProperty( "wrapper.java.command", java ); + _config.setProperty( "wrapper.ntservice.java.command", java ); + } + + if (java == null) + java = _config.getString( "wrapper.java.command", "java" ); + + return java; + } + + // Searches Environment Path for JAVA_HOME equivalent + private String findJavaHomeFromPath( String javaHome ) + { + if ( javaHome != null ) + { + File fJavaHome = new File( javaHome ); + if ( fJavaHome.exists() ) + return javaHome; + } + + // search java in environment path + if (System.getenv( "path" ) == null) + return null; + String[] paths = System.getenv( "path" ).split( File.pathSeparator ); + for ( String path : paths ) + { + if ( path.contains( "jdk" ) || path.contains( "jre" ) ) + { + File fJavaHome = new File( path + File.separator + "java" ); + if ( fJavaHome.exists() ) + { + return fJavaHome.getParentFile().getParentFile().getAbsolutePath(); + } + } + } + + return null; + } + + public void setLogger(InternalLogger logger) + { + _logger = logger; + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/PosixKeyboard.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/PosixKeyboard.java new file mode 100644 index 0000000000..9a49294724 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/PosixKeyboard.java @@ -0,0 +1,14 @@ +package org.rzo.yajsw.os.posix; + +import org.rzo.yajsw.os.Keyboard; + +public class PosixKeyboard +{ + + public static Keyboard instance() + { + // TODO Auto-generated method stub + return null; + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/PosixMouse.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/PosixMouse.java new file mode 100644 index 0000000000..5f5e93c42e --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/PosixMouse.java @@ -0,0 +1,115 @@ +package org.rzo.yajsw.os.posix; + +import java.util.concurrent.Executor; + +import org.rzo.yajsw.os.Mouse; + +import com.sun.jna.NativeLong; +import com.sun.jna.platform.unix.X11; +import com.sun.jna.platform.unix.X11.XEvent; + +public class PosixMouse implements Mouse +{ + //static final Display display = X11.INSTANCE.XOpenDisplay(null); + static final XEvent xevent = new XEvent(); + static boolean _registered = false; + static Thread thread; + static boolean stop; + + public interface Xlib extends X11 { + + int XGrabKey(Display display, int keycode, NativeLong modifiers, Window grab_window, boolean owner_events, int pointer_mode, int keyboard_mode); + /* + Display *display; + int keycode; + unsigned int modifiers; + Window grab_window; + Bool owner_events; + int pointer_mode, keyboard_mode; + */ + int XGrabButton(Display display, NativeLong button, NativeLong modifiers, Window grab_window, boolean owner_events, NativeLong event_mask, + int pointer_mode, int keyboard_mode, Window confine_to, Cursor cursor); + /* + Display *display; + unsigned int button; + unsigned int modifiers; + Window grab_window; + Bool owner_events; + unsigned int event_mask; + int pointer_mode, keyboard_mode; + Window confine_to; + Cursor cursor; + + + int XGrabPointer(display, grab_window, owner_events, event_mask, pointer_mode, + keyboard_mode, confine_to, cursor, time) + Display *display; + Window grab_window; + Bool owner_events; + unsigned int event_mask; + int pointer_mode, keyboard_mode; + Window confine_to; + Cursor cursor; + Time time; + */ + } + + + private static Mouse instance; + + public static Mouse instance() + { + System.out.println("posix mouse"); + if (instance == null) + instance = new PosixMouse(); + return instance; + } + + public void registerMouseUpListner(final Runnable listner, Executor executor) + { + /* + if (!stop) + return; + stop = false; + thread = new Thread(new Runnable() + { + public void run() + { + System.out.println("start mouse listener "); + try + { + while (!isStop()) + { + int r = X11.INSTANCE.XNextEvent(display, xevent); + System.out.println("xnextevent "+r); + if (xevent.type == X11.ButtonRelease && !stop) + listner.run(); + } + } + catch (Exception ex) + { + ex.printStackTrace(); + } + System.out.println("end mouse listener "); + } + + }); + thread.start(); + */ + } + + private boolean isStop() + { + return stop; + } + + public void unregisterMouseUpListner() + { + /* + stop = true; + if (thread != null) + thread.interrupt(); + */ + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/PosixProcess.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/PosixProcess.java new file mode 100644 index 0000000000..89ee2b1769 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/PosixProcess.java @@ -0,0 +1,1726 @@ +package org.rzo.yajsw.os.posix; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileDescriptor; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; +import java.util.logging.Logger; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.rzo.yajsw.io.CyclicBufferFileInputStream; +import org.rzo.yajsw.io.CyclicBufferFilePrintStream; +import org.rzo.yajsw.os.AbstractProcess; +import org.rzo.yajsw.os.OperatingSystem; +import org.rzo.yajsw.os.Process; +import org.rzo.yajsw.util.DaemonThreadFactory; + +import com.sun.jna.Library; +import com.sun.jna.Memory; +import com.sun.jna.Native; +import com.sun.jna.NativeLibrary; +import com.sun.jna.Platform; +import com.sun.jna.Pointer; +import com.sun.jna.Structure; +import com.sun.jna.ptr.IntByReference; + +public class PosixProcess extends AbstractProcess +{ + protected int[] _inPipe = new int[2]; + protected int[] _outPipe = new int[2]; + protected int[] _errPipe = new int[2]; + public IntByReference status = new IntByReference(); + int _exitCodeKill = -1; + + protected static final Executor executor = Executors.newCachedThreadPool(new DaemonThreadFactory("posix.process.terminate")); + protected boolean lock = true; + volatile protected boolean _terminated = false; + protected Utils _utils = new Utils(); + boolean _stopWaiter = false; + String[] _env = null; + int stdout = -1;// should not be called in all sub classes // getStdOutNo(); + int stderr = -1; //getStdErrNo();// CLibrary.INSTANCE.fileno(NativeLibrary.getInstance("c").getFunction(getStdErrName()).getPointer(0)); + int stdin = -1; // getStdInNo();// CLibrary.INSTANCE.fileno(NativeLibrary.getInstance("c").getFunction(getStdInName()).getPointer(0)); + + + public interface CLibrary extends Library + { + + CLibrary INSTANCE = (CLibrary) Native.loadLibrary(Platform.isLinux() ? "libc.so.6" : "c", CLibrary.class); + + int fork(); + + void exit(int status); + + String strerror(int errnum); + + /* + * int readlink (const char *filename, char *buffer, size_t size) + */ + short readlink(String filename, Memory buffer, short size); + + /* + * int execv (const charfilename, charconst argv[]) + */ + int execvp(String filename, String[] argv); + + /* + * execve (const char *filename, char *const argv[], char *const env[]) + */ + int execve(String path, String[] argv, String[] envp); + + /* + * int pipe (int filedes[2]) + */ + int pipe(int filedes[]); + + /* + * int dup2(int oldfd, int newfd) + */ + int dup2(int oldfd, int newfd); + + /* + * int close(int fd) + */ + int close(Pointer fd); + + int close(int fd); + + /* + * mode_t umask (mode_t mask) + */ + void umask(int mask); + + int setsid(); + + /* + * FILE freopen ( const char filename, const char mode, FILE stream ); + */ + Pointer freopen(String filename, String mode, int stream); + + /* + * int kill (pid_t pid, int signum) + */ + int kill(int pid, int signum); + + static final int SIGTERM = 15; + static final int SIGKILL = 9; + + /* + * pid_t waitpid(pid_t pid, intstat_loc, int options); + */ + int waitpid(int pid, IntByReference stat_loc, int options); + + static final int ESRCH = 3; + + /* + * int chdir(const charpath); + */ + int chdir(String path); + + Pointer getcwd( Memory buffer, short size ); + + static final int WNOHANG = 1; /* don't hang in wait */ + static final int WUNTRACED = 2; /* + * tell about stopped, untraced children + */ + + /* + * int fputc (int c, FILEstream) + */ + int fputc(int c, Pointer stream); + + /* + * FILEfdopen(int fildes, const chartype); + */ + Pointer fdopen(Pointer fildes, String type); + + /* + * int fileno(FILEstream); + */ + int fileno(Pointer stream); + + /* + * struct dirent64 { __u64 d_ino; __s64 d_off; unsigned short d_reclen; + * unsigned char d_type; char d_name[256]; }; + */ + class dirent64 extends Structure + { + public long d_ino; + public long d_off; + public short d_reclen; + public char d_type; + public char[] d_name = new char[256]; + + public String getName() + { + return getPointer().getString(8 + 8 + 2 + 1, false); + } + }; + + /* + * struct dirent { long d_ino; off_t d_off; unsigned short d_reclen; + * char d_name[NAME_MAX+1]; }; + */ + class dirent extends Structure + { + public int d_ino; + public int d_off; + public short d_reclen; + public String d_name; + }; + + /* + * DIR opendir (const chardirname) + */ + Pointer opendir(String dirname); + + /* + * struct dirent64 readdir64 (DIRdirstream) + */ + dirent64 readdir64(Pointer dirstream); + + /* + * int closedir (DIRdirstream) + */ + int closedir(Pointer dirstream); + + /* + * int nice (int increment) + */ + int nice(int increment); + + /* + * int sched_setaffinity (pid_t pid, size_t cpusetsize, const cpu_set_t + * cpuset) + */ + int sched_setaffinity(int pid, int cpusetsize, IntByReference cpuset); + + /* + * pid_t getpid(void); + */ + int getpid(); + + /* + * int symlink (const charoldname, const charnewname) + */ + int symlink(String oldname, String newname); + + /* + * struct passwd + * + * The passwd data structure is used to hold information about entries + * in the system user data base. It has at least the following members: + * + * charpw_name The user's login name. charpw_passwd. The encrypted + * password string. uid_t pw_uid The user ID number. gid_t pw_gid The + * user's default group ID number. charpw_gecos A string typically + * containing the user's real name, and possibly other information such + * as a phone number. charpw_dir The user's home directory, or initial + * working directory. This might be a null pointer, in which case the + * interpretation is system-dependent. charpw_shell The user's default + * shell, or the initial program run when the user logs in. This might + * be a null pointer, indicating that the system default should be used. + */ + + public static class passwd extends Structure + { + public passwd(Pointer p) + { + super(); + if (p != null) + { + this.useMemory(p); + this.read(); + } + } + + public String pw_name; + public String pw_passwd; + public int pw_uid; + public int pw_gid; + public String pw_gecos; + public String pw_dir; + public String pw_shell; + + public String getName() + { + return pw_name; + } + + public int getUid() + { + return pw_uid; + } + + public int getGid() + { + return pw_gid; + } + } + + /* + * struct passwd getpwnam (const charname) This function returns a + * pointer to a statically-allocated structure containing information + * about the user whose user name is name. This structure may be + * overwritten on subsequent calls to getpwnam. + * + * A null pointer return indicates there is no user named name. + */ + Pointer getpwnam(String name); + + /* + * uid_t geteuid (void) + * + * The geteuid function returns the effective user ID of the process. + */ + int geteuid(); + + /* + * struct passwd getpwuid (uid_t uid) + * + * This function returns a pointer to a statically-allocated structure + * containing information about the user whose user ID is uid. This + * structure may be overwritten on subsequent calls to getpwuid. + * + * A null pointer value indicates there is no user in the data base with + * user ID uid. + */ + + Pointer getpwuid(int uid); + + /* + * int setreuid (uid_t ruid, uid_t euid) + */ + int setreuid(int ruid, int euid); + + /* + * struct group * getgrgid (gid_t gid) This function returns a pointer + * to a statically-allocated structure containing information about the + * group whose group ID is gid. This structure may be overwritten by + * subsequent calls to getgrgid. A null pointer indicates there is no + * group with ID gid. + */ + Pointer getgrgid(int gid); + + /* + * gid_t getegid (void) The getegid function returns the effective group + * ID of the process. + */ + int getegid(); + + /* + * struct group The group structure is used to hold information about an + * entry in the system group database. It has at least the following + * members: char *gr_name - The name of the group. gid_t gr_gid - The + * group ID of the group. char **gr_mem - A vector of pointers to the + * names of users in the group. Each user name is a null-terminated + * string, and the vector itself is terminated by a null pointer. + */ + public static class group extends Structure + { + public group(Pointer p) + { + super(); + if (p != null) + { + this.useMemory(p); + this.read(); + } + // for (int i = 0; i= 0) + return _exitCodeKill; + return _exitCode; + + } + + public boolean kill(int code) + { + /* + * bkowal + * Suppress extraneous output unless debug is enabled. + */ + if (_logger != null && _debug ) + _logger.info("killing " + _pid); + int count = 0; + while (_exitCode < 0 && count < 3) + { + count++; + if (_logger != null) + _logger.info("send kill sig"); + int r = CLibrary.INSTANCE.kill(_pid, CLibrary.SIGKILL); + if (r == 0) + { + _exitCodeKill = code; + return true; + } + else + { + if (_logger != null) + _logger.fine("error calling kill: " + r); + } + if (_exitCode < 0) + try + { + Thread.sleep(100); + } + catch (InterruptedException e) + { + e.printStackTrace(); + Thread.currentThread().interrupt(); + } + } + return false; + } + + public boolean killTree(int code) + { + // TODO Auto-generated method stub + return false; + } + + public boolean start() + { + // log(">> env 1 " +_environment.size()); + + if (_arrCmd == null && _cmd == null) + return false; + if (_arrCmd == null) + { + _arrCmd = _cmd.split(" "); + /* + * bkowal + * Suppress extraneous output unless debug is enabled. + */ + if (_debug) + { + log("exec: " + _cmd); + } + } + else + { + String cmd = ""; + for (String c : _arrCmd) + { + if (c != null) + cmd += c + " "; + } + /* + * bkowal + * Suppress extraneous output unless debug is enabled. + */ + if (_debug) + { + log("exec:" + cmd); + } + } + // + if (stdout == -1) + { + stdout = getStdOutNo(); + stderr = getStdErrNo(); + stdin = getStdInNo(); + } + + + if (_environment.size() > 0) + { + _env = new String[_environment.size()]; + int i = 0; + for (String[] entry : _environment) + _env[i++] = entry[0] + "=" + entry[1]; + } + else + _env = null; + + int pid = 0; + _exitCode = -2; + String title = _title == null ? "yajsw" : _title; + _terminated = false; + if (_visible) + setCommand(String.format("xterm -hold -sb -T %1$s -e %2$s", title, getCommand())); + + // System.out.println("exec \n"+getCommand()); + // System.out.println("working dir\n"+getWorkingDir()); + + if (_visible) + _pipeStreams = false; + + // if (_pipeStreams) + { + CLibrary.INSTANCE.pipe(_inPipe); + CLibrary.INSTANCE.pipe(_outPipe); + CLibrary.INSTANCE.pipe(_errPipe); + // System.out.println(_outPipe[0]+" "+_outPipe[1]); + } + + String forkLogName = "forkLog" + System.currentTimeMillis() + ".log"; + + // fork a child process + if ((pid = CLibrary.INSTANCE.fork()) == 0) + { + /* + * bkowal + * Suppress extraneous output unless debug is enabled. + */ + if (_debug) + { + System.out.println("fork 0"); + } + + // closeDescriptors(); + + // set working dir + if (getWorkingDir() != null) + if (CLibrary.INSTANCE.chdir(getWorkingDir()) != 0) + log("could not set working dir"); + + /* + * bkowal + * Suppress extraneous output unless debug is enabled. + */ + if (_debug) + { + System.out.println("fork 1"); + } + + // set priority + if (_priority == PRIORITY_BELOW_NORMAL) + { + if (CLibrary.INSTANCE.nice(1) == -1) + log("could not set priority "); + } + else if (_priority == PRIORITY_LOW) + { + if (CLibrary.INSTANCE.nice(2) == -1) + log("could not set priority "); + } + else if (_priority == PRIORITY_ABOVE_NORMAL) + { + if (CLibrary.INSTANCE.nice(-1) == -1) + log("could not set priority "); + } + else if (_priority == PRIORITY_HIGH) + { + if (CLibrary.INSTANCE.nice(-2) == -1) + log("could not set priority "); + } + if (getUser() != null) + switchUser(getUser(), getPassword()); + /* + * bkowal + * Suppress extraneous output unless debug is enabled. + */ + if (_debug) + { + System.out.println("fork 2"); + } + + // try + // { + // closeDescriptors(new int[]{ + // stdin, stdout, stderr, _inPipe[1], _inPipe[0], + // _outPipe[0], _outPipe[1], _errPipe[0], + // _errPipe[1] + // }); + // } + // catch (Throwable ex) + // { + // ex.printStackTrace(); + // } + + // pipe streams to OS pipes + if (_pipeStreams) + { + CLibrary.INSTANCE.close(_inPipe[1]); + moveDescriptor(_inPipe[0], stdin); + CLibrary.INSTANCE.close(_outPipe[0]); + moveDescriptor(_outPipe[1], stdout); + CLibrary.INSTANCE.close(_errPipe[0]); + moveDescriptor(_errPipe[1], stderr); + } + + try + { + int res; + + // disconect from parent + CLibrary.INSTANCE.umask(0); + if (CLibrary.INSTANCE.setsid() < 0) + { + CLibrary.INSTANCE.exit(-1); + } + if (_env != null) + { + res = CLibrary.INSTANCE.execve(_arrCmd[0], _arrCmd, _env); + } + else + { + res = CLibrary.INSTANCE.execvp(_arrCmd[0], _arrCmd); + } + int err = Native.getLastError(); + log("error in execv: errno " + err + " " + CLibrary.INSTANCE.strerror(err)); + log("exec res " + res); + + } + catch (Exception ex) + { + ex.printStackTrace(); + } + lock = false; + // CLibrary.INSTANCE.exit(-1); + } // child code + else if (pid > 0) + { + _pid = pid; + try + { + Thread.sleep(500); + } + catch (InterruptedException e1) + { + } + // or pipe streams to cyclic buffer files + if (_teeName != null && _tmpPath != null) + { + // System.out.println("opening tee streams"); + File f = new File(_tmpPath); + try + { + if (!f.exists()) + f.mkdir(); + } + catch (Exception ex) + { + if (_logger != null) + _logger.throwing(PosixProcess.class.getName(), "start", ex); + Thread.currentThread().interrupt(); + } + try + { + // System.out.println("opening tee streams out"); + _inputStream = new CyclicBufferFileInputStream(createRWfile(_tmpPath, "out_" + _teeName)); + } + catch (Exception e) + { + e.printStackTrace(); + } + try + { + // System.out.println("opening tee streams err"); + _errorStream = new CyclicBufferFileInputStream(createRWfile(_tmpPath, "err_" + _teeName)); + } + catch (Exception e) + { + if (_logger != null) + _logger.throwing(PosixProcess.class.getName(), "start", e); + } + try + { + // System.out.println("opening tee streams in"); + _outputStream = new CyclicBufferFilePrintStream(createRWfile(_tmpPath, "in_" + _teeName)); + } + catch (Exception e) + { + if (_logger != null) + _logger.throwing(PosixProcess.class.getName(), "start", e); + } + // System.out.println("- opening tee streams"); + } + /* + * if (!_pipeStreams) { + * System.out.println("setting out streams to /dev/null/"); + * CLibrary.INSTANCE.freopen("/dev/null", "w", _outPipe[0]); + * System.out.println("setting err streams to /dev/null/"); + * CLibrary.INSTANCE.freopen("/dev/null", "w", _errPipe[0]); + * //System.out.println("setting streams to /dev/null/"); + * //CLibrary.INSTANCE.freopen("/dev/null", "r", _inPipe[1]); + * System.out.println("- setting streams to /dev/null/"); } + */ + + // System.out.println("parent"); + if (_pipeStreams && _teeName == null) + { + writefd(in_fd, _inPipe[1]); + writefd(out_fd, _outPipe[0]); + writefd(err_fd, _errPipe[0]); + + _outputStream = new BufferedOutputStream(new FileOutputStream(in_fd)); + _inputStream = new BufferedInputStream(new FileInputStream(out_fd)); + _errorStream = new BufferedInputStream(new FileInputStream(err_fd)); + + CLibrary.INSTANCE.close(_inPipe[0]); + CLibrary.INSTANCE.close(_outPipe[1]); + CLibrary.INSTANCE.close(_errPipe[1]); + + } + if (_cpuAffinity != AFFINITY_UNDEFINED) + { + IntByReference affinity = new IntByReference(); + affinity.setValue(_cpuAffinity); + if (CLibrary.INSTANCE.sched_setaffinity(_pid, 4, affinity) == -1) + log("error setting affinity"); + } + _stopWaiter = true; + executor.execute(new Runnable() + { + + public void run() + { + int r = 0; + while ( r != _pid && r != -1 ) + { + r = CLibrary.INSTANCE.waitpid( _pid, status, 0 ); + /* + * bkowal + * Suppress extraneous output unless debug is enabled. + */ + if ( _logger != null && _debug ) + _logger.info( "waitpid " + r + " " + status.getValue() ); + } + if ( r == _pid ) + { + int code = status.getValue(); + + // Exited Normally + if ( WIFEXITED( code ) != 0 ) + _exitCode = WEXITSTATUS( code ); + // Exited Ab-Normally + else + _exitCode = 0; + } + /* + * bkowal + * Suppress extraneous output unless debug is enabled. + */ + if ( _logger != null && _debug ) + _logger.info( "exit code posix process: " +status.getValue()+" application: "+ _exitCode ); + _terminated = true; + } + + }); + + /* + * bkowal + * Suppress extraneous output unless debug is enabled. + */ + if (_logger != null && _debug) + _logger.info("started process " + _pid); + return true; + } // parent process + else if (pid < 0) + { + if (_logger != null) + _logger.info("failed to fork: " + pid); + return false; + } + return false; + + } + + public int WIFEXITED( int code ) { + return (code & 0xFF); + } + + public int WEXITSTATUS( int code ) { + return ((code >> 8) & 0xFF); + } + + protected File createRWfile( String path, String fname ) throws IOException + { + File result = new File( path, fname ); + result.deleteOnExit(); + /* + * String absPath = result.getAbsolutePath(); System.out.println("PosixProcess.createRWfile "+absPath); if + * (!result.exists()) { result.createNewFile(); } result.deleteOnExit(); String name = result.getCanonicalPath(); + * System.out.println("chmod 777 " + name); //Runtime.getRuntime().exec("chmod 777 " + name); int res = + * CLibrary.INSTANCE.chmod(absPath, 777); if (res != 0) System.out.println("chmod failed "+res); + */ + + return result; + } + + public boolean stop(int timeout, int code) + { + /* + * bkowal + * Suppress extraneous output unless debug is enabled. + */ + if (_logger != null && _debug ) + _logger.info("killing " + _pid); + if (!isRunning()) + return true; + int r = CLibrary.INSTANCE.kill(_pid, CLibrary.SIGTERM); + waitFor(timeout); + int count = 0; + while (isRunning() && count++ < 4) + { + CLibrary.INSTANCE.kill(_pid, CLibrary.SIGKILL); + if (isRunning()) + try + { + Thread.sleep(100); + } + catch (InterruptedException e) + { + if (_logger != null) + _logger.throwing(PosixProcess.class.getName(), "stop", e); + Thread.currentThread().interrupt(); + } + } + return !isRunning(); + } + + protected void moveDescriptor(int fd_from, int fd_to) + { + // System.out.println("move desc "+fd_from+" "+fd_to); + if (fd_from != fd_to) + { + CLibrary.INSTANCE.dup2(fd_from, fd_to); + CLibrary.INSTANCE.close(fd_from); + } + } + + int closeDescriptors(int[] avoid) + { + // Pointer dir; + // CLibrary.dirent64 dirp; + // int from_fd = FAIL_FILENO + 1; + + /* + * We're trying to close all file descriptors, but opendir() might + * itself be implemented using a file descriptor, and we certainly don't + * want to close that while it's in use. We assume that if opendir() is + * implemented using a file descriptor, then it uses the lowest numbered + * file descriptor, just like open(). So we close a couple explicitly. + */ + + // close(from_fd); /* for possible use by opendir() */ + // close(from_fd + 1); /* another one for good luck */ + /* + * if ((dir = CLibrary.INSTANCE.opendir("/proc/self/fd")) == null) { + * //log("error in opendir(/proc/self/fd) "+dir); return 0; } + * + * + * /* We use readdir64 instead of readdir to work around Solaris bug + * 6395699: /proc/self/fd fails to report file descriptors >= 1024 on + * Solaris 9 + */ + /* + * while ((dirp = CLibrary.INSTANCE.readdir64(dir)) != null) try { + * log("readdir64 dir "+dir); dirp.read(); String name = dirp.getName(); + * if (name == null) return 0; if (name.contains(".")) continue; + * log("closing "+name); int fd = Integer.parseInt(name); + * log("closing "+fd); //int r = CLibrary.INSTANCE.close(fd); + * //log("closing "+name+" "+r); } catch (Exception ex){ + * ex.printStackTrace(); } + * + * CLibrary.INSTANCE.closedir(dir); + */ + /* + * File f = new File("/proc/self/fd"); String[] ff = f.list(); f = null; + * int start = 19; //if (start < 0) //start = 0; for (int j = + * ff.length-2; j>=0; j--) { String x = ff[j]; if (x == null || + * "".equals(x)) continue; //CLibrary.stat buf = new CLibrary.stat(); + * //buf.size(); + * + * short BUFSIZE = 512; Memory result = new Memory(BUFSIZE); + * result.clear(); String readLink = null; short size = + * CLibrary.INSTANCE.readlink("/proc/self/fd/"+x, result, + * (short)(BUFSIZE-1)); if (size <= 0) { + * System.out.println("error reading /proc/self/fd/"+x); } else { + * result.setByte((long)size, (byte)0); + * System.out.println(x+" -> "+result.getString(0)); readLink = + * result.getString(0); } + * + * + * try { int xx = Integer.parseInt(x); /* int res = + * CLibrary.INSTANCE.fstat(xx, buf.getPointer()); if (res == 0) { + * buf.read(); System.out.println("mode "+xx+" "+buf.st_mode); if + * (buf.isSocket()) System.out.println("is socket "+xx); } else + * System.out.println("error in fstat "+res+" "+xx); + */ + /* + * boolean remove = true; for (int i=0; i 34) if (readLink != null && (!(xx < 2 || + * readLink.contains("rt.jar") || readLink.contains("wrapper.jar") || + * readLink.contains("jna-") || readLink.contains("jnacontrib") ))) // + * if (readLink != null && (readLink.startsWith("socket:["))) { + * System.out.println("closing "+xx); //CLibrary.INSTANCE.close(xx); } + * else System.out.println("not closing "+xx); //* } catch (Throwable + * ex) { ex.printStackTrace(); } + * + * } + */ + for (int i = 10; i < 54; i++) + CLibrary.INSTANCE.close(i); + for (int i = 56; i < 76; i++) + CLibrary.INSTANCE.close(i); + + return 1; + } + + public void waitFor() + { + waitFor(Long.MAX_VALUE); + } + + public void waitFor(long timeout) + { + long start = System.currentTimeMillis(); + File f = new File("/proc/" + _pid); + + while (System.currentTimeMillis() - start < timeout) + { + if (!isRunning() || !f.exists()) + { + return; + } + try + { + Thread.sleep(100); + } + catch (InterruptedException e) + { + if (_logger != null) + _logger.throwing(PosixProcess.class.getName(), "waitFor", e); + Thread.currentThread().interrupt(); + } + } + + } + + // test + /** + * The main method. + * + * @param args + * the arguments + * @throws IOException + */ + public static void main(String[] args) throws IOException + { + PosixProcess[] p = new PosixProcess[1]; + boolean pipe = true; + for (int i = 0; i < p.length; i++) + { + p[i] = new PosixProcess(); + + // p[i].setPipeStreams(true, false); + // p[i].setCommand("xeyes");// "java -cp yajsw.jar + // org.rzo.yajsw.HelloWorld > + // t.log"); + // p[i].setCommand("/usr/java/jre1.5.0_10/bin/java -classpath ./bin test.HelloWorld"); + p[i] + .setCommand("/usr/java/jre1.5.0_10/bin/java -classpath /home/test/rzodyndns/test/wrapper.jar -Dwrapper.config=/home/test/rzodyndns/test/bat/../conf/wrapper.conf -Dwrapper.port=15003 -Dwrapper.key=6566092584194115879 -Dwrapper.teeName=6566092584194115879$1225016378236 -Dwrapper.tmpPath=/tmp org.rzo.yajsw.app.WrapperJVMMain"); + // p[i].setWorkingDir("/home/test/rzodyndns/test/bat/."); + p[i].setVisible(false); + // p[i].setPriority(PRIORITY_BELOW_NORMAL); + // p[i].setCpuAffinity(1); + + p[i].setPipeStreams(pipe, false); + } + boolean doit = true; + while (doit) + { + doit = false; + // System.out.println("START"); + // doit = false; + for (int i = 0; i < p.length; i++) + { + + p[i].start(); + // p[i].getPid(); + // Runtime.getRuntime().exec(p[i].getCommand()); + // System.out.println("started"); + // for (int j=0; i<10000; j++) + // { + // System.out.println("b"+j); + // try + // { + // Thread.sleep(00); + // } + // catch (InterruptedException e) + // { + // // TODO Auto-generated catch block + // e.printStackTrace(); + // } + // } + // return; + try + { + Thread.yield(); + Thread.sleep(1000); + } + catch (InterruptedException e1) + { + // TODO Auto-generated catch block + e1.printStackTrace(); + } + if (pipe) + { + InputStreamReader isr = new InputStreamReader(p[i].getInputStream()); + // System.out.println("in stream " + p[i].getInputStream() + + // " " + p[i].getInputStream().available()); + + BufferedReader br = new BufferedReader(isr); + String line = "?"; + int k = 0; + try + { + + while (k < 10 && (line = br.readLine()) != null) + { + System.out.println(line); + k++; + } + + } + catch (Exception e) + { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + } + p[0].waitFor(1000); + // System.out.println("exit code "+p[0].getExitCode()); + System.out.println("KILL"); + + for (int i = 0; i < p.length; i++) + { + // System.out.println(p[i].isRunning()); + p[i].kill(999); + System.out.println("exit code " + p[i].getExitCode()); + // System.out.println(p[i].isRunning()); + // p[i].destory(); + } + try + { + Thread.sleep(1000); + } + catch (InterruptedException e) + { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + // p.setCommand("java -classpath z:\dev\yajsw\wrapper.jar org.rzo." ) + } + + /** + * Writefd. + * + * @param fd + * the fd + * @param pointer + * the pointer + */ + protected void writefd(FileDescriptor fd, int pointer) + { + try + { + // Field[] fields = FileDescriptor.class.getDeclaredFields(); + // System.out.println("fields"); + // for (Field field : fields){ + // System.out.println(field.getName()); + // } + // System.out.println("writefd"); + Field handleField = FileDescriptor.class.getDeclaredField("fd"); + handleField.setAccessible(true); + Field peerField = Pointer.class.getDeclaredField("peer"); + peerField.setAccessible(true); + long value = pointer;// peerField.getLong(pointer); + // System.out.println(value); + // System.out.flush(); + handleField.setInt(fd, (int) value); + // System.out.println(fd.valid()); + // Method sync = FileDescriptor.class.getDeclaredMethod("sync", new + // Class[0]); + // sync.setAccessible(true); + // sync.invoke(fd, new Object[0]); + + } + catch (Exception e) + { + e.printStackTrace(); + } + + } + + public boolean reconnectStreams() + { + if (_teeName != null) + try + { + _inputStream = new CyclicBufferFileInputStream(new File(_tmpPath, "out_" + _teeName)); + _errorStream = new CyclicBufferFileInputStream(new File(_tmpPath, "err_" + _teeName)); + _outputStream = new CyclicBufferFilePrintStream(new File(_tmpPath, "in_" + _teeName)); + return true; + } + catch (Exception ex) + { + ex.printStackTrace(); + } + + return false; + } + + private String getCommandInternal() + { + String result = _utils.readFile("/proc/" + getPid() + "/cmdline"); + if (result == null || result.length() == 0) + result = "?"; + // System.out.println("cmd line: "+result); + return result; + } + + private List getEnvironmentInternal() + { + String result = _utils.readFile("/proc/" + getPid() + "/environ"); + return parseEnvironment(result); + } + + private List parseEnvironment(String env) + { + List result = new ArrayList(); + if (env == null || "".equals(env)) + return result; + String sp = "(\\S+)=([^=.]+)( |$)"; + Pattern p = Pattern.compile(sp, Pattern.DOTALL); + Matcher m = p.matcher(env); + while (m.find()) + { + String[] str = m.group().trim().split("=", 2); + if (str.length == 2) + { + result.add(new String[] + { str[0], str[1] }); + } + } + return result; + + } + + protected String getWorkingDirInternal() + { + String result = null; + File f = new File("/proc/" + getPid() + "/cwd"); + try + { + result = f.getCanonicalPath(); + } + catch (IOException e) + { + e.printStackTrace(); + } + return result; + + } + + public String getWorkingDir() + { + return _workingDir; + } + + public static Process getProcess(int pid) + { + PosixProcess result = null; + File f = new File("/proc/" + pid); + if (f.exists()) + { + // TODO this may not always work + result = (PosixProcess) OperatingSystem.instance().processManagerInstance().createProcess(); + result._pid = pid; + result._user = result.getUserInternal(); + result._cmd = result.getCommandInternal(); + result._workingDir = result.getWorkingDirInternal(); + result._environment = result.getEnvironmentInternal(); + + } + return result; + } + + public static int currentProcessId() + { + return CLibrary.INSTANCE.getpid(); + } + + public String currentUser() + { + int euid = CLibrary.INSTANCE.geteuid(); + // log("current user euid "+ euid); + Pointer p = CLibrary.INSTANCE.getpwuid(euid); + if (p == null) + log("could not get current user"); + return new CLibrary.passwd(p).getName(); + + } + + public String currentGroup() + { + int egid = CLibrary.INSTANCE.getegid(); + // System.out.println("current group egid "+ egid); + Pointer pg = CLibrary.INSTANCE.getgrgid(egid); + if (pg == null) + { + log("could not get current group"); + return null; + } + return new CLibrary.group(pg).getName(); + } + + public String defaultGroup(String user) + { + Pointer p = CLibrary.INSTANCE.getpwnam(user); + if (p == null) + { + log("could not get user " + user); + return null; + } + int gid = new CLibrary.passwd(p).getGid(); + //System.out.println("default group gid " + gid); + Pointer pg = CLibrary.INSTANCE.getgrgid(gid); + if (pg == null) + { + log("could not get default group for user " + user); + return null; + } + return new CLibrary.group(pg).getName(); + + } + + public void switchUser(String name, String password) + { + if (name == null || "".equals(name)) + return; + String[] x = name.split("\\\\"); + String user = x.length == 1 ? x[0] : x[1]; + String group = x.length == 1 ? null : x[0]; + + if (group == null) + group = defaultGroup(user); + + String currentUser = currentUser(); + String currentGroup = currentGroup(); + + log("switch group " + currentGroup + " -> " + group); + + if (currentGroup != null && !currentGroup.equals(group)) + { + Pointer p = CLibrary.INSTANCE.getgrnam(group); + CLibrary.group g = new CLibrary.group(p); + int newGid = g.getGid(); + String nam = g.getName(); + if (newGid == 0) + log("could not get group " + group); + // System.out.println("switching to group name/id "+nam+"/"+newGid); + int res = CLibrary.INSTANCE.setregid(newGid, newGid); + if (res != 0) + log("could not change to group " + group); + } + + log("switch user " + currentUser + " -> " + user); + + if (currentUser != null && !currentUser.equals(user)) + { + Pointer p = CLibrary.INSTANCE.getpwnam(user); + int newUid = new CLibrary.passwd(p).getUid(); + if (newUid == 0) + log("could not get user " + user); + int res = CLibrary.INSTANCE.setreuid(newUid, newUid); + if (res != 0) + log("could not change to user " + user); + } + + currentUser = currentUser(); + if (!user.equals(currentUser)) + log("could not set user. current user: " + currentUser); + + currentGroup = currentGroup(); + if (!group.equals(currentGroup)) + log("could not set group. current group: " + currentGroup); + + } + + public String getUserInternal() + { + String status = _utils.readFile("/proc/" + _pid + "/status"); + // System.out.println("status "+status); + if (status != null) + try + { + // ruid, euid, suid fuid + String sp = ".*[U|u]id:\\s*(\\d+)\\s*(\\d+)\\s*(\\d+)\\s*(\\d+).*"; + Pattern p = Pattern.compile(sp, Pattern.DOTALL); + Matcher m = p.matcher(status); + m.find(); + // get ruid + int ruid = Integer.parseInt(m.group(1)); + //System.out.println("rudi " + ruid); + Pointer po = CLibrary.INSTANCE.getpwuid(ruid); + if (po == null) + System.out.println("could not get user"); + return new CLibrary.passwd(po).getName().trim(); + } + catch (Exception ex) + { + log("Error in getUser() " + ex.getMessage()); + } + + return ""; + + } + + public String getUser() + { + return _user; + } + + public String getStdInName() + { + return "stdin"; + } + + public String getStdOutName() + { + return "stdout"; + } + + public String getStdErrName() + { + return "stderr"; + } + + public int getStdOutNo() + { + return CLibrary.INSTANCE.fileno(NativeLibrary.getInstance("c").getFunction(getStdOutName()).getPointer(0)); + } + + public int getStdErrNo() + { + return CLibrary.INSTANCE.fileno(NativeLibrary.getInstance("c").getFunction(getStdErrName()).getPointer(0)); + } + + public int getStdInNo() + { + return CLibrary.INSTANCE.fileno(NativeLibrary.getInstance("c").getFunction(getStdInName()).getPointer(0)); + } + + public int getCurrentHandles() + { + if (!isRunning()) + return -1; + File f = new File("/proc/" + _pid + "/fd"); + if (!f.exists() || !f.isDirectory()) + return -1; + return f.list().length; + } + + public int getCurrentThreads() + { + int result = -1; + if (!isRunning()) + return result; + String status = _utils.readFile("/proc/" + _pid + "/status"); + // System.out.println("status "+status); + if (status != null) + try + { + // thread count + String sp = ".*[T|t]hreads:\\s*(\\d+).*"; + Pattern p = Pattern.compile(sp, Pattern.DOTALL); + Matcher m = p.matcher(status); + m.find(); + // get threads + result = Integer.parseInt(m.group(1)); + } + catch (Exception ex) + { + if (_logger != null) + _logger.info("Error in getCurrentThreads() " + ex.getMessage()); + } + + return result; + } + + long _currentTotalCPU = -1; + long _oldTotalCPU = -1; + long _lastCPUReadTime = Long.MAX_VALUE; + + public int getCurrentCpu() + { + int result = -1; + if (!isRunning()) + return result; + + String stat = _utils.readFile("/proc/" + _pid + "/stat"); + // System.out.println("status "+status); + if (status != null) + try + { + // ucpu scpu (13th) + String sp = "(?:[^\\s]+[\\s]+){13}(\\d+)\\s+(\\d+).+"; + Pattern p = Pattern.compile(sp, Pattern.DOTALL); + Matcher m = p.matcher(stat); + m.find(); + // get threads + int ucpu = Integer.parseInt(m.group(1).trim()); + int scpu = Integer.parseInt(m.group(2).trim()); + // System.out.println(ucpu + "<<" + scpu); + _oldTotalCPU = _currentTotalCPU; + _currentTotalCPU = ucpu + scpu; + double elapsed = ((double) (System.currentTimeMillis() - _lastCPUReadTime)) / 1000; + double used = _currentTotalCPU - _oldTotalCPU; + // System.out.println(elapsed + "<<" + used); + if (elapsed > 0) + result = (int) (used / elapsed); + _lastCPUReadTime = System.currentTimeMillis(); + + } + catch (Exception ex) + { + if (_logger != null) + _logger.info("Error in getCurrentCPU() " + ex.getMessage()); + } + + return result; + } + + public boolean isTerminated() + { + return _terminated; + } + + public boolean changeWorkingDir(String name) + { + File f = new File(name); + String dir; + if (!f.exists() || !f.isDirectory()) + { + log("changeWorkingDirectory failed. file not found " + name); + return false; + } + else + try + { + dir = f.getCanonicalPath(); + } + catch (IOException e) + { + if (_logger != null) + _logger.throwing(PosixProcess.class.getName(), "setWorkingDirectory", e); + return false; + } + boolean result = CLibrary.INSTANCE.chdir(name) == 0; + if (result) + System.setProperty("user.dir", dir); + return result; + } + + public void setTerminated(boolean terminated) + { + _terminated = terminated; + } + + @Override + public void setLogger(Logger logger) + { + super.setLogger(logger); + _utils.setLog(logger); + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/PosixProcessManager.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/PosixProcessManager.java new file mode 100644 index 0000000000..b93a88f76c --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/PosixProcessManager.java @@ -0,0 +1,59 @@ +package org.rzo.yajsw.os.posix; + +import java.util.List; + +import org.rzo.yajsw.os.Process; +import org.rzo.yajsw.os.ProcessManager; +import org.rzo.yajsw.os.TaskList; + +public class PosixProcessManager implements ProcessManager +{ + private static ProcessManager _instance; + + public static synchronized ProcessManager instance() + { + if (_instance == null) + _instance = new PosixProcessManager(); + return _instance; + } + + public Process createProcess() + { + return new PosixProcess(); + } + + public int currentProcessId() + { + return PosixProcess.currentProcessId(); + } + + public Process getProcess(int pid) + { + return PosixProcess.getProcess(pid); + } + + public List getProcessTree(int pid) + { + // TODO Auto-generated method stub + return null; + } + + public int processIdOfActiveWindow() + { + // TODO Auto-generated method stub + return 0; + } + + public TaskList taskListInstance() + { + // TODO Auto-generated method stub + return null; + } + + public List getProcessIds() + { + // TODO Auto-generated method stub + return null; + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/PosixService.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/PosixService.java new file mode 100644 index 0000000000..5d1401eac2 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/PosixService.java @@ -0,0 +1,618 @@ +package org.rzo.yajsw.os.posix; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Map.Entry; +import java.util.logging.Logger; + +import org.apache.velocity.Template; +import org.apache.velocity.VelocityContext; +import org.apache.velocity.app.VelocityEngine; +import org.rzo.yajsw.Constants; +import org.rzo.yajsw.boot.WrapperLoader; +import org.rzo.yajsw.config.YajswConfigurationImpl; +import org.rzo.yajsw.os.AbstractService; +import org.rzo.yajsw.os.JavaHome; +import org.rzo.yajsw.os.OperatingSystem; +import org.rzo.yajsw.os.posix.PosixProcess.CLibrary; + +public class PosixService extends AbstractService implements Constants +{ + protected String _daemonDir; + String _daemonTemplate; + String _daemonScript; + String _wrapperPidFile; + String _appPidFile; + String _pidDir; + String _confFile; + int _stopTimeout; + + String[] _startCmd; + String[] _execCmd; + String[] _stopCmd; + String[] _statusCmd; + + List _ksLinks = new ArrayList(); + UpdateRcParser _updateRcParser; + Utils _utils = new Utils(); + + @Override + public void setLogger(Logger logger) + { + super.setLogger(logger); + _utils.setLog(logger); + } + + private String getDOption(String key, String value) + { + if (value != null && !value.contains(" ")) + return "-D"+key+"="+value; + else + return "-D"+key+"=\""+value+"\""; + } + + + public void init() + { + if (_name == null) + { + _logger.warning("no name for daemon -> abort"); + return; + } + _daemonDir = _config.getString("wrapper.daemon.dir", getDefaultDaemonDir()); + File daemonDir = new File(_daemonDir); + if (!daemonDir.exists() || !daemonDir.isDirectory()) + { + _logger.warning("Error " + _daemonDir + " : is not a directory"); + return; + } + _pidDir = _config.getString("wrapper.daemon.pid.dir", Constants.DEFAULT_DAEMON_PID_DIR); + File pidDir = new File(_pidDir); + if (!pidDir.exists() || !pidDir.isDirectory()) + { + _logger.warning("Error " + _pidDir + " : is not a directory"); + return; + } + String wrapperJar = WrapperLoader.getWrapperJar().trim(); + String wrapperHome = "."; + try + { + wrapperHome = new File(wrapperJar).getParentFile().getCanonicalPath(); + } + catch (IOException e1) + { + e1.printStackTrace(); + } + + String confFile = _config.getString("wrapper.config"); + String confDir = null; + if (confFile != null) + { + File f = new File(confFile); + if (f.exists()) + try + { + confDir = f.getParentFile().getCanonicalPath(); + } + catch (IOException e) + { + } + } + if (confDir == null) + confDir = wrapperHome + "/conf"; + if (confFile == null) + { + _logger.warning("no conf file found -> abort"); + return; + } + try + { + _confFile = new File(confFile).getCanonicalPath(); + } + catch (IOException e) + { + _logger.throwing(this.getClass().getName(), "init", e); + } + + // Find working java + JavaHome javaHome = OperatingSystem.instance().getJavaHome( _config ); + String java = javaHome.findJava( null, null ); + + boolean bDone = false; + File fJava = ((java == null) ? null : new File( java )); + + // Create symlink to Java Home for CustomProcName if necessary + String custProcName = _config.getString( "wrapper.java.customProcName" ); + if ( custProcName != null && custProcName.length() > 0 ) + { + File fcustom = new File( custProcName ); + bDone = false; + + while ( !bDone ) + { + // If customProcName File/Symlink already exists - continue, do nothing... + if ( fcustom.exists() && fcustom.canExecute() ) + { + _config.setProperty( "wrapper.java.custProcName", fcustom.getAbsolutePath() ); + bDone = true; + break; + } + else + { + // Find Path to Regular Java + if ( fJava == null ) + { + // not found - exit with tears + bDone = true; + break; + } + + // is CustProcName Complete Path + if ( fcustom.isAbsolute() && fcustom.getParentFile().exists() ) + { + // Create Symlink for java at the specified location. + if ( CLibrary.INSTANCE.symlink( fJava.getAbsolutePath(), fcustom.getAbsolutePath() ) != 0 ) + { + if ( _logger != null ) + _logger.info( "error on creating script link " + fcustom ); + } + } + else + { + // create Symlink in tmp Directory + File tmpPath = new File( _config.getString( "wrapper.tmp.path", "/tmp" ) ); + if ( tmpPath.exists() ) + { + File link = new File( tmpPath, fcustom.getName() ); + if ( CLibrary.INSTANCE.symlink( fJava.getAbsolutePath(), link.getAbsolutePath() ) != 0 ) + { + if ( _logger != null ) + _logger.info( "error on creating script link " + link ); + } + else + { + _config.setProperty( "wrapper.java.custProcName", link.getAbsolutePath() ); + } + + } + else + { + + if ( _logger != null ) + _logger.info( "error on creating customProcName link - temp directory [" + tmpPath.getAbsolutePath() + + "] does not exist" ); + } + + } + } + + bDone = true; // do not want to loop + } // while( !bDone ) + + } + + + _daemonTemplate = _config.getString("wrapper.daemon.template", wrapperHome + "/templates/daemon.vm"); + File daemonTemplate = new File(_daemonTemplate); + if (!daemonTemplate.exists() || !daemonTemplate.isFile()) + { + if (_logger != null) + _logger.warning("Error " + _daemonTemplate + " : template file not found"); + return; + } + File daemonScript = getDaemonScript(); + if (daemonScript.exists()) + if (_logger != null) + _logger.info(daemonScript.getAbsolutePath() + " already exists"); + String pidName = null; + try + { + pidName = _config.getString("wrapper.pidfile", new File(pidDir, "wrapper." + getName() + ".pid").getCanonicalPath()); + } + catch (IOException e) + { + if (_logger != null) + _logger.throwing(this.getClass().getName(), "init", e); + } + File pidFile = new File(pidName); + String apidName = null; + try + { + apidName = _config.getString("wrapper.java.pidfile", new File(pidDir, "wrapper.java." + getName() + ".pid").getCanonicalPath()); + } + catch (IOException e) + { + if (_logger != null) + _logger.throwing(this.getClass().getName(), "init", e); + } + File apidFile = new File(apidName); + try + { + _daemonTemplate = daemonTemplate.getCanonicalPath(); + _wrapperPidFile = pidFile.getCanonicalPath(); + _appPidFile = apidFile.getCanonicalPath(); + _daemonScript = daemonScript.getCanonicalPath(); + } + catch (Exception ex) + { + if (_logger != null) + _logger.throwing(this.getClass().getName(), "init", ex); + } + //JavaHome javaHome = OperatingSystem.instance().getJavaHome(_config); + //String java = javaHome.findJava(_config.getString("wrapper.ntservice.java.command"), _config.getString("wrapper.java.customProcName")); + java = javaHome.findJava(_config.getString("wrapper.ntservice.java.command"), _config.getString("wrapper.java.customProcName")); + String tmpDir = _config.getString("wrapper.tmp.path", System.getProperty("jna_tmpdir", null)); + ArrayList result = new ArrayList(); + String opt = null; + if (tmpDir != null) + { + opt = getDOption("jna_tmpdir", tmpDir); + result.add(opt); + } + YajswConfigurationImpl config = (YajswConfigurationImpl) _config; + for (Iterator it=config.subset("wrapper").getKeys(); it.hasNext(); ) + try + { + config.getProperty((String) it.next()); + } + catch (Exception ex) + { + + } + // first add lookup vars eg ${lookup} + if (_config.getBoolean("wrapper.save_interpolated", true)) + { + + for (Entry e : config.getEnvLookupSet().entrySet()) + { + opt = getDOption(e.getKey(),e.getValue()); + if (!result.contains(opt)) + result.add(opt); + } + } + + for (Iterator it = _config.getKeys("wrapper.ntservice.additional"); it.hasNext();) + { + String key = (String) it.next(); + String value = _config.getString(key); + result.add(value); + } + List configs = _config.getList("wrapperx.config"); + + _startCmd = new String[10+result.size()+configs.size()]; + _startCmd[0] = java; + _startCmd[1] = "-Dwrapper.pidfile=" + _wrapperPidFile; + _startCmd[2] = "-Dwrapper.service=true"; + _startCmd[3] = "-Dwrapper.visible=false"; + for (int i=0; i abort"); + return false; + } + try + { + File daemonTemplate = new File(_daemonTemplate); + VelocityEngine ve = new VelocityEngine(); + ve.setProperty(VelocityEngine.RESOURCE_LOADER, "file"); + ve.setProperty("file.resource.loader.path", daemonTemplate.getParent()); + ve.setProperty("runtime.log.logsystem.class", VelocityLog.class.getCanonicalName()); + // TODO find a way to set a non static logger + VelocityLog.setLogger(_logger); + ve.init(); + Template t = ve.getTemplate(daemonTemplate.getName()); + VelocityContext context = new VelocityContext(); + context.put("w_name", _name); + context.put("w_long_name", _displayName); + context.put("w_start_cmd", toStrCommand(_startCmd)); + context.put("w_stop_cmd", toStrCommand(_stopCmd)); + context.put("w_status_cmd", toStrCommand(_statusCmd)); + context.put("w_description", _description); + context.put("w_conf_file", _confFile); + context.put("w_app_pid_file", _appPidFile); + context.put("w_wrapper_pid_file", _wrapperPidFile); + FileWriter writer = new FileWriter(_daemonScript); + + t.merge(context, writer); + writer.flush(); + writer.close(); + File daemonScript = new File(_daemonScript); + if (daemonScript.exists()) + { + if (_logger != null) + _logger.warning("created daemon script: " + _daemonScript); + } + else + { + if (_logger != null) + _logger.warning("error creating daemon script: " + _daemonScript); + } + // only jdk 1.6 daemonScript.setExecutable(true); + Runtime.getRuntime().exec("chmod 755 " + _daemonScript); + if ("AUTO_START".equals(_config.getString("wrapper.ntservice.starttype", DEFAULT_SERVICE_START_TYPE)) + || "DELAYED_AUTO_START".equals(_config.getString("wrapper.ntservice.starttype", DEFAULT_SERVICE_START_TYPE))) + { + + for (String link : _ksLinks) + if (CLibrary.INSTANCE.symlink(_daemonScript, link) != 0) + { + if (_logger != null) + _logger.info("error on creating script link " + link); + } + else + { + if (new File(link).exists()) + { + if (_logger != null) + _logger.info("created link : " + link); + } + else + { + if (_logger != null) + _logger.info("error on creating script link " + link); + } + } + + // Runtime.getRuntime().exec(String.format("ln -s %1$s %2$s", + // _klink, _daemonScript)); + // Runtime.getRuntime().exec(String.format("ln -s %1$s %2$s", + // _slink, _daemonScript)); + } + + } + catch (Exception ex) + { + if (_logger != null) + _logger.throwing(this.getClass().getName(), "install", ex); + return false; + } + return true; + } + + private String toStrCommand(String[] cmd) + { + String tmp = ""; + for (String s : cmd) + { + if (s != null) + tmp += "\"" + s + "\" "; + } + return tmp; + } + + public boolean isInstalled() + { + return _daemonScript != null && new File(_daemonScript).exists(); + } + + public boolean isRunning() + { + int pid = getPid(); + if (pid < 0) + return false; + org.rzo.yajsw.os.Process p = OperatingSystem.instance().processManagerInstance().getProcess(pid); + return p != null && p.isRunning(); + } + + public boolean start() + { + if (isRunning()) + { + if (_logger != null) + _logger.info("already running"); + return true; + } + File f = new File(_daemonScript); + if (!OperatingSystem.instance().setWorkingDir(f.getParent())) + { + if (_logger != null) + _logger.warning("could not set working dir. pls check configuration or user rights :"+f.getParent()); + else + System.out.println("could not set working dir. pls check configuration or user rights :"+f.getParent()); + } + String txt = _utils.osCommand(_daemonScript + " start", 45000); + if (_logger != null) + _logger.info(txt); + return isRunning(); + } + + public boolean stop() + { + if (_logger != null) + _logger.info(_utils.osCommand(_daemonScript + " stop", _stopTimeout)); + return !isRunning(); + } + + public boolean startProcess() + { + if (isRunning()) + { + _logger.info("already running"); + return true; + } + /* + * org.rzo.yajsw.os.Process p = + * OperatingSystem.instance().processManagerInstance().createProcess(); + * p.setCommand(cleanCmd(_execCmd)); p.setVisible(false); + * p.setPipeStreams(false, false); + * p.setDebug(_config.getBoolean("wrapper.debug", false)); p.start(); + */ + try + { + if (_logger != null) + _logger.info("calling " + toStrCommand(_execCmd)); + Runtime.getRuntime().exec(cleanCmd(_execCmd)); + } + catch (Exception e) + { + if (_logger != null) + _logger.throwing(this.getClass().getName(), "startProcess", e); + + } + return true; // p.isRunning(); + } + + public boolean stopProcess() + { + int pid = getPid(); + if (_logger != null) + _logger.info("stop daemon with pid " + pid); + if (pid <= 0) + return false; + org.rzo.yajsw.os.Process p = OperatingSystem.instance().processManagerInstance().getProcess(pid); + if (p == null) + { + if (_logger != null) + _logger.info("process not running"); + return true; + } + + p.stop(_stopTimeout, 0); + int apid = getAppPid(); + if (_logger != null) + _logger.info("stop daemon app with pid " + apid); + if (apid <= 0) + return false; + org.rzo.yajsw.os.Process ap = OperatingSystem.instance().processManagerInstance().getProcess(apid); + + if (ap != null) + ap.kill(999); + return true; + } + + public boolean uninstall() + { + if (isRunning()) + stop(); + new File(_daemonScript).delete(); + for (String link : _ksLinks) + new File(link).delete(); + return true; + } + + public int state() + { + int result = 0; + if (new File(_daemonScript).exists()) + result |= STATE_INSTALLED; + if (isRunning()) + result |= STATE_RUNNING; + return result; + } + + public int getPid() + { +if ( _logger != null ) + _logger.info("wrapper pid file: " + _wrapperPidFile); + if (_wrapperPidFile != null && new File(_wrapperPidFile).exists()) + try + { + BufferedReader reader = new BufferedReader(new FileReader(_wrapperPidFile)); + String pid = reader.readLine(); + reader.close(); + return Integer.parseInt(pid); + } + catch (Exception e) + { + if (_logger != null) + _logger.throwing(this.getClass().getName(), "getPid", e); + } + return -1; + + } + + public int getAppPid() + { + _logger.info("app pid file: " + _appPidFile); + if (_appPidFile != null && new File(_appPidFile).exists()) + try + { + BufferedReader reader = new BufferedReader(new FileReader(_appPidFile)); + String pid = reader.readLine(); + reader.close(); + return Integer.parseInt(pid); + } + catch (Exception e) + { + if (_logger != null) + _logger.throwing(this.getClass().getName(), "getAppPid", e); + } + return -1; + + } + + public String[] cleanCmd(String[] cmd) + { + List cmdl = new ArrayList(); + for (String s : cmd) + { + if (s != null && !"".equals(s)) + cmdl.add(s); + } + String[] result = new String[cmdl.size()]; + int i = 0; + for (String s : cmdl) + result[i++] = s; + return result; + + } + + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/PosixServiceManager.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/PosixServiceManager.java new file mode 100644 index 0000000000..86b7532763 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/PosixServiceManager.java @@ -0,0 +1,35 @@ +package org.rzo.yajsw.os.posix; + +import java.util.Map; + +import org.rzo.yajsw.os.Service; +import org.rzo.yajsw.os.ServiceInfo; +import org.rzo.yajsw.os.ServiceManager; + +public class PosixServiceManager implements ServiceManager +{ + + public Service createService() + { + return new PosixService(); + } + + public Service getService(String name) + { + // TODO Auto-generated method stub + return null; + } + + public Map getServiceList() + { + // TODO Auto-generated method stub + return null; + } + + public ServiceInfo getServiceInfo(String name) + { + // TODO Auto-generated method stub + return null; + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/PosixSystemInformation.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/PosixSystemInformation.java new file mode 100644 index 0000000000..2c6b19a842 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/PosixSystemInformation.java @@ -0,0 +1,59 @@ +package org.rzo.yajsw.os.posix; + +import java.util.logging.Logger; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.rzo.yajsw.os.SystemInformation; + +public class PosixSystemInformation implements SystemInformation +{ + Utils _utils = new Utils(); + Logger _logger; + + public void setLogger(Logger logger) + { + _logger = logger; + } + + public long freeRAM() + { + String info = _utils.readFile("/proc/meminfo"); + if (info != null) + try + { + String sp = ".*MemFree:\\s*(\\d+) kB.*"; + Pattern p = Pattern.compile(sp, Pattern.DOTALL); + Matcher m = p.matcher(info); + m.find(); + return Long.parseLong(m.group(1)) * 1024; + } + catch (Exception ex) + { + if (_logger != null) + _logger.throwing(PosixSystemInformation.class.getName(), "freeRAM", ex); + } + return 0; + } + + public long totalRAM() + { + String info = _utils.readFile("/proc/meminfo"); + if (info != null) + try + { + String sp = ".*MemTotal:\\s*(\\d+) kB.*"; + Pattern p = Pattern.compile(sp, Pattern.DOTALL); + Matcher m = p.matcher(info); + m.find(); + return Long.parseLong(m.group(1)) * 1024; + } + catch (Exception ex) + { + if (_logger != null) + _logger.throwing(PosixSystemInformation.class.getName(), "totalRAM", ex); + } + return 0; + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/UpdateRcParser.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/UpdateRcParser.java new file mode 100644 index 0000000000..ceadfbca2e --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/UpdateRcParser.java @@ -0,0 +1,188 @@ +package org.rzo.yajsw.os.posix; + +import java.util.HashSet; +import java.util.Set; + +public class UpdateRcParser +{ + + Set _startLinks = new HashSet(); + Set _stopLinks = new HashSet(); + String _runLevelDir; + String _serviceName; + String _stopLevels = ""; + String _startLevels = ""; + + public UpdateRcParser(String property, String runLevelDir, String serviceName) + { + if (runLevelDir == null || "".equals(runLevelDir)) + return; + if (serviceName == null || "".equals(serviceName)) + return; + + _runLevelDir = runLevelDir; + _serviceName = serviceName; + + if (property == null || "".equals(property)) + setDefault(); + else + setLinks(property); + + } + + private void setDefault() + { + setDefaultStartLinks(20); + setDefaultStopLinks(20); + } + + private void setDefaultStopLinks(int priority) + { + addStopLink(0, priority); + addStopLink(1, priority); + addStopLink(6, priority); + } + + private void addStopLink(String level, int priority) + { + _stopLinks.add(_runLevelDir.replaceFirst("X", "" + level) + "/K" + priority + _serviceName); + } + + private void addStopLink(int level, int priority) + { + _stopLinks.add(_runLevelDir.replaceFirst("X", "" + level) + "/K" + priority + _serviceName); + } + + private void setDefaultStartLinks(int priority) + { + addStartLink(2, priority); + addStartLink(3, priority); + addStartLink(4, priority); + addStartLink(5, priority); + } + + private void addStartLink(String level, int priority) + { + _startLinks.add(_runLevelDir.replaceFirst("X", "" + level) + "/S" + priority + _serviceName); + } + + private void addStartLink(int level, int priority) + { + _startLinks.add(_runLevelDir.replaceFirst("X", "" + level) + "/S" + priority + _serviceName); + } + + private void setLinks(String property) + { + try + { + String[] x = property.split(" "); + if (x.length == 1) + { + int priority = Integer.parseInt(x[0]); + setDefaultStartLinks(priority); + setDefaultStopLinks(priority); + } + else if (x.length == 2) + { + int startPriority = Integer.parseInt(x[0]); + int stopPriority = Integer.parseInt(x[1]); + setDefaultStartLinks(startPriority); + setDefaultStopLinks(stopPriority); + } + else + { + int i = 0; + int priority=0; + String level="0"; + String txt; + + while ( i < x.length ) + { + txt = x[i++]; + if ( txt.trim().equals( "start" ) ) + { + priority = Integer.parseInt( x[i++] ); + while ( i < x.length && !".".equals( x[i].trim() ) ) + { + txt = x[i++].trim(); + + // Allow S for Single User Mode ( Solaris ) + if ( txt.equalsIgnoreCase( "S" ) ) + level = "S"; + else + level = "" + Integer.parseInt( txt ); + + addStartLink( level, priority ); + _startLevels += level + " "; + } + } + else if ( txt.trim().equals( "stop" ) ) + { + priority = Integer.parseInt( x[i++] ); + while ( i < x.length && !".".equals( x[i].trim() ) ) + { + txt = x[i++].trim(); + + // Allow S for Single User Mode ( Solaris ) + if ( txt.equalsIgnoreCase( "S" ) ) + level = "S"; + else + level = "" + Integer.parseInt( txt ); + + addStopLink( level, priority ); + _stopLevels += level + " "; + + } + } + } + } + } + catch (Exception ex) + { + System.out.println("error parsing wrapper.daemon.update_rc " + ex); + } + } + + public Set getStopLinks() + { + return _stopLinks; + } + + public Set getStartLinks() + { + return _startLinks; + } + + public static void main(String[] args) + { + UpdateRcParser p; + + p = new UpdateRcParser(null, "/etc/rcX.d", "serviceName"); + System.out.println(p.getStartLinks()); + System.out.println(p.getStopLinks()); + + p = new UpdateRcParser("91", "/etc/rcX.d", "serviceName"); + System.out.println(p.getStartLinks()); + System.out.println(p.getStopLinks()); + + p = new UpdateRcParser("20 80", "/etc/rcX.d", "serviceName"); + System.out.println(p.getStartLinks()); + System.out.println(p.getStopLinks()); + + p = new UpdateRcParser("start 20 2 3 4 . start 30 5 . stop 80 0 1 6", "/etc/rcX.d", "serviceName"); + System.out.println(p.getStartLinks()); + System.out.println(p.getStopLinks()); + + } + + public String getStopLevels() + { + return _stopLevels; + } + + public String getStartLevels() + { + return _startLevels; + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/Utils.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/Utils.java new file mode 100644 index 0000000000..4f74cfe638 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/Utils.java @@ -0,0 +1,121 @@ +package org.rzo.yajsw.os.posix; + +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStream; +import java.util.concurrent.Callable; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; +import java.util.concurrent.FutureTask; +import java.util.concurrent.TimeUnit; +import java.util.logging.Logger; + +import org.rzo.yajsw.util.DaemonThreadFactory; + +public class Utils +{ + protected static final Executor executor = Executors.newCachedThreadPool(new DaemonThreadFactory("util.osCommand")); + protected Logger _logger; + + public void setLog(Logger logger) + { + _logger = logger; + } + + public String readFile(String file) + { + String result = ""; + File f = new File(file); + if (f.exists()) + try + { + InputStream in = new FileInputStream(f); + byte[] buffer = new byte[10 * 1024]; + int size = 0; + + while ((size = in.read(buffer)) > 0) + { + // System.out.println("size "+size); + for (int i = 0; i < size; i++) + if (buffer[i] == 0) + buffer[i] = (byte) ' '; + result += new String(buffer, 0, size); + } + in.close(); + } + catch (Exception e) + { + if (_logger != null) + _logger.throwing(Utils.class.getName(), "readFile", e); + } + else + { + if (_logger != null) + _logger.info("could not find file " + f.getAbsolutePath()); + // throw new NullPointerException(); + } + return result; + + } + + public String osCommand(String cmd) + { + StringBuffer result = new StringBuffer(); + try + { + Process p = Runtime.getRuntime().exec(cmd); + InputStream in = p.getInputStream(); + int x; + while ((x = in.read()) != -1) + result.append((char) x); + } + catch (Exception ex) + { + if (_logger != null) + _logger.warning("Error executing \"" + cmd + "\": " + ex); + } + return result.toString(); + } + + public String osCommand(String cmd, long timeout) + { + Process p = null; + try + { + p = Runtime.getRuntime().exec(cmd); + final Process fp = p; + FutureTask future = new FutureTask(new Callable() + { + + public String call() throws Exception + { + StringBuffer result = new StringBuffer(); + InputStream in = fp.getInputStream(); + int x; + while ((x = in.read()) != -1) + result.append((char) x); + + return result.toString(); + } + }); + executor.execute(future); + String result = future.get(timeout, TimeUnit.MILLISECONDS); + return result; + + } + catch (Exception e) + { + if (_logger != null) + _logger.warning("Error executing \"" + cmd + "\": " + e); + if (p != null) + p.destroy(); + } + return null; + } + + public static void main(String[] args) + { + System.out.println(new Utils().osCommand("cmd /C dir", 500)); + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/VelocityLog.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/VelocityLog.java new file mode 100644 index 0000000000..4cd9d4e516 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/VelocityLog.java @@ -0,0 +1,41 @@ +package org.rzo.yajsw.os.posix; + +import java.util.logging.Logger; + +import org.apache.velocity.runtime.RuntimeServices; +import org.apache.velocity.runtime.log.LogChute; + +public class VelocityLog implements LogChute +{ + static Logger _logger = null; + + static public void setLogger(Logger logger) + { + _logger = logger; + } + + public void init(RuntimeServices arg0) throws Exception + { + // TODO Auto-generated method stub + + } + + public boolean isLevelEnabled(int arg0) + { + // TODO Auto-generated method stub + return false; + } + + public void log(int arg0, String arg1) + { + // TODO Auto-generated method stub + + } + + public void log(int arg0, String arg1, Throwable arg2) + { + if (_logger != null) + _logger.throwing(VelocityLog.class.getName(), arg1, arg2); + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/bsd/AppStarter.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/bsd/AppStarter.java new file mode 100644 index 0000000000..18967f45ab --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/bsd/AppStarter.java @@ -0,0 +1,114 @@ +package org.rzo.yajsw.os.posix.bsd; + +import java.util.ArrayList; +import java.util.List; + +import org.rzo.yajsw.os.posix.PosixProcess; +import org.rzo.yajsw.os.posix.PosixProcess.CLibrary; + +public class AppStarter +{ + public static void main(String[] args) + { + // get pid and send it to parent + int pid = CLibrary.INSTANCE.getpid(); + System.out.println("PID:" + pid); + System.out.flush(); + + // set priority + if (CLibrary.INSTANCE.nice(1) == -1) + System.out.println("could not set priority "); + if (getUser() != null) + try + { + new PosixProcess().switchUser(getUser(), getPassword()); + } + catch (Throwable ex) + { + ex.printStackTrace(); + } + + + // detach from parent + CLibrary.INSTANCE.umask(0); + CLibrary.INSTANCE.setsid(); + + /* + * bkowal + * Suppress extraneous output. + */ + //System.out.println("calling exec"); + // close streams ? + if (!isPipeStreams()) + { + /* + try + { + System.in.close(); + } + catch (IOException e) + { + e.printStackTrace(); + } + */ + + System.out.close(); + System.err.close(); + } + + String[] env = null;//getEnv(); + + // start the subprocess + int ret = -1; + try + { + if (env == null) + CLibrary.INSTANCE.execvp(args[0], args); + else + CLibrary.INSTANCE.execve(args[0], args, env); + System.out.println("ret "+ret); + } + catch (Exception ex) + { + ex.printStackTrace(); + } + + } + + private static boolean isPipeStreams() + { + return System.getProperty("wrapperx.pipeStreams") != null; + } + + private static String getPassword() + { + return System.getProperty("wrapperx.password"); + } + + private static String getUser() + { + return System.getProperty("wrapperx.user"); + } + + private static String[] getEnv() + { + List result = new ArrayList(); + for (String key : System.getenv().keySet()) + { + result.add(key+"="+System.getenv(key)); + } + if (result.isEmpty()) + return null; + String[] arr = new String[result.size()]; + int i = 0; + for (String x : result) + { + arr[i] = x; + System.out.println(x); + i++; + } + return arr; + } + + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/bsd/BSDErrorHandler.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/bsd/BSDErrorHandler.java new file mode 100644 index 0000000000..839c00142b --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/bsd/BSDErrorHandler.java @@ -0,0 +1,21 @@ +package org.rzo.yajsw.os.posix.bsd; + +import org.rzo.yajsw.os.ErrorHandler; +import org.rzo.yajsw.os.OsException; + +public class BSDErrorHandler implements ErrorHandler +{ + + public void throwException(int id) throws OsException + { + // TODO Auto-generated method stub + + } + + public String toString(int id) + { + // TODO Auto-generated method stub + return null; + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/bsd/BSDFileManager.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/bsd/BSDFileManager.java new file mode 100644 index 0000000000..06e600c9e2 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/bsd/BSDFileManager.java @@ -0,0 +1,8 @@ +package org.rzo.yajsw.os.posix.bsd; + +import org.rzo.yajsw.os.posix.PosixFileManager; + +public class BSDFileManager extends PosixFileManager +{ + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/bsd/BSDJavaHome.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/bsd/BSDJavaHome.java new file mode 100644 index 0000000000..5dd0882a6a --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/bsd/BSDJavaHome.java @@ -0,0 +1,14 @@ +package org.rzo.yajsw.os.posix.bsd; + +import org.apache.commons.configuration.Configuration; +import org.rzo.yajsw.os.posix.PosixJavaHome; + +public class BSDJavaHome extends PosixJavaHome +{ + + public BSDJavaHome(Configuration config) + { + super(config); + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/bsd/BSDProcess.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/bsd/BSDProcess.java new file mode 100644 index 0000000000..acf0b02579 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/bsd/BSDProcess.java @@ -0,0 +1,390 @@ +package org.rzo.yajsw.os.posix.bsd; + +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.URI; +import java.util.ArrayList; +import java.util.List; + +import org.rzo.yajsw.boot.WrapperLoader; +import org.rzo.yajsw.io.CyclicBufferFileInputStream; +import org.rzo.yajsw.io.CyclicBufferFilePrintStream; +import org.rzo.yajsw.os.OperatingSystem; +import org.rzo.yajsw.os.Process; +import org.rzo.yajsw.os.posix.PosixProcess; + +import com.sun.jna.FromNativeConverter; +import com.sun.jna.ptr.IntByReference; + +public class BSDProcess extends PosixProcess +{ + java.lang.Process _process; + + @Override + public String getStdInName() + { + return "__stdinp"; + } + + @Override + public String getStdOutName() + { + return "__stdoutp"; + } + + @Override + public String getStdErrName() + { + return "__stderrp"; + } + + + + + private String getDOption(String key, String value) + { + // posix: setting quotes does not work (cmd is str array). windows: quotes are set in Process class. + //if (value != null && !value.contains(" ")) + return "-D"+key+"="+value; + //else + // return "-D"+key+"=\""+value+"\""; + } + + + public boolean start() + { + _terminated = false; + _exitCode = -1; + ArrayList cmdList = new ArrayList(); + cmdList.add(getCurrentJava()); + String tmpDir = _tmpPath; + if (tmpDir == null) + tmpDir = System.getProperty("jna_tmpdir", null); + if (tmpDir != null) + { + String opt = getDOption("jna_tmpdir", tmpDir); + if (!cmdList.contains(opt)) + cmdList.add(opt); + } + cmdList.add("-classpath"); + cmdList.add(getStartClasspath()); + if (_pipeStreams) + cmdList.add("-Dwrapperx.pipeStreams=true"); + if (_user != null) + cmdList.add("-Dwrapperx.user=" + _user); + //if (_password != null) + // cmdList.add("-Dwrapperx.password=" + _password); + String[] xenv = getXEnv(); + cmdList.add(AppStarter.class.getName()); + for (int i = 0; i < _arrCmd.length; i++) + cmdList.add(_arrCmd[i]); + String[] cmd = new String[cmdList.size()]; + for (int i = 0; i < cmd.length; i++) + { + cmd[i] = cmdList.get(i); + } + System.out.flush(); + + final java.lang.Process p; + + try + { + p = Runtime.getRuntime().exec(cmd, xenv, new File(_workingDir)); + } + catch (IOException e) + { + e.printStackTrace(); + _terminated = true; + return false; + } + BufferedReader in = new BufferedReader(new InputStreamReader(p.getInputStream())); + String line; + try + { + do + { + line = in.readLine(); + //System.out.println("line: " +line); + if (line != null && line.contains("PID:")) + { + setPid(Integer.parseInt(line.substring(4))); + if (this._teeName == null) + line = null; + // otherwise the stream is closed by the wrapped app + // we will continue reading the input stream in the gobbler + } + else if (line != null && _debug) + System.out.println(line); + } + while (line != null); + } + catch (IOException e) + { + e.printStackTrace(); + _terminated = true; + return false; + } + _process = p; + executor.execute(new Runnable() + { + + public void run() + { + try + { + p.waitFor(); + } + catch (InterruptedException e) + { + e.printStackTrace(); + Thread.currentThread().interrupt(); + } + _terminated = true; + _exitCode = p.exitValue(); + /* + * bkowal + * Suppress extraneous output unless debug is enabled. + */ + if (_debug) + { + System.out.println("exit code bsd process " + _exitCode); + } + BSDProcess.this.setTerminated(true); + } + + }); + + if (_teeName != null && _tmpPath != null) + { + File f = new File(_tmpPath); + try + { + if (!f.exists()) + f.mkdir(); + } + catch (Exception ex) + { + ex.printStackTrace(); + } + try + { + // System.out.println("opening tee streams out"); + _inputStream = new CyclicBufferFileInputStream(createRWfile(_tmpPath, "out_" + _teeName)); + } + catch (Exception e) + { + e.printStackTrace(); + } + try + { + // System.out.println("opening tee streams err"); + _errorStream = new CyclicBufferFileInputStream(createRWfile(_tmpPath, "err_" + _teeName)); + } + catch (Exception e) + { + e.printStackTrace(); + } + try + { + // System.out.println("opening tee streams in"); + _outputStream = new CyclicBufferFilePrintStream(createRWfile(_tmpPath, "in_" + _teeName)); + } + catch (Exception e) + { + e.printStackTrace(); + } + } + if (_pipeStreams && _teeName == null) + { + + _outputStream = _process.getOutputStream(); + _inputStream = _process.getInputStream(); + _errorStream = _process.getErrorStream(); + + } + if (_cpuAffinity != AFFINITY_UNDEFINED) + { + IntByReference affinity = new IntByReference(); + affinity.setValue(_cpuAffinity); + if (CLibrary.INSTANCE.sched_setaffinity(_pid, 4, affinity) == -1) + System.out.println("error setting affinity"); + } + + /* + * bkowal + * Suppress extraneous output unless debug is enabled. + */ + if (_debug) + { + System.out.println("started process " + _pid); + } + + return true; + } + + private String[] getXEnv() + { + List env = getEnvironment(); + if (env != null && !env.isEmpty()) + { + String [] result = new String[env.size()]; + int i = 0; + for (String[] x : env) + { + result[i] = x[0]+"="+x[1]; + System.out.println("bsd env "+result[i]); + i++; + } + return result; + } + return null; + } + + private String getStartClasspath() + { + String wrapperJar = WrapperLoader.getWrapperJar(); + File wrapperHome = new File(wrapperJar).getParentFile(); + File jnaFile = new File(getJNAJar()); + try + { + return wrapperJar + ":" + jnaFile.getCanonicalPath(); + } + catch (IOException e) + { + e.printStackTrace(); + } + return null; + } + + private String getCurrentJava() + { + int myPid = OperatingSystem.instance().processManagerInstance().currentProcessId(); + Process myProcess = OperatingSystem.instance().processManagerInstance().getProcess(myPid); + String cmd = myProcess.getCommand(); + String jvm = null; + if (cmd.startsWith("\"")) + jvm = cmd.substring(0, cmd.indexOf("\" ") + 1); + else + jvm = cmd.substring(0, cmd.indexOf(" ")); + + /* + * bkowal + * Always return the AWIPS II Java. + */ + return "/awips2/java/bin/java"; + } + + public String getCommandInternal() + { + if (_pid < 0) + return null; + String cmd = String.format("ps -p %1$s -o command", _pid); + String res = _utils.osCommand(cmd, 5000); + if (res == null) + return null; + String[] resx = res.split(System.getProperty("line.separator")); + if (resx.length < 2) + return null; + return resx[1]; + } + + public String getUserInternal() + { + if (_pid < 0) + return null; + String cmd = String.format("ps -p %1$s -o user", _pid); + String res = _utils.osCommand(cmd, 5000); + if (res == null) + return null; + String[] resx = res.split(System.getProperty("line.separator")); + if (resx.length < 2) + return null; + return resx[1]; + } + + public String getWorkingDirInternal() + { + if (_pid < 0) + return null; + return null; + } + + public static Process getProcess(int pid) + { + BSDProcess result = null; + result = new BSDProcess(); + result.setPid(pid); + result.setUser(result.getUserInternal()); + result.setCommand(result.getCommandInternal()); + result.setWorkingDir(result.getWorkingDirInternal()); + if (result.getCommand() == null) + return null; + return result; + } + + public static void main(String[] args) + { + BSDProcess p = new BSDProcess(); + System.out.println(p.getCurrentJava()); + p.setCommand(new String[] + { "ping", "localhost" }); + p.setPipeStreams(true, false); + p.start(); + BufferedReader in = new BufferedReader(new InputStreamReader(p.getInputStream())); + String line; + try + { + do + { + line = in.readLine(); + System.out.println(line); + } + while (line != null); + } + catch (IOException e) + { + e.printStackTrace(); + } + + } + + private boolean checkPath(String path) + { + int ix = path.indexOf("!"); + if (ix == -1) + { + System.out.println("/lib/core/jna/jna-xxx.jar not found, please check classpath. aborting wrapper !"); + //Runtime.getRuntime().halt(999);// -> groovy eclipse plugin crashes + return false; + } + return true; + + } + + private String getJNAJar() + { + String cn = FromNativeConverter.class.getCanonicalName(); + String rn = cn.replace('.', '/') + ".class"; + String path = "."; + try + { + path = FromNativeConverter.class.getClassLoader().getResource(rn).getPath(); + if (!checkPath(path)) + return null; + path = path.substring(0, path.indexOf("!")); + path = new URI(path).getPath(); + path.replaceAll("%20", " "); + return path; + } + catch (Exception e1) + { + e1.printStackTrace(); + } + return null; + } + + + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/bsd/BSDProcessManager.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/bsd/BSDProcessManager.java new file mode 100644 index 0000000000..415b3e95d9 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/bsd/BSDProcessManager.java @@ -0,0 +1,18 @@ +package org.rzo.yajsw.os.posix.bsd; + +import org.rzo.yajsw.os.Process; +import org.rzo.yajsw.os.posix.PosixProcessManager; + +public class BSDProcessManager extends PosixProcessManager +{ + public Process createProcess() + { + return (Process) new BSDProcess(); + } + + public Process getProcess(int pid) + { + return BSDProcess.getProcess(pid); + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/bsd/BSDService.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/bsd/BSDService.java new file mode 100644 index 0000000000..1a578ef4eb --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/bsd/BSDService.java @@ -0,0 +1,19 @@ +package org.rzo.yajsw.os.posix.bsd; + +import java.io.File; + +import org.rzo.yajsw.os.posix.PosixService; + +public class BSDService extends PosixService +{ + protected File getDaemonScript() + { + return new File(new File(_daemonDir), "wrapper." + getName() + ".sh"); + } + + protected String getDefaultDaemonDir() + { + return "/usr/local/etc/rc.d"; + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/bsd/BSDServiceManager.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/bsd/BSDServiceManager.java new file mode 100644 index 0000000000..3dd5d26886 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/bsd/BSDServiceManager.java @@ -0,0 +1,35 @@ +package org.rzo.yajsw.os.posix.bsd; + +import java.util.Map; + +import org.rzo.yajsw.os.Service; +import org.rzo.yajsw.os.ServiceInfo; +import org.rzo.yajsw.os.ServiceManager; + +public class BSDServiceManager implements ServiceManager +{ + + public Service createService() + { + return new BSDService(); + } + + public Service getService(String name) + { + // TODO Auto-generated method stub + return null; + } + + public Map getServiceList() + { + // TODO Auto-generated method stub + return null; + } + + public ServiceInfo getServiceInfo(String name) + { + // TODO Auto-generated method stub + return null; + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/bsd/BSDSystemInformation.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/bsd/BSDSystemInformation.java new file mode 100644 index 0000000000..b840fcc8ea --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/bsd/BSDSystemInformation.java @@ -0,0 +1,28 @@ +package org.rzo.yajsw.os.posix.bsd; + +import java.util.logging.Logger; + +import org.rzo.yajsw.os.SystemInformation; + +public class BSDSystemInformation implements SystemInformation +{ + + public long freeRAM() + { + // TODO Auto-generated method stub + return 0; + } + + public long totalRAM() + { + // TODO Auto-generated method stub + return 0; + } + + public void setLogger(Logger logger) + { + // TODO Auto-generated method stub + + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/bsd/OperatingSystemBSD.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/bsd/OperatingSystemBSD.java new file mode 100644 index 0000000000..294163bb8c --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/bsd/OperatingSystemBSD.java @@ -0,0 +1,69 @@ +package org.rzo.yajsw.os.posix.bsd; + +import org.apache.commons.configuration.Configuration; +import org.rzo.yajsw.os.ErrorHandler; +import org.rzo.yajsw.os.FileManager; +import org.rzo.yajsw.os.JavaHome; +import org.rzo.yajsw.os.Keyboard; +import org.rzo.yajsw.os.ProcessManager; +import org.rzo.yajsw.os.ServiceManager; +import org.rzo.yajsw.os.SystemInformation; +import org.rzo.yajsw.os.posix.OperatingSystemPosix; + +public class OperatingSystemBSD extends OperatingSystemPosix +{ + + private static ProcessManager _processManagerInstance; + private static FileManager _fileManagerInstance; + private static ServiceManager _serviceManagerInstance; + private static SystemInformation _systemInformation = new BSDSystemInformation(); + + @Override + public ErrorHandler errorHandlerInstance() + { + return new BSDErrorHandler(); + } + + @Override + public JavaHome getJavaHome(Configuration config) + { + return new BSDJavaHome(config); + } + + @Override + public Keyboard keyboardInstance() + { + return null; + } + + @Override + public ProcessManager processManagerInstance() + { + if (_processManagerInstance == null) + _processManagerInstance = new BSDProcessManager(); + return _processManagerInstance; + } + + @Override + public ServiceManager serviceManagerInstance() + { + if (_serviceManagerInstance == null) + _serviceManagerInstance = new BSDServiceManager(); + return _serviceManagerInstance; + } + + @Override + public SystemInformation systemInformation() + { + return _systemInformation; + } + + @Override + public FileManager fileManagerInstance() + { + if (_fileManagerInstance == null) + _fileManagerInstance = new BSDFileManager(); + return _fileManagerInstance; + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/bsd/macosx/MacOsXErrorHandler.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/bsd/macosx/MacOsXErrorHandler.java new file mode 100644 index 0000000000..a4dc822996 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/bsd/macosx/MacOsXErrorHandler.java @@ -0,0 +1,8 @@ +package org.rzo.yajsw.os.posix.bsd.macosx; + +import org.rzo.yajsw.os.posix.bsd.BSDErrorHandler; + +public class MacOsXErrorHandler extends BSDErrorHandler +{ + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/bsd/macosx/MacOsXFileManager.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/bsd/macosx/MacOsXFileManager.java new file mode 100644 index 0000000000..e77c1421e2 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/bsd/macosx/MacOsXFileManager.java @@ -0,0 +1,8 @@ +package org.rzo.yajsw.os.posix.bsd.macosx; + +import org.rzo.yajsw.os.posix.PosixFileManager; + +public class MacOsXFileManager extends PosixFileManager +{ + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/bsd/macosx/MacOsXJavaHome.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/bsd/macosx/MacOsXJavaHome.java new file mode 100644 index 0000000000..ee973d06ac --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/bsd/macosx/MacOsXJavaHome.java @@ -0,0 +1,14 @@ +package org.rzo.yajsw.os.posix.bsd.macosx; + +import org.apache.commons.configuration.Configuration; +import org.rzo.yajsw.os.posix.bsd.BSDJavaHome; + +public class MacOsXJavaHome extends BSDJavaHome +{ + + public MacOsXJavaHome(Configuration config) + { + super(config); + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/bsd/macosx/MacOsXProcess.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/bsd/macosx/MacOsXProcess.java new file mode 100644 index 0000000000..ccf5d510c3 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/bsd/macosx/MacOsXProcess.java @@ -0,0 +1,390 @@ +package org.rzo.yajsw.os.posix.bsd.macosx; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; + +import org.rzo.yajsw.io.CyclicBufferFileInputStream; +import org.rzo.yajsw.io.CyclicBufferFilePrintStream; +import org.rzo.yajsw.os.OperatingSystem; +import org.rzo.yajsw.os.Process; +import org.rzo.yajsw.os.posix.PosixProcess; + +import com.sun.jna.Native; +import com.sun.jna.ptr.IntByReference; + +public class MacOsXProcess extends PosixProcess +{ + + @Override + public String getStdInName() + { + return "__stdinp"; + } + + @Override + public String getStdOutName() + { + return "__stdoutp"; + } + + @Override + public String getStdErrName() + { + return "__stderrp"; + } + + /** + * Gets the current cpu. + * + * @return the current cpu + */ + public int getCurrentCpu() + { + return -1; + } + + public int getCurrentThreads() + { + return -1; + } + + /** + * Gets the current physical memory. + * + * @return the current physical memory + */ + public int getCurrentPhysicalMemory() + { + return -1; + } + + /** + * Gets the current virtual memory. + * + * @return the current virtual memory + */ + public int getCurrentVirtualMemory() + { + return -1; + } + + /** + * Gets the current page faults. + * + * @return the current page faults + */ + public int getCurrentPageFaults() + { + return -1; + } + + public boolean start() + { + if (_arrCmd == null && _cmd == null) + return false; + if (_arrCmd == null) + { + _arrCmd = _cmd.split(" "); + if (_debug) + log("exec: " + _cmd); + } + else + { + String cmd = ""; + for (String c : _arrCmd) + cmd += c + " "; + if (_debug) + log("exec:" + cmd); + } + + log("starting "); + + int pid = 0; + _exitCode = -2; + String title = _title == null ? "yajsw" : _title; + _terminated = false; + if (_visible) + setCommand(String.format("xterm -hold -sb -T %1$s -e %2$s", title, getCommand())); + + // System.out.println("exec \n"+getCommand()); + // System.out.println("working dir\n"+getWorkingDir()); + + if (_visible) + _pipeStreams = false; + /* + * // if (_pipeStreams) { CLibrary.INSTANCE.pipe(_inPipe); + * CLibrary.INSTANCE.pipe(_outPipe); CLibrary.INSTANCE.pipe(_errPipe); + * // System.out.println(_outPipe[0]+" "+_outPipe[1]); } + */ + + // fork a child process + if ((pid = CLibrary.INSTANCE.fork()) == 0) + { + // System.out.println("afer fork"); + + int stdout = getStdOutNo(); + int stderr = getStdErrNo();// CLibrary.INSTANCE.fileno(NativeLibrary.getInstance("c").getFunction(getStdErrName()).getPointer(0)); + int stdin = getStdInNo();// CLibrary.INSTANCE.fileno(NativeLibrary.getInstance("c").getFunction(getStdInName()).getPointer(0)); + + // pipe streams to OS pipes + // if (_pipeStreams) + { + CLibrary.INSTANCE.close(_inPipe[1]); + moveDescriptor(_inPipe[0], stdin); + CLibrary.INSTANCE.close(_outPipe[0]); + moveDescriptor(_outPipe[1], stdout); + CLibrary.INSTANCE.close(_errPipe[0]); + moveDescriptor(_errPipe[1], stderr); + } + + // closeDescriptors(); + + // disconect from parent + CLibrary.INSTANCE.umask(0); + if (CLibrary.INSTANCE.setsid() < 0) + CLibrary.INSTANCE.exit(-1); + + // set working dir + if (getWorkingDir() != null) + if (CLibrary.INSTANCE.chdir(getWorkingDir()) != 0) + log("could not set working dir"); + + // set priority + if (_priority == PRIORITY_BELOW_NORMAL) + { + if (CLibrary.INSTANCE.nice(1) == -1) + log("could not set priority "); + } + else if (_priority == PRIORITY_LOW) + { + if (CLibrary.INSTANCE.nice(2) == -1) + log("could not set priority "); + } + else if (_priority == PRIORITY_ABOVE_NORMAL) + { + if (CLibrary.INSTANCE.nice(-1) == -1) + log("could not set priority "); + } + else if (_priority == PRIORITY_HIGH) + { + if (CLibrary.INSTANCE.nice(-2) == -1) + log("could not set priority "); + } + if (getUser() != null) + switchUser(getUser(), getPassword()); + + try + { + int res = CLibrary.INSTANCE.execvp(_arrCmd[0], _arrCmd); + int err = Native.getLastError(); + log("errno " + err + " " + CLibrary.INSTANCE.strerror(err)); + log("exec res " + res); + } + catch (Exception ex) + { + ex.printStackTrace(); + } + lock = false; + // CLibrary.INSTANCE.exit(-1); + } // child code + else if (pid > 0) + { + _pid = pid; + try + { + Thread.sleep(1000); + } + catch (InterruptedException e1) + { + } + + // or pipe streams to cyclic buffer files + if (_teeName != null && _tmpPath != null) + { + // System.out.println("opening tee streams"); + File f = new File(_tmpPath); + try + { + if (!f.exists()) + f.mkdir(); + } + catch (Exception ex) + { + ex.printStackTrace(); + Thread.currentThread().interrupt(); + } + try + { + // System.out.println("opening tee streams out"); + _inputStream = new CyclicBufferFileInputStream(createRWfile(_tmpPath, "out_" + _teeName)); + } + catch (Exception e) + { + e.printStackTrace(); + } + try + { + // System.out.println("opening tee streams err"); + _errorStream = new CyclicBufferFileInputStream(createRWfile(_tmpPath, "err_" + _teeName)); + } + catch (Exception e) + { + e.printStackTrace(); + } + try + { + // System.out.println("opening tee streams in"); + _outputStream = new CyclicBufferFilePrintStream(createRWfile(_tmpPath, "in_" + _teeName)); + } + catch (Exception e) + { + e.printStackTrace(); + } + // System.out.println("- opening tee streams"); + } + /* + * if (!_pipeStreams) { + * System.out.println("setting out streams to /dev/null/"); + * CLibrary.INSTANCE.freopen("/dev/null", "w", _outPipe[0]); + * System.out.println("setting err streams to /dev/null/"); + * CLibrary.INSTANCE.freopen("/dev/null", "w", _errPipe[0]); + * //System.out.println("setting streams to /dev/null/"); + * //CLibrary.INSTANCE.freopen("/dev/null", "r", _inPipe[1]); + * System.out.println("- setting streams to /dev/null/"); } + */ + + // System.out.println("parent"); + if (_pipeStreams && _teeName == null && _tmpPath == null) + { + writefd(in_fd, _inPipe[1]); + writefd(out_fd, _outPipe[0]); + writefd(err_fd, _errPipe[0]); + + _outputStream = new BufferedOutputStream(new FileOutputStream(in_fd)); + _inputStream = new BufferedInputStream(new FileInputStream(out_fd)); + _errorStream = new BufferedInputStream(new FileInputStream(err_fd)); + + CLibrary.INSTANCE.close(_inPipe[0]); + CLibrary.INSTANCE.close(_outPipe[1]); + CLibrary.INSTANCE.close(_errPipe[1]); + + } + + if (_cpuAffinity != AFFINITY_UNDEFINED) + { + IntByReference affinity = new IntByReference(); + affinity.setValue(_cpuAffinity); + if (CLibrary.INSTANCE.sched_setaffinity(_pid, 4, affinity) == -1) + log("error setting affinity"); + } + + executor.execute(new Runnable() + { + + public void run() + { + int r = CLibrary.INSTANCE.waitpid(_pid, status, 0); + // System.out.println("wait for "+r); + if (r == _pid) + _exitCode = status.getValue(); + log("exit code linux process " + _exitCode); + _terminated = true; + } + + }); + + log("started process " + _pid); + return true; + } // parent process + else if (pid < 0) + { + log("failed to fork: " + pid); + return false; + } + return false; + + } + + private String getCurrentJava() + { + int myPid = OperatingSystem.instance().processManagerInstance().currentProcessId(); + Process myProcess = OperatingSystem.instance().processManagerInstance().getProcess(myPid); + String cmd = myProcess.getCommand(); + String jvm = null; + if (cmd.startsWith("\"")) + jvm = cmd.substring(0, cmd.indexOf("\" ") + 1); + else + jvm = cmd.substring(0, cmd.indexOf(" ")); + + return jvm; + } + + public String getCommandInternal() + { + if (_pid < 0) + return null; + String cmd = String.format("ps -p %1$s -o command", _pid); + String res = _utils.osCommand(cmd, 5000); + if (res == null) + return null; + String[] resx = res.split(System.getProperty("line.separator")); + return resx[1]; + } + + public String getUserInternal() + { + if (_pid < 0) + return null; + String cmd = String.format("ps -p %1$s -o user", _pid); + String res = _utils.osCommand(cmd, 5000); + if (res == null) + return null; + String[] resx = res.split(System.getProperty("line.separator")); + return resx[1]; + } + + public String getWorkingDirInternal() + { + if (_pid < 0) + return null; + return null; + } + + public static Process getProcess(int pid) + { + MacOsXProcess result = null; + result = new MacOsXProcess(); + result.setPid(pid); + result.setUser(result.getUserInternal()); + result.setCommand(result.getCommandInternal()); + result.setWorkingDir(result.getWorkingDirInternal()); + if (result.getCommand() == null) + return null; + return result; + } + + public void waitFor(long timeout) + { + long start = System.currentTimeMillis(); + while (System.currentTimeMillis() - start < timeout) + { + if (!isRunning()) + return; + try + { + Thread.sleep(100); + } + catch (InterruptedException e) + { + if (_logger != null) + _logger.throwing(PosixProcess.class.getName(), "waitFor", e); + Thread.currentThread().interrupt(); + } + } + + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/bsd/macosx/MacOsXProcessManager.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/bsd/macosx/MacOsXProcessManager.java new file mode 100644 index 0000000000..bad3c651df --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/bsd/macosx/MacOsXProcessManager.java @@ -0,0 +1,18 @@ +package org.rzo.yajsw.os.posix.bsd.macosx; + +import org.rzo.yajsw.os.Process; +import org.rzo.yajsw.os.posix.PosixProcessManager; + +public class MacOsXProcessManager extends PosixProcessManager +{ + public Process createProcess() + { + return (Process) new MacOsXProcess(); + } + + public Process getProcess(int pid) + { + return MacOsXProcess.getProcess(pid); + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/bsd/macosx/MacOsXService.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/bsd/macosx/MacOsXService.java new file mode 100644 index 0000000000..77993f70d4 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/bsd/macosx/MacOsXService.java @@ -0,0 +1,242 @@ +package org.rzo.yajsw.os.posix.bsd.macosx; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.util.Arrays; +import java.util.logging.Logger; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.apache.velocity.Template; +import org.apache.velocity.VelocityContext; +import org.apache.velocity.app.VelocityEngine; +import org.rzo.yajsw.Constants; +import org.rzo.yajsw.boot.WrapperLoader; +import org.rzo.yajsw.os.AbstractService; +import org.rzo.yajsw.os.JavaHome; +import org.rzo.yajsw.os.OperatingSystem; +import org.rzo.yajsw.os.posix.Utils; +import org.rzo.yajsw.os.posix.VelocityLog; + +public class MacOsXService extends AbstractService implements Constants +{ + String _launchdDir; + String _plistTemplate; + String _plistFile; + int _stopTimeout; + String _plistName; + + String _execCmd; + + String _confFile; + Utils _utils = new Utils(); + + public void init() + { + if (_name == null) + { + System.out.println("no name for daemon -> abort"); + return; + } + _launchdDir = _config.getString("wrapper.launchd.dir", System.getProperty("user.home") + "/Library/LaunchAgents"); + File daemonDir = new File(_launchdDir); + if (!daemonDir.exists() || !daemonDir.isDirectory()) + { + System.out.println("Error " + _launchdDir + " : is not a directory"); + return; + } + String wrapperJar = WrapperLoader.getWrapperJar().trim(); + String wrapperHome = "."; + try + { + wrapperHome = new File(wrapperJar).getParentFile().getCanonicalPath(); + } + catch (IOException e1) + { + e1.printStackTrace(); + } + String confFile = _config.getString("wrapper.config"); + String confDir = null; + if (confFile != null) + { + File f = new File(confFile); + if (f.exists()) + try + { + confDir = f.getParentFile().getCanonicalPath(); + } + catch (IOException e) + { + } + } + if (confDir == null) + confDir = wrapperHome + "/conf"; + if (confFile == null) + { + System.out.println("no conf file found -> abort"); + return; + } + try + { + _confFile = new File(confFile).getCanonicalPath(); + } + catch (IOException e) + { + e.printStackTrace(); + } + _plistTemplate = _config.getString("wrapper.launchd.template", wrapperHome + "/templates/launchd.plist.vm"); + File daemonTemplate = new File(_plistTemplate); + if (!daemonTemplate.exists() || !daemonTemplate.isFile()) + { + System.out.println("Error " + _plistTemplate + " : template file not found"); + return; + } + File daemonScript = new File(daemonDir, "wrapper." + getName()); + if (daemonScript.exists()) + System.out.println(daemonScript.getAbsolutePath() + " already exists -> overwrite"); + + _plistName = "wrapper." + _name; + File plistFile = new File(_launchdDir, _plistName + ".plist"); + try + { + _plistFile = plistFile.getCanonicalPath(); + } + catch (IOException e) + { + e.printStackTrace(); + } + JavaHome javaHome = OperatingSystem.instance().getJavaHome(_config); + String java = System.clearProperty("java.home") + "/bin/java"; + try + { + java = new File(java).getCanonicalPath(); + } + catch (IOException e) + { + e.printStackTrace(); + } + + _execCmd = String.format("%1$s -Dwrapper.service=true -Dwrapper.visible=false -jar %2$s -c %3$s", java, wrapperJar, _confFile); + + } + + public boolean install() + { + if (_plistFile == null) + { + System.out.println("Error : not initialized -> abort"); + return false; + } + try + { + File daemonTemplate = new File(_plistTemplate); + VelocityEngine ve = new VelocityEngine(); + ve.setProperty(VelocityEngine.RESOURCE_LOADER, "file"); + ve.setProperty("file.resource.loader.path", daemonTemplate.getParent()); + ve.setProperty("runtime.log.logsystem.class", VelocityLog.class.getCanonicalName()); + ve.init(); + Template t = ve.getTemplate(daemonTemplate.getName()); + VelocityContext context = new VelocityContext(); + context.put("name", _plistName); + context.put("command", Arrays.asList(_execCmd.split(" "))); + context.put("autoStart", "AUTOMATIC".equals(_config.getString("wrapper.ntservice.starttype", DEFAULT_SERVICE_START_TYPE))); + FileWriter writer = new FileWriter(_plistFile); + + t.merge(context, writer); + writer.flush(); + writer.close(); + _utils.osCommand("launchctl load " + _plistFile, 5000); + + } + catch (Exception ex) + { + ex.printStackTrace(); + return false; + } + return isInstalled(); + } + + public boolean isInstalled() + { + String sp = String.format(".*\\d+.*%1$s.*", _plistName); + Pattern p = Pattern.compile(sp, Pattern.DOTALL); + String list = _utils.osCommand("launchctl list", 5000); + Matcher m = p.matcher(list); + return m.matches(); + } + + public boolean isRunning() + { + int pid = getPid(); + return pid > 0; + } + + public boolean start() + { + if (isRunning()) + { + System.out.println("already running"); + return true; + } + _utils.osCommand("launchctl start " + _plistName, 5000); + return isRunning(); + } + + public boolean stop() + { + if (isRunning()) + { + _utils.osCommand("launchctl stop " + _plistName, 5000); + return !isRunning(); + } + return true; + } + + public boolean uninstall() + { + if (isRunning()) + stop(); + _utils.osCommand("launchctl unload " + _plistFile, 5000); + new File(_plistFile).delete(); + return true; + } + + public int state() + { + int result = 0; + if (isInstalled()) + result |= STATE_INSTALLED; + if (isRunning()) + result |= STATE_RUNNING; + return result; + } + + public int getPid() + { + try + { + String sp = String.format("(\\d+)\\s*\\-\\s*%1$s", _plistName); + Pattern p = Pattern.compile(sp, Pattern.DOTALL); + String list = _utils.osCommand("launchctl list", 5000); + Matcher m = p.matcher(list); + m.find(); + int pid = Integer.parseInt(m.group(1)); + return pid; + } + catch (Exception ex) + { + // ex.printStackTrace(); + } + + return -1; + + } + + public void setLogger(Logger logger) + { + super.setLogger(logger); + _utils.setLog(logger); + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/bsd/macosx/MacOsXServiceManager.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/bsd/macosx/MacOsXServiceManager.java new file mode 100644 index 0000000000..7664e71d59 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/bsd/macosx/MacOsXServiceManager.java @@ -0,0 +1,35 @@ +package org.rzo.yajsw.os.posix.bsd.macosx; + +import java.util.Map; + +import org.rzo.yajsw.os.Service; +import org.rzo.yajsw.os.ServiceInfo; +import org.rzo.yajsw.os.ServiceManager; + +public class MacOsXServiceManager implements ServiceManager +{ + + public Service createService() + { + return new MacOsXService(); + } + + public Service getService(String name) + { + // TODO Auto-generated method stub + return null; + } + + public Map getServiceList() + { + // TODO Auto-generated method stub + return null; + } + + public ServiceInfo getServiceInfo(String name) + { + // TODO Auto-generated method stub + return null; + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/bsd/macosx/MacOsXSystemInformation.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/bsd/macosx/MacOsXSystemInformation.java new file mode 100644 index 0000000000..0eb2bf2cd1 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/bsd/macosx/MacOsXSystemInformation.java @@ -0,0 +1,28 @@ +package org.rzo.yajsw.os.posix.bsd.macosx; + +import java.util.logging.Logger; + +import org.rzo.yajsw.os.SystemInformation; + +public class MacOsXSystemInformation implements SystemInformation +{ + + public long freeRAM() + { + // TODO Auto-generated method stub + return 0; + } + + public long totalRAM() + { + // TODO Auto-generated method stub + return 0; + } + + public void setLogger(Logger logger) + { + // TODO Auto-generated method stub + + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/bsd/macosx/OperatingSystemMacOsX.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/bsd/macosx/OperatingSystemMacOsX.java new file mode 100644 index 0000000000..65277dbe75 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/bsd/macosx/OperatingSystemMacOsX.java @@ -0,0 +1,77 @@ +package org.rzo.yajsw.os.posix.bsd.macosx; + +import org.apache.commons.configuration.Configuration; +import org.rzo.yajsw.os.ErrorHandler; +import org.rzo.yajsw.os.FileManager; +import org.rzo.yajsw.os.JavaHome; +import org.rzo.yajsw.os.Keyboard; +import org.rzo.yajsw.os.ProcessManager; +import org.rzo.yajsw.os.ServiceManager; +import org.rzo.yajsw.os.SystemInformation; +import org.rzo.yajsw.os.posix.OperatingSystemPosix; +import org.rzo.yajsw.os.posix.PosixProcess; + +public class OperatingSystemMacOsX extends OperatingSystemPosix +{ + + private static ProcessManager _processManagerInstance; + private static ServiceManager _serviceManagerInstance; + private static SystemInformation _systemInformation = new MacOsXSystemInformation(); + private static FileManager _fileManagerInstance; + + @Override + public ErrorHandler errorHandlerInstance() + { + return new MacOsXErrorHandler(); + } + + @Override + public JavaHome getJavaHome(Configuration config) + { + return new MacOsXJavaHome(config); + } + + @Override + public Keyboard keyboardInstance() + { + return null; + } + + @Override + public boolean setWorkingDir(String name) + { + return new MacOsXProcess().changeWorkingDir(name); + } + + + @Override + public ProcessManager processManagerInstance() + { + if (_processManagerInstance == null) + _processManagerInstance = new MacOsXProcessManager(); + return _processManagerInstance; + } + + @Override + public ServiceManager serviceManagerInstance() + { + if (_serviceManagerInstance == null) + _serviceManagerInstance = new MacOsXServiceManager(); + return _serviceManagerInstance; + } + + @Override + public SystemInformation systemInformation() + { + return _systemInformation; + } + + @Override + public FileManager fileManagerInstance() + { + if (_fileManagerInstance == null) + _fileManagerInstance = new MacOsXFileManager(); + return _fileManagerInstance; + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/linux/LinuxErrorHandler.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/linux/LinuxErrorHandler.java new file mode 100644 index 0000000000..b8a50ff0de --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/linux/LinuxErrorHandler.java @@ -0,0 +1,8 @@ +package org.rzo.yajsw.os.posix.linux; + +import org.rzo.yajsw.os.posix.PosixErrorHandler; + +public class LinuxErrorHandler extends PosixErrorHandler +{ + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/linux/LinuxFileManager.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/linux/LinuxFileManager.java new file mode 100644 index 0000000000..a30dde5ff8 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/linux/LinuxFileManager.java @@ -0,0 +1,8 @@ +package org.rzo.yajsw.os.posix.linux; + +import org.rzo.yajsw.os.posix.PosixFileManager; + +public class LinuxFileManager extends PosixFileManager +{ + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/linux/LinuxJavaHome.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/linux/LinuxJavaHome.java new file mode 100644 index 0000000000..7ee90bdb3b --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/linux/LinuxJavaHome.java @@ -0,0 +1,14 @@ +package org.rzo.yajsw.os.posix.linux; + +import org.apache.commons.configuration.Configuration; +import org.rzo.yajsw.os.posix.PosixJavaHome; + +public class LinuxJavaHome extends PosixJavaHome +{ + + public LinuxJavaHome(Configuration config) + { + super(config); + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/linux/LinuxKeyboard.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/linux/LinuxKeyboard.java new file mode 100644 index 0000000000..267ff52bf1 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/linux/LinuxKeyboard.java @@ -0,0 +1,8 @@ +package org.rzo.yajsw.os.posix.linux; + +import org.rzo.yajsw.os.posix.PosixKeyboard; + +public class LinuxKeyboard extends PosixKeyboard +{ + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/linux/LinuxProcess.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/linux/LinuxProcess.java new file mode 100644 index 0000000000..a709d60b49 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/linux/LinuxProcess.java @@ -0,0 +1,8 @@ +package org.rzo.yajsw.os.posix.linux; + +import org.rzo.yajsw.os.posix.PosixProcess; + +public class LinuxProcess extends PosixProcess +{ + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/linux/LinuxProcessManager.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/linux/LinuxProcessManager.java new file mode 100644 index 0000000000..10efb0cf4e --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/linux/LinuxProcessManager.java @@ -0,0 +1,7 @@ +package org.rzo.yajsw.os.posix.linux; + +import org.rzo.yajsw.os.posix.PosixProcessManager; + +public class LinuxProcessManager extends PosixProcessManager +{ +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/linux/LinuxServiceManager.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/linux/LinuxServiceManager.java new file mode 100644 index 0000000000..737de6651f --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/linux/LinuxServiceManager.java @@ -0,0 +1,8 @@ +package org.rzo.yajsw.os.posix.linux; + +import org.rzo.yajsw.os.posix.PosixServiceManager; + +public class LinuxServiceManager extends PosixServiceManager +{ + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/linux/OperatingSystemLinux.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/linux/OperatingSystemLinux.java new file mode 100644 index 0000000000..7e5b8804d4 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/linux/OperatingSystemLinux.java @@ -0,0 +1,83 @@ +/* This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + *

    + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ +package org.rzo.yajsw.os.posix.linux; + +import org.apache.commons.configuration.Configuration; +import org.rzo.yajsw.os.ErrorHandler; +import org.rzo.yajsw.os.FileManager; +import org.rzo.yajsw.os.JavaHome; +import org.rzo.yajsw.os.Keyboard; +import org.rzo.yajsw.os.ProcessManager; +import org.rzo.yajsw.os.ServiceManager; +import org.rzo.yajsw.os.SystemInformation; +import org.rzo.yajsw.os.posix.OperatingSystemPosix; +import org.rzo.yajsw.os.posix.PosixSystemInformation; + +// TODO: Auto-generated Javadoc +/** + * The Class OperatingSystemWindowsXP. + */ +public class OperatingSystemLinux extends OperatingSystemPosix +{ + + private static ProcessManager _processManagerInstance; + private static ServiceManager _serviceManagerInstance; + private static SystemInformation _systemInformation = new PosixSystemInformation(); + private static FileManager _fileManagerInstance; + + @Override + public ErrorHandler errorHandlerInstance() + { + return new LinuxErrorHandler(); + } + + @Override + public JavaHome getJavaHome(Configuration config) + { + return new LinuxJavaHome(config); + } + + @Override + public Keyboard keyboardInstance() + { + return null; + } + + @Override + public ProcessManager processManagerInstance() + { + if (_processManagerInstance == null) + _processManagerInstance = new LinuxProcessManager(); + return _processManagerInstance; + } + + @Override + public ServiceManager serviceManagerInstance() + { + if (_serviceManagerInstance == null) + _serviceManagerInstance = new LinuxServiceManager(); + return _serviceManagerInstance; + } + + @Override + public SystemInformation systemInformation() + { + return _systemInformation; + } + + @Override + public FileManager fileManagerInstance() + { + if (_fileManagerInstance == null) + _fileManagerInstance = new LinuxFileManager(); + return _fileManagerInstance; + } +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/solaris/OperatingSystemSolaris.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/solaris/OperatingSystemSolaris.java new file mode 100644 index 0000000000..0e3d6e5403 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/solaris/OperatingSystemSolaris.java @@ -0,0 +1,75 @@ +package org.rzo.yajsw.os.posix.solaris; + +import org.apache.commons.configuration.Configuration; +import org.rzo.yajsw.os.ErrorHandler; +import org.rzo.yajsw.os.FileManager; +import org.rzo.yajsw.os.JavaHome; +import org.rzo.yajsw.os.Keyboard; +import org.rzo.yajsw.os.ProcessManager; +import org.rzo.yajsw.os.ServiceManager; +import org.rzo.yajsw.os.SystemInformation; +import org.rzo.yajsw.os.posix.OperatingSystemPosix; + +public class OperatingSystemSolaris extends OperatingSystemPosix +{ + + private static ProcessManager _processManagerInstance; + private static ServiceManager _serviceManagerInstance; + private static SystemInformation _systemInformation = new SolarisSystemInformation(); + private static FileManager _fileManagerInstance; + + @Override + public ErrorHandler errorHandlerInstance() + { + return new SolarisErrorHandler(); + } + + @Override + public boolean setWorkingDir(String name) + { + return new SolarisProcess().changeWorkingDir(name); + } + + @Override + public JavaHome getJavaHome(Configuration config) + { + return new SolarisJavaHome(config); + } + + @Override + public Keyboard keyboardInstance() + { + return null; + } + + @Override + public ProcessManager processManagerInstance() + { + if (_processManagerInstance == null) + _processManagerInstance = new SolarisProcessManager(); + return _processManagerInstance; + } + + @Override + public ServiceManager serviceManagerInstance() + { + if (_serviceManagerInstance == null) + _serviceManagerInstance = new SolarisServiceManager(); + return _serviceManagerInstance; + } + + @Override + public SystemInformation systemInformation() + { + return _systemInformation; + } + + @Override + public FileManager fileManagerInstance() + { + if (_fileManagerInstance == null) + _fileManagerInstance = new SolarisFileManager(); + return _fileManagerInstance; + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/solaris/SolarisErrorHandler.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/solaris/SolarisErrorHandler.java new file mode 100644 index 0000000000..36ed9a179c --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/solaris/SolarisErrorHandler.java @@ -0,0 +1,21 @@ +package org.rzo.yajsw.os.posix.solaris; + +import org.rzo.yajsw.os.ErrorHandler; +import org.rzo.yajsw.os.OsException; + +public class SolarisErrorHandler implements ErrorHandler +{ + + public void throwException(int id) throws OsException + { + // TODO Auto-generated method stub + + } + + public String toString(int id) + { + // TODO Auto-generated method stub + return null; + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/solaris/SolarisFileManager.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/solaris/SolarisFileManager.java new file mode 100644 index 0000000000..d3ea5658bc --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/solaris/SolarisFileManager.java @@ -0,0 +1,8 @@ +package org.rzo.yajsw.os.posix.solaris; + +import org.rzo.yajsw.os.posix.PosixFileManager; + +public class SolarisFileManager extends PosixFileManager +{ + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/solaris/SolarisJavaHome.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/solaris/SolarisJavaHome.java new file mode 100644 index 0000000000..1c902fdc38 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/solaris/SolarisJavaHome.java @@ -0,0 +1,14 @@ +package org.rzo.yajsw.os.posix.solaris; + +import org.apache.commons.configuration.Configuration; +import org.rzo.yajsw.os.posix.PosixJavaHome; + +public class SolarisJavaHome extends PosixJavaHome +{ + + public SolarisJavaHome(Configuration config) + { + super(config); + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/solaris/SolarisProcess.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/solaris/SolarisProcess.java new file mode 100644 index 0000000000..e4ab76d136 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/solaris/SolarisProcess.java @@ -0,0 +1,270 @@ +package org.rzo.yajsw.os.posix.solaris; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.channels.FileChannel; + +import org.rzo.yajsw.os.Process; +import org.rzo.yajsw.os.posix.PosixProcess; +import org.rzo.yajsw.os.posix.bsd.macosx.MacOsXProcess; + +import com.sun.jna.Memory; + +public class SolarisProcess extends PosixProcess +{ + + /* + * #define PRARGSZ 80 /* number of chars of arguments typedef struct psinfo + * { 0int pr_flag; /* process flags (DEPRECATED; do not use) 4int pr_nlwp; + * /* number of active lwps in the process 8pid_t pr_pid; /* unique process + * id 12pid_t pr_ppid; /* process id of parent 16pid_t pr_pgid; /* pid of + * process group leader 20pid_t pr_sid; /* session id 24uid_t pr_uid; /* + * real user id 28uid_t pr_euid; /* effective user id 32gid_t pr_gid; /* + * real group id 36gid_t pr_egid; /* effective group id 40uintptr_t pr_addr; + * /* address of process 44size_t pr_size; /* size of process image in + * Kbytes 48size_t pr_rssize; /* resident set size in Kbytes 52size_t + * pr_pad1; 56dev_t pr_ttydev; /* controlling tty device (or PRNODEV) /* The + * following percent numbers are 16-bit binary /* fractions [0 .. 1] with + * the binary point to the /* right of the high-order bit (1.0 == 0x8000) + * 60ushort_t pr_pctcpu; /* % of recent cpu time used by all lwps 62ushort_t + * pr_pctmem; /* % of system memory used by process 62timestruc_t pr_start; + * /* process start time, from the epoch 94timestruc_t pr_time; /* usr+sys + * cpu time for this process 126timestruc_t pr_ctime; /* usr+sys cpu time + * for reaped children 158char pr_fname[PRFNSZ]; /* name of execed file char + * pr_psargs[PRARGSZ]; /* initial characters of arg list int pr_wstat; /* if + * zombie, the wait() status int pr_argc; /* initial argument count + * uintptr_t pr_argv; /* address of initial argument vector uintptr_t + * pr_envp; /* address of initial environment vector char pr_dmodel; /* data + * model of the process char pr_pad2[3]; taskid_t pr_taskid; /* task id + * projid_t pr_projid; /* project id int pr_nzomb; /* number of zombie lwps + * in the process poolid_t pr_poolid; /* pool id zoneid_t pr_zoneid; /* zone + * id id_t pr_contract; /* process contract int pr_filler[1]; /* reserved + * for future use lwpsinfo_t pr_lwp; /* information for representative lwp } + * psinfo_t; + */ + + static int PRARGSZ = 80; /* number of chars of arguments */ + + public static class psinfo + { + ByteBuffer _b; + + psinfo(ByteBuffer b) + { + _b = b; + } + + int getPid() + { + return _b.getInt(8); + } + + int getNlwp() + { + return _b.getInt(4); + } + + int getPctcpu() + { + return (int) (((double) _b.getShort(60)) * 100) / 0x8000; + } + + int getPr_size() + { + return _b.getInt(44); + } + + } + + public int getStdOutNo() + { + return 1; + } + + public int getStdErrNo() + { + return 2; + } + + public int getStdInNo() + { + return 3; + } + + + public static Process getProcess(int pid) + { + SolarisProcess result = null; + File f = new File("/proc/" + pid); + if (f.exists()) + { + result = new SolarisProcess(); + result._pid = pid; + result._user = result.getUserInternal(); + result._cmd = result.getCommandInternal(); + result._workingDir = result.getWorkingDirInternal(); + } + return result; + } + + private String getCommandInternal() + { + String result = _utils.osCommand(String.format("pargs -l %1$s", _pid), 5000); + if (result == null) + result = "?"; + // System.out.println("cmd line: "+result); + return result; + } + + public String getUserInternal() + { + if (_pid < 0) + return null; + String cmd = String.format("ps -p %1$s -o user", _pid); + String res = _utils.osCommand(cmd, 5000); + if (res == null) + return null; + String[] resx = res.split(System.getProperty("line.separator")); + return resx[1]; + } + + private psinfo getPsinfo() + { + FileChannel in; + try + { + in = new FileInputStream(String.format("/proc/%1$s/psinfo", _pid)).getChannel(); + } + catch (FileNotFoundException e) + { + System.out.println("error in getCurrentThreads() " + e.getMessage()); + return null; + } + int size; + try + { + size = (int) in.size(); + } + catch (IOException e) + { + System.out.println("error in getCurrentThreads() " + e.getMessage()); + return null; + } + ByteBuffer b = ByteBuffer.allocateDirect(size); + b.order(ByteOrder.LITTLE_ENDIAN); + try + { + in.read(b); + } + catch (IOException e) + { + System.out.println("error in getCurrent*() " + e.getMessage()); + return null; + } + psinfo s = new psinfo(b); + try + { + in.close(); + } + catch (IOException e) + { + e.printStackTrace(); + } + return s; + } + + public int getCurrentCpu() + { + int result = -1; + if (!isRunning()) + return result; + psinfo s = getPsinfo(); + if (s == null) + return -1; + return s.getPctcpu(); + } + + public int getCurrentThreads() + { + int result = -1; + if (!isRunning()) + return result; + psinfo s = getPsinfo(); + if (s == null) + return -1; + return s.getNlwp(); + } + + public int getCurrentVirtualMemory() + { + int result = -1; + if (!isRunning()) + return result; + psinfo s = getPsinfo(); + if (s == null) + return -1; + return s.getPr_size() * 1024; + } + + protected String getWorkingDirInternal() + { + String result = ""; + String cwd; + boolean success = false; + String procCwd = "/proc/" + getPid() + "/cwd"; + try + { + + short BUFSIZE = 4096; + Memory dir = new Memory(BUFSIZE); + dir.clear(); + + if( CLibrary.INSTANCE.getcwd(dir, BUFSIZE) != null ) + { + cwd = dir.getString(0); + dir.clear(); + + if( CLibrary.INSTANCE.chdir(procCwd) == 0 ) + { + if( CLibrary.INSTANCE.getcwd(dir, BUFSIZE) != null ){ + result = new File(dir.getString(0)).getCanonicalPath(); + success = true; + } + // Restore starting directory ( if different ) + CLibrary.INSTANCE.chdir(cwd); + } + + } + + if( !success ) + System.out.println("error reading process working dir -> please edit wrapper.working.dir in configuration file"); + + } catch (IOException e){ } + + return result; + +//-KBG: short BUFSIZE = 512; +//-KBG: Memory result = new Memory(BUFSIZE); +//-KBG: result.clear(); +//-KBG: short size = CLibrary.INSTANCE.readlink(f, result, (short) (BUFSIZE - 1)); +//-KBG: if (size <= 0) +//-KBG: { +//-KBG: System.out.println("error reading process working dir -> please edit wrapper.working.dir in configuration file"); +//-KBG: return f; +//-KBG: } +//-KBG: result.setByte((long) size, (byte) 0); +//-KBG: return result.getString(0); + + /* + * String result = null; File f = new File("/proc/" + getPid() + + * "/cwd"); try { result = f.getCanonicalPath(); } catch (IOException e) + * { e.printStackTrace(); } return result; + */ + + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/solaris/SolarisProcessManager.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/solaris/SolarisProcessManager.java new file mode 100644 index 0000000000..53810ffae0 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/solaris/SolarisProcessManager.java @@ -0,0 +1,18 @@ +package org.rzo.yajsw.os.posix.solaris; + +import org.rzo.yajsw.os.Process; +import org.rzo.yajsw.os.posix.PosixProcessManager; + +public class SolarisProcessManager extends PosixProcessManager +{ + public Process createProcess() + { + return (Process) new SolarisProcess(); + } + + public Process getProcess(int pid) + { + return SolarisProcess.getProcess(pid); + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/solaris/SolarisService.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/solaris/SolarisService.java new file mode 100644 index 0000000000..80f1c40a61 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/solaris/SolarisService.java @@ -0,0 +1,243 @@ +package org.rzo.yajsw.os.posix.solaris; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.util.Arrays; +import java.util.logging.Logger; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.apache.velocity.Template; +import org.apache.velocity.VelocityContext; +import org.apache.velocity.app.VelocityEngine; +import org.rzo.yajsw.Constants; +import org.rzo.yajsw.boot.WrapperLoader; +import org.rzo.yajsw.os.AbstractService; +import org.rzo.yajsw.os.JavaHome; +import org.rzo.yajsw.os.OperatingSystem; +import org.rzo.yajsw.os.posix.Utils; +import org.rzo.yajsw.os.posix.VelocityLog; + +public class SolarisService extends AbstractService implements Constants +{ + String _launchdDir; + String _plistTemplate; + String _plistFile; + int _stopTimeout; + String _plistName; + + String _execCmd; + + String _confFile; + + Utils _utils = new Utils(); + + public void init() + { + if (_name == null) + { + System.out.println("no name for daemon -> abort"); + return; + } + _launchdDir = _config.getString("wrapper.launchd.dir", System.getProperty("user.home") + "/Library/LaunchAgents"); + File daemonDir = new File(_launchdDir); + if (!daemonDir.exists() || !daemonDir.isDirectory()) + { + System.out.println("Error " + _launchdDir + " : is not a directory"); + return; + } + String wrapperJar = WrapperLoader.getWrapperJar().trim(); + String wrapperHome = "."; + try + { + wrapperHome = new File(wrapperJar).getParentFile().getCanonicalPath(); + } + catch (IOException e1) + { + e1.printStackTrace(); + } + String confFile = _config.getString("wrapper.config"); + String confDir = null; + if (confFile != null) + { + File f = new File(confFile); + if (f.exists()) + try + { + confDir = f.getParentFile().getCanonicalPath(); + } + catch (IOException e) + { + } + } + if (confDir == null) + confDir = wrapperHome + "/conf"; + if (confFile == null) + { + System.out.println("no conf file found -> abort"); + return; + } + try + { + _confFile = new File(confFile).getCanonicalPath(); + } + catch (IOException e) + { + e.printStackTrace(); + } + _plistTemplate = _config.getString("wrapper.launchd.template", wrapperHome + "templeates/launchd.plist.vm"); + File daemonTemplate = new File(_plistTemplate); + if (!daemonTemplate.exists() || !daemonTemplate.isFile()) + { + System.out.println("Error " + _plistTemplate + " : template file not found"); + return; + } + File daemonScript = new File(daemonDir, "wrapper." + getName()); + if (daemonScript.exists()) + System.out.println(daemonScript.getAbsolutePath() + " already exists -> overwrite"); + + _plistName = "wrapper." + _name; + File plistFile = new File(_launchdDir, _plistName + ".plist"); + try + { + _plistFile = plistFile.getCanonicalPath(); + } + catch (IOException e) + { + e.printStackTrace(); + } + JavaHome javaHome = OperatingSystem.instance().getJavaHome(_config); + String java = System.clearProperty("java.home") + "/bin/java"; + try + { + java = new File(java).getCanonicalPath(); + } + catch (IOException e) + { + e.printStackTrace(); + } + + _execCmd = String.format("%1$s -Dwrapper.service=true -Dwrapper.visible=false -jar %2$s -c %3$s", java, wrapperJar, _confFile); + + } + + public boolean install() + { + if (_plistFile == null) + { + System.out.println("Error : not initialized -> abort"); + return false; + } + try + { + File daemonTemplate = new File(_plistTemplate); + VelocityEngine ve = new VelocityEngine(); + ve.setProperty(VelocityEngine.RESOURCE_LOADER, "file"); + ve.setProperty("file.resource.loader.path", daemonTemplate.getParent()); + ve.setProperty("runtime.log.logsystem.class", VelocityLog.class.getCanonicalName()); + ve.init(); + Template t = ve.getTemplate(daemonTemplate.getName()); + VelocityContext context = new VelocityContext(); + context.put("name", _plistName); + context.put("command", Arrays.asList(_execCmd.split(" "))); + context.put("autoStart", "AUTOMATIC".equals(_config.getString("wrapper.ntservice.starttype", DEFAULT_SERVICE_START_TYPE))); + FileWriter writer = new FileWriter(_plistFile); + + t.merge(context, writer); + writer.flush(); + writer.close(); + _utils.osCommand("launchctl load " + _plistFile, 5000); + + } + catch (Exception ex) + { + ex.printStackTrace(); + return false; + } + return isInstalled(); + } + + public boolean isInstalled() + { + String sp = String.format(".*\\d+.*%1$s.*", _plistName); + Pattern p = Pattern.compile(sp, Pattern.DOTALL); + String list = _utils.osCommand("launchctl list", 5000); + Matcher m = p.matcher(list); + return m.matches(); + } + + public boolean isRunning() + { + int pid = getPid(); + return pid > 0; + } + + public boolean start() + { + if (isRunning()) + { + System.out.println("already running"); + return true; + } + _utils.osCommand("launchctl start " + _plistName, 5000); + return isRunning(); + } + + public boolean stop() + { + if (isRunning()) + { + _utils.osCommand("launchctl stop " + _plistName, 5000); + return !isRunning(); + } + return true; + } + + public boolean uninstall() + { + if (isRunning()) + stop(); + _utils.osCommand("launchctl unload " + _plistFile, 5000); + new File(_plistFile).delete(); + return true; + } + + public int state() + { + int result = 0; + if (isInstalled()) + result |= STATE_INSTALLED; + if (isRunning()) + result |= STATE_RUNNING; + return result; + } + + public int getPid() + { + try + { + String sp = String.format("(\\d+)\\s*\\-\\s*%1$s", _plistName); + Pattern p = Pattern.compile(sp, Pattern.DOTALL); + String list = _utils.osCommand("launchctl list", 5000); + Matcher m = p.matcher(list); + m.find(); + int pid = Integer.parseInt(m.group(1)); + return pid; + } + catch (Exception ex) + { + // ex.printStackTrace(); + } + + return -1; + + } + + public void setLogger(Logger logger) + { + super.setLogger(logger); + _utils.setLog(logger); + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/solaris/SolarisServiceManager.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/solaris/SolarisServiceManager.java new file mode 100644 index 0000000000..1f606cd06b --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/solaris/SolarisServiceManager.java @@ -0,0 +1,18 @@ +package org.rzo.yajsw.os.posix.solaris; + +import org.rzo.yajsw.os.posix.PosixServiceManager; + +public class SolarisServiceManager extends PosixServiceManager +{ + + /* + * public Service createService() { return new SolarisService(); } + * + * public Service getService(String name) { // TODO Auto-generated method + * stub return null; } + * + * public List getServiceList() { // TODO Auto-generated method stub return + * null; } + */ + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/solaris/SolarisSystemInformation.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/solaris/SolarisSystemInformation.java new file mode 100644 index 0000000000..b6aa1da8b5 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/os/posix/solaris/SolarisSystemInformation.java @@ -0,0 +1,28 @@ +package org.rzo.yajsw.os.posix.solaris; + +import java.util.logging.Logger; + +import org.rzo.yajsw.os.SystemInformation; + +public class SolarisSystemInformation implements SystemInformation +{ + + public long freeRAM() + { + // TODO Auto-generated method stub + return 0; + } + + public long totalRAM() + { + // TODO Auto-generated method stub + return 0; + } + + public void setLogger(Logger logger) + { + // TODO Auto-generated method stub + + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/package.html b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/package.html new file mode 100644 index 0000000000..53486fbbb7 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/package.html @@ -0,0 +1,7 @@ + + + + + Provides... + + \ No newline at end of file diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/quartz/RestartJob.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/quartz/RestartJob.java new file mode 100644 index 0000000000..fc14a1ee65 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/quartz/RestartJob.java @@ -0,0 +1,38 @@ +/* This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + *

    + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ +package org.rzo.yajsw.quartz; + +import org.quartz.Job; +import org.quartz.JobDataMap; +import org.quartz.JobExecutionContext; +import org.quartz.JobExecutionException; +import org.rzo.yajsw.wrapper.WrappedProcess; + +// TODO: Auto-generated Javadoc +/** + * The Class RestartJob. + */ +public class RestartJob implements Job +{ + + /* + * (non-Javadoc) + * + * @see org.quartz.Job#execute(org.quartz.JobExecutionContext) + */ + public void execute(JobExecutionContext context) throws JobExecutionException + { + JobDataMap dataMap = context.getJobDetail().getJobDataMap(); + WrappedProcess process = (WrappedProcess) dataMap.get("process"); + process.startByTimer(); + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/quartz/StartJob.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/quartz/StartJob.java new file mode 100644 index 0000000000..2b4dbd1587 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/quartz/StartJob.java @@ -0,0 +1,38 @@ +/* This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + *

    + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ +package org.rzo.yajsw.quartz; + +import org.quartz.Job; +import org.quartz.JobDataMap; +import org.quartz.JobExecutionContext; +import org.quartz.JobExecutionException; +import org.rzo.yajsw.wrapper.WrappedProcess; + +// TODO: Auto-generated Javadoc +/** + * The Class StartJob. + */ +public class StartJob implements Job +{ + + /* + * (non-Javadoc) + * + * @see org.quartz.Job#execute(org.quartz.JobExecutionContext) + */ + public void execute(JobExecutionContext context) throws JobExecutionException + { + JobDataMap dataMap = context.getJobDetail().getJobDataMap(); + WrappedProcess process = (WrappedProcess) dataMap.get("process"); + process.startByTimer(); + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/quartz/StopJob.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/quartz/StopJob.java new file mode 100644 index 0000000000..2d360b96cb --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/quartz/StopJob.java @@ -0,0 +1,38 @@ +/* This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + *

    + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ +package org.rzo.yajsw.quartz; + +import org.quartz.Job; +import org.quartz.JobDataMap; +import org.quartz.JobExecutionContext; +import org.quartz.JobExecutionException; +import org.rzo.yajsw.wrapper.WrappedProcess; + +// TODO: Auto-generated Javadoc +/** + * The Class StopJob. + */ +public class StopJob implements Job +{ + + /* + * (non-Javadoc) + * + * @see org.quartz.Job#execute(org.quartz.JobExecutionContext) + */ + public void execute(JobExecutionContext context) throws JobExecutionException + { + JobDataMap dataMap = context.getJobDetail().getJobDataMap(); + WrappedProcess process = (WrappedProcess) dataMap.get("process"); + process.stop("TIMER"); + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/quartz/Timer.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/quartz/Timer.java new file mode 100644 index 0000000000..8d936ef6c5 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/quartz/Timer.java @@ -0,0 +1,524 @@ +/* This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + *

    + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ +package org.rzo.yajsw.quartz; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Iterator; + +import org.quartz.CronExpression; +import org.quartz.CronTrigger; +import org.quartz.JobDataMap; +import org.quartz.JobDetail; +import org.quartz.Scheduler; +import org.quartz.SchedulerException; +import org.quartz.SchedulerFactory; +import org.quartz.SimpleTrigger; +import org.quartz.Trigger; +import org.rzo.yajsw.config.YajswConfigurationImpl; +import org.rzo.yajsw.wrapper.WrappedProcess; + +// TODO: Auto-generated Javadoc +/** + * The Class Timer. + */ +public class Timer +{ + + /** The _config. */ + YajswConfigurationImpl _config; + + /** The _wp. */ + WrappedProcess _wp; + + /** The _scheduler. */ + static Scheduler _scheduler; + + /** The _cron start. */ + MyCronTrigger _cronStart; + + /** The _cron stop. */ + MyCronTrigger _cronStop; + + /** The _cron restart. */ + MyCronTrigger _cronRestart; + + /** The _simple start. */ + MySimpleTrigger _simpleStart; + + /** The _simple stop. */ + MySimpleTrigger _simpleStop; + + /** The _simple restart. */ + MySimpleTrigger _simpleRestart; + + /** The _has trigger. */ + boolean _hasTrigger = false; + + /** The _start immediate. */ + boolean _startImmediate = true; + + /** The _triggered. */ + boolean _triggered = false; + + /** + * Instantiates a new timer. + * + * @param config + * the config + * @param wp + * the wp + */ + public Timer(YajswConfigurationImpl config, WrappedProcess wp) + { + _config = config; + _wp = wp; + } + + /** + * Inits the. + */ + public synchronized void init() + { + for (Iterator keys = _config.getKeys("wrapper.timer"); keys.hasNext();) + { + String key = (String) keys.next(); + if (key.contains(".simple.")) + { + if (key.contains(".START.")) + { + if (_simpleStart == null) + _simpleStart = getSimpleTrigger(key); + } + else if (key.contains(".STOP.")) + { + if (_simpleStop == null) + _simpleStop = getSimpleTrigger(key); + } + else if (key.contains(".RESTART.")) + { + if (_simpleRestart == null) + _simpleRestart = getSimpleTrigger(key); + } + else + System.out.println("Cannot interpret timer property: " + key); + } + else if (key.contains(".cron.")) + { + if (key.contains(".START")) + _cronStart = getCronTrigger(key); + else if (key.contains(".STOP")) + _cronStop = getCronTrigger(key); + else if (key.contains(".RESTART")) + _cronRestart = getCronTrigger(key); + else + System.out.println("Cannot interpret timer property: " + key); + } + else + { + System.out.println("Cannot interpret timer property: " + key); + } + } + + } + + /** + * Gets the simple trigger. + * + * @param key + * the key + * + * @return the simple trigger + */ + private MySimpleTrigger getSimpleTrigger(String key) + { + JobDetail jobDetail = new JobDetail(); + JobDataMap jobDataMap = new JobDataMap(); + jobDataMap.put("process", _wp); + jobDetail.setJobDataMap(jobDataMap); + + Class jobClass = getJobClass(key); + if (jobClass == null) + return null; + jobDetail.setJobClass(jobClass); + jobDetail.setName(key); + + MySimpleTrigger trigger = new MySimpleTrigger(jobDetail); + Date startTime = getStartTime(key); + if (startTime != null) + { + trigger.setStartTime(startTime); + } + int repeatCount = getRepeatCount(key); + if (repeatCount > 0) + trigger.setRepeatCount(repeatCount); + int interval = getInterval(key); + if (interval > 0) + trigger.setRepeatInterval(interval * 1000); + _hasTrigger = true; + + if (trigger != null) + trigger.setName(key); + _startImmediate = false; // getStartTime will always return a date. + // per default the current time. + trigger.setMisfireInstruction(SimpleTrigger.MISFIRE_INSTRUCTION_FIRE_NOW); + return trigger; + } + + /** + * Gets the interval. + * + * @param key + * the key + * + * @return the interval + */ + private int getInterval(String key) + { + return _config.getInt(key.substring(0, key.lastIndexOf(".")) + ".INTERVAL", SimpleTrigger.REPEAT_INDEFINITELY); + } + + /** + * Gets the repeat count. + * + * @param key + * the key + * + * @return the repeat count + */ + private int getRepeatCount(String key) + { + return _config.getInt(key.substring(0, key.lastIndexOf(".")) + ".COUNT", -1); + } + + /** + * Gets the start time. + * + * @param key + * the key + * + * @return the start time + */ + private Date getStartTime(String key) + { + String str = _config.getString(key.substring(0, key.lastIndexOf(".")) + ".FIRST"); + if (str == null) + return new Date(); + SimpleDateFormat df = null; + if (str.contains(" ")) + df = new SimpleDateFormat("dd.MM.yyyy HH:mm:ss"); + else + df = new SimpleDateFormat("HH:mm:ss"); + try + { + return df.parse(str); + } + catch (ParseException e) + { + e.printStackTrace(); + return null; + } + } + + /** + * Gets the job class. + * + * @param key + * the key + * + * @return the job class + */ + private Class getJobClass(String key) + { + if (key.contains(".RESTART")) + return RestartJob.class; + else if (key.contains(".STOP")) + return StopJob.class; + else if (key.contains(".START")) + return StartJob.class; + return null; + } + + /** + * Gets the cron trigger. + * + * @param key + * the key + * + * @return the cron trigger + */ + private MyCronTrigger getCronTrigger(String key) + { + JobDetail jobDetail = new JobDetail(); + JobDataMap jobDataMap = new JobDataMap(); + jobDataMap.put("process", _wp); + jobDetail.setJobDataMap(jobDataMap); + jobDetail.setName(key); + + Class jobClass = getJobClass(key); + if (jobClass == null) + return null; + jobDetail.setJobClass(jobClass); + + MyCronTrigger trigger = new MyCronTrigger(jobDetail); + CronExpression cronExpression = getCronExpression(key); + if (cronExpression != null) + { + trigger.setCronExpression(cronExpression); + if (jobClass.equals(StartJob.class)) + _startImmediate = false; + _hasTrigger = true; + } + else + { + return null; + } + + trigger.setName(key); + trigger.setMisfireInstruction(trigger.MISFIRE_INSTRUCTION_FIRE_ONCE_NOW); + return trigger; + } + + /** + * Gets the cron expression. + * + * @param key + * the key + * + * @return the cron expression + */ + private CronExpression getCronExpression(String key) + { + String str = _config.getString(key); + if (str == null) + { + return null; + } + try + { + return new CronExpression(str); + } + catch (ParseException e) + { + e.printStackTrace(); + return null; + } + } + + /** + * Start. + */ + public synchronized void start() + { + if (!_hasTrigger) + return; + if (getScheduler() == null) + return; + try + { + if (!_scheduler.isStarted()) + _scheduler.start(); + } + catch (SchedulerException e) + { + e.printStackTrace(); + return; + } + + if (_cronStart != null) + startTrigger(_cronStart, _cronStart.getJobDetail()); + if (_cronStop != null) + startTrigger(_cronStop, _cronStop.getJobDetail()); + if (_cronRestart != null) + startTrigger(_cronRestart, _cronRestart.getJobDetail()); + if (_simpleStart != null) + startTrigger(_simpleStart, _simpleStart.getJobDetail()); + if (_simpleStop != null) + startTrigger(_simpleStop, _simpleStop.getJobDetail()); + if (_simpleRestart != null) + startTrigger(_simpleRestart, _simpleRestart.getJobDetail()); + _triggered = true; + } + + /** + * Gets the scheduler. + * + * @return the scheduler + */ + private Scheduler getScheduler() + { + if (_scheduler == null) + { + SchedulerFactory schedFact = new org.quartz.impl.StdSchedulerFactory(); + try + { + _scheduler = schedFact.getScheduler(); + } + catch (SchedulerException e) + { + e.printStackTrace(); + _scheduler = null; + } + } + return _scheduler; + } + + /** + * Start trigger. + * + * @param trigger + * the trigger + * @param jobDetail + * the job detail + */ + private void startTrigger(Trigger trigger, JobDetail jobDetail) + { + if (trigger != null) + try + { + _scheduler.scheduleJob(jobDetail, trigger); + } + catch (SchedulerException e) + { + e.printStackTrace(); + } + } + + /** + * Stop. + */ + public void stop() + { + if (!_hasTrigger) + return; + stopTrigger(_cronStart); + stopTrigger(_cronStop); + stopTrigger(_cronRestart); + stopTrigger(_simpleStart); + stopTrigger(_simpleStop); + stopTrigger(_simpleRestart); + + } + + /** + * Stop trigger. + * + * @param trigger + * the trigger + */ + private synchronized void stopTrigger(Trigger trigger) + { + try + { + _scheduler.shutdown(); + } + catch (SchedulerException e) + { + e.printStackTrace(); + return; + } + _triggered = false; + } + + /** + * Checks if is triggered. + * + * @return true, if is triggered + */ + public boolean isTriggered() + { + return _triggered; + } + + /** + * Checks if is start immediate. + * + * @return true, if is start immediate + */ + public boolean isStartImmediate() + { + return _startImmediate; + } + + /** + * Checks if is checks for trigger. + * + * @return true, if is checks for trigger + */ + public boolean isHasTrigger() + { + return _hasTrigger; + } + + /** + * The Class MyCronTrigger. + */ + class MyCronTrigger extends CronTrigger + { + + /** The _job detail. */ + JobDetail _jobDetail; + + /** + * Instantiates a new my cron trigger. + * + * @param jobDetail + * the job detail + */ + MyCronTrigger(JobDetail jobDetail) + { + _jobDetail = jobDetail; + } + + /** + * Gets the job detail. + * + * @return the job detail + */ + JobDetail getJobDetail() + { + return _jobDetail; + } + } + + /** + * The Class MySimpleTrigger. + */ + class MySimpleTrigger extends SimpleTrigger + { + + /** The _job detail. */ + JobDetail _jobDetail; + + /** + * Instantiates a new my simple trigger. + * + * @param jobDetail + * the job detail + */ + MySimpleTrigger(JobDetail jobDetail) + { + _jobDetail = jobDetail; + } + + /** + * Gets the job detail. + * + * @return the job detail + */ + JobDetail getJobDetail() + { + return _jobDetail; + } + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/quartz/package.html b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/quartz/package.html new file mode 100644 index 0000000000..53486fbbb7 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/quartz/package.html @@ -0,0 +1,7 @@ + + + + + Provides... + + \ No newline at end of file diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/script/AbstractScript.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/script/AbstractScript.java new file mode 100644 index 0000000000..3e11aef612 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/script/AbstractScript.java @@ -0,0 +1,147 @@ +/* This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + *

    + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ +package org.rzo.yajsw.script; + +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; +import java.util.concurrent.SynchronousQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +import org.jboss.netty.util.HashedWheelTimer; +import org.jboss.netty.util.Timeout; +import org.jboss.netty.util.Timer; +import org.jboss.netty.util.TimerTask; +import org.rzo.yajsw.util.DaemonThreadFactory; +import org.rzo.yajsw.wrapper.WrappedProcess; + +// TODO: Auto-generated Javadoc +/** + * The Class AbstractScript. + */ +public abstract class AbstractScript implements Script +{ + + /** The _name. */ + String _name; + + /** The _timeout. */ + int _timeout = 30000; + + WrappedProcess _process; + + String _id; + + String[] _args; + + final static Timer TIMER = new HashedWheelTimer(); + static final ExecutorService EXECUTOR = (ThreadPoolExecutor) new ThreadPoolExecutor(0, 50, 120L, TimeUnit.SECONDS, + new SynchronousQueue(), new DaemonThreadFactory("scriptExecutorInternal")); + volatile Future _future; + volatile Timeout _timerTimeout; + + + + /** + * Instantiates a new abstract script. + * + * @param script + * the script + * @param timeout + */ + public AbstractScript(String script, String id, WrappedProcess process, String[] args, int timeout) + { + _name = script; + _process = process; + _id = id; + _args = args; + if (timeout > 0) + _timeout = timeout * 1000; + } + + /* + * (non-Javadoc) + * + * @see org.rzo.yajsw.script.Script#execute(java.lang.String, + * java.lang.String, java.lang.String, java.lang.String, java.lang.String, + * java.lang.String, java.lang.Object) + */ + public abstract Object execute(String line); + public abstract void interrupt(); + abstract void log(String msg); + + synchronized public void executeWithTimeout(final String line) + { + Object result = null; + _timerTimeout = TIMER.newTimeout(new TimerTask() + { + + public void run(Timeout arg0) throws Exception + { + log("script takes too long -> interrupt"); + try + { + interrupt(); + } + catch (Throwable e) + { + + } + } + + } + , _timeout, TimeUnit.MILLISECONDS); + _future = EXECUTOR.submit(new Callable() + { + public Object call() + { + Object result = execute(line); + if (_timerTimeout != null) + _timerTimeout.cancel(); + _timerTimeout = null; + return result; + } + }); + } + + /* + * (non-Javadoc) + * + * @see org.rzo.yajsw.script.Script#getScript() + */ + public String getScript() + { + return _name; + } + + /** + * Gets the timeout. + * + * @return the timeout + */ + public int getTimeout() + { + return _timeout; + } + + /** + * Sets the timeout. + * + * @param timeout + * the new timeout + */ + public void setTimeout(int timeout) + { + _timeout = timeout; + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/script/GroovyScript.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/script/GroovyScript.java new file mode 100644 index 0000000000..47fcd47ecb --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/script/GroovyScript.java @@ -0,0 +1,265 @@ +/* This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + *

    + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ +package org.rzo.yajsw.script; + +import groovy.lang.Binding; +import groovy.lang.GroovyClassLoader; +import groovy.lang.GroovyObject; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang.StringUtils; +import org.apache.commons.vfs2.FileName; +import org.apache.commons.vfs2.FileObject; +import org.codehaus.groovy.control.CompilationFailedException; +import org.jboss.netty.logging.InternalLogger; +import org.rzo.yajsw.boot.WrapperLoader; +import org.rzo.yajsw.util.VFSUtils; +import org.rzo.yajsw.wrapper.WrappedJavaProcess; +import org.rzo.yajsw.wrapper.WrappedProcess; + +/** + * The Class GroovyScript. + */ +public class GroovyScript extends AbstractScript +{ + + public static Map context = Collections.synchronizedMap(new HashMap()); + /** The binding. */ + final Binding binding; + + final InternalLogger _logger; + + volatile GroovyObject _script; + + final boolean _reload; + + final String _encoding; + + + + /** + * Instantiates a new groovy script. + * + * @param script + * the script + * @param timeout + * @throws IOException + * @throws CompilationFailedException + * @throws IllegalAccessException + * @throws InstantiationException + * @throws ClassNotFoundException + */ + public GroovyScript(final String script, final String id, final WrappedProcess process, final String[] args, final int timeout, final InternalLogger logger, String encoding, boolean reload) throws CompilationFailedException, IOException, + InstantiationException, IllegalAccessException, ClassNotFoundException + { + super(script, id, process, args, timeout); + _reload = reload; + _encoding = encoding; + + // let's call some method on an instance + _script = getScriptInstance(script, encoding); + binding = (Binding) _script.invokeMethod("getBinding", null); + binding.setVariable("args", args); + binding.setVariable("callCount", 0); + binding.setVariable("context", context); + if (process != null && logger == null) + _logger = process.getInternalWrapperLogger(); + else + _logger = logger; + binding.setVariable("logger", _logger); + } + + private void setGroovyClasspath(GroovyClassLoader loader) + { + ArrayList cp = WrapperLoader.getGroovyClasspath(); + for (Iterator it = cp.listIterator(); it.hasNext(); ) + loader.addURL((URL)it.next()); + } + + static GroovyClassLoader groovyClassLoader; + + private GroovyObject getScriptInstance(String scriptFileName, String encoding) throws IOException, InstantiationException, + IllegalAccessException, ClassNotFoundException + { + FileObject fileObject = VFSUtils.resolveFile(".", scriptFileName); + FileName fileName = fileObject.getName(); + long lastModified = fileObject.getContent().getLastModifiedTime(); + String scriptName = StringUtils.removeEnd(fileName.getBaseName(), "." + fileName.getExtension()) + "_" + + lastModified; + + synchronized (GroovyScript.class) + { + if (groovyClassLoader == null) + { + groovyClassLoader = new GroovyClassLoader(getClass().getClassLoader()); + setGroovyClasspath(groovyClassLoader); + } + + try + { + Class clazz = Class.forName(scriptName, true, groovyClassLoader); + if (_script == null) + return (GroovyObject) clazz.newInstance(); + else + return _script; + } + catch (ClassNotFoundException e) + { + if (_script != null) + log("script changed -> reloading"); + InputStream in = null; + String scriptSrc = null; + try + { + in = fileObject.getContent().getInputStream(); + if (encoding == null) + scriptSrc = IOUtils.toString(in); + else + scriptSrc = IOUtils.toString(in, encoding); + } + finally + { + if (in != null) + in.close(); + } + return (GroovyObject) groovyClassLoader.parseClass(scriptSrc, scriptName + ".groovy").newInstance(); + } + } + } + + /* + * (non-Javadoc) + * + * @see org.rzo.yajsw.script.AbstractScript#execute(java.lang.String, + * java.lang.String, java.lang.String, java.lang.String, java.lang.String, + * java.lang.String, java.lang.Object) + */ + synchronized public Object execute(String line) + { + Object result = null; + + if (_script == null) + { + System.out.println("cannot execute script " + _name); + return null; + } + if (_reload) + { + GroovyObject script = null; + try + { + script = getScriptInstance(_name, _encoding); + } + catch (Exception e) + { + e.printStackTrace(); + } + if (script != null) + { + if (_script != script) + { + script.invokeMethod("setBinding", binding); + _script = script; + } + } + } + binding.setVariable("id", _id); + if (_process != null) + { + binding.setVariable("state", _process.getStringState()); + binding.setVariable("count", _process.getRestartCount()); + binding.setVariable("pid", _process.getAppPid()); + binding.setVariable("exitCode", _process.getExitCode()); + binding.setVariable("line", line); + binding.setVariable("process", _process); + } + try + { + result = _script.invokeMethod("run", new Object[]{}); + } + catch (Throwable e) + { + if (_logger != null) + _logger.info("execption in script "+this._name, e); + else + e.printStackTrace(); + } + binding.setVariable("callCount", ((Integer) binding.getVariable("callCount")).intValue() + 1); + return result; + } + + public static void main(String[] args) throws Exception, IOException, InstantiationException, IllegalAccessException + { + WrappedJavaProcess w = new WrappedJavaProcess(); + w.getLocalConfiguration().setProperty("wrapper.config", "conf/wrapper.helloworld.conf"); + w.init(); + GroovyScript script = new GroovyScript("./scripts/timeCondition.gv", "id", w, new String[] + { "11", "12" }, 0, null, null, false); + script.execute(); + script.execute(); + script = new GroovyScript("./scripts/fileCondition.gv", "id", w, new String[] + { "anchor.lck" }, 0, null, null, false); + script.execute(); + script.execute(); + script = new GroovyScript("./scripts/snmpTrap.gv", "id", w, new String[] + { "192.168.0.1", "1", "msg" }, 0, null, null, false); + script.execute(); + + } + + public Object execute() + { + return execute(""); + } + + public void executeWithTimeout() + { + executeWithTimeout(""); + } + + public void interrupt() + { + if (_future != null) + _future.cancel(true); + } + + void log(String msg) + { + if (_logger != null) + _logger.info(msg); + else + System.out.println(msg); + } + + public Object invoke(String method, Object ... x ) + { + Object result = null; + try + { + result = _script.invokeMethod(method, x); + } + catch (Exception ex) + { + ex.printStackTrace(); + } + return result; + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/script/RunScript.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/script/RunScript.java new file mode 100644 index 0000000000..a879d39c2a --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/script/RunScript.java @@ -0,0 +1,61 @@ +package org.rzo.yajsw.script; + +import java.io.File; +import java.util.Iterator; + +import org.rzo.yajsw.boot.WrapperLoader; +import org.rzo.yajsw.config.YajswConfigurationImpl; +import org.rzo.yajsw.os.OperatingSystem; +import org.rzo.yajsw.wrapper.WrappedProcess; +import org.rzo.yajsw.wrapper.WrappedProcessFactory; + +public class RunScript +{ + + public static void main(String[] args) + { + String wrapperJar = WrapperLoader.getWrapperJar(); + String homeDir = new File(wrapperJar).getParent(); + if (!OperatingSystem.instance().setWorkingDir(homeDir)) + System.out.println("could not set working dir. pls check configuration or user rights :"+homeDir); + + String configFile = args[0]; + String script = args[1]; + int count = args.length == 3 ? Integer.parseInt(args[3]) : 1; + + // get the script arguments from the configuration + String scriptKey = null; + System.setProperty("wrapper.config", configFile); + YajswConfigurationImpl config = new YajswConfigurationImpl(true); + for (Iterator it = config.getKeys(); it.hasNext();) + { + String key = (String) it.next(); + if (key.contains(".script.") && !key.endsWith(".args") && config.getString(key).equals(script)) + { + scriptKey = key; + break; + } + } + if (scriptKey == null) + { + System.out.println("script not found in configuration -> abort"); + return; + } + String[] scriptArgs = config.getStringArray(scriptKey + ".args"); + + // get a dummy process but do not start it + WrappedProcess process = WrappedProcessFactory.createProcess(config); + process.init(); + + Script s = ScriptFactory.createScript(script, "test", process, scriptArgs, null, 0, config.getString("wrapper.script.encoding"), config.getBoolean("wrapper.script.reload", false), true); + if (s == null) + { + System.out.println("error initializing script " + script); + return; + } + for (int i = 0; i < count; i++) + s.execute(); + Runtime.getRuntime().halt(0); + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/script/RunScriptBooter.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/script/RunScriptBooter.java new file mode 100644 index 0000000000..1f5542bd63 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/script/RunScriptBooter.java @@ -0,0 +1,27 @@ +package org.rzo.yajsw.script; + +import java.lang.reflect.Method; +import java.net.URLClassLoader; + +import org.rzo.yajsw.boot.WrapperLoader; + +public class RunScriptBooter +{ + public static void main(String[] args) + { + URLClassLoader cl = WrapperLoader.getWrapperClassLoader(); + Thread.currentThread().setContextClassLoader(cl); + try + { + Class cls = Class.forName("org.rzo.yajsw.script.RunScript", true, cl); + Method mainMethod = cls.getDeclaredMethod("main", new Class[] + { String[].class }); + mainMethod.invoke(null, new Object[] + { args }); + } + catch (Exception e) + { + e.printStackTrace(); + } + } +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/script/Script.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/script/Script.java new file mode 100644 index 0000000000..bff35f9e1e --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/script/Script.java @@ -0,0 +1,54 @@ +/* This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + *

    + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ +package org.rzo.yajsw.script; + +import org.rzo.yajsw.wrapper.TriggerAction; + +// TODO: Auto-generated Javadoc +/** + * The Interface Script. + */ +public interface Script extends TriggerAction +{ + + /** + * Execute. + * + * @param id + * the id + * @param state + * the state + * @param count + * the count + * @param pid + * the pid + * @param exitCode + * the exit code + * @param line + * the line + * @param wrapperJavaProcess + * the wrapper java process + */ + public Object execute(); + + public Object execute(String line); + + /** + * Gets the script. + * + * @return the script + */ + public String getScript(); + + public void executeWithTimeout(); + + public void executeWithTimeout(String line); +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/script/ScriptFactory.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/script/ScriptFactory.java new file mode 100644 index 0000000000..31d61bb7c9 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/script/ScriptFactory.java @@ -0,0 +1,67 @@ +/* This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + *

    + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ +package org.rzo.yajsw.script; + +import java.util.List; + +import org.jboss.netty.logging.InternalLogger; +import org.rzo.yajsw.wrapper.WrappedProcess; + +// TODO: Auto-generated Javadoc +/** + * A factory for creating Script objects. + */ +public class ScriptFactory +{ + + /** + * Creates a new Script object. + * + * @param script + * the script + * @param timeout + * + * @return the script + */ + public static Script createScript(String script, String id, WrappedProcess process, String[] args, InternalLogger log, int timeout, String encoding, boolean reload, boolean debug) + { + if (script == null || "".equals(script)) + return null; + if (log != null && debug) + log.info("create script: " + script); + if (script.endsWith(".bat") || script.endsWith(".sh")) + return new ShellScript(script, id, process, args, timeout); + if (script.endsWith(".gv") || script.endsWith(".groovy")) + try + { + return new GroovyScript(script, id, process, args, timeout, log, encoding, reload); + } + catch (Throwable e) + { + if (log != null) + log.info("Error in createScript " + script, e); + } + return null; + } + + public static Script createScript(String script, String id, WrappedProcess process, List args, InternalLogger log, int timeout, String encoding, boolean reload, boolean debug) + { + String[] argsArr = new String[0]; + if (args != null && args.size() > 0) + { + argsArr = new String[args.size()]; + for (int i = 0; i < argsArr.length; i++) + argsArr[i] = args.get(i).toString(); + } + return createScript(script, id, process, argsArr, log, timeout, encoding, reload, debug); + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/script/ShellScript.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/script/ShellScript.java new file mode 100644 index 0000000000..4cab0a5210 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/script/ShellScript.java @@ -0,0 +1,101 @@ +/* This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + *

    + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ +package org.rzo.yajsw.script; + +import org.rzo.yajsw.os.OperatingSystem; +import org.rzo.yajsw.os.Process; +import org.rzo.yajsw.wrapper.WrappedProcess; + +// TODO: Auto-generated Javadoc +/** + * The Class ShellScript. + */ +public class ShellScript extends AbstractScript +{ + volatile Process p = null; + + /** + * Instantiates a new shell script. + * + * @param script + * the script + * @param timeout + */ + public ShellScript(String script, String id, WrappedProcess process, String[] args, int timeout) + { + super("scripts/" + script, id, process, args, timeout); + } + + /* + * (non-Javadoc) + * + * @see org.rzo.yajsw.script.AbstractScript#execute(java.lang.String, + * java.lang.String, java.lang.String, java.lang.String, java.lang.String, + * java.lang.String, java.lang.Object) + */ + public Object execute(String line) + { + String id = _id; + String state = _process.getStringState(); + String count = "" + _process.getRestartCount(); + String pid = "" + _process.getAppPid(); + String exitCode = "" + _process.getExitCode(); + try + { + p = OperatingSystem.instance().processManagerInstance().createProcess(); + p.setCommand(getScript() + " " + id + " " + state + " " + count + " " + pid + " " + exitCode); + p.setPipeStreams(false, false); + p.start(); + p.waitFor(getTimeout()); + if (p.isRunning()) + p.kill(999); + if (p.getExitCode() != 0) + System.out.println("script " + getScript() + "returned " + p.getExitCode()); + p.destroy(); + p = null; + } + catch (Exception ex) + { + ex.printStackTrace(); + } + return null; + } + + public Object execute() + { + return execute(""); + } + + public void executeWithTimeout() + { + // TODO Auto-generated method stub + + } + + @Override + public void interrupt() + { + if (p != null) + { + p.destroy(); + } + } + + void log(String msg) + { + if (_process != null && _process.getInternalWrapperLogger() != null) + _process.getInternalWrapperLogger().info(msg); + else + System.out.println(msg); + } + + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/script/package.html b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/script/package.html new file mode 100644 index 0000000000..53486fbbb7 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/script/package.html @@ -0,0 +1,7 @@ + + + + + Provides... + + \ No newline at end of file diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/timer/DummyTimer.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/timer/DummyTimer.java new file mode 100644 index 0000000000..0e297121a9 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/timer/DummyTimer.java @@ -0,0 +1,33 @@ +package org.rzo.yajsw.timer; + +public class DummyTimer implements Timer +{ + + public void init() + { + } + + public boolean isHasTrigger() + { + return false; + } + + public boolean isStartImmediate() + { + return true; + } + + public boolean isTriggered() + { + return false; + } + + public void start() + { + } + + public void stop() + { + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/timer/RestartJob.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/timer/RestartJob.java new file mode 100644 index 0000000000..eea6a068f0 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/timer/RestartJob.java @@ -0,0 +1,38 @@ +/* This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + *

    + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ +package org.rzo.yajsw.timer; + +import org.quartz.Job; +import org.quartz.JobDataMap; +import org.quartz.JobExecutionContext; +import org.quartz.JobExecutionException; +import org.rzo.yajsw.wrapper.WrappedProcess; + +// TODO: Auto-generated Javadoc +/** + * The Class RestartJob. + */ +public class RestartJob implements Job +{ + + /* + * (non-Javadoc) + * + * @see org.quartz.Job#execute(org.quartz.JobExecutionContext) + */ + public void execute(JobExecutionContext context) throws JobExecutionException + { + JobDataMap dataMap = context.getJobDetail().getJobDataMap(); + WrappedProcess process = (WrappedProcess) dataMap.get("process"); + process.restartByTimer(); + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/timer/StartJob.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/timer/StartJob.java new file mode 100644 index 0000000000..6fcb7b853d --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/timer/StartJob.java @@ -0,0 +1,38 @@ +/* This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + *

    + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ +package org.rzo.yajsw.timer; + +import org.quartz.Job; +import org.quartz.JobDataMap; +import org.quartz.JobExecutionContext; +import org.quartz.JobExecutionException; +import org.rzo.yajsw.wrapper.WrappedProcess; + +// TODO: Auto-generated Javadoc +/** + * The Class StartJob. + */ +public class StartJob implements Job +{ + + /* + * (non-Javadoc) + * + * @see org.quartz.Job#execute(org.quartz.JobExecutionContext) + */ + public void execute(JobExecutionContext context) throws JobExecutionException + { + JobDataMap dataMap = context.getJobDetail().getJobDataMap(); + WrappedProcess process = (WrappedProcess) dataMap.get("process"); + process.startByTimer(); + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/timer/StopJob.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/timer/StopJob.java new file mode 100644 index 0000000000..23f549a73b --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/timer/StopJob.java @@ -0,0 +1,38 @@ +/* This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + *

    + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ +package org.rzo.yajsw.timer; + +import org.quartz.Job; +import org.quartz.JobDataMap; +import org.quartz.JobExecutionContext; +import org.quartz.JobExecutionException; +import org.rzo.yajsw.wrapper.WrappedProcess; + +// TODO: Auto-generated Javadoc +/** + * The Class StopJob. + */ +public class StopJob implements Job +{ + + /* + * (non-Javadoc) + * + * @see org.quartz.Job#execute(org.quartz.JobExecutionContext) + */ + public void execute(JobExecutionContext context) throws JobExecutionException + { + JobDataMap dataMap = context.getJobDetail().getJobDataMap(); + WrappedProcess process = (WrappedProcess) dataMap.get("process"); + process.stop("TIMER"); + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/timer/Timer.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/timer/Timer.java new file mode 100644 index 0000000000..53455b7741 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/timer/Timer.java @@ -0,0 +1,18 @@ +package org.rzo.yajsw.timer; + +public interface Timer +{ + + void init(); + + boolean isHasTrigger(); + + boolean isTriggered(); + + boolean isStartImmediate(); + + void start(); + + void stop(); + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/timer/TimerFactory.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/timer/TimerFactory.java new file mode 100644 index 0000000000..46078c3a56 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/timer/TimerFactory.java @@ -0,0 +1,20 @@ +package org.rzo.yajsw.timer; + +import org.rzo.yajsw.config.YajswConfigurationImpl; +import org.rzo.yajsw.wrapper.WrappedProcess; + +public class TimerFactory +{ + public static Timer createTimer(YajswConfigurationImpl config, WrappedProcess wp) + { + try + { + return new TimerImpl(config, wp); + } + catch (Throwable ex) + { + } + return new DummyTimer(); + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/timer/TimerImpl.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/timer/TimerImpl.java new file mode 100644 index 0000000000..9935e7de40 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/timer/TimerImpl.java @@ -0,0 +1,524 @@ +/* This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + *

    + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ +package org.rzo.yajsw.timer; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Iterator; + +import org.quartz.CronExpression; +import org.quartz.CronTrigger; +import org.quartz.JobDataMap; +import org.quartz.JobDetail; +import org.quartz.Scheduler; +import org.quartz.SchedulerException; +import org.quartz.SchedulerFactory; +import org.quartz.SimpleTrigger; +import org.quartz.Trigger; +import org.rzo.yajsw.config.YajswConfigurationImpl; +import org.rzo.yajsw.wrapper.WrappedProcess; + +// TODO: Auto-generated Javadoc +/** + * The Class Timer. + */ +public class TimerImpl implements Timer +{ + + /** The _config. */ + YajswConfigurationImpl _config; + + /** The _wp. */ + WrappedProcess _wp; + + /** The _scheduler. */ + static Scheduler _scheduler; + + /** The _cron start. */ + MyCronTrigger _cronStart; + + /** The _cron stop. */ + MyCronTrigger _cronStop; + + /** The _cron restart. */ + MyCronTrigger _cronRestart; + + /** The _simple start. */ + MySimpleTrigger _simpleStart; + + /** The _simple stop. */ + MySimpleTrigger _simpleStop; + + /** The _simple restart. */ + MySimpleTrigger _simpleRestart; + + /** The _has trigger. */ + boolean _hasTrigger = false; + + /** The _start immediate. */ + boolean _startImmediate = true; + + /** The _triggered. */ + boolean _triggered = false; + + /** + * Instantiates a new timer. + * + * @param config + * the config + * @param wp + * the wp + */ + public TimerImpl(YajswConfigurationImpl config, WrappedProcess wp) + { + _config = config; + _wp = wp; + } + + /** + * Inits the. + */ + public synchronized void init() + { + for (Iterator keys = _config.getKeys("wrapper.timer"); keys.hasNext();) + { + String key = (String) keys.next(); + if (key.contains(".simple.")) + { + if (key.contains(".START.")) + { + if (_simpleStart == null) + _simpleStart = getSimpleTrigger(key); + } + else if (key.contains(".STOP.")) + { + if (_simpleStop == null) + _simpleStop = getSimpleTrigger(key); + } + else if (key.contains(".RESTART.")) + { + if (_simpleRestart == null) + _simpleRestart = getSimpleTrigger(key); + } + else + System.out.println("Cannot interpret timer property: " + key); + } + else if (key.contains(".cron.")) + { + if (key.contains(".START")) + _cronStart = getCronTrigger(key); + else if (key.contains(".STOP")) + _cronStop = getCronTrigger(key); + else if (key.contains(".RESTART")) + _cronRestart = getCronTrigger(key); + else + System.out.println("Cannot interpret timer property: " + key); + } + else + { + System.out.println("Cannot interpret timer property: " + key); + } + } + + } + + /** + * Gets the simple trigger. + * + * @param key + * the key + * + * @return the simple trigger + */ + private MySimpleTrigger getSimpleTrigger(String key) + { + JobDetail jobDetail = new JobDetail(); + JobDataMap jobDataMap = new JobDataMap(); + jobDataMap.put("process", _wp); + jobDetail.setJobDataMap(jobDataMap); + + Class jobClass = getJobClass(key); + if (jobClass == null) + return null; + jobDetail.setJobClass(jobClass); + jobDetail.setName(key); + + MySimpleTrigger trigger = new MySimpleTrigger(jobDetail); + Date startTime = getStartTime(key); + if (startTime != null) + { + trigger.setStartTime(startTime); + } + int repeatCount = getRepeatCount(key); + if (repeatCount >= -1) + trigger.setRepeatCount(repeatCount); + int interval = getInterval(key); + if (interval > 0) + trigger.setRepeatInterval(interval * 1000); + _hasTrigger = true; + + if (trigger != null) + trigger.setName(key); + _startImmediate = false; // getStartTime will always return a date. + // per default the current time. + trigger.setMisfireInstruction(SimpleTrigger.MISFIRE_INSTRUCTION_FIRE_NOW); + return trigger; + } + + /** + * Gets the interval. + * + * @param key + * the key + * + * @return the interval + */ + private int getInterval(String key) + { + return _config.getInt(key.substring(0, key.lastIndexOf(".")) + ".INTERVAL", SimpleTrigger.REPEAT_INDEFINITELY); + } + + /** + * Gets the repeat count. + * + * @param key + * the key + * + * @return the repeat count + */ + private int getRepeatCount(String key) + { + return _config.getInt(key.substring(0, key.lastIndexOf(".")) + ".COUNT", SimpleTrigger.REPEAT_INDEFINITELY); + } + + /** + * Gets the start time. + * + * @param key + * the key + * + * @return the start time + */ + private Date getStartTime(String key) + { + String str = _config.getString(key.substring(0, key.lastIndexOf(".")) + ".FIRST"); + if (str == null) + return new Date(); + SimpleDateFormat df = null; + if (str.contains(" ")) + df = new SimpleDateFormat("dd.MM.yyyy HH:mm:ss"); + else + df = new SimpleDateFormat("HH:mm:ss"); + try + { + return df.parse(str); + } + catch (ParseException e) + { + e.printStackTrace(); + return null; + } + } + + /** + * Gets the job class. + * + * @param key + * the key + * + * @return the job class + */ + private Class getJobClass(String key) + { + if (key.contains(".RESTART")) + return RestartJob.class; + else if (key.contains(".STOP")) + return StopJob.class; + else if (key.contains(".START")) + return StartJob.class; + return null; + } + + /** + * Gets the cron trigger. + * + * @param key + * the key + * + * @return the cron trigger + */ + private MyCronTrigger getCronTrigger(String key) + { + JobDetail jobDetail = new JobDetail(); + JobDataMap jobDataMap = new JobDataMap(); + jobDataMap.put("process", _wp); + jobDetail.setJobDataMap(jobDataMap); + jobDetail.setName(key); + + Class jobClass = getJobClass(key); + if (jobClass == null) + return null; + jobDetail.setJobClass(jobClass); + + MyCronTrigger trigger = new MyCronTrigger(jobDetail); + CronExpression cronExpression = getCronExpression(key); + if (cronExpression != null) + { + trigger.setCronExpression(cronExpression); + if (jobClass.equals(StartJob.class)) + _startImmediate = false; + _hasTrigger = true; + } + else + { + return null; + } + + trigger.setName(key); + trigger.setMisfireInstruction(trigger.MISFIRE_INSTRUCTION_FIRE_ONCE_NOW); + return trigger; + } + + /** + * Gets the cron expression. + * + * @param key + * the key + * + * @return the cron expression + */ + private CronExpression getCronExpression(String key) + { + String str = _config.getString(key); + if (str == null) + { + return null; + } + try + { + return new CronExpression(str); + } + catch (ParseException e) + { + e.printStackTrace(); + return null; + } + } + + /** + * Start. + */ + public synchronized void start() + { + if (!_hasTrigger) + return; + if (getScheduler() == null) + return; + try + { + if (!_scheduler.isStarted()) + _scheduler.start(); + } + catch (SchedulerException e) + { + e.printStackTrace(); + return; + } + + if (_cronStart != null) + startTrigger(_cronStart, _cronStart.getJobDetail()); + if (_cronStop != null) + startTrigger(_cronStop, _cronStop.getJobDetail()); + if (_cronRestart != null) + startTrigger(_cronRestart, _cronRestart.getJobDetail()); + if (_simpleStart != null) + startTrigger(_simpleStart, _simpleStart.getJobDetail()); + if (_simpleStop != null) + startTrigger(_simpleStop, _simpleStop.getJobDetail()); + if (_simpleRestart != null) + startTrigger(_simpleRestart, _simpleRestart.getJobDetail()); + _triggered = true; + } + + /** + * Gets the scheduler. + * + * @return the scheduler + */ + private Scheduler getScheduler() + { + if (_scheduler == null) + { + SchedulerFactory schedFact = new org.quartz.impl.StdSchedulerFactory(); + try + { + _scheduler = schedFact.getScheduler(); + } + catch (SchedulerException e) + { + e.printStackTrace(); + _scheduler = null; + } + } + return _scheduler; + } + + /** + * Start trigger. + * + * @param trigger + * the trigger + * @param jobDetail + * the job detail + */ + private void startTrigger(Trigger trigger, JobDetail jobDetail) + { + if (trigger != null) + try + { + _scheduler.scheduleJob(jobDetail, trigger); + } + catch (SchedulerException e) + { + e.printStackTrace(); + } + } + + /** + * Stop. + */ + public void stop() + { + if (!_hasTrigger) + return; + stopTrigger(_cronStart); + stopTrigger(_cronStop); + stopTrigger(_cronRestart); + stopTrigger(_simpleStart); + stopTrigger(_simpleStop); + stopTrigger(_simpleRestart); + + } + + /** + * Stop trigger. + * + * @param trigger + * the trigger + */ + private synchronized void stopTrigger(Trigger trigger) + { + try + { + _scheduler.pauseAll(); + } + catch (SchedulerException e) + { + e.printStackTrace(); + return; + } + _triggered = false; + } + + /** + * Checks if is triggered. + * + * @return true, if is triggered + */ + public boolean isTriggered() + { + return _triggered; + } + + /** + * Checks if is start immediate. + * + * @return true, if is start immediate + */ + public boolean isStartImmediate() + { + return _startImmediate; + } + + /** + * Checks if is checks for trigger. + * + * @return true, if is checks for trigger + */ + public boolean isHasTrigger() + { + return _hasTrigger; + } + + /** + * The Class MyCronTrigger. + */ + class MyCronTrigger extends CronTrigger + { + + /** The _job detail. */ + JobDetail _jobDetail; + + /** + * Instantiates a new my cron trigger. + * + * @param jobDetail + * the job detail + */ + MyCronTrigger(JobDetail jobDetail) + { + _jobDetail = jobDetail; + } + + /** + * Gets the job detail. + * + * @return the job detail + */ + JobDetail getJobDetail() + { + return _jobDetail; + } + } + + /** + * The Class MySimpleTrigger. + */ + class MySimpleTrigger extends SimpleTrigger + { + + /** The _job detail. */ + JobDetail _jobDetail; + + /** + * Instantiates a new my simple trigger. + * + * @param jobDetail + * the job detail + */ + MySimpleTrigger(JobDetail jobDetail) + { + _jobDetail = jobDetail; + } + + /** + * Gets the job detail. + * + * @return the job detail + */ + JobDetail getJobDetail() + { + return _jobDetail; + } + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/timer/package.html b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/timer/package.html new file mode 100644 index 0000000000..53486fbbb7 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/timer/package.html @@ -0,0 +1,7 @@ + + + + + Provides... + + \ No newline at end of file diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/tools/ConfigGenerator.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/tools/ConfigGenerator.java new file mode 100644 index 0000000000..15cc909579 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/tools/ConfigGenerator.java @@ -0,0 +1,373 @@ +package org.rzo.yajsw.tools; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.configuration.PropertiesConfiguration; +import org.rzo.yajsw.os.OperatingSystem; +import org.rzo.yajsw.os.Process; + +public class ConfigGenerator +{ + private static void usage() + { + System.out.println("Usage: java -cp wrapper.jar org.rzo.yajsw.ConfigurationGenerator "); + System.exit(-1); + } + + public static void generate(int pid, File input, File output) + { + Process p = OperatingSystem.instance().processManagerInstance().getProcess(pid); + if (p == null) + { + System.out.println("cannot find process " + pid); + return; + } + String cmd = p.getCommand().trim(); + if (cmd == null) + { + System.out.println("cannot get command line of process"); + return; + } + + System.out.println(); + System.out.println("process command line:"); + System.out.println(p.getCommand()); + System.out.println(); + createConfigFile(p, input, output, cmd); + + } + + public static void main(String[] args) + { + if (args.length < 2) + { + usage(); + } + int pid = -1; + try + { + pid = Integer.parseInt(args[0]); + } + catch (Exception ex) + { + ex.printStackTrace(); + usage(); + } + File output = null; + try + { + output = new File(args[1]); + } + catch (Exception ex) + { + ex.printStackTrace(); + usage(); + } + File input = null; + try + { + input = new File(args[2]); + } + catch (Exception ex) + { + } + + generate(pid, input, output); + + } + + private static void createConfigFile(Process p, File input, File output, String cmd) + { + if (isJavaCmd(p)) + createJavaConfigFile(p, input, output); + else + createImageConfigFile(p, input, output); + + System.out.println("-----------------"); + System.out.println("Output file: " + output.getAbsolutePath()); + System.out.println("-----------------"); + System.out.println("NOTE: check/edit the following properties in the config file!"); + System.out.println(); + System.out.println("wrapper.app.account, wrapper.app.password: either set the password or remove the account"); + System.out.println("wrapper.java.command"); + System.out.println("wrapper.working.dir"); + System.out.println("wrapper.ntservice.name, wrapper.ntservice.displayname, wrapper.ntservice.description"); + System.out.println("-----------------"); + + } + + // TODO + private static boolean isJavaCmd(Process p) + { + /* + * try { MonitoredHost monitoredhost = + * MonitoredHost.getMonitoredHost("//localhost"); VmIdentifier + * vmidentifier = new VmIdentifier("" + pid); MonitoredVm monitoredvm = + * monitoredhost.getMonitoredVm(vmidentifier, 0); return monitoredvm != + * null; } catch (Exception e) { // e.printStackTrace(); } return false; + */ + return p.getCommand().contains("java"); + } + + private static void createImageConfigFile(Process p, File input, File output) + { + System.out.println("creating image configuration file"); + System.out.println("NOT YET IMPLEMENTED"); + } + + private static void createJavaConfigFile(Process p, File input, File output) + { + System.out.println("creating java configuration file"); + + try + { + String workingDir = p.getWorkingDir(); + String cmd = p.getCommand(); + + /* + * MonitoredHost monitoredhost = + * MonitoredHost.getMonitoredHost("//localhost"); VmIdentifier + * vmidentifier = new VmIdentifier("" + p.getPid()); MonitoredVm vm + * = monitoredhost.getMonitoredVm(vmidentifier, 0); + */ + + // System.out.println("cmd " +MonitoredVmUtil.commandLine(vm)); + PropertiesConfiguration conf; + if (input == null) + conf = new PropertiesConfiguration(); + else + conf = new PropertiesConfiguration(input); + + JCLParser parsedCmd = JCLParser.parse(cmd); + /* + * String mainClass = MonitoredVmUtil.mainClass(vm, true); if + * (!isNotNullEmpty(mainClass)) {System.out.println( + * "could not retrieve main class of java application -> abort"); + * return; } mainClass = confString(mainClass); if + * (mainClass.endsWith(".jar")) + * conf.setProperty("wrapper.java.app.jar", + * relativeString(mainClass, workingDir)); else + * conf.setProperty("wrapper.java.app.mainclass", mainClass); + */ + if (parsedCmd.getMainClass() != null) + conf.setProperty("wrapper.java.app.mainclass", parsedCmd.getMainClass()); + else + conf.setProperty("wrapper.java.app.jar", relativeString(parsedCmd.getJar(), workingDir)); + + /* + * // this does not seem to work correctly -> get jvm the hard way + * // System.out.println("vmVersion " + vmVersion); String jvm = + * null; if (cmd.startsWith("\"")) jvm = cmd.substring(0, + * cmd.indexOf("\" ") + 1); else jvm = cmd.substring(0, + * cmd.indexOf(" ")); if (isNotNullEmpty(jvm)) { jvm = + * confString(jvm); conf.setProperty("wrapper.java.command", jvm); } + */ + conf.setProperty("wrapper.java.command", parsedCmd.getJava()); + /* + * String classpath = ((StringMonitor) + * vm.findByName("java.property.java.class.path")).stringValue(); if + * (isNotNullEmpty(classpath)) { classpath = + * relativeString(classpath, workingDir); classpath = + * confString(classpath); String[] classpaths = + * classpath.split(System.getProperty("path.separator")); int i = 1; + * for (String file : classpaths) { + * conf.setProperty("wrapper.java.classpath." + i, file); i++; } } + */ + int i = 1; + List classpathList = parsedCmd.getClasspath(); + // no longer required - wrapper will automatically add the jar to the classpath + //if (conf.getString("wrapper.java.app.jar", null) != null) + // classpathList.add(conf.getString("wrapper.java.app.jar")); + if (classpathList == null || classpathList.isEmpty()) + classpathList = getClasspathFromEnvironment(p); + if (classpathList.isEmpty() && parsedCmd.getJar() == null) + classpathList.add("."); + for (String classpath : classpathList) + { + classpath = relativeString(classpath, workingDir); + classpath = confString(classpath); + // yajsw handles wildcards differently. + if (classpath.endsWith("*")) + classpath = classpath + ".jar"; + conf.setProperty("wrapper.java.classpath." + i++, classpath); + } + + /* + * // bug in MonitoredVMUtil 'c:/x.txt "d d"' returns 'c:/x.txt d d' + * //String mainArgs = MonitoredVmUtil.mainArgs(vm); // TODO really + * parse the cmd String mainArgs = + * cmd.substring(cmd.indexOf(" "+mainClass + * +" ")+mainClass.length()+2); if (isNotNullEmpty(mainArgs)) { List + * args = splitArgs(mainArgs); int i = 1; for (Iterator + * it=args.iterator(); it.hasNext(); ) { String arg = (String) + * it.next(); arg = relativeString(arg, workingDir); arg = + * confString(arg); conf.setProperty("wrapper.app.parameter."+i++, + * arg); } } + */ + + i = 1; + for (String arg : parsedCmd.getArgs()) + { + arg = relativeString(arg, workingDir); + arg = confString(arg); + conf.setProperty("wrapper.app.parameter." + i++, arg); + } + /* + * // bug in MonitoredVMUtil '"-Xd=a a"' returns '-Xd=a a' //String + * jvmArgs = MonitoredVmUtil.jvmArgs(vm); // TODO really parse the + * cmd String jvmArgs = cmd.substring(jvm.length(), + * cmd.indexOf(" "+mainClass+" ")); if (cmd.startsWith("\"")) + * jvmArgs = jvmArgs.substring(1); jvmArgs = + * jvmArgs.replace(classpath, ""); jvmArgs = + * jvmArgs.replace(" -classpath ", ""); jvmArgs = + * jvmArgs.replace(" -cp ", ""); + * + * if (isNotNullEmpty(jvmArgs)) { List args = splitArgs(jvmArgs); + * int i = 1; for (Iterator it=args.iterator(); it.hasNext(); ) { + * String arg = (String) it.next(); arg = relativeString(arg, + * workingDir); arg = confString(arg); + * conf.setProperty("wrapper.java.additional."+i++, arg); } } + * + * String jvmFlags = MonitoredVmUtil.jvmFlags(vm); + */ + i = 1; + for (String opt : parsedCmd.getVmOptions()) + { + opt = relativeString(opt, workingDir); + opt = confString(opt); + conf.setProperty("wrapper.java.additional." + i++, opt); + } + + if (isNotNullEmpty(workingDir)) + { + workingDir = confString(workingDir); + conf.setProperty("wrapper.working.dir", workingDir); + } + String title = p.getTitle(); + if (cmd.equals(title)) + title = parsedCmd.getMainClass(); + + if (isNotNullEmpty(title)) + { + title = confString(title); + conf.setProperty("wrapper.console.title", title); + conf.setProperty("wrapper.ntservice.name", title); + conf.setProperty("wrapper.ntservice.displayname", title); + conf.setProperty("wrapper.ntservice.description", title); + } + /* + * String account = p.getUser(); if (account != null && + * !"".equals(account)) conf.setProperty("wrapper.app.account", + * account); + */ + + /* + * List l = vm.findByPattern(".*"); for (Iterator it = l.iterator(); + * it.hasNext(); ) { Monitor m = (Monitor) it.next(); + * System.out.println(m.getName()); System.out.println("> "+ + * m.getValue()); } + */ + + conf.save(output); + } + catch (Exception ex) + { + ex.printStackTrace(); + } + + } + + private static List getClasspathFromEnvironment(Process p) + { + List result = new ArrayList(); + try + { + String cp = (String) p.getEnvironmentAsMap().get("CLASSPATH"); + if (cp == null) + return result; + String[] cpArr = cp.split(File.pathSeparator); + for (String cc : cpArr) + { + cc = cc.replaceAll("\"", ""); + result.add(cc.trim()); + } + } + catch (Exception ex) + { + ex.printStackTrace(); + } + return result; + + } + + public static List splitArgs(String jvmArgs) + { + // split either by " or by space + List result = new ArrayList(); + while (jvmArgs.length() > 0) + { + String arg; + jvmArgs = jvmArgs.trim(); + if (jvmArgs.startsWith("\"")) + { + jvmArgs = jvmArgs.substring(1); + int index = jvmArgs.indexOf("\""); + arg = jvmArgs.substring(0, index); + jvmArgs = jvmArgs.substring(index + 1); + } + else + { + int index = jvmArgs.indexOf(" "); + int index2 = jvmArgs.indexOf("\""); + if (index2 < index && index2 != -1) + { + index = jvmArgs.indexOf("\"", index); + } + if (index > -1) + { + arg = jvmArgs.substring(0, index); + jvmArgs = jvmArgs.substring(index + 1); + } + else + { + arg = jvmArgs; + jvmArgs = ""; + } + } + arg = arg.trim(); + arg = arg.replaceAll("\"", ""); + if (arg.length() > 0) + result.add(arg); + } + return result; + } + + private static String relativeString(String str, String base) + { + if (isNotNullEmpty(base)) + { + String baseRegEx = base.replaceAll("\\\\", "\\\\\\\\"); + baseRegEx = baseRegEx.replaceAll("\\.", "\\."); + String sep = "\\".equals(File.separator) ? "\\\\" : File.separator; + String result = str.replaceAll(baseRegEx, "." + sep); + return result; + } + return str; + + } + + public static String confString(String str) + { + String result = str.replaceAll("\\\\", "\\\\"); + result = result.replaceAll(",", "\\,"); + return result; + } + + public static boolean isNotNullEmpty(String str) + { + return str != null && !"".equals(str) && !"Unknown".equals(str); + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/tools/JCLParser.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/tools/JCLParser.java new file mode 100644 index 0000000000..dd8b69ecb3 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/tools/JCLParser.java @@ -0,0 +1,285 @@ +package org.rzo.yajsw.tools; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class JCLParser +{ + List _classpath = new ArrayList(); + List _vmOptions = new ArrayList(); + List _args = new ArrayList(); + String _java = null; + String _mainClass = null; + String _jar = null; + List bs = new ArrayList(); + + + private JCLParser(String commandLine) + { + parseInternal(commandLine); + } + + public static JCLParser parse(String commandLine) + { + JCLParser result = null; + result = new JCLParser(commandLine); + return result; + } + + private boolean inBrackets(int s) + { + boolean result = false; + for (int k = 0; k < bs.size(); k += 2) + { + if (s >= bs.get(k) && s < bs.get(k + 1)) + { + result = true; + break; + } + } + return result; + } + + private int bracketEnd(int s) + { + int result = -1; + for (int k = 0; k < bs.size(); k += 2) + { + if (s >= bs.get(k) && s < bs.get(k + 1)) + { + result = bs.get(k + 1); + break; + } + } + return result; + } + + // TODO this should cover most cases but is not complete + private void parseInternal(String commandLine) + { + Matcher mr; + Pattern p; + // last position of _java in commandLine + int posJ = 0; + // last position of _classpath in commandLine + int posCp = 0; + // last position of __vmOptions in commandLine + int posOpts = 0; + // last position of _mainClass + int posclp = 0; + // last position of _jar + int posJar = 0; + + // parse java + p = Pattern.compile("\\A(\"[^\"]+\")|(\\S+) "); + mr = p.matcher(commandLine); + if (mr.find()) + { + _java = mr.group(); + _java = _java.replaceAll("\"", ""); + _java = _java.trim(); + posJ = mr.end() - 1; + } + else + throw new RuntimeException("could not parse command line " + commandLine); + + // parse jar + p = Pattern.compile(" -jar +((\"[^\"]+\")|(\\S+))"); + mr = p.matcher(commandLine); + if (mr.find(posJ)) + { + _jar = mr.group(1); + _jar = _jar.replaceAll("\"", ""); + _jar = _jar.trim(); + posJar = mr.end() - 1; + } + + // parse classpath + p = Pattern.compile("(( -cp)|( -classpath)|( \"-classpath\")) +((\"[^\"]+\")|(\\S+)) "); + mr = p.matcher(commandLine); + if (mr.find(posJ)) + { + String cp = mr.group().trim(); + posCp = mr.end() - 1; + cp = cp.substring(cp.indexOf(' ')); + String[] cpArr = cp.split(File.pathSeparator); + for (String cc : cpArr) + { + cc = cc.replaceAll("\"", "").trim(); + if (cc.length() != 0) + _classpath.add(cc); + } + } + + // find brackets + p = Pattern.compile("\"([^\"])+\""); + mr = p.matcher(commandLine); + int i = 0; + while (mr.find(i)) + { + bs.add(mr.start()); + bs.add(mr.end()-1); + int k = 0; + while (mr.end()+k < commandLine.length() && commandLine.charAt(mr.end()+k) == '"') + k++; + i = mr.end()+k; + } + + // parse main class + if (_jar == null) + { + //p = Pattern.compile(" ([^- ])+( |$)"); + // "-" may be in class name + p = Pattern.compile(" ([^ ])+( |$)"); + mr = p.matcher(commandLine); + int max = Math.max(posJ, posCp); + while (mr.find(max)) + { + int s = mr.start(); + String mc = mr.group(); + if (!inBrackets(s) && !mc.trim().startsWith("-")) + { + _mainClass = mc; + _mainClass = _mainClass.replaceAll("\"", ""); + _mainClass = _mainClass.trim(); + posclp = mr.end() - 1; + break; + } + else + max = mr.end() - 1; + } + } + + // parse JVM options + p = Pattern.compile("(( -\\S+)|( -\"[^\"]+\")|( \"-[^\"]+\")) "); + mr = p.matcher(commandLine); + int max = Math.max(posJar, posclp); + int pos = 0; + while (mr.find(pos)) + { + String opt = mr.group().trim(); + pos = mr.end() - 1; + opt = opt.replaceAll("\"", ""); + if (!opt.startsWith("-jar") && !opt.startsWith("-cp") && !opt.startsWith("-classpath") && mr.end() < max) + { + if (inBrackets(pos)) + { + int end = bracketEnd(pos); + opt = commandLine.substring(mr.start(), end); + opt = opt.replaceAll("\"", ""); + _vmOptions.add(opt); + posOpts = end+1; + pos = end; + } + else + { + _vmOptions.add(opt); + posOpts = mr.end(); + } + } + } + + // parse args + p = Pattern.compile(" ((\"[^\"]+\")|(\\S+))( |$)"); + mr = p.matcher(commandLine); + max = Math.max(posclp, posJar); + max = Math.max(max, posOpts); + if (mr.find(max)) + { + String arg = mr.group(); + arg = arg.replaceAll("\"", ""); + _args.add(arg.trim()); + max = mr.end() - 1; + while (mr.find(max)) + { + arg = mr.group(); + arg = arg.replaceAll("\"", ""); + arg = arg.trim(); + if (arg.length() > 0) + _args.add(arg.trim()); + max = mr.end() - 1; + } + } + + if (_java == null || "".equals(_java) || ((_mainClass == null || "".equals(_mainClass)) && ((_jar == null || "".equals(_jar))))) + throw new RuntimeException("error parsing java command line "); + + } + + public List getClasspath() + { + return _classpath; + } + + public List getVmOptions() + { + return _vmOptions; + } + + public List getArgs() + { + return _args; + } + + public String getJava() + { + return _java; + } + + public String getMainClass() + { + return _mainClass; + } + + public String getJar() + { + return _jar; + } + + public static void main(String[] args) + { + String[] cmds = new String[] + { + "/opt/jdk/bin/java -Dcom.sun.management.jmxremote.port=9875 -Dcom.sun.management.jmxremote.authenticate=true -Dcom.sun.management.jmxremote.login.config=virgo-kernel-Dcom.sun.management.jmxremote.access.file=/opt/virgo-tomcat-server-3.0.3.RELEASE/config/org.eclipse.virgo.kernel.jmxremote.access.properties -Djavax.net.ssl.keyStore=/opt/virgo-tomcat-server-3.0.3.RELEASE/config/keystore -Djavax.net.ssl.keyStorePassword=changeit -Dcom.sun.management.jmxremote.ssl=true -Dcom.sun.management.jmxremote.ssl.need.client.auth=false -XX:+HeapDumpOnOutOfMemoryError -XX:ErrorFile=/opt/virgo-tomcat-server-3.0.3.RELEASE/serviceability/error.log -XX:HeapDumpPath=/opt/virgo-tomcat-server-3.0.3.RELEASE/serviceability/heap_dump.hprof -Djava.security.auth.login.config=/opt/virgo-tomcat-server-3.0.3.RELEASE/config/org.eclipse.virgo.kernel.authentication.config -Dorg.eclipse.virgo.kernel.authentication.file=/opt/virgo-tomcat-server-3.0.3.RELEASE/config/org.eclipse.virgo.kernel.users.properties -Djava.io.tmpdir=/opt/virgo-tomcat-server-3.0.3.RELEASE/work/tmp -Dorg.eclipse.virgo.kernel.home=/opt/virgo-tomcat-server-3.0.3.RELEASE -Dorg.eclipse.equinox.console.jaas.file=/opt/virgo-tomcat-server-3.0.3.RELEASE/config/store -Dssh.server.keystore=/opt/virgo-tomcat-server-3.0.3.RELEASE/config/hostkey.ser -Dgosh.args=--nointeractive -classpath :/opt/virgo-tomcat-server-3.0.3.RELEASE/lib/com.springsource.javax.transaction-1.1.0.jar:/opt/virgo-tomcat-server-3.0.3.RELEASE/lib/com.springsource.org.apache.mina.core-2.0.2.jar:/opt/virgo-tomcat-server-3.0.3.RELEASE/lib/com.springsource.org.apache.sshd.core-0.5.0.jar:/opt/virgo-tomcat-server-3.0.3.RELEASE/lib/com.springsource.slf4j.api-1.6.1.jar:/opt/virgo-tomcat-server-3.0.3.RELEASE/lib/org.apache.felix.gogo.runtime-0.8.0.v201105062003.jar:/opt/virgo-tomcat-server-3.0.3.RELEASE/lib/org.eclipse.equinox.cm-1.0.300.v20101204.jar:/opt/virgo-tomcat-server-3.0.3.RELEASE/lib/org.eclipse.equinox.console.supportability-1.0.0.201108021516.jar:/opt/virgo-tomcat-server-3.0.3.RELEASE/lib/org.eclipse.osgi-3.7.0.v20110613.jar:/opt/virgo-tomcat-server-3.0.3.RELEASE/lib/org.eclipse.osgi.services-3.3.0.v20110110.jar:/opt/virgo-tomcat-server-3.0.3.RELEASE/lib/org.eclipse.virgo.kernel.authentication-3.0.3.RELEASE.jar:/opt/virgo-tomcat-server-3.0.3.RELEASE/lib/org.eclipse.virgo.kernel.shutdown-3.0.3.RELEASE.jar:/opt/virgo-tomcat-server-3.0.3.RELEASE/lib/org.eclipse.virgo.osgi.console-3.0.3.RELEASE.jar:/opt/virgo-tomcat-server-3.0.3.RELEASE/lib/org.eclipse.virgo.osgi.extensions.equinox-3.0.3.RELEASE.jar:/opt/virgo-tomcat-server-3.0.3.RELEASE/lib/org.eclipse.virgo.osgi.launcher-3.0.3.RELEASE.jar org.eclipse.virgo.osgi.launcher.Launcher -config /opt/virgo-tomcat-server-3.0.3.RELEASE/lib/org.eclipse.virgo.kernel.launch.properties -Forg.eclipse.virgo.kernel.home=/opt/virgo-tomcat-server-3.0.3.RELEASE -Forg.eclipse.virgo.kernel.config=/opt/virgo-tomcat-server-3.0.3.RELEASE/config -Fosgi.configuration.area=/opt/virgo-tomcat-server-3.0.3.RELEASE/work/osgi/configuration -Fosgi.java.profile=file:/opt/virgo-tomcat-server-3.0.3.RELEASE/lib/java6-server.profile", + "java -cp wrapper.jar -Xrs x.Test -c conf/wrapper.conf ", + "java -cp test.jar test.Main", + "/opt/jdk/bin/java -Dcom.sun.management.jmxremote.port=9875 -Dcom.sun.management.jmxremote.authenticate=true -Dcom.sun.management.jmxremote.login.config=virgo-kernel -Dcom.sun.management.jmxremote.access.file=/opt/virgo-tomcat-server-3.5.0.RELEASE/configuration/org.eclipse.virgo.kernel.jmxremote.access.properties -Djavax.net.ssl.keyStore=/opt/virgo-tomcat-server-3.5.0.RELEASE/configuration/keystore -Djavax.net.ssl.keyStorePassword=changeit -Dcom.sun.management.jmxremote.ssl=true -Dcom.sun.management.jmxremote.ssl.need.client.auth=false -XX:+HeapDumpOnOutOfMemoryError -XX:ErrorFile=/opt/virgo-tomcat-server-3.5.0.RELEASE/serviceability/error.log -XX:HeapDumpPath=/opt/virgo-tomcat-server-3.5.0.RELEASE/serviceability/heap_dump.hprof -Djava.security.auth.login.config=/opt/virgo-tomcat-server-3.5.0.RELEASE/configuration/org.eclipse.virgo.kernel.authentication.config -Dorg.eclipse.virgo.kernel.authentication.file=/opt/virgo-tomcat-server-3.5.0.RELEASE/configuration/org.eclipse.virgo.kernel.users.properties -Djava.io.tmpdir=/opt/virgo-tomcat-server-3.5.0.RELEASE/work/tmp -Dorg.eclipse.virgo.kernel.home=/opt/virgo-tomcat-server-3.5.0.RELEASE -Dorg.eclipse.virgo.kernel.config=/opt/virgo-tomcat-server-3.5.0.RELEASE/configuration -Dosgi.java.profile=file:/opt/virgo-tomcat-server-3.5.0.RELEASE/configuration/java6-server.profile -Declipse.ignoreApp=true -Dosgi.install.area=/opt/virgo-tomcat-server-3.5.0.RELEASE -Dosgi.configuration.area=/opt/virgo-tomcat-server-3.5.0.RELEASE/work -Dssh.server.keystore=/opt/virgo-tomcat-server-3.5.0.RELEASE/configuration/hostkey.ser -Dosgi.frameworkClassPath=,file:/opt/virgo-tomcat-server-3.5.0.RELEASE/lib/javax.annotation_1.1.0.v201108011116.jar,file:/opt/virgo-tomcat-server-3.5.0.RELEASE/lib/javax.transaction_1.1.1.v201105210645.jar,file:/opt/virgo-tomcat-server-3.5.0.RELEASE/lib/org.eclipse.equinox.launcher_1.3.0.v20120308-1358.jar,file:/opt/virgo-tomcat-server-3.5.0.RELEASE/lib/org.eclipse.osgi_3.8.0.v20120508-2119.jar,file:/opt/virgo-tomcat-server-3.5.0.RELEASE/lib/org.eclipse.virgo.kernel.authentication_3.5.0.RELEASE.jar,file:/opt/virgo-tomcat-server-3.5.0.RELEASE/lib/org.eclipse.virgo.kernel.shutdown_3.5.0.RELEASE.jar,file:/opt/virgo-tomcat-server-3.5.0.RELEASE/lib/org.eclipse.virgo.osgi.console_3.5.0.RELEASE.jar,file:/opt/virgo-tomcat-server-3.5.0.RELEASE/lib/org.eclipse.virgo.osgi.extensions.equinox_3.5.0.RELEASE.jar,file:/opt/virgo-tomcat-server-3.5.0.RELEASE/lib/org.eclipse.virgo.osgi.launcher_3.5.0.RELEASE.jar,file:/opt/virgo-tomcat-server-3.5.0.RELEASE/plugins/org.eclipse.osgi_3.8.0.v20120508-2119.jar -classpath :/opt/virgo-tomcat-server-3.5.0.RELEASE/lib/javax.annotation_1.1.0.v201108011116.jar:/opt/virgo-tomcat-server-3.5.0.RELEASE/lib/javax.transaction_1.1.1.v201105210645.jar:/opt/virgo-tomcat-server-3.5.0.RELEASE/lib/org.eclipse.equinox.launcher_1.3.0.v20120308-1358.jar:/opt/virgo-tomcat-server-3.5.0.RELEASE/lib/org.eclipse.osgi_3.8.0.v20120508-2119.jar:/opt/virgo-tomcat-server-3.5.0.RELEASE/lib/org.eclipse.virgo.kernel.authentication_3.5.0.RELEASE.jar:/opt/virgo-tomcat-server-3.5.0.RELEASE/lib/org.eclipse.virgo.kernel.shutdown_3.5.0.RELEASE.jar:/opt/virgo-tomcat-server-3.5.0.RELEASE/lib/org.eclipse.virgo.osgi.console_3.5.0.RELEASE.jar:/opt/virgo-tomcat-server-3.5.0.RELEASE/lib/org.eclipse.virgo.osgi.extensions.equinox_3.5.0.RELEASE.jar:/opt/virgo-tomcat-server-3.5.0.RELEASE/lib/org.eclipse.virgo.osgi.launcher_3.5.0.RELEASE.jar:/opt/virgo-tomcat-server-3.5.0.RELEASE/plugins/org.eclipse.osgi_3.8.0.v20120508-2119.jar:/opt/virgo-tomcat-server-3.5.0.RELEASE/plugins/org.eclipse.equinox.console.ssh_1.0.0.v20120430-1356.jar org.eclipse.equinox.launcher.Main-noExit", + "\"C:\\Program Files\\Java\\jdk1.6.0_20\\bin\\java\" -Dcom.sun.management.jmxremote.port=9875 -Dcom.sun.management.jmxremote.authenticate=true -Dcom.sun.management.jmxremote.login.config=virgo-kernel -Dcom.sun.management.jmxremote.access.file=\"C:\\ABC-~1.0-S\\config\\org.eclipse.virgo.kernel.jmxremote.access.properties\" -Djavax.net.ssl.keyStore=\"C:\\ABC-~1.0-S\\config\\keystore\" -Djavax.net.ssl.keyStorePassword=abc123 -Dcom.sun.management.jmxremote.ssl=true -Dcom.sun.management.jmxremote.ssl.need.client.auth=false -XX:+HeapDumpOnOutOfMemoryError -XX:ErrorFile=\"C:\\ABC-~1.0-S\\serviceability\\error.log\" -XX:HeapDumpPath=\"C:\\ABC-~1.0-S\\serviceability\\heap_dump.hprof\" -Djava.security.auth.login.config=\"C:\\ABC-~1.0-S\\config\\org.eclipse.virgo.kernel.authentication.config\" -Dorg.eclipse.virgo.kernel.authentication.file=\"C:\\ABC-~1.0-S\\config\\org.eclipse.virgo.kernel.users.properties\" -Djava.io.tmpdir=\"\"C:\\ABC-~1.0-S\\work\tmp\\\"\" -Dorg.eclipse.virgo.kernel.home=\"C:\\ABC-~1.0-S\" -Dorg.eclipse.equinox.console.jaas.file=\"C:\\ABC-~1.0-S\\config/store\" -Dssh.server.keystore=\"C:\\ABC-~1.0-S\\config/hostkey.ser\" -Dgosh.args=\"--nointeractive\" -classpath \"C:\\ABC-~1.0-S\\lib\\com.springsource.javax.transaction-1.1.0.jar;C:\\ABC-~1.0-S\\lib\\com.springsource.org.apache.mina.core-2.0.2.jar;C:\\ABC-~1.0-S\\lib\\com.springsource.org.apache.sshd.core-0.5.0.jar;C:\\ABC-~1.0-S\\lib\\com.springsource.slf4j.api-1.6.1.jar;C:\\ABC-~1.0-S\\lib\\org.apache.felix.gogo.runtime-0.8.0.v201107131313.jar;C:\\ABC-~1.0-S\\lib\\org.eclipse.equinox.cm-1.0.300.v20101204.jar;C:\\ABC-~1.0-S\\lib\\org.eclipse.equinox.console.supportability-1.0.0.201108021516.jar;C:\\ABC-~1.0-S\\lib\\org.eclipse.osgi-3.7.0.v20110613.jar;C:\\ABC-~1.0-S\\lib\\org.eclipse.osgi.services-3.3.0.v20110110.jar;C:\\ABC-~1.0-S\\lib\\org.eclipse.virgo.kernel.authentication-3.0.2.RELEASE.jar;C:\\ABC-~1.0-S\\lib\\org.eclipse.virgo.kernel.shutdown-3.0.2.RELEASE.jar;C:\\ABC-~1.0-S\\lib\\org.eclipse.virgo.osgi.console-3.0.2.RELEASE.jar;C:\\ABC-~1.0-S\\lib\\org.eclipse.virgo.osgi.extensions.equinox-3.0.2.RELEASE.jar;C:\\ABC-~1.0-S\\lib\\org.eclipse.virgo.osgi.launcher-3.0.2.RELEASE.jar\" org.eclipse.virgo.osgi.launcher.Launcher -config \"C:\\ABC-~1.0-S\\lib\\org.eclipse.virgo.kernel.launch.properties\" -Forg.eclipse.virgo.kernel.home=\"C:\\ABC-~1.0-S\" -Forg.eclipse.virgo.kernel.config=\"C:\\ABC-~1.0-S\\config\" -Fosgi.configuration.area=\"C:\\ABC-~1.0-S\\work\\osgi\\configuration\" -Fosgi.java.profile=\"file:C:\\ABC-~1.0-S\\lib\\java6-server.profile\"", + "\"C:\\Program Files\\Java\\jdk1.6.0_20\\bin\\java\" -Dcom.sun.management.jmxremote.port=9875 -Dcom.sun.management.jmxremote.authenticate=true -Dcom.sun.management.jmxremote.login.config=virgo-kernel -Dcom.sun.management.jmxremote.access.file=\"C:\\ABC-~1.0-S\\config\\org.eclipse.virgo.kernel.jmxremote.access.properties\" -Djavax.net.ssl.keyStore=\"C:\\ABC-~1.0-S\\config\\keystore\" -Djavax.net.ssl.keyStorePassword=abc123 -Dcom.sun.management.jmxremote.ssl=true -Dcom.sun.management.jmxremote.ssl.need.client.auth=false -XX:+HeapDumpOnOutOfMemoryError -XX:ErrorFile=\"C:\\ABC-~1.0-S\\serviceability\\error.log\" -XX:HeapDumpPath=\"C:\\ABC-~1.0-S\\serviceability\\heap_dump.hprof\" -Djava.security.auth.login.config=\"C:\\ABC-~1.0-S\\config\\org.eclipse.virgo.kernel.authentication.config\" -Dorg.eclipse.virgo.kernel.authentication.file=\"C:\\ABC-~1.0-S\\config\\org.eclipse.virgo.kernel.users.properties\" -Djava.io.tmpdir=\"C:\\ABC-~1.0-S\\work\tmp\\\" -Dorg.eclipse.virgo.kernel.home=\"C:\\ABC-~1.0-S\" -Dorg.eclipse.equinox.console.jaas.file=\"C:\\ABC-~1.0-S\\config/store\" -Dssh.server.keystore=\"C:\\ABC-~1.0-S\\config/hostkey.ser\" -Dgosh.args=\"--nointeractive\" -classpath \"C:\\ABC-~1.0-S\\lib\\com.springsource.javax.transaction-1.1.0.jar;C:\\ABC-~1.0-S\\lib\\com.springsource.org.apache.mina.core-2.0.2.jar;C:\\ABC-~1.0-S\\lib\\com.springsource.org.apache.sshd.core-0.5.0.jar;C:\\ABC-~1.0-S\\lib\\com.springsource.slf4j.api-1.6.1.jar;C:\\ABC-~1.0-S\\lib\\org.apache.felix.gogo.runtime-0.8.0.v201107131313.jar;C:\\ABC-~1.0-S\\lib\\org.eclipse.equinox.cm-1.0.300.v20101204.jar;C:\\ABC-~1.0-S\\lib\\org.eclipse.equinox.console.supportability-1.0.0.201108021516.jar;C:\\ABC-~1.0-S\\lib\\org.eclipse.osgi-3.7.0.v20110613.jar;C:\\ABC-~1.0-S\\lib\\org.eclipse.osgi.services-3.3.0.v20110110.jar;C:\\ABC-~1.0-S\\lib\\org.eclipse.virgo.kernel.authentication-3.0.2.RELEASE.jar;C:\\ABC-~1.0-S\\lib\\org.eclipse.virgo.kernel.shutdown-3.0.2.RELEASE.jar;C:\\ABC-~1.0-S\\lib\\org.eclipse.virgo.osgi.console-3.0.2.RELEASE.jar;C:\\ABC-~1.0-S\\lib\\org.eclipse.virgo.osgi.extensions.equinox-3.0.2.RELEASE.jar;C:\\ABC-~1.0-S\\lib\\org.eclipse.virgo.osgi.launcher-3.0.2.RELEASE.jar\" org.eclipse.virgo.osgi.launcher.Launcher -config \"C:\\ABC-~1.0-S\\lib\\org.eclipse.virgo.kernel.launch.properties\" -Forg.eclipse.virgo.kernel.home=\"C:\\ABC-~1.0-S\" -Forg.eclipse.virgo.kernel.config=\"C:\\ABC-~1.0-S\\config\" -Fosgi.configuration.area=\"C:\\ABC-~1.0-S\\work\\osgi\\configuration\" -Fosgi.java.profile=\"file:C:\\ABC-~1.0-S\\lib\\java6-server.profile\"", + "\"java\" -cp \"C:\\Program Files\\yajsw-alpha-9.5\\bat\\/../wrapper.jar\" test.HelloWorld", + "java -Xrs -jar \"Z:\\dev\\yajsw\\bat\\/..\\wrapper.jar\" -c conf/wrapper.conf ", + "java -cp wrapper.jar -Xrs x.Test -c conf/wrapper.conf ", + "\"java\" -cp \"C:\\Program Files\\yajsw-alpha-9.5\\bat\\/../wrapper.jar\" test.HelloWorld start \n ", + "\"java\" test.HelloWorld", + "\"C:\\Program Files\\Java\\jre7\\bin\\javaw.exe\" -Xmx512m -jar \"C:\\automa tisation\\bin\\sendfile-server.jar\" abc ", + "java -jar testJar.jar", + "java -jar LogConsolidation-1.0.one-jar.jar", + "java -Dlog4j.debug -Dlog4j.configuration=file:../conf/log4j.xml -jar myApp.jar start", + "/usr/DAVIDweb/jdk1.6.0_18/bin/java -Dwrapper.teeName=6849389861148562201$1312438311981 -Dwrapper.config=/usr/DAVIDweb/Tomcat557_AAA-DHK_3/AAA-DHK_3/bin/conf/wrapper.conf -Dwrapper.key=6849389861148562201 -Dwrapper.visible=false -Dwrapper.pidfile=/var/run/wrapper.ApacheTomcatAAADHK3.pid -Dwrapper.port=15003 -Dwrapper.key=6849389861148562201 -Dwrapper.teeName=6849389861148562201$1312438311981 -Dwrapper.tmpPath=/tmp -classpath /usr/DAVIDweb/Tomcat557_AAA-DHK_3/AAA-DHK_3/bin/wrapper.jar:/usr/DAVIDweb/Tomcat557_AAA-DHK_3/bin/bootstrap.jar -server -Djava.endorsed.dirs=/DAVIDweb/Tomcat557_AAA-DHK_3/common/endorsed -Dcatalina.home=/DAVIDweb/Tomcat557_AAA-DHK_3 -Dcatalina.base=/DAVIDweb/Tomcat557_AAA-DHK_3/AAA-DHK_3 -Dcatalina.properties=/DAVIDweb/Tomcat557_AAA-DHK_3/AAA-DHK_3/conf/catalina.properties -Djava.io.tmpdir=/DAVIDweb/Tomcat557_AAA-DHK_3/AAA-DHK_3/temp -Dibr.debug=false -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/DAVIDweb/Tomcat557_AAA-DHK_3/AAA-DHK_3/webapps/AAA-DHK_3/logs -XX:+DisableExplicitGC -Xss1024k -Dibr.dhk.geoinfodok=gid600 -Dibr.dhk.lib=/DAVIDweb/Tomcat557_AAA-DHK_3/AAA-DHK_3/webapps/AAA-DHK_3/WEB-INF/lib/gid600 -Xrs -Dwrapper.service=true -Dwrapper.console.visible=false -Xms512m -Xmx512m org.rzo.yajsw.app.WrapperJVMMain", + "java -Dsimple.sleepFor=1200 -classpath \";..\\bin\\run.jar;/app/my/dist/runtime.jar;bin\" -DHTTP_PROXY_IP=192.1.21.1 -DHTTP_PROXY_PORT=1211 -Djboss.partition.name:DefaultPartition=app1Cluster -DACCOSA_APP_ROOT=d:/opt/ -DJ2EE_SERVER=JBOSS -DDB_SERVER=db2 -Dcom.APP1.aa.EnableCache=YES -Dcom.APP1.ff.aa.DisableLogging=YES -DCACHE_TO_USE=\"Memcached\" -DCACHE_SERVER_LIST=\"127.0.0.1:11413 127.0.0.1:11415\" -Dcom.APP1.ff.forceIPAndPort=192.168.1.23_192.168.1.22:3331 -Danother.asdasd=343434_asdasdasd -Danother.asdasd.1=1-343434_asdasdasd -Danother.asdasd.2=2-343434_asdasdasd -Danother.asdasd.3=3-343434_asdasdasd -Danother.asdasd.4=4-343434_asdasdasd -Danother.asdasd.5=5-343434_asdasdasd -Danother.asdasd.6=6-343434_asdasdasd com.simple.SimpleConsole args_param1 args_param2 args_param3", + "java -Dsimple.sleepFor=1200 -classpath \";..\\bin\\run.jar;/app/my/dist/runtime.jar;bin\" -DHTTP_PROXY_IP=192.1.21.1 -DHTTP_PROXY_PORT=1211 -Djboss.partition.name:DefaultPartition=app1Cluster -DACCOSA_APP_ROOT=d:/opt/ -DJ2EE_SERVER=JBOSS -DDB_SERVER=db2 -Dcom.APP1.aa.EnableCache=YES -Dcom.APP1.ff.aa.DisableLogging=YES -DCACHE_TO_USE=\"Memcached\" -DCACHE_SERVER_LIST=\"127.0.0.1:11413 127.0.0.1:11415 \" -Dcom.APP1.ff.forceIPAndPort=192.168.1.23_192.168.1.22:3331 -Danother.asdasd=343434_asdasdasd -Danother.asdasd.1=1-343434_asdasdasd -Danother.asdasd.2=2-343434_asdasdasd -Danother.asdasd.3=3-343434_asdasdasd -Danother.asdasd.4=4-343434_asdasdasd -Danother.asdasd.5=5-343434_asdasdasd -Danother.asdasd.6=6-343434_asdasdasd com.simple.SimpleConsole args_param1 args_param2 args_param3" }; + for (String cmd : cmds) + { + System.out.println("---------------------"); + System.out.println(cmd); + System.out.println("---------------------"); + JCLParser p = JCLParser.parse(cmd); + System.out.println(" java:"); + System.out.println(p.getJava()); + System.out.println(" jar:"); + System.out.println(p.getJar()); + System.out.println(" main class:"); + System.out.println(p.getMainClass()); + System.out.println(" args:"); + System.out.println(p.getArgs()); + System.out.println(" classpath:"); + System.out.println(p.getClasspath()); + System.out.println(" options:"); + System.out.println(p.getVmOptions()); + } + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/tray/ByteFormat.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/tray/ByteFormat.java new file mode 100644 index 0000000000..43672c3857 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/tray/ByteFormat.java @@ -0,0 +1,125 @@ +/* This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + *

    + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ +package org.rzo.yajsw.tray; + +/** + * taken from + * http://groups.google.com/group/comp.lang.java.help/browse_thread/thread + * /0db818517ca9de79/b0a55aa19f911204 thanks to Piotr Kobzda Formatter for Bytes + */ +public class ByteFormat +{ + /** + * The Enum StorageUnit. + */ + public enum StorageUnit + { + + /** The BYTE. */ + BYTE("B", 1L), + /** The KILOBYTE. */ + KILOBYTE("KB", 1L << 10), + /** The MEGABYTE. */ + MEGABYTE("MB", 1L << 20), + /** The GIGABYTE. */ + GIGABYTE("GB", 1L << 30), + /** The TERABYTE. */ + TERABYTE("TB", 1L << 40), + /** The PETABYTE. */ + PETABYTE("PB", 1L << 50), + /** The EXABYTE. */ + EXABYTE("EB", 1L << 60); + + /** The Constant BASE. */ + public static final StorageUnit BASE = BYTE; + + private final String symbol; + private final long divider; // divider of BASE unit + + /** + * Instantiates a new storage unit. + * + * @param name + * the name + * @param divider + * the divider + */ + StorageUnit(String name, long divider) + { + this.symbol = name; + this.divider = divider; + } + + /** + * Of. + * + * @param number + * the number + * + * @return the storage unit + */ + public static StorageUnit of(final long number) + { + final long n = number > 0 ? -number : number; + if (n > -(1L << 10)) + { + return BYTE; + } + else if (n > -(1L << 20)) + { + return KILOBYTE; + } + else if (n > -(1L << 30)) + { + return MEGABYTE; + } + else if (n > -(1L << 40)) + { + return GIGABYTE; + } + else if (n > -(1L << 50)) + { + return TERABYTE; + } + else if (n > -(1L << 60)) + { + return PETABYTE; + } + else + { // n >= Long.MIN_VALUE + return EXABYTE; + } + } + } + + /** + * Format. + * + * @param number + * the number of bytes + * + * @return the formatted string + */ + public String format(long number) + { + StorageUnit st = StorageUnit.of(number); + return nf.format((double) number / st.divider) + " " + st.symbol; + } + + private static java.text.NumberFormat nf = java.text.NumberFormat.getInstance(); + static + { + nf.setGroupingUsed(false); + nf.setMinimumFractionDigits(0); + nf.setMaximumFractionDigits(1); + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/tray/Console.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/tray/Console.java new file mode 100644 index 0000000000..4581bb96c7 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/tray/Console.java @@ -0,0 +1,583 @@ +/* This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + *

    + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ +package org.rzo.yajsw.tray; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.LinkedList; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; +import java.util.concurrent.locks.ReentrantLock; + +import javax.swing.AbstractAction; +import javax.swing.Icon; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JMenuItem; +import javax.swing.JTextArea; +import javax.swing.WindowConstants; +import javax.swing.text.BadLocationException; +import javax.swing.text.Element; + +import org.rzo.netty.ahessian.utils.MyReentrantLock; +import org.rzo.yajsw.util.DaemonThreadFactory; + +/** + * The YAJSW System Tray Console User interface + */ +public class Console extends JFrame +{ + + /** The _tray icon. */ + WrapperTrayIconImpl _trayIcon; + + /** true if the console has been shut down */ + boolean stop; + protected static final Executor executor = Executors.newCachedThreadPool(new DaemonThreadFactory("console")); + + /** The max lines in the output window */ + int maxLines = 1500; + + /** The console UI. */ + ConsoleForm _consoleForm = new ConsoleForm(); + + /** The date time format. */ + SimpleDateFormat _dateTimeFormat = new SimpleDateFormat(); + + /** The byte format. */ + ByteFormat _byteFormat = new ByteFormat(); + + /** The ok icon. */ + Icon _okIcon; + + JMenuItem _startOutputItem = new JMenuItem(); + + JMenuItem _pauseOutputItem = new JMenuItem(); + + JMenuItem _clearOutputItem = new JMenuItem(); + + volatile boolean _outputPaused = false; + volatile String _outputFilter = null; + volatile LinkedList _outputLines = new LinkedList(); + ReentrantLock _outputLock = new MyReentrantLock(); + + /** + * Instantiates a new console. + * + * @param trayIcon + * the tray icon + */ + public Console(WrapperTrayIconImpl trayIcon) + { + _trayIcon = trayIcon; + this.setTitle(_trayIcon.toolTipPrefix + "Console"); + this.addWindowListener(new WindowEventHandler()); + this.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); + _okIcon = _trayIcon.createImageIcon("/resources/tick.png"); + + _clearOutputItem.setAction(new AbstractAction(null, WrapperTrayIconImpl.createImageIcon("/resources/edit-clear.png")) + { + public void actionPerformed(ActionEvent e) + { + clearOutput(); + } + + }); + + _pauseOutputItem.setAction(new AbstractAction(null, WrapperTrayIconImpl.createImageIcon("/resources/pause.png")) + { + public void actionPerformed(ActionEvent e) + { + _outputPaused = true; + _consoleForm._START_OUTPUT_BUTTON.setEnabled(true); + _consoleForm._PAUSE_OUTPUT_BUTTON.setEnabled(false); + } + + }); + + _startOutputItem.setAction(new AbstractAction(null, WrapperTrayIconImpl.createImageIcon("/resources/start.png")) + { + public void actionPerformed(ActionEvent e) + { + _outputPaused = false; + _consoleForm._START_OUTPUT_BUTTON.setEnabled(false); + _consoleForm._PAUSE_OUTPUT_BUTTON.setEnabled(true); + } + + }); + + _consoleForm.__OUTPUT_FILTER.addActionListener(new ActionListener() + { + + public void actionPerformed(ActionEvent e) + { + String txt = _consoleForm.__OUTPUT_FILTER.getText(); + _outputFilter = "".equals(txt) ? null : txt; + filterOutput(); + } + + }); + + initOutput(); + initInput(); + initPerformance(); + initButtons(); + _consoleForm._START_OUTPUT_BUTTON.setEnabled(false); + + this.getContentPane().add(_consoleForm); + + this.pack(); + this.setVisible(true); + stop = false; + } + + private void initButtons() + { + initButton(_consoleForm._EXIT_TRAY_ICON_BUTTON, _trayIcon._exitItem); + initButton(_consoleForm._EXIT_WRAPPER_BUTTON, _trayIcon._exitWrapperItem); + initButton(_consoleForm._THREAD_DUMP_WRAPPER_BUTTON, _trayIcon._threadDumpWrapperItem); + initButton(_consoleForm._RESTART_BUTTON, _trayIcon._restartItem); + initButton(_consoleForm._START_BUTTON, _trayIcon._startItem); + initButton(_consoleForm._STOP_BUTTON, _trayIcon._stopItem); + initButton(_consoleForm._STOP_TIMER_BUTTON, _trayIcon._stopTimerItem); + initButton(_consoleForm._THREAD_DUMP_BUTTON, _trayIcon._threadDumpItem); + initButton(_consoleForm._GC_BUTTON, _trayIcon._gcItem); + initButton(_consoleForm._DUMP_HEAP_BUTTON, _trayIcon._dumpHeapItem); + initButton(_consoleForm._jbutton1, _trayIcon._closeConsoleItem); + initButton(_consoleForm._PAUSE_OUTPUT_BUTTON, _pauseOutputItem); + initButton(_consoleForm._START_OUTPUT_BUTTON, _startOutputItem); + initButton(_consoleForm._CLEAR_OUTPUT_BUTTON, _clearOutputItem); + + } + + private void initButton(JButton button, JMenuItem item) + { + button.setAction(item.getAction()); + } + + private void initPerformance() + { + executor.execute(new Runnable() + { + + public void run() + { + while (!stop) + { + setAppCpu(_trayIcon._process.getAppCpu()); + setAppHandles(_trayIcon._process.getAppHandles()); + setAppMemory(_trayIcon._process.getAppPMemory(), _trayIcon._process.getAppVMemory()); + setAppThreads(_trayIcon._process.getAppThreads()); + try + { + Thread.sleep(1000); + } + catch (InterruptedException e) + { + e.printStackTrace(); + Thread.currentThread().interrupt(); + } + } + } + }); + } + + /** + * Sets the state. + * + * @param state + * the new state + */ + void setState(String state) + { + if (state != null) + _consoleForm._state.setText(state); + _consoleForm._state.repaint(); + } + + /** + * Sets the app pid. + * + * @param pid + * the new app pid + */ + void setAppPid(int pid) + { + if (pid > 0) + _consoleForm._appPid.setText("" + pid); + else + _consoleForm._appPid.setText("?"); + + } + + /** + * Sets the wrapper pid. + * + * @param pid + * the new wrapper pid + */ + void setWrapperPid(int pid) + { + if (pid > 0) + _consoleForm._wPid.setText("" + pid); + else + _consoleForm._wPid.setText("?"); + } + + /** + * Sets the trigger. + * + * @param trigger + * the new trigger + */ + void setTrigger(String trigger) + { + if (trigger != null) + _consoleForm._trigger.setText(trigger); + } + + /** + * Sets the app started. + * + * @param time + * the new app started + */ + void setAppStarted(Date time) + { + if (time != null) + _consoleForm._appStartTime.setText(_dateTimeFormat.format(time)); + } + + /** + * Sets the app stopped. + * + * @param time + * the new app stopped + */ + void setAppStopped(Date time) + { + if (time != null) + _consoleForm._appStopTime.setText(_dateTimeFormat.format(time)); + } + + /** + * Sets the app restart count. + * + * @param total + * the total + * @param count + * the count + */ + void setAppRestartCount(int total, int count) + { + if (count > 0) + _consoleForm._count.setText(total + "[" + count + "]"); + } + + /** + * Sets the wrapper started. + * + * @param time + * the new wrapper started + */ + void setWrapperStarted(Date time) + { + if (time != null) + _consoleForm._wStartTime.setText(_dateTimeFormat.format(time)); + } + + /** + * Sets the app threads. + * + * @param count + * the new app threads + */ + void setAppThreads(int count) + { + if (count > 0) + _consoleForm._threads.setText("" + count); + else + _consoleForm._threads.setText("?"); + + } + + /** + * Sets the app handles. + * + * @param count + * the new app handles + */ + void setAppHandles(int count) + { + if (count > 0) + _consoleForm._handles.setText("" + count); + else + _consoleForm._handles.setText("?"); + } + + /** + * Sets the app memory. + * + * @param bytes + * the new app memory + */ + void setAppMemory(long pBytes, long vBytes) + { + String sPBytes = pBytes > 0 ? _byteFormat.format(pBytes) : "?"; + String sVBytes = vBytes > 0 ? _byteFormat.format(vBytes) : "?"; + _consoleForm._memory.setText(sPBytes+"["+sVBytes+"]"); + } + + /** + * Sets the app cpu. + * + * @param count + * the new app cpu + */ + void setAppCpu(int count) + { + if (count >= 0) + _consoleForm._cpu.setText("" + count); + else + _consoleForm._cpu.setText("?"); + + } + + /** + * Sets the exit code. + * + * @param code + * the new exit code + */ + void setExitCode(int code) + { + if (code >= 0) + _consoleForm._exitCode.setText("" + code); + } + + /** + * Sets the wrapper type. + * + * @param type + * the new wrapper type + */ + void setWrapperType(String type) + { + _consoleForm._wrapperType.setText(type); + } + + /** + * Sets the condition. + * + * @param active + * the new condition + */ + void setCondition(boolean active) + { + if (active) + { + _consoleForm._condition.setText(""); + _consoleForm._condition.setIcon(_okIcon); + } + else + { + _consoleForm._condition.setText("-"); + _consoleForm._condition.setIcon(null); + } + + } + + /** + * Sets the timer. + * + * @param active + * the new timer + */ + void setTimer(boolean active) + { + if (active) + { + _consoleForm._timer.setText(""); + _consoleForm._timer.setIcon(_okIcon); + } + else + { + _consoleForm._timer.setText("-"); + _consoleForm._timer.setIcon(null); + } + + } + + private void initInput() + { + + _consoleForm._input.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + // process may have not yet been started or it may have been + // stopped + try + { + if (_trayIcon._process == null || !_trayIcon._process.hasOutput()) + { + _consoleForm._input.setText("No input possible"); + _consoleForm._input.selectAll(); + return; + } + String txt = _consoleForm._input.getText(); + _trayIcon._process.writeOutput(txt); + _consoleForm._input.selectAll(); + } + catch (Throwable ex) + { + ex.printStackTrace(); + } + } + }); + + } + + private void initOutput() + { + + executor.execute(new Runnable() + { + + public void run() + { + _trayIcon._process.startDrain(); + while (!stop) + { + if (!_outputPaused) + { + _trayIcon.showState(_trayIcon._process.getState()); + String line; + while ((line = _trayIcon._process.readDrainLine()) != null) + addLine(line); + } + try + { + Thread.sleep(500); + } + catch (InterruptedException e) + { + e.printStackTrace(); + } + } + _trayIcon._process.stopDrain(); + + } + + }); + + } + + private void filterOutput() + { + executor.execute(new Runnable() + { + public void run() + { + _outputLock.lock(); + try + { + _consoleForm._output.getDocument().remove(0, _consoleForm._output.getDocument().getLength()); + } + catch (BadLocationException e) + { + e.printStackTrace(); + } + for (String line : _outputLines) + if (_outputFilter == null || line.contains(_outputFilter)) + addToTextArea(line); + _outputLock.unlock(); + } + }); + } + + private void clearOutput() + { + _outputLock.lock(); + _outputLines.clear(); + filterOutput(); + _outputLock.unlock(); + + } + + private void addLine(String line) + { + _outputLock.lock(); + _outputLines.addLast(line); + if (_outputLines.size() > maxLines) + _outputLines.removeFirst(); + if (_outputFilter == null || line.contains(_outputFilter)) + addToTextArea(line); + _outputLock.unlock(); + } + + private void addToTextArea(String line) + { + JTextArea textArea = _consoleForm._output; + textArea.append(line + "\n"); + if (textArea.getLineCount() > maxLines) + { + Element root = textArea.getDocument().getDefaultRootElement(); + Element firstLine = root.getElement(0); + + try + { + textArea.getDocument().remove(0, firstLine.getEndOffset()); + } + catch (Exception ble) + { + System.out.println(ble.getMessage()); + } + } + textArea.setCaretPosition(textArea.getDocument().getLength()); + + } + + /** + * Close. + */ + public void close() + { + stop = true; + this.setVisible(false); + this.dispose(); + } + + /** + * The Class WindowEventHandler. + */ + class WindowEventHandler extends WindowAdapter + { + + /* + * (non-Javadoc) + * + * @see + * java.awt.event.WindowAdapter#windowClosing(java.awt.event.WindowEvent + * ) + */ + public void windowClosing(WindowEvent evt) + { + close(); + _trayIcon.closeConsole(); + } + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/tray/ConsoleForm.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/tray/ConsoleForm.java new file mode 100644 index 0000000000..d35bb1e0c8 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/tray/ConsoleForm.java @@ -0,0 +1,505 @@ +package org.rzo.yajsw.tray; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.ComponentOrientation; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; + +import javax.swing.Box; +import javax.swing.ImageIcon; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTextArea; +import javax.swing.JTextField; + +import com.jeta.forms.components.border.TitledBorderBottom; +import com.jeta.forms.components.border.TitledBorderLabel; +import com.jeta.forms.components.border.TitledBorderSide; +import com.jgoodies.forms.layout.CellConstraints; +import com.jgoodies.forms.layout.FormLayout; + + +public class ConsoleForm extends JPanel +{ + JTextArea _output = new JTextArea(); + JTextField _input = new JTextField(); + JButton _START_BUTTON = new JButton(); + JButton _STOP_BUTTON = new JButton(); + JButton _RESTART_BUTTON = new JButton(); + JButton _EXIT_WRAPPER_BUTTON = new JButton(); + JButton _THREAD_DUMP_BUTTON = new JButton(); + JLabel _appStopTime = new JLabel(); + JLabel _state = new JLabel(); + JLabel _wStartTime = new JLabel(); + JLabel _trigger = new JLabel(); + JButton _STOP_TIMER_BUTTON = new JButton(); + JLabel _appPid = new JLabel(); + JLabel _appStartTime = new JLabel(); + JLabel _wPid = new JLabel(); + TitledBorderLabel _titledborderlabel1 = new TitledBorderLabel(); + TitledBorderSide _titledborderside1 = new TitledBorderSide(); + TitledBorderSide _titledborderside2 = new TitledBorderSide(); + TitledBorderBottom _titledborderbottom1 = new TitledBorderBottom(); + TitledBorderLabel _titledborderlabel2 = new TitledBorderLabel(); + TitledBorderSide _titledborderside3 = new TitledBorderSide(); + TitledBorderSide _titledborderside4 = new TitledBorderSide(); + TitledBorderBottom _titledborderbottom2 = new TitledBorderBottom(); + JButton _jbutton1 = new JButton(); + JLabel _timer = new JLabel(); + JLabel _condition = new JLabel(); + JLabel _wrapperType = new JLabel(); + JLabel _cpu = new JLabel(); + JLabel _memory = new JLabel(); + JLabel _handles = new JLabel(); + JLabel _threads = new JLabel(); + JLabel _count = new JLabel(); + JLabel _exitCode = new JLabel(); + JButton _THREAD_DUMP_WRAPPER_BUTTON = new JButton(); + JButton _EXIT_TRAY_ICON_BUTTON = new JButton(); + JButton _START_OUTPUT_BUTTON = new JButton(); + JButton _PAUSE_OUTPUT_BUTTON = new JButton(); + JTextField __OUTPUT_FILTER = new JTextField(); + JButton _CLEAR_OUTPUT_BUTTON = new JButton(); + JButton _GC_BUTTON = new JButton(); + JButton _DUMP_HEAP_BUTTON = new JButton(); + + /** + * Default constructor + */ + public ConsoleForm() + { + initializePanel(); + } + + /** + * Main method for panel + */ + public static void main(String[] args) + { + JFrame frame = new JFrame(); + frame.setSize(600, 400); + frame.setLocation(100, 100); + frame.getContentPane().add(new ConsoleForm()); + frame.setVisible(true); + + frame.addWindowListener( new WindowAdapter() + { + public void windowClosing( WindowEvent evt ) + { + System.exit(0); + } + }); + } + + /** + * Adds fill components to empty cells in the first row and first column of the grid. + * This ensures that the grid spacing will be the same as shown in the designer. + * @param cols an array of column indices in the first row where fill components should be added. + * @param rows an array of row indices in the first column where fill components should be added. + */ + void addFillComponents( Container panel, int[] cols, int[] rows ) + { + Dimension filler = new Dimension(10,10); + + boolean filled_cell_11 = false; + CellConstraints cc = new CellConstraints(); + if ( cols.length > 0 && rows.length > 0 ) + { + if ( cols[0] == 1 && rows[0] == 1 ) + { + /** add a rigid area */ + panel.add( Box.createRigidArea( filler ), cc.xy(1,1) ); + filled_cell_11 = true; + } + } + + for( int index = 0; index < cols.length; index++ ) + { + if ( cols[index] == 1 && filled_cell_11 ) + { + continue; + } + panel.add( Box.createRigidArea( filler ), cc.xy(cols[index],1) ); + } + + for( int index = 0; index < rows.length; index++ ) + { + if ( rows[index] == 1 && filled_cell_11 ) + { + continue; + } + panel.add( Box.createRigidArea( filler ), cc.xy(1,rows[index]) ); + } + + } + + /** + * Helper method to load an image file from the CLASSPATH + * @param imageName the package and name of the file to load relative to the CLASSPATH + * @return an ImageIcon instance with the specified image file + * @throws IllegalArgumentException if the image resource cannot be loaded. + */ + public ImageIcon loadImage( String imageName ) + { + try + { + ClassLoader classloader = getClass().getClassLoader(); + java.net.URL url = classloader.getResource( imageName ); + if ( url != null ) + { + ImageIcon icon = new ImageIcon( url ); + return icon; + } + } + catch( Exception e ) + { + e.printStackTrace(); + } + throw new IllegalArgumentException( "Unable to load image: " + imageName ); + } + + /** + * Method for recalculating the component orientation for + * right-to-left Locales. + * @param orientation the component orientation to be applied + */ + public void applyComponentOrientation( ComponentOrientation orientation ) + { + // Not yet implemented... + // I18NUtils.applyComponentOrientation(this, orientation); + super.applyComponentOrientation(orientation); + } + + public JPanel createPanel() + { + JPanel jpanel1 = new JPanel(); + FormLayout formlayout1 = new FormLayout("FILL:4DLU:NONE,FILL:4DLU:NONE,FILL:4DLU:NONE,LEFT:100PX:NONE,FILL:4DLU:NONE,RIGHT:100PX:NONE,FILL:4DLU:NONE,LEFT:100PX:NONE,FILL:4DLU:NONE,LEFT:100PX:NONE,FILL:4DLU:NONE,LEFT:100PX:NONE,FILL:4DLU:NONE,FILL:4DLU:NONE,FILL:4DLU:NONE,RIGHT:100PX:NONE,FILL:4DLU:NONE,FILL:100PX:NONE,FILL:4DLU:NONE,FILL:100PX:NONE,FILL:4DLU:NONE","CENTER:2DLU:NONE,CENTER:DEFAULT:NONE,CENTER:2DLU:NONE,FILL:238PX:NONE,CENTER:2DLU:NONE,CENTER:DEFAULT:NONE,CENTER:DEFAULT:NONE,FILL:14DLU:NONE,CENTER:DEFAULT:NONE,CENTER:2DLU:NONE,CENTER:DEFAULT:NONE,CENTER:2DLU:NONE,CENTER:DEFAULT:NONE,CENTER:2DLU:NONE,CENTER:DEFAULT:NONE,CENTER:4DLU:NONE,CENTER:DEFAULT:NONE,CENTER:DEFAULT:NONE,CENTER:4DLU:NONE,CENTER:4DLU:NONE,CENTER:DEFAULT:NONE,CENTER:2DLU:NONE"); + CellConstraints cc = new CellConstraints(); + jpanel1.setLayout(formlayout1); + + JLabel jlabel1 = new JLabel(); + jlabel1.setBackground(new Color(204,204,204)); + jlabel1.setFont(new Font("Tahoma",Font.BOLD,11)); + jlabel1.setOpaque(true); + jlabel1.setText("Output"); + jpanel1.add(jlabel1,new CellConstraints(4,2,1,1,CellConstraints.FILL,CellConstraints.DEFAULT)); + + _output.setName("output"); + JScrollPane jscrollpane1 = new JScrollPane(); + jscrollpane1.setViewportView(_output); + jscrollpane1.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED); + jscrollpane1.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); + jpanel1.add(jscrollpane1,cc.xywh(4,4,17,1)); + + JLabel jlabel2 = new JLabel(); + jlabel2.setFont(new Font("Tahoma",Font.BOLD,11)); + jlabel2.setText("Input (CR terminated)"); + jpanel1.add(jlabel2,cc.xy(4,6)); + + JLabel jlabel3 = new JLabel(); + jlabel3.setBackground(new Color(204,204,204)); + jlabel3.setFont(new Font("Tahoma",Font.BOLD,11)); + jlabel3.setOpaque(true); + jlabel3.setText("State "); + jlabel3.setHorizontalAlignment(JLabel.LEFT); + jpanel1.add(jlabel3,new CellConstraints(4,9,1,1,CellConstraints.FILL,CellConstraints.DEFAULT)); + + _input.setName("input"); + jpanel1.add(_input,cc.xywh(6,6,15,1)); + + _START_BUTTON.setActionCommand("Start"); + _START_BUTTON.setName("START_BUTTON"); + _START_BUTTON.setToolTipText("Start"); + jpanel1.add(_START_BUTTON,cc.xy(4,18)); + + _STOP_BUTTON.setActionCommand("Stop"); + _STOP_BUTTON.setName("STOP_BUTTON"); + _STOP_BUTTON.setToolTipText("Stop"); + jpanel1.add(_STOP_BUTTON,cc.xy(6,18)); + + _RESTART_BUTTON.setActionCommand("Restart"); + _RESTART_BUTTON.setName("RESTART_BUTTON"); + _RESTART_BUTTON.setToolTipText("Restart"); + jpanel1.add(_RESTART_BUTTON,cc.xy(8,18)); + + _EXIT_WRAPPER_BUTTON.setActionCommand("Exit"); + _EXIT_WRAPPER_BUTTON.setName("EXIT_WRAPPER_BUTTON"); + _EXIT_WRAPPER_BUTTON.setToolTipText("Stop Wrapper"); + jpanel1.add(_EXIT_WRAPPER_BUTTON,new CellConstraints(20,18,1,1,CellConstraints.RIGHT,CellConstraints.DEFAULT)); + + _THREAD_DUMP_BUTTON.setActionCommand("Thread Dump"); + _THREAD_DUMP_BUTTON.setName("THREAD_DUMP_BUTTON"); + _THREAD_DUMP_BUTTON.setToolTipText("Thread Dump"); + jpanel1.add(_THREAD_DUMP_BUTTON,new CellConstraints(10,18,1,1,CellConstraints.LEFT,CellConstraints.DEFAULT)); + + _appStopTime.setName("appStopTime"); + _appStopTime.setText("-"); + jpanel1.add(_appStopTime,cc.xy(10,11)); + + JLabel jlabel4 = new JLabel(); + jlabel4.setBackground(new Color(204,204,204)); + jlabel4.setFont(new Font("Tahoma",Font.BOLD,11)); + jlabel4.setOpaque(true); + jlabel4.setText("Started"); + jpanel1.add(jlabel4,new CellConstraints(8,9,1,1,CellConstraints.FILL,CellConstraints.DEFAULT)); + + _state.setBackground(new Color(255,255,255)); + _state.setName("state"); + _state.setText("IDLE"); + jpanel1.add(_state,cc.xy(4,11)); + + JLabel jlabel5 = new JLabel(); + jlabel5.setBackground(new Color(204,204,204)); + jlabel5.setFont(new Font("Tahoma",Font.BOLD,11)); + jlabel5.setOpaque(true); + jlabel5.setText("Stopped"); + jpanel1.add(jlabel5,new CellConstraints(10,9,1,1,CellConstraints.FILL,CellConstraints.DEFAULT)); + + _wStartTime.setName("wStartTime"); + _wStartTime.setText("-"); + jpanel1.add(_wStartTime,cc.xy(18,11)); + + _trigger.setName("trigger"); + _trigger.setText("-"); + jpanel1.add(_trigger,cc.xy(20,11)); + + JLabel jlabel6 = new JLabel(); + jlabel6.setBackground(new Color(204,204,204)); + jlabel6.setFont(new Font("Tahoma",Font.BOLD,11)); + jlabel6.setOpaque(true); + jlabel6.setText("PID"); + jpanel1.add(jlabel6,new CellConstraints(16,9,1,1,CellConstraints.FILL,CellConstraints.DEFAULT)); + + JLabel jlabel7 = new JLabel(); + jlabel7.setBackground(new Color(204,204,204)); + jlabel7.setFont(new Font("Tahoma",Font.BOLD,11)); + jlabel7.setOpaque(true); + jlabel7.setText("Started"); + jpanel1.add(jlabel7,new CellConstraints(18,9,1,1,CellConstraints.FILL,CellConstraints.DEFAULT)); + + JLabel jlabel8 = new JLabel(); + jlabel8.setBackground(new Color(204,204,204)); + jlabel8.setFont(new Font("Tahoma",Font.BOLD,11)); + jlabel8.setOpaque(true); + jlabel8.setText("Trigger"); + jpanel1.add(jlabel8,new CellConstraints(20,9,1,1,CellConstraints.FILL,CellConstraints.DEFAULT)); + + _STOP_TIMER_BUTTON.setActionCommand("Stop Timer / Condition"); + _STOP_TIMER_BUTTON.setName("STOP_TIMER_BUTTON"); + _STOP_TIMER_BUTTON.setToolTipText("Stop Timer / Condition"); + jpanel1.add(_STOP_TIMER_BUTTON,new CellConstraints(18,18,1,1,CellConstraints.RIGHT,CellConstraints.DEFAULT)); + + _appPid.setName("appPid"); + _appPid.setText("-"); + jpanel1.add(_appPid,cc.xy(6,11)); + + _appStartTime.setName("appStartTime"); + _appStartTime.setText("-"); + jpanel1.add(_appStartTime,cc.xy(8,11)); + + _wPid.setName("wPid"); + _wPid.setText("-"); + jpanel1.add(_wPid,cc.xy(16,11)); + + _titledborderlabel1.setText("Application"); + jpanel1.add(_titledborderlabel1,cc.xywh(3,8,10,1)); + + jpanel1.add(_titledborderside1,cc.xywh(2,8,1,12)); + + _titledborderside2.setOrientation(TitledBorderSide.RIGHT); + jpanel1.add(_titledborderside2,cc.xywh(13,8,1,12)); + + jpanel1.add(_titledborderbottom1,cc.xywh(3,19,10,1)); + + _titledborderlabel2.setText("Wrapper"); + jpanel1.add(_titledborderlabel2,cc.xywh(15,8,6,1)); + + jpanel1.add(_titledborderside3,cc.xywh(14,8,1,12)); + + _titledborderside4.setOrientation(TitledBorderSide.RIGHT); + jpanel1.add(_titledborderside4,cc.xywh(21,8,1,12)); + + jpanel1.add(_titledborderbottom2,cc.xywh(15,19,6,1)); + + _jbutton1.setActionCommand("Close Console"); + _jbutton1.setText("Close Console"); + _jbutton1.setToolTipText("Close Console The Console Window"); + jpanel1.add(_jbutton1,cc.xy(20,21)); + + _timer.setName("timer"); + _timer.setText("-"); + jpanel1.add(_timer,cc.xy(16,15)); + + _condition.setName("condition"); + _condition.setText("-"); + jpanel1.add(_condition,cc.xy(18,15)); + + _wrapperType.setName("wrapperType"); + _wrapperType.setText("-"); + jpanel1.add(_wrapperType,cc.xy(20,15)); + + JLabel jlabel9 = new JLabel(); + jlabel9.setBackground(new Color(204,204,204)); + jlabel9.setFont(new Font("Tahoma",Font.BOLD,11)); + jlabel9.setOpaque(true); + jlabel9.setText("PID"); + jlabel9.setHorizontalAlignment(JLabel.LEFT); + jpanel1.add(jlabel9,new CellConstraints(6,9,1,1,CellConstraints.FILL,CellConstraints.DEFAULT)); + + JLabel jlabel10 = new JLabel(); + jlabel10.setBackground(new Color(204,204,204)); + jlabel10.setFont(new Font("Tahoma",Font.BOLD,11)); + jlabel10.setOpaque(true); + jlabel10.setText("Timer"); + jlabel10.setHorizontalAlignment(JLabel.LEFT); + jpanel1.add(jlabel10,new CellConstraints(16,13,1,1,CellConstraints.FILL,CellConstraints.DEFAULT)); + + JLabel jlabel11 = new JLabel(); + jlabel11.setBackground(new Color(204,204,204)); + jlabel11.setFont(new Font("Tahoma",Font.BOLD,11)); + jlabel11.setOpaque(true); + jlabel11.setText("Condition"); + jlabel11.setHorizontalAlignment(JLabel.LEFT); + jpanel1.add(jlabel11,new CellConstraints(18,13,1,1,CellConstraints.FILL,CellConstraints.DEFAULT)); + + JLabel jlabel12 = new JLabel(); + jlabel12.setBackground(new Color(204,204,204)); + jlabel12.setFont(new Font("Tahoma",Font.BOLD,11)); + jlabel12.setOpaque(true); + jlabel12.setText("Type"); + jlabel12.setHorizontalAlignment(JLabel.LEFT); + jpanel1.add(jlabel12,new CellConstraints(20,13,1,1,CellConstraints.FILL,CellConstraints.DEFAULT)); + + JLabel jlabel13 = new JLabel(); + jlabel13.setBackground(new Color(204,204,204)); + jlabel13.setFont(new Font("Tahoma",Font.BOLD,11)); + jlabel13.setOpaque(true); + jlabel13.setText("CPU"); + jlabel13.setHorizontalAlignment(JLabel.LEFT); + jpanel1.add(jlabel13,new CellConstraints(4,13,1,1,CellConstraints.FILL,CellConstraints.DEFAULT)); + + _cpu.setName("cpu"); + _cpu.setText("-"); + jpanel1.add(_cpu,cc.xy(4,15)); + + JLabel jlabel14 = new JLabel(); + jlabel14.setBackground(new Color(204,204,204)); + jlabel14.setFont(new Font("Tahoma",Font.BOLD,11)); + jlabel14.setOpaque(true); + jlabel14.setText("Memory"); + jlabel14.setHorizontalAlignment(JLabel.LEFT); + jpanel1.add(jlabel14,new CellConstraints(6,13,1,1,CellConstraints.FILL,CellConstraints.DEFAULT)); + + _memory.setName("memory"); + _memory.setText("-"); + jpanel1.add(_memory,cc.xy(6,15)); + + JLabel jlabel15 = new JLabel(); + jlabel15.setBackground(new Color(204,204,204)); + jlabel15.setFont(new Font("Tahoma",Font.BOLD,11)); + jlabel15.setOpaque(true); + jlabel15.setText("Handles"); + jlabel15.setHorizontalAlignment(JLabel.LEFT); + jpanel1.add(jlabel15,new CellConstraints(8,13,1,1,CellConstraints.FILL,CellConstraints.DEFAULT)); + + _handles.setName("handles"); + _handles.setText("-"); + jpanel1.add(_handles,cc.xy(8,15)); + + JLabel jlabel16 = new JLabel(); + jlabel16.setBackground(new Color(204,204,204)); + jlabel16.setFont(new Font("Tahoma",Font.BOLD,11)); + jlabel16.setOpaque(true); + jlabel16.setText("Threads"); + jlabel16.setHorizontalAlignment(JLabel.LEFT); + jpanel1.add(jlabel16,new CellConstraints(10,13,1,1,CellConstraints.FILL,CellConstraints.DEFAULT)); + + _threads.setName("threads"); + _threads.setText("-"); + jpanel1.add(_threads,cc.xy(10,15)); + + _count.setName("count"); + _count.setText("-"); + jpanel1.add(_count,cc.xy(12,15)); + + JLabel jlabel17 = new JLabel(); + jlabel17.setBackground(new Color(204,204,204)); + jlabel17.setFont(new Font("Tahoma",Font.BOLD,11)); + jlabel17.setOpaque(true); + jlabel17.setText("Restarts"); + jpanel1.add(jlabel17,new CellConstraints(12,13,1,1,CellConstraints.FILL,CellConstraints.DEFAULT)); + + JLabel jlabel18 = new JLabel(); + jlabel18.setBackground(new Color(204,204,204)); + jlabel18.setFont(new Font("Tahoma",Font.BOLD,11)); + jlabel18.setOpaque(true); + jlabel18.setText("Exit Code"); + jpanel1.add(jlabel18,new CellConstraints(12,9,1,1,CellConstraints.FILL,CellConstraints.DEFAULT)); + + _exitCode.setName("exitCode"); + _exitCode.setText("-"); + jpanel1.add(_exitCode,cc.xy(12,11)); + + _THREAD_DUMP_WRAPPER_BUTTON.setActionCommand("Exit"); + _THREAD_DUMP_WRAPPER_BUTTON.setName("THREAD_DUMP_WRAPPER_BUTTON"); + _THREAD_DUMP_WRAPPER_BUTTON.setToolTipText("Thread Dump Wrapper"); + jpanel1.add(_THREAD_DUMP_WRAPPER_BUTTON,new CellConstraints(16,18,1,1,CellConstraints.RIGHT,CellConstraints.DEFAULT)); + + _EXIT_TRAY_ICON_BUTTON.setActionCommand("Exit"); + _EXIT_TRAY_ICON_BUTTON.setName("EXIT_TRAY_ICON_BUTTON"); + _EXIT_TRAY_ICON_BUTTON.setToolTipText("Exit Tray Icon"); + jpanel1.add(_EXIT_TRAY_ICON_BUTTON,new CellConstraints(18,21,1,1,CellConstraints.RIGHT,CellConstraints.DEFAULT)); + + _START_OUTPUT_BUTTON.setEnabled(false); + _START_OUTPUT_BUTTON.setName("START_OUTPUT_BUTTON"); + _START_OUTPUT_BUTTON.setToolTipText("Start Console Output"); + jpanel1.add(_START_OUTPUT_BUTTON,cc.xy(6,2)); + + _PAUSE_OUTPUT_BUTTON.setName("PAUSE_OUTPUT_BUTTON"); + _PAUSE_OUTPUT_BUTTON.setToolTipText("Pause Console Output"); + jpanel1.add(_PAUSE_OUTPUT_BUTTON,cc.xy(8,2)); + + JLabel jlabel19 = new JLabel(); + jlabel19.setFont(new Font("Tahoma",Font.BOLD,11)); + jlabel19.setText("Filter"); + jpanel1.add(jlabel19,cc.xy(16,2)); + + __OUTPUT_FILTER.setName("_OUTPUT_FILTER"); + jpanel1.add(__OUTPUT_FILTER,cc.xywh(18,2,3,1)); + + _CLEAR_OUTPUT_BUTTON.setName("CLEAR_OUTPUT_BUTTON"); + _CLEAR_OUTPUT_BUTTON.setToolTipText("Clear Output"); + jpanel1.add(_CLEAR_OUTPUT_BUTTON,cc.xy(10,2)); + + _GC_BUTTON.setActionCommand("Thread Dump"); + _GC_BUTTON.setName("GC_BUTTON"); + _GC_BUTTON.setToolTipText("Thread Dump"); + jpanel1.add(_GC_BUTTON,cc.xy(12,18)); + + _DUMP_HEAP_BUTTON.setActionCommand("Thread Dump"); + _DUMP_HEAP_BUTTON.setName("DUMP_HEAP_BUTTON"); + _DUMP_HEAP_BUTTON.setToolTipText("Thread Dump"); + jpanel1.add(_DUMP_HEAP_BUTTON,cc.xy(10,17)); + + addFillComponents(jpanel1,new int[]{ 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21 },new int[]{ 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22 }); + return jpanel1; + } + + /** + * Initializer + */ + protected void initializePanel() + { + setLayout(new BorderLayout()); + add(createPanel(), BorderLayout.CENTER); + } + + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/tray/ConsoleForm3.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/tray/ConsoleForm3.java new file mode 100644 index 0000000000..8da9b6f912 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/tray/ConsoleForm3.java @@ -0,0 +1,507 @@ +package org.rzo.yajsw.tray; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.ComponentOrientation; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; + +import javax.swing.Box; +import javax.swing.ImageIcon; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTextArea; +import javax.swing.JTextField; + +import com.jeta.forms.components.border.TitledBorderBottom; +import com.jeta.forms.components.border.TitledBorderLabel; +import com.jeta.forms.components.border.TitledBorderSide; +import com.jgoodies.forms.layout.CellConstraints; +import com.jgoodies.forms.layout.FormLayout; + +public class ConsoleForm3 extends JPanel +{ + JTextArea _output = new JTextArea(); + JTextField _input = new JTextField(); + JButton _START_BUTTON = new JButton(); + JButton _STOP_BUTTON = new JButton(); + JButton _RESTART_BUTTON = new JButton(); + JButton _EXIT_WRAPPER_BUTTON = new JButton(); + JButton _THREAD_DUMP_BUTTON = new JButton(); + JLabel _appStopTime = new JLabel(); + JLabel _state = new JLabel(); + JLabel _wStartTime = new JLabel(); + JLabel _trigger = new JLabel(); + JButton _STOP_TIMER_BUTTON = new JButton(); + JLabel _appPid = new JLabel(); + JLabel _appStartTime = new JLabel(); + JLabel _wPid = new JLabel(); + TitledBorderLabel _titledborderlabel1 = new TitledBorderLabel(); + TitledBorderSide _titledborderside1 = new TitledBorderSide(); + TitledBorderSide _titledborderside2 = new TitledBorderSide(); + TitledBorderBottom _titledborderbottom1 = new TitledBorderBottom(); + TitledBorderLabel _titledborderlabel2 = new TitledBorderLabel(); + TitledBorderSide _titledborderside3 = new TitledBorderSide(); + TitledBorderSide _titledborderside4 = new TitledBorderSide(); + TitledBorderBottom _titledborderbottom2 = new TitledBorderBottom(); + JButton _jbutton1 = new JButton(); + JLabel _timer = new JLabel(); + JLabel _condition = new JLabel(); + JLabel _wrapperType = new JLabel(); + JLabel _cpu = new JLabel(); + JLabel _memory = new JLabel(); + JLabel _handles = new JLabel(); + JLabel _threads = new JLabel(); + JLabel _count = new JLabel(); + JLabel _exitCode = new JLabel(); + JButton _THREAD_DUMP_WRAPPER_BUTTON = new JButton(); + JButton _EXIT_TRAY_ICON_BUTTON = new JButton(); + JButton _START_OUTPUT_BUTTON = new JButton(); + JButton _PAUSE_OUTPUT_BUTTON = new JButton(); + JTextField __OUTPUT_FILTER = new JTextField(); + JButton _CLEAR_OUTPUT_BUTTON = new JButton(); + + /** + * Default constructor + */ + public ConsoleForm3() + { + initializePanel(); + } + + /** + * Main method for panel + */ + public static void main(String[] args) + { + JFrame frame = new JFrame(); + frame.setSize(600, 400); + frame.setLocation(100, 100); + frame.getContentPane().add(new ConsoleForm3()); + frame.setVisible(true); + + frame.addWindowListener(new WindowAdapter() + { + public void windowClosing(WindowEvent evt) + { + System.exit(0); + } + }); + } + + /** + * Adds fill components to empty cells in the first row and first column of + * the grid. This ensures that the grid spacing will be the same as shown in + * the designer. + * + * @param cols + * an array of column indices in the first row where fill + * components should be added. + * @param rows + * an array of row indices in the first column where fill + * components should be added. + */ + void addFillComponents(Container panel, int[] cols, int[] rows) + { + Dimension filler = new Dimension(10, 10); + + boolean filled_cell_11 = false; + CellConstraints cc = new CellConstraints(); + if (cols.length > 0 && rows.length > 0) + { + if (cols[0] == 1 && rows[0] == 1) + { + /** add a rigid area */ + panel.add(Box.createRigidArea(filler), cc.xy(1, 1)); + filled_cell_11 = true; + } + } + + for (int index = 0; index < cols.length; index++) + { + if (cols[index] == 1 && filled_cell_11) + { + continue; + } + panel.add(Box.createRigidArea(filler), cc.xy(cols[index], 1)); + } + + for (int index = 0; index < rows.length; index++) + { + if (rows[index] == 1 && filled_cell_11) + { + continue; + } + panel.add(Box.createRigidArea(filler), cc.xy(1, rows[index])); + } + + } + + /** + * Helper method to load an image file from the CLASSPATH + * + * @param imageName + * the package and name of the file to load relative to the + * CLASSPATH + * @return an ImageIcon instance with the specified image file + * @throws IllegalArgumentException + * if the image resource cannot be loaded. + */ + public ImageIcon loadImage(String imageName) + { + try + { + ClassLoader classloader = getClass().getClassLoader(); + java.net.URL url = classloader.getResource(imageName); + if (url != null) + { + ImageIcon icon = new ImageIcon(url); + return icon; + } + } + catch (Exception e) + { + e.printStackTrace(); + } + throw new IllegalArgumentException("Unable to load image: " + imageName); + } + + /** + * Method for recalculating the component orientation for right-to-left + * Locales. + * + * @param orientation + * the component orientation to be applied + */ + public void applyComponentOrientation(ComponentOrientation orientation) + { + // Not yet implemented... + // I18NUtils.applyComponentOrientation(this, orientation); + super.applyComponentOrientation(orientation); + } + + public JPanel createPanel() + { + JPanel jpanel1 = new JPanel(); + FormLayout formlayout1 = new FormLayout( + "FILL:4DLU:NONE,FILL:4DLU:NONE,FILL:4DLU:NONE,LEFT:100PX:NONE,FILL:4DLU:NONE,RIGHT:100PX:NONE,FILL:4DLU:NONE,LEFT:100PX:NONE,FILL:4DLU:NONE,LEFT:100PX:NONE,FILL:4DLU:NONE,LEFT:100PX:NONE,FILL:4DLU:NONE,FILL:4DLU:NONE,FILL:4DLU:NONE,RIGHT:100PX:NONE,FILL:4DLU:NONE,FILL:100PX:NONE,FILL:4DLU:NONE,FILL:100PX:NONE,FILL:4DLU:NONE", + "CENTER:2DLU:NONE,CENTER:DEFAULT:NONE,CENTER:2DLU:NONE,FILL:238PX:NONE,CENTER:2DLU:NONE,CENTER:DEFAULT:NONE,CENTER:DEFAULT:NONE,FILL:14DLU:NONE,CENTER:DEFAULT:NONE,CENTER:2DLU:NONE,CENTER:DEFAULT:NONE,CENTER:2DLU:NONE,CENTER:DEFAULT:NONE,CENTER:2DLU:NONE,CENTER:DEFAULT:NONE,CENTER:4DLU:NONE,CENTER:DEFAULT:NONE,CENTER:4DLU:NONE,CENTER:4DLU:NONE,CENTER:DEFAULT:NONE,CENTER:2DLU:NONE"); + CellConstraints cc = new CellConstraints(); + jpanel1.setLayout(formlayout1); + + JLabel jlabel1 = new JLabel(); + jlabel1.setBackground(new Color(204, 204, 204)); + jlabel1.setFont(new Font("Tahoma", Font.BOLD, 11)); + jlabel1.setOpaque(true); + jlabel1.setText("Output"); + jpanel1.add(jlabel1, new CellConstraints(4, 2, 1, 1, CellConstraints.FILL, CellConstraints.DEFAULT)); + + _output.setName("output"); + JScrollPane jscrollpane1 = new JScrollPane(); + jscrollpane1.setViewportView(_output); + jscrollpane1.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED); + jscrollpane1.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); + jpanel1.add(jscrollpane1, cc.xywh(4, 4, 17, 1)); + + JLabel jlabel2 = new JLabel(); + jlabel2.setFont(new Font("Tahoma", Font.BOLD, 11)); + jlabel2.setText("Input (CR terminated)"); + jpanel1.add(jlabel2, cc.xy(4, 6)); + + JLabel jlabel3 = new JLabel(); + jlabel3.setBackground(new Color(204, 204, 204)); + jlabel3.setFont(new Font("Tahoma", Font.BOLD, 11)); + jlabel3.setOpaque(true); + jlabel3.setText("State "); + jlabel3.setHorizontalAlignment(JLabel.LEFT); + jpanel1.add(jlabel3, new CellConstraints(4, 9, 1, 1, CellConstraints.FILL, CellConstraints.DEFAULT)); + + _input.setName("input"); + jpanel1.add(_input, cc.xywh(6, 6, 15, 1)); + + _START_BUTTON.setActionCommand("Start"); + _START_BUTTON.setName("START_BUTTON"); + _START_BUTTON.setToolTipText("Start"); + jpanel1.add(_START_BUTTON, cc.xy(4, 17)); + + _STOP_BUTTON.setActionCommand("Stop"); + _STOP_BUTTON.setName("STOP_BUTTON"); + _STOP_BUTTON.setToolTipText("Stop"); + jpanel1.add(_STOP_BUTTON, cc.xy(6, 17)); + + _RESTART_BUTTON.setActionCommand("Restart"); + _RESTART_BUTTON.setName("RESTART_BUTTON"); + _RESTART_BUTTON.setToolTipText("Restart"); + jpanel1.add(_RESTART_BUTTON, cc.xy(8, 17)); + + _EXIT_WRAPPER_BUTTON.setActionCommand("Exit"); + _EXIT_WRAPPER_BUTTON.setName("EXIT_WRAPPER_BUTTON"); + _EXIT_WRAPPER_BUTTON.setToolTipText("Stop Wrapper"); + jpanel1.add(_EXIT_WRAPPER_BUTTON, new CellConstraints(20, 17, 1, 1, CellConstraints.RIGHT, CellConstraints.DEFAULT)); + + _THREAD_DUMP_BUTTON.setActionCommand("Thread Dump"); + _THREAD_DUMP_BUTTON.setName("THREAD_DUMP_BUTTON"); + _THREAD_DUMP_BUTTON.setToolTipText("Thread Dump"); + jpanel1.add(_THREAD_DUMP_BUTTON, new CellConstraints(10, 17, 1, 1, CellConstraints.LEFT, CellConstraints.DEFAULT)); + + _appStopTime.setName("appStopTime"); + _appStopTime.setText("-"); + jpanel1.add(_appStopTime, cc.xy(10, 11)); + + JLabel jlabel4 = new JLabel(); + jlabel4.setBackground(new Color(204, 204, 204)); + jlabel4.setFont(new Font("Tahoma", Font.BOLD, 11)); + jlabel4.setOpaque(true); + jlabel4.setText("Started"); + jpanel1.add(jlabel4, new CellConstraints(8, 9, 1, 1, CellConstraints.FILL, CellConstraints.DEFAULT)); + + _state.setBackground(new Color(255, 255, 255)); + _state.setName("state"); + _state.setText("IDLE"); + jpanel1.add(_state, cc.xy(4, 11)); + + JLabel jlabel5 = new JLabel(); + jlabel5.setBackground(new Color(204, 204, 204)); + jlabel5.setFont(new Font("Tahoma", Font.BOLD, 11)); + jlabel5.setOpaque(true); + jlabel5.setText("Stopped"); + jpanel1.add(jlabel5, new CellConstraints(10, 9, 1, 1, CellConstraints.FILL, CellConstraints.DEFAULT)); + + _wStartTime.setName("wStartTime"); + _wStartTime.setText("-"); + jpanel1.add(_wStartTime, cc.xy(18, 11)); + + _trigger.setName("trigger"); + _trigger.setText("-"); + jpanel1.add(_trigger, cc.xy(20, 11)); + + JLabel jlabel6 = new JLabel(); + jlabel6.setBackground(new Color(204, 204, 204)); + jlabel6.setFont(new Font("Tahoma", Font.BOLD, 11)); + jlabel6.setOpaque(true); + jlabel6.setText("PID"); + jpanel1.add(jlabel6, new CellConstraints(16, 9, 1, 1, CellConstraints.FILL, CellConstraints.DEFAULT)); + + JLabel jlabel7 = new JLabel(); + jlabel7.setBackground(new Color(204, 204, 204)); + jlabel7.setFont(new Font("Tahoma", Font.BOLD, 11)); + jlabel7.setOpaque(true); + jlabel7.setText("Started"); + jpanel1.add(jlabel7, new CellConstraints(18, 9, 1, 1, CellConstraints.FILL, CellConstraints.DEFAULT)); + + JLabel jlabel8 = new JLabel(); + jlabel8.setBackground(new Color(204, 204, 204)); + jlabel8.setFont(new Font("Tahoma", Font.BOLD, 11)); + jlabel8.setOpaque(true); + jlabel8.setText("Trigger"); + jpanel1.add(jlabel8, new CellConstraints(20, 9, 1, 1, CellConstraints.FILL, CellConstraints.DEFAULT)); + + _STOP_TIMER_BUTTON.setActionCommand("Stop Timer / Condition"); + _STOP_TIMER_BUTTON.setName("STOP_TIMER_BUTTON"); + _STOP_TIMER_BUTTON.setToolTipText("Stop Timer / Condition"); + jpanel1.add(_STOP_TIMER_BUTTON, new CellConstraints(18, 17, 1, 1, CellConstraints.RIGHT, CellConstraints.DEFAULT)); + + _appPid.setName("appPid"); + _appPid.setText("-"); + jpanel1.add(_appPid, cc.xy(6, 11)); + + _appStartTime.setName("appStartTime"); + _appStartTime.setText("-"); + jpanel1.add(_appStartTime, cc.xy(8, 11)); + + _wPid.setName("wPid"); + _wPid.setText("-"); + jpanel1.add(_wPid, cc.xy(16, 11)); + + _titledborderlabel1.setText("Application"); + jpanel1.add(_titledborderlabel1, cc.xywh(3, 8, 10, 1)); + + jpanel1.add(_titledborderside1, cc.xywh(2, 8, 1, 11)); + + _titledborderside2.setOrientation(TitledBorderSide.RIGHT); + jpanel1.add(_titledborderside2, cc.xywh(13, 8, 1, 11)); + + jpanel1.add(_titledborderbottom1, cc.xywh(3, 18, 10, 1)); + + _titledborderlabel2.setText("Wrapper"); + jpanel1.add(_titledborderlabel2, cc.xywh(15, 8, 6, 1)); + + jpanel1.add(_titledborderside3, cc.xywh(14, 8, 1, 11)); + + _titledborderside4.setOrientation(TitledBorderSide.RIGHT); + jpanel1.add(_titledborderside4, cc.xywh(21, 8, 1, 11)); + + jpanel1.add(_titledborderbottom2, cc.xywh(15, 18, 6, 1)); + + _jbutton1.setActionCommand("Close Console"); + _jbutton1.setText("Close Console"); + _jbutton1.setToolTipText("Close Console The Console Window"); + jpanel1.add(_jbutton1, cc.xy(20, 20)); + + _timer.setName("timer"); + _timer.setText("-"); + jpanel1.add(_timer, cc.xy(16, 15)); + + _condition.setName("condition"); + _condition.setText("-"); + jpanel1.add(_condition, cc.xy(18, 15)); + + _wrapperType.setName("wrapperType"); + _wrapperType.setText("-"); + jpanel1.add(_wrapperType, cc.xy(20, 15)); + + JLabel jlabel9 = new JLabel(); + jlabel9.setBackground(new Color(204, 204, 204)); + jlabel9.setFont(new Font("Tahoma", Font.BOLD, 11)); + jlabel9.setOpaque(true); + jlabel9.setText("PID"); + jlabel9.setHorizontalAlignment(JLabel.LEFT); + jpanel1.add(jlabel9, new CellConstraints(6, 9, 1, 1, CellConstraints.FILL, CellConstraints.DEFAULT)); + + JLabel jlabel10 = new JLabel(); + jlabel10.setBackground(new Color(204, 204, 204)); + jlabel10.setFont(new Font("Tahoma", Font.BOLD, 11)); + jlabel10.setOpaque(true); + jlabel10.setText("Timer"); + jlabel10.setHorizontalAlignment(JLabel.LEFT); + jpanel1.add(jlabel10, new CellConstraints(16, 13, 1, 1, CellConstraints.FILL, CellConstraints.DEFAULT)); + + JLabel jlabel11 = new JLabel(); + jlabel11.setBackground(new Color(204, 204, 204)); + jlabel11.setFont(new Font("Tahoma", Font.BOLD, 11)); + jlabel11.setOpaque(true); + jlabel11.setText("Condition"); + jlabel11.setHorizontalAlignment(JLabel.LEFT); + jpanel1.add(jlabel11, new CellConstraints(18, 13, 1, 1, CellConstraints.FILL, CellConstraints.DEFAULT)); + + JLabel jlabel12 = new JLabel(); + jlabel12.setBackground(new Color(204, 204, 204)); + jlabel12.setFont(new Font("Tahoma", Font.BOLD, 11)); + jlabel12.setOpaque(true); + jlabel12.setText("Type"); + jlabel12.setHorizontalAlignment(JLabel.LEFT); + jpanel1.add(jlabel12, new CellConstraints(20, 13, 1, 1, CellConstraints.FILL, CellConstraints.DEFAULT)); + + JLabel jlabel13 = new JLabel(); + jlabel13.setBackground(new Color(204, 204, 204)); + jlabel13.setFont(new Font("Tahoma", Font.BOLD, 11)); + jlabel13.setOpaque(true); + jlabel13.setText("CPU"); + jlabel13.setHorizontalAlignment(JLabel.LEFT); + jpanel1.add(jlabel13, new CellConstraints(4, 13, 1, 1, CellConstraints.FILL, CellConstraints.DEFAULT)); + + _cpu.setName("cpu"); + _cpu.setText("-"); + jpanel1.add(_cpu, cc.xy(4, 15)); + + JLabel jlabel14 = new JLabel(); + jlabel14.setBackground(new Color(204, 204, 204)); + jlabel14.setFont(new Font("Tahoma", Font.BOLD, 11)); + jlabel14.setOpaque(true); + jlabel14.setText("Memory"); + jlabel14.setHorizontalAlignment(JLabel.LEFT); + jpanel1.add(jlabel14, new CellConstraints(6, 13, 1, 1, CellConstraints.FILL, CellConstraints.DEFAULT)); + + _memory.setName("memory"); + _memory.setText("-"); + jpanel1.add(_memory, cc.xy(6, 15)); + + JLabel jlabel15 = new JLabel(); + jlabel15.setBackground(new Color(204, 204, 204)); + jlabel15.setFont(new Font("Tahoma", Font.BOLD, 11)); + jlabel15.setOpaque(true); + jlabel15.setText("Handles"); + jlabel15.setHorizontalAlignment(JLabel.LEFT); + jpanel1.add(jlabel15, new CellConstraints(8, 13, 1, 1, CellConstraints.FILL, CellConstraints.DEFAULT)); + + _handles.setName("handles"); + _handles.setText("-"); + jpanel1.add(_handles, cc.xy(8, 15)); + + JLabel jlabel16 = new JLabel(); + jlabel16.setBackground(new Color(204, 204, 204)); + jlabel16.setFont(new Font("Tahoma", Font.BOLD, 11)); + jlabel16.setOpaque(true); + jlabel16.setText("Threads"); + jlabel16.setHorizontalAlignment(JLabel.LEFT); + jpanel1.add(jlabel16, new CellConstraints(10, 13, 1, 1, CellConstraints.FILL, CellConstraints.DEFAULT)); + + _threads.setName("threads"); + _threads.setText("-"); + jpanel1.add(_threads, cc.xy(10, 15)); + + _count.setName("count"); + _count.setText("-"); + jpanel1.add(_count, cc.xy(12, 15)); + + JLabel jlabel17 = new JLabel(); + jlabel17.setBackground(new Color(204, 204, 204)); + jlabel17.setFont(new Font("Tahoma", Font.BOLD, 11)); + jlabel17.setOpaque(true); + jlabel17.setText("Restarts"); + jpanel1.add(jlabel17, new CellConstraints(12, 13, 1, 1, CellConstraints.FILL, CellConstraints.DEFAULT)); + + JLabel jlabel18 = new JLabel(); + jlabel18.setBackground(new Color(204, 204, 204)); + jlabel18.setFont(new Font("Tahoma", Font.BOLD, 11)); + jlabel18.setOpaque(true); + jlabel18.setText("Exit Code"); + jpanel1.add(jlabel18, new CellConstraints(12, 9, 1, 1, CellConstraints.FILL, CellConstraints.DEFAULT)); + + _exitCode.setName("exitCode"); + _exitCode.setText("-"); + jpanel1.add(_exitCode, cc.xy(12, 11)); + + _THREAD_DUMP_WRAPPER_BUTTON.setActionCommand("Exit"); + _THREAD_DUMP_WRAPPER_BUTTON.setName("THREAD_DUMP_WRAPPER_BUTTON"); + _THREAD_DUMP_WRAPPER_BUTTON.setToolTipText("Thread Dump Wrapper"); + jpanel1.add(_THREAD_DUMP_WRAPPER_BUTTON, new CellConstraints(16, 17, 1, 1, CellConstraints.RIGHT, CellConstraints.DEFAULT)); + + _EXIT_TRAY_ICON_BUTTON.setActionCommand("Exit"); + _EXIT_TRAY_ICON_BUTTON.setName("EXIT_TRAY_ICON_BUTTON"); + _EXIT_TRAY_ICON_BUTTON.setToolTipText("Exit Tray Icon"); + jpanel1.add(_EXIT_TRAY_ICON_BUTTON, new CellConstraints(18, 20, 1, 1, CellConstraints.RIGHT, CellConstraints.DEFAULT)); + + _START_OUTPUT_BUTTON.setEnabled(false); + _START_OUTPUT_BUTTON.setName("START_OUTPUT_BUTTON"); + _START_OUTPUT_BUTTON.setToolTipText("Start Console Output"); + jpanel1.add(_START_OUTPUT_BUTTON, cc.xy(6, 2)); + + _PAUSE_OUTPUT_BUTTON.setName("PAUSE_OUTPUT_BUTTON"); + _PAUSE_OUTPUT_BUTTON.setToolTipText("Pause Console Output"); + jpanel1.add(_PAUSE_OUTPUT_BUTTON, cc.xy(8, 2)); + + JLabel jlabel19 = new JLabel(); + jlabel19.setFont(new Font("Tahoma", Font.BOLD, 11)); + jlabel19.setText("Filter"); + jpanel1.add(jlabel19, cc.xy(16, 2)); + + __OUTPUT_FILTER.setName("_OUTPUT_FILTER"); + jpanel1.add(__OUTPUT_FILTER, cc.xywh(18, 2, 3, 1)); + + _CLEAR_OUTPUT_BUTTON.setName("CLEAR_OUTPUT_BUTTON"); + _CLEAR_OUTPUT_BUTTON.setToolTipText("Clear Output"); + jpanel1.add(_CLEAR_OUTPUT_BUTTON, cc.xy(10, 2)); + + addFillComponents(jpanel1, new int[] + { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21 }, new int[] + { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21 }); + return jpanel1; + } + + /** + * Initializer + */ + protected void initializePanel() + { + setLayout(new BorderLayout()); + add(createPanel(), BorderLayout.CENTER); + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/tray/ConsoleForm4.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/tray/ConsoleForm4.java new file mode 100644 index 0000000000..f019e69266 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/tray/ConsoleForm4.java @@ -0,0 +1,505 @@ +package org.rzo.yajsw.tray; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.ComponentOrientation; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; + +import javax.swing.Box; +import javax.swing.ImageIcon; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTextArea; +import javax.swing.JTextField; + +import com.jeta.forms.components.border.TitledBorderBottom; +import com.jeta.forms.components.border.TitledBorderLabel; +import com.jeta.forms.components.border.TitledBorderSide; +import com.jgoodies.forms.layout.CellConstraints; +import com.jgoodies.forms.layout.FormLayout; + + +public class ConsoleForm4 extends JPanel +{ + JTextArea _output = new JTextArea(); + JTextField _input = new JTextField(); + JButton _START_BUTTON = new JButton(); + JButton _STOP_BUTTON = new JButton(); + JButton _RESTART_BUTTON = new JButton(); + JButton _EXIT_WRAPPER_BUTTON = new JButton(); + JButton _THREAD_DUMP_BUTTON = new JButton(); + JLabel _appStopTime = new JLabel(); + JLabel _state = new JLabel(); + JLabel _wStartTime = new JLabel(); + JLabel _trigger = new JLabel(); + JButton _STOP_TIMER_BUTTON = new JButton(); + JLabel _appPid = new JLabel(); + JLabel _appStartTime = new JLabel(); + JLabel _wPid = new JLabel(); + TitledBorderLabel _titledborderlabel1 = new TitledBorderLabel(); + TitledBorderSide _titledborderside1 = new TitledBorderSide(); + TitledBorderSide _titledborderside2 = new TitledBorderSide(); + TitledBorderBottom _titledborderbottom1 = new TitledBorderBottom(); + TitledBorderLabel _titledborderlabel2 = new TitledBorderLabel(); + TitledBorderSide _titledborderside3 = new TitledBorderSide(); + TitledBorderSide _titledborderside4 = new TitledBorderSide(); + TitledBorderBottom _titledborderbottom2 = new TitledBorderBottom(); + JButton _jbutton1 = new JButton(); + JLabel _timer = new JLabel(); + JLabel _condition = new JLabel(); + JLabel _wrapperType = new JLabel(); + JLabel _cpu = new JLabel(); + JLabel _memory = new JLabel(); + JLabel _handles = new JLabel(); + JLabel _threads = new JLabel(); + JLabel _count = new JLabel(); + JLabel _exitCode = new JLabel(); + JButton _THREAD_DUMP_WRAPPER_BUTTON = new JButton(); + JButton _EXIT_TRAY_ICON_BUTTON = new JButton(); + JButton _START_OUTPUT_BUTTON = new JButton(); + JButton _PAUSE_OUTPUT_BUTTON = new JButton(); + JTextField __OUTPUT_FILTER = new JTextField(); + JButton _CLEAR_OUTPUT_BUTTON = new JButton(); + JButton _GC_BUTTON = new JButton(); + JButton _DUMP_HEAP_BUTTON = new JButton(); + + /** + * Default constructor + */ + public ConsoleForm4() + { + initializePanel(); + } + + /** + * Main method for panel + */ + public static void main(String[] args) + { + JFrame frame = new JFrame(); + frame.setSize(600, 400); + frame.setLocation(100, 100); + frame.getContentPane().add(new ConsoleForm4()); + frame.setVisible(true); + + frame.addWindowListener( new WindowAdapter() + { + public void windowClosing( WindowEvent evt ) + { + System.exit(0); + } + }); + } + + /** + * Adds fill components to empty cells in the first row and first column of the grid. + * This ensures that the grid spacing will be the same as shown in the designer. + * @param cols an array of column indices in the first row where fill components should be added. + * @param rows an array of row indices in the first column where fill components should be added. + */ + void addFillComponents( Container panel, int[] cols, int[] rows ) + { + Dimension filler = new Dimension(10,10); + + boolean filled_cell_11 = false; + CellConstraints cc = new CellConstraints(); + if ( cols.length > 0 && rows.length > 0 ) + { + if ( cols[0] == 1 && rows[0] == 1 ) + { + /** add a rigid area */ + panel.add( Box.createRigidArea( filler ), cc.xy(1,1) ); + filled_cell_11 = true; + } + } + + for( int index = 0; index < cols.length; index++ ) + { + if ( cols[index] == 1 && filled_cell_11 ) + { + continue; + } + panel.add( Box.createRigidArea( filler ), cc.xy(cols[index],1) ); + } + + for( int index = 0; index < rows.length; index++ ) + { + if ( rows[index] == 1 && filled_cell_11 ) + { + continue; + } + panel.add( Box.createRigidArea( filler ), cc.xy(1,rows[index]) ); + } + + } + + /** + * Helper method to load an image file from the CLASSPATH + * @param imageName the package and name of the file to load relative to the CLASSPATH + * @return an ImageIcon instance with the specified image file + * @throws IllegalArgumentException if the image resource cannot be loaded. + */ + public ImageIcon loadImage( String imageName ) + { + try + { + ClassLoader classloader = getClass().getClassLoader(); + java.net.URL url = classloader.getResource( imageName ); + if ( url != null ) + { + ImageIcon icon = new ImageIcon( url ); + return icon; + } + } + catch( Exception e ) + { + e.printStackTrace(); + } + throw new IllegalArgumentException( "Unable to load image: " + imageName ); + } + + /** + * Method for recalculating the component orientation for + * right-to-left Locales. + * @param orientation the component orientation to be applied + */ + public void applyComponentOrientation( ComponentOrientation orientation ) + { + // Not yet implemented... + // I18NUtils.applyComponentOrientation(this, orientation); + super.applyComponentOrientation(orientation); + } + + public JPanel createPanel() + { + JPanel jpanel1 = new JPanel(); + FormLayout formlayout1 = new FormLayout("FILL:4DLU:NONE,FILL:4DLU:NONE,FILL:4DLU:NONE,LEFT:100PX:NONE,FILL:4DLU:NONE,RIGHT:100PX:NONE,FILL:4DLU:NONE,LEFT:100PX:NONE,FILL:4DLU:NONE,LEFT:100PX:NONE,FILL:4DLU:NONE,LEFT:100PX:NONE,FILL:4DLU:NONE,FILL:4DLU:NONE,FILL:4DLU:NONE,RIGHT:100PX:NONE,FILL:4DLU:NONE,FILL:100PX:NONE,FILL:4DLU:NONE,FILL:100PX:NONE,FILL:4DLU:NONE","CENTER:2DLU:NONE,CENTER:DEFAULT:NONE,CENTER:2DLU:NONE,FILL:238PX:NONE,CENTER:2DLU:NONE,CENTER:DEFAULT:NONE,CENTER:DEFAULT:NONE,FILL:14DLU:NONE,CENTER:DEFAULT:NONE,CENTER:2DLU:NONE,CENTER:DEFAULT:NONE,CENTER:2DLU:NONE,CENTER:DEFAULT:NONE,CENTER:2DLU:NONE,CENTER:DEFAULT:NONE,CENTER:4DLU:NONE,CENTER:DEFAULT:NONE,CENTER:DEFAULT:NONE,CENTER:4DLU:NONE,CENTER:4DLU:NONE,CENTER:DEFAULT:NONE,CENTER:2DLU:NONE"); + CellConstraints cc = new CellConstraints(); + jpanel1.setLayout(formlayout1); + + JLabel jlabel1 = new JLabel(); + jlabel1.setBackground(new Color(204,204,204)); + jlabel1.setFont(new Font("Tahoma",Font.BOLD,11)); + jlabel1.setOpaque(true); + jlabel1.setText("Output"); + jpanel1.add(jlabel1,new CellConstraints(4,2,1,1,CellConstraints.FILL,CellConstraints.DEFAULT)); + + _output.setName("output"); + JScrollPane jscrollpane1 = new JScrollPane(); + jscrollpane1.setViewportView(_output); + jscrollpane1.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED); + jscrollpane1.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); + jpanel1.add(jscrollpane1,cc.xywh(4,4,17,1)); + + JLabel jlabel2 = new JLabel(); + jlabel2.setFont(new Font("Tahoma",Font.BOLD,11)); + jlabel2.setText("Input (CR terminated)"); + jpanel1.add(jlabel2,cc.xy(4,6)); + + JLabel jlabel3 = new JLabel(); + jlabel3.setBackground(new Color(204,204,204)); + jlabel3.setFont(new Font("Tahoma",Font.BOLD,11)); + jlabel3.setOpaque(true); + jlabel3.setText("State "); + jlabel3.setHorizontalAlignment(JLabel.LEFT); + jpanel1.add(jlabel3,new CellConstraints(4,9,1,1,CellConstraints.FILL,CellConstraints.DEFAULT)); + + _input.setName("input"); + jpanel1.add(_input,cc.xywh(6,6,15,1)); + + _START_BUTTON.setActionCommand("Start"); + _START_BUTTON.setName("START_BUTTON"); + _START_BUTTON.setToolTipText("Start"); + jpanel1.add(_START_BUTTON,cc.xy(4,18)); + + _STOP_BUTTON.setActionCommand("Stop"); + _STOP_BUTTON.setName("STOP_BUTTON"); + _STOP_BUTTON.setToolTipText("Stop"); + jpanel1.add(_STOP_BUTTON,cc.xy(6,18)); + + _RESTART_BUTTON.setActionCommand("Restart"); + _RESTART_BUTTON.setName("RESTART_BUTTON"); + _RESTART_BUTTON.setToolTipText("Restart"); + jpanel1.add(_RESTART_BUTTON,cc.xy(8,18)); + + _EXIT_WRAPPER_BUTTON.setActionCommand("Exit"); + _EXIT_WRAPPER_BUTTON.setName("EXIT_WRAPPER_BUTTON"); + _EXIT_WRAPPER_BUTTON.setToolTipText("Stop Wrapper"); + jpanel1.add(_EXIT_WRAPPER_BUTTON,new CellConstraints(20,18,1,1,CellConstraints.RIGHT,CellConstraints.DEFAULT)); + + _THREAD_DUMP_BUTTON.setActionCommand("Thread Dump"); + _THREAD_DUMP_BUTTON.setName("THREAD_DUMP_BUTTON"); + _THREAD_DUMP_BUTTON.setToolTipText("Thread Dump"); + jpanel1.add(_THREAD_DUMP_BUTTON,new CellConstraints(10,18,1,1,CellConstraints.LEFT,CellConstraints.DEFAULT)); + + _appStopTime.setName("appStopTime"); + _appStopTime.setText("-"); + jpanel1.add(_appStopTime,cc.xy(10,11)); + + JLabel jlabel4 = new JLabel(); + jlabel4.setBackground(new Color(204,204,204)); + jlabel4.setFont(new Font("Tahoma",Font.BOLD,11)); + jlabel4.setOpaque(true); + jlabel4.setText("Started"); + jpanel1.add(jlabel4,new CellConstraints(8,9,1,1,CellConstraints.FILL,CellConstraints.DEFAULT)); + + _state.setBackground(new Color(255,255,255)); + _state.setName("state"); + _state.setText("IDLE"); + jpanel1.add(_state,cc.xy(4,11)); + + JLabel jlabel5 = new JLabel(); + jlabel5.setBackground(new Color(204,204,204)); + jlabel5.setFont(new Font("Tahoma",Font.BOLD,11)); + jlabel5.setOpaque(true); + jlabel5.setText("Stopped"); + jpanel1.add(jlabel5,new CellConstraints(10,9,1,1,CellConstraints.FILL,CellConstraints.DEFAULT)); + + _wStartTime.setName("wStartTime"); + _wStartTime.setText("-"); + jpanel1.add(_wStartTime,cc.xy(18,11)); + + _trigger.setName("trigger"); + _trigger.setText("-"); + jpanel1.add(_trigger,cc.xy(20,11)); + + JLabel jlabel6 = new JLabel(); + jlabel6.setBackground(new Color(204,204,204)); + jlabel6.setFont(new Font("Tahoma",Font.BOLD,11)); + jlabel6.setOpaque(true); + jlabel6.setText("PID"); + jpanel1.add(jlabel6,new CellConstraints(16,9,1,1,CellConstraints.FILL,CellConstraints.DEFAULT)); + + JLabel jlabel7 = new JLabel(); + jlabel7.setBackground(new Color(204,204,204)); + jlabel7.setFont(new Font("Tahoma",Font.BOLD,11)); + jlabel7.setOpaque(true); + jlabel7.setText("Started"); + jpanel1.add(jlabel7,new CellConstraints(18,9,1,1,CellConstraints.FILL,CellConstraints.DEFAULT)); + + JLabel jlabel8 = new JLabel(); + jlabel8.setBackground(new Color(204,204,204)); + jlabel8.setFont(new Font("Tahoma",Font.BOLD,11)); + jlabel8.setOpaque(true); + jlabel8.setText("Trigger"); + jpanel1.add(jlabel8,new CellConstraints(20,9,1,1,CellConstraints.FILL,CellConstraints.DEFAULT)); + + _STOP_TIMER_BUTTON.setActionCommand("Stop Timer / Condition"); + _STOP_TIMER_BUTTON.setName("STOP_TIMER_BUTTON"); + _STOP_TIMER_BUTTON.setToolTipText("Stop Timer / Condition"); + jpanel1.add(_STOP_TIMER_BUTTON,new CellConstraints(18,18,1,1,CellConstraints.RIGHT,CellConstraints.DEFAULT)); + + _appPid.setName("appPid"); + _appPid.setText("-"); + jpanel1.add(_appPid,cc.xy(6,11)); + + _appStartTime.setName("appStartTime"); + _appStartTime.setText("-"); + jpanel1.add(_appStartTime,cc.xy(8,11)); + + _wPid.setName("wPid"); + _wPid.setText("-"); + jpanel1.add(_wPid,cc.xy(16,11)); + + _titledborderlabel1.setText("Application"); + jpanel1.add(_titledborderlabel1,cc.xywh(3,8,10,1)); + + jpanel1.add(_titledborderside1,cc.xywh(2,8,1,12)); + + _titledborderside2.setOrientation(TitledBorderSide.RIGHT); + jpanel1.add(_titledborderside2,cc.xywh(13,8,1,12)); + + jpanel1.add(_titledborderbottom1,cc.xywh(3,19,10,1)); + + _titledborderlabel2.setText("Wrapper"); + jpanel1.add(_titledborderlabel2,cc.xywh(15,8,6,1)); + + jpanel1.add(_titledborderside3,cc.xywh(14,8,1,12)); + + _titledborderside4.setOrientation(TitledBorderSide.RIGHT); + jpanel1.add(_titledborderside4,cc.xywh(21,8,1,12)); + + jpanel1.add(_titledborderbottom2,cc.xywh(15,19,6,1)); + + _jbutton1.setActionCommand("Close Console"); + _jbutton1.setText("Close Console"); + _jbutton1.setToolTipText("Close Console The Console Window"); + jpanel1.add(_jbutton1,cc.xy(20,21)); + + _timer.setName("timer"); + _timer.setText("-"); + jpanel1.add(_timer,cc.xy(16,15)); + + _condition.setName("condition"); + _condition.setText("-"); + jpanel1.add(_condition,cc.xy(18,15)); + + _wrapperType.setName("wrapperType"); + _wrapperType.setText("-"); + jpanel1.add(_wrapperType,cc.xy(20,15)); + + JLabel jlabel9 = new JLabel(); + jlabel9.setBackground(new Color(204,204,204)); + jlabel9.setFont(new Font("Tahoma",Font.BOLD,11)); + jlabel9.setOpaque(true); + jlabel9.setText("PID"); + jlabel9.setHorizontalAlignment(JLabel.LEFT); + jpanel1.add(jlabel9,new CellConstraints(6,9,1,1,CellConstraints.FILL,CellConstraints.DEFAULT)); + + JLabel jlabel10 = new JLabel(); + jlabel10.setBackground(new Color(204,204,204)); + jlabel10.setFont(new Font("Tahoma",Font.BOLD,11)); + jlabel10.setOpaque(true); + jlabel10.setText("Timer"); + jlabel10.setHorizontalAlignment(JLabel.LEFT); + jpanel1.add(jlabel10,new CellConstraints(16,13,1,1,CellConstraints.FILL,CellConstraints.DEFAULT)); + + JLabel jlabel11 = new JLabel(); + jlabel11.setBackground(new Color(204,204,204)); + jlabel11.setFont(new Font("Tahoma",Font.BOLD,11)); + jlabel11.setOpaque(true); + jlabel11.setText("Condition"); + jlabel11.setHorizontalAlignment(JLabel.LEFT); + jpanel1.add(jlabel11,new CellConstraints(18,13,1,1,CellConstraints.FILL,CellConstraints.DEFAULT)); + + JLabel jlabel12 = new JLabel(); + jlabel12.setBackground(new Color(204,204,204)); + jlabel12.setFont(new Font("Tahoma",Font.BOLD,11)); + jlabel12.setOpaque(true); + jlabel12.setText("Type"); + jlabel12.setHorizontalAlignment(JLabel.LEFT); + jpanel1.add(jlabel12,new CellConstraints(20,13,1,1,CellConstraints.FILL,CellConstraints.DEFAULT)); + + JLabel jlabel13 = new JLabel(); + jlabel13.setBackground(new Color(204,204,204)); + jlabel13.setFont(new Font("Tahoma",Font.BOLD,11)); + jlabel13.setOpaque(true); + jlabel13.setText("CPU"); + jlabel13.setHorizontalAlignment(JLabel.LEFT); + jpanel1.add(jlabel13,new CellConstraints(4,13,1,1,CellConstraints.FILL,CellConstraints.DEFAULT)); + + _cpu.setName("cpu"); + _cpu.setText("-"); + jpanel1.add(_cpu,cc.xy(4,15)); + + JLabel jlabel14 = new JLabel(); + jlabel14.setBackground(new Color(204,204,204)); + jlabel14.setFont(new Font("Tahoma",Font.BOLD,11)); + jlabel14.setOpaque(true); + jlabel14.setText("Memory"); + jlabel14.setHorizontalAlignment(JLabel.LEFT); + jpanel1.add(jlabel14,new CellConstraints(6,13,1,1,CellConstraints.FILL,CellConstraints.DEFAULT)); + + _memory.setName("memory"); + _memory.setText("-"); + jpanel1.add(_memory,cc.xy(6,15)); + + JLabel jlabel15 = new JLabel(); + jlabel15.setBackground(new Color(204,204,204)); + jlabel15.setFont(new Font("Tahoma",Font.BOLD,11)); + jlabel15.setOpaque(true); + jlabel15.setText("Handles"); + jlabel15.setHorizontalAlignment(JLabel.LEFT); + jpanel1.add(jlabel15,new CellConstraints(8,13,1,1,CellConstraints.FILL,CellConstraints.DEFAULT)); + + _handles.setName("handles"); + _handles.setText("-"); + jpanel1.add(_handles,cc.xy(8,15)); + + JLabel jlabel16 = new JLabel(); + jlabel16.setBackground(new Color(204,204,204)); + jlabel16.setFont(new Font("Tahoma",Font.BOLD,11)); + jlabel16.setOpaque(true); + jlabel16.setText("Threads"); + jlabel16.setHorizontalAlignment(JLabel.LEFT); + jpanel1.add(jlabel16,new CellConstraints(10,13,1,1,CellConstraints.FILL,CellConstraints.DEFAULT)); + + _threads.setName("threads"); + _threads.setText("-"); + jpanel1.add(_threads,cc.xy(10,15)); + + _count.setName("count"); + _count.setText("-"); + jpanel1.add(_count,cc.xy(12,15)); + + JLabel jlabel17 = new JLabel(); + jlabel17.setBackground(new Color(204,204,204)); + jlabel17.setFont(new Font("Tahoma",Font.BOLD,11)); + jlabel17.setOpaque(true); + jlabel17.setText("Restarts"); + jpanel1.add(jlabel17,new CellConstraints(12,13,1,1,CellConstraints.FILL,CellConstraints.DEFAULT)); + + JLabel jlabel18 = new JLabel(); + jlabel18.setBackground(new Color(204,204,204)); + jlabel18.setFont(new Font("Tahoma",Font.BOLD,11)); + jlabel18.setOpaque(true); + jlabel18.setText("Exit Code"); + jpanel1.add(jlabel18,new CellConstraints(12,9,1,1,CellConstraints.FILL,CellConstraints.DEFAULT)); + + _exitCode.setName("exitCode"); + _exitCode.setText("-"); + jpanel1.add(_exitCode,cc.xy(12,11)); + + _THREAD_DUMP_WRAPPER_BUTTON.setActionCommand("Exit"); + _THREAD_DUMP_WRAPPER_BUTTON.setName("THREAD_DUMP_WRAPPER_BUTTON"); + _THREAD_DUMP_WRAPPER_BUTTON.setToolTipText("Thread Dump Wrapper"); + jpanel1.add(_THREAD_DUMP_WRAPPER_BUTTON,new CellConstraints(16,18,1,1,CellConstraints.RIGHT,CellConstraints.DEFAULT)); + + _EXIT_TRAY_ICON_BUTTON.setActionCommand("Exit"); + _EXIT_TRAY_ICON_BUTTON.setName("EXIT_TRAY_ICON_BUTTON"); + _EXIT_TRAY_ICON_BUTTON.setToolTipText("Exit Tray Icon"); + jpanel1.add(_EXIT_TRAY_ICON_BUTTON,new CellConstraints(18,21,1,1,CellConstraints.RIGHT,CellConstraints.DEFAULT)); + + _START_OUTPUT_BUTTON.setEnabled(false); + _START_OUTPUT_BUTTON.setName("START_OUTPUT_BUTTON"); + _START_OUTPUT_BUTTON.setToolTipText("Start Console Output"); + jpanel1.add(_START_OUTPUT_BUTTON,cc.xy(6,2)); + + _PAUSE_OUTPUT_BUTTON.setName("PAUSE_OUTPUT_BUTTON"); + _PAUSE_OUTPUT_BUTTON.setToolTipText("Pause Console Output"); + jpanel1.add(_PAUSE_OUTPUT_BUTTON,cc.xy(8,2)); + + JLabel jlabel19 = new JLabel(); + jlabel19.setFont(new Font("Tahoma",Font.BOLD,11)); + jlabel19.setText("Filter"); + jpanel1.add(jlabel19,cc.xy(16,2)); + + __OUTPUT_FILTER.setName("_OUTPUT_FILTER"); + jpanel1.add(__OUTPUT_FILTER,cc.xywh(18,2,3,1)); + + _CLEAR_OUTPUT_BUTTON.setName("CLEAR_OUTPUT_BUTTON"); + _CLEAR_OUTPUT_BUTTON.setToolTipText("Clear Output"); + jpanel1.add(_CLEAR_OUTPUT_BUTTON,cc.xy(10,2)); + + _GC_BUTTON.setActionCommand("Thread Dump"); + _GC_BUTTON.setName("GC_BUTTON"); + _GC_BUTTON.setToolTipText("Thread Dump"); + jpanel1.add(_GC_BUTTON,cc.xy(12,18)); + + _DUMP_HEAP_BUTTON.setActionCommand("Thread Dump"); + _DUMP_HEAP_BUTTON.setName("DUMP_HEAP_BUTTON"); + _DUMP_HEAP_BUTTON.setToolTipText("Thread Dump"); + jpanel1.add(_DUMP_HEAP_BUTTON,cc.xy(10,17)); + + addFillComponents(jpanel1,new int[]{ 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21 },new int[]{ 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22 }); + return jpanel1; + } + + /** + * Initializer + */ + protected void initializePanel() + { + setLayout(new BorderLayout()); + add(createPanel(), BorderLayout.CENTER); + } + + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/tray/TrayIconMain.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/tray/TrayIconMain.java new file mode 100644 index 0000000000..062188365e --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/tray/TrayIconMain.java @@ -0,0 +1,279 @@ +/* This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + *

    + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ +package org.rzo.yajsw.tray; + +import java.io.File; +import java.io.RandomAccessFile; +import java.nio.channels.FileChannel; +import java.nio.channels.FileLock; +import java.nio.channels.OverlappingFileLockException; + +import javax.management.MBeanServerConnection; +import javax.management.MBeanServerInvocationHandler; +import javax.management.ObjectName; +import javax.management.remote.JMXServiceURL; + +import org.apache.commons.configuration.BaseConfiguration; +import org.apache.commons.configuration.Configuration; +import org.rzo.yajsw.config.YajswConfigurationImpl; +import org.rzo.yajsw.tray.ahessian.client.AHessianJmxClient; +import org.rzo.yajsw.wrapper.AbstractWrappedProcessMBean; +import org.rzo.yajsw.wrapper.TrayIconProxy.Types; + +// TODO: Auto-generated Javadoc +/** + * The Class TrayIconMain. + */ +public class TrayIconMain +{ + + /** The jmxc. */ + volatile static MBeanServerConnection jmxc = null; + + /** The url. */ + static JMXServiceURL url = null; + + /** The user. */ + static String user = null; + + /** The password. */ + static String password = null; + + /** The proxy. */ + volatile static AbstractWrappedProcessMBean proxy = null; + + /** The o name. */ + static ObjectName oName = null; + + /** The _tray icon. */ + static WrapperTrayIconImpl _trayIcon = null; + + /** The lock. */ + static FileLock lock = null; + + /** The _ahessian client. */ + static AHessianJmxClient _ahessianClient; + + private static String getName(Configuration _config) + { + String result = ""; + if (_config == null) + return result; + if (_config.getBoolean("wrapper.service", false)) + result += "Service "; + String name = _config.getString("wrapper.console.title"); + if (name == null) + name = _config.getString("wrapper.ntservice.name"); + if (name == null) + name = _config.getString("wrapper.image"); + if (name == null) + name = _config.getString("wrapper.groovy"); + if (name == null) + name = _config.getString("wrapper.java.app.mainclass"); + if (name == null) + name = _config.getString("wrapper.java.app.jar"); + if (name == null) + name = ""; + result += name; + return result; + + } + + private static void reconnect() throws InterruptedException + { + if (jmxc != null) + { + try + { + _ahessianClient.close(); + } catch (Throwable e) + { + e.printStackTrace(); + } + jmxc = null; + } + _trayIcon.closeConsole(); + _trayIcon.setProcess(null); + proxy = null; + System.out.println("trying to connect"); + while (jmxc == null) + try + { + if (_trayIcon.isStop()) + return; + // TODO disableFunctions(); + + jmxc = _ahessianClient.getMBeanServer(); + if (jmxc != null) + { + proxy = (AbstractWrappedProcessMBean) MBeanServerInvocationHandler.newProxyInstance(jmxc, oName, + AbstractWrappedProcessMBean.class, false); + _trayIcon.setProcess(proxy); + _ahessianClient.open(); + System.out.println("connected"); + } + else + Thread.sleep(1000); + } + catch (Exception e) + { + e.printStackTrace(); + Thread.sleep(1000); + } + + } + + /** + * The main method. + * + * @param args + * the arguments + * + * @throws Exception + * the exception + */ + public static void main(String[] args) throws Exception + { + String config = null; + if (args.length > 0) + config = args[0]; + File f = new File(config); + if (!f.exists()) + { + System.out.println("file not found " + f); + config = null; + } + + String canonName = null; + if (config != null) + { + canonName = new File(config).getCanonicalPath(); + File lockFile = new File(System.getProperty("java.io.tmpdir"), "" + "yajsw." + canonName.hashCode() + ".lck"); + System.out.println("system tray lock file: " + lockFile.getCanonicalPath()); + FileChannel channel = new RandomAccessFile(lockFile, "rw").getChannel(); + // Try acquiring the lock without blocking. This method returns + // null or throws an exception if the file is already locked. + try + { + lock = channel.tryLock(); + } + catch (OverlappingFileLockException e) + { + // File is already locked in this thread or virtual machine + return; + } + if (lock == null) + return; + } + + System.setProperty("wrapper.config", config); + + Configuration localConf = new BaseConfiguration(); + if (config != null) + localConf.addProperty("wrapper.config", config); + YajswConfigurationImpl _config = new YajswConfigurationImpl(localConf, true); + + _ahessianClient = new AHessianJmxClient(canonName, _config.getInt("wrapper.tray.port", 0)); + + try + { + String name = _config.getString("wrapper.console.title"); + if (name == null) + name = _config.getString("wrapper.ntservice.name"); + if (name == null) + name = "yajsw.noname"; + oName = new ObjectName("org.rzo.yajsw", "name", name); + _trayIcon = (WrapperTrayIconImpl) WrapperTrayIconFactory.createTrayIcon(getName(_config), _config.getString("wrapper.tray.icon"), _config); + reconnect(); + while (!_trayIcon.isStop()) + { + if (jmxc != null && proxy != null) + try + { + if (!showInquire(proxy.getInquireMessage())) + { + showMessages(proxy.getTrayIconMessages()); + _trayIcon.showState(proxy.getState()); + _trayIcon.showColor(proxy.getUserTrayColor()); + } + } + catch (Exception ex) + { + ex.printStackTrace(); + System.out.println("error accessing server " + ex); + reconnect(); + } + // System.out.println(">> "+proxy); + try + { + Thread.sleep(1000); + } + catch (InterruptedException e) + { + System.out.println(e.getMessage()); + Thread.currentThread().interrupt(); + } + } + } + catch (Exception ex) + { + System.out.println(ex.getMessage()); + return; + } + Runtime.getRuntime().halt(0); + } + + private static boolean showInquire(String message) + { + if (message == null) + return false; + if (_trayIcon == null) + return false; + if (_trayIcon._inquireMessage == null) + { + _trayIcon.message("Input Required", message + "\n enter data through response menue"); + _trayIcon._inquireMessage = message; + return true; + } + return true; + + } + + private static void showMessages(String[][] messages) + { + if (_trayIcon == null) + return; + if (messages == null) + return; + for (String[] message : messages) + { + Types type = Types.valueOf(message[0]); + switch (type) + { + case ERROR: + _trayIcon.error(message[1], message[2]); + break; + case INFO: + _trayIcon.info(message[1], message[2]); + break; + case MESSAGE: + _trayIcon.message(message[1], message[2]); + break; + case WARNING: + _trayIcon.warning(message[1], message[2]); + break; + default: + System.out.println("wrong message type"); + } + } + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/tray/TrayIconMainBooter.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/tray/TrayIconMainBooter.java new file mode 100644 index 0000000000..4ce6e05407 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/tray/TrayIconMainBooter.java @@ -0,0 +1,50 @@ +/* This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + *

    + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ +package org.rzo.yajsw.tray; + +import java.lang.reflect.Method; +import java.net.URLClassLoader; + +import org.rzo.yajsw.boot.WrapperLoader; + +// TODO: Auto-generated Javadoc +/** + * The Class TrayIconMainBooter. + */ +public class TrayIconMainBooter +{ + + /** + * The main method. + * + * @param args + * the arguments + */ + public static void main(String[] args) + { + URLClassLoader cl = WrapperLoader.getWrapperClassLoader(); + Thread.currentThread().setContextClassLoader(cl); + try + { + Class cls = Class.forName(TrayIconMain.class.getName(), true, cl); + Method mainMethod = cls.getDeclaredMethod("main", new Class[] + { String[].class }); + mainMethod.invoke(null, new Object[] + { args }); + } + catch (Exception e) + { + e.printStackTrace(); + } + + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/tray/WrapperTrayIcon.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/tray/WrapperTrayIcon.java new file mode 100644 index 0000000000..3df22c1d81 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/tray/WrapperTrayIcon.java @@ -0,0 +1,97 @@ +/* This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + *

    + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ +package org.rzo.yajsw.tray; + +import org.rzo.yajsw.wrapper.AbstractWrappedProcessMBean; + +// TODO: Auto-generated Javadoc +/** + * The Interface WrapperTrayIcon. + */ +public interface WrapperTrayIcon +{ + + /** + * Checks if is inits the. + * + * @return true, if is inits the + */ + public boolean isInit(); + + /** + * Info. + * + * @param caption + * the caption + * @param message + * the message + */ + public void info(String caption, String message); + + /** + * Error. + * + * @param caption + * the caption + * @param message + * the message + */ + public void error(String caption, String message); + + /** + * Warning. + * + * @param caption + * the caption + * @param message + * the message + */ + public void warning(String caption, String message); + + /** + * Message. + * + * @param caption + * the caption + * @param message + * the message + */ + public void message(String caption, String message); + + /** + * Sets the process. + * + * @param proxy + * the new process + */ + public void setProcess(AbstractWrappedProcessMBean proxy); + + /** + * Close console. + */ + public void closeConsole(); + + /** + * Show state. + * + * @param state + * the state + */ + public void showState(int state); + + /** + * Checks if is stop. + * + * @return true, if is stop + */ + public boolean isStop(); + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/tray/WrapperTrayIconDummy.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/tray/WrapperTrayIconDummy.java new file mode 100644 index 0000000000..e030840d02 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/tray/WrapperTrayIconDummy.java @@ -0,0 +1,125 @@ +/* This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + *

    + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ +package org.rzo.yajsw.tray; + +import org.rzo.yajsw.wrapper.AbstractWrappedProcessMBean; + +// TODO: Auto-generated Javadoc +/** + * The Class WrapperTrayIconDummy. + */ +public class WrapperTrayIconDummy implements WrapperTrayIcon +{ + + /* + * (non-Javadoc) + * + * @see org.rzo.yajsw.tray.WrapperTrayIcon#isInit() + */ + public boolean isInit() + { + return true; + } + + /* + * (non-Javadoc) + * + * @see org.rzo.yajsw.tray.WrapperTrayIcon#error(java.lang.String, + * java.lang.String) + */ + public void error(String caption, String message) + { + // TODO Auto-generated method stub + + } + + /* + * (non-Javadoc) + * + * @see org.rzo.yajsw.tray.WrapperTrayIcon#info(java.lang.String, + * java.lang.String) + */ + public void info(String caption, String message) + { + // TODO Auto-generated method stub + + } + + /* + * (non-Javadoc) + * + * @see org.rzo.yajsw.tray.WrapperTrayIcon#message(java.lang.String, + * java.lang.String) + */ + public void message(String caption, String message) + { + // TODO Auto-generated method stub + + } + + /* + * (non-Javadoc) + * + * @see org.rzo.yajsw.tray.WrapperTrayIcon#warning(java.lang.String, + * java.lang.String) + */ + public void warning(String caption, String message) + { + // TODO Auto-generated method stub + + } + + /* + * (non-Javadoc) + * + * @seeorg.rzo.yajsw.tray.WrapperTrayIcon#setProcess(org.rzo.yajsw.wrapper. + * AbstractWrappedProcessMBean) + */ + public void setProcess(AbstractWrappedProcessMBean proxy) + { + // TODO Auto-generated method stub + + } + + /* + * (non-Javadoc) + * + * @see org.rzo.yajsw.tray.WrapperTrayIcon#closeConsole() + */ + public void closeConsole() + { + // TODO Auto-generated method stub + + } + + /* + * (non-Javadoc) + * + * @see org.rzo.yajsw.tray.WrapperTrayIcon#showState(int) + */ + public void showState(int state) + { + // TODO Auto-generated method stub + + } + + /* + * (non-Javadoc) + * + * @see org.rzo.yajsw.tray.WrapperTrayIcon#isStop() + */ + public boolean isStop() + { + // TODO Auto-generated method stub + return false; + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/tray/WrapperTrayIconFactory.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/tray/WrapperTrayIconFactory.java new file mode 100644 index 0000000000..25317985da --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/tray/WrapperTrayIconFactory.java @@ -0,0 +1,162 @@ +/* This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + *

    + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ +package org.rzo.yajsw.tray; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map.Entry; +import java.util.logging.Logger; + +import org.rzo.yajsw.boot.WrapperLoader; +import org.rzo.yajsw.config.YajswConfigurationImpl; +import org.rzo.yajsw.os.OperatingSystem; +import org.rzo.yajsw.os.Process; + +// TODO: Auto-generated Javadoc +/** + * A factory for creating WrapperTrayIcon objects. + */ +public class WrapperTrayIconFactory +{ + + /** + * Creates a new WrapperTrayIcon object. + * + * @param name + * the name + * @param icon + * the icon + * + * @return the wrapper tray icon + */ + public static WrapperTrayIcon createTrayIcon(String name, String icon, YajswConfigurationImpl config) + { + WrapperTrayIcon result = null; + if (config == null) + config = new YajswConfigurationImpl(); + try + { + result = new WrapperTrayIconImpl(name, icon, config); + } + catch (Throwable ex) + { + System.out.println("java version does not support SystemTray: " + ex.getMessage()); + ex.printStackTrace(); + } + if (result == null || !result.isInit()) + result = new WrapperTrayIconDummy(); + return result; + } + + static private String getDOption(String key, String value) + { + if (value != null && !value.contains(" ")) + return "-D"+key+"="+value; + else + return "-D"+key+"=\""+value+"\""; + } + + + /** + * Start tray icon process. + * + * @param config + * the config + * + * @return the process + */ + public static Process startTrayIconProcess(YajswConfigurationImpl config, Logger logger) + { + if (config == null) + return null; + String wrapperConfFileName = config.getCachedPath(false); + + final Process osProcess = OperatingSystem.instance().processManagerInstance().createProcess(); + + try + { + List cmd = new ArrayList(); + cmd.add(getJava()); + for (Entry e : config.getEnvLookupSet().entrySet()) + { + String opt = getDOption(e.getKey(), e.getValue()); + if (!cmd.contains(opt)) + cmd.add(opt); + } + String tmpDir = config.getString("wrapper.tmp.path", System.getProperty("jna_tmpdir", null)); + if (tmpDir != null) + { + String opt = getDOption("jna_tmpdir", tmpDir); + if (!cmd.contains(opt)) + cmd.add(opt); + } + else + { + tmpDir = config.getString("wrapper.tmp.path", System.getProperty("java.io.tmpdir", null)); + if (tmpDir != null) + { + String opt = getDOption("jna_tmpdir", tmpDir); + if (!cmd.contains(opt)) + cmd.add(opt); + } + } + + cmd.add("-classpath"); + cmd.add(WrapperLoader.getWrapperJar()); + cmd.add(TrayIconMainBooter.class.getName()); + cmd.add(wrapperConfFileName); + String[] arrCmd = new String[cmd.size()]; + for (int i = 0; i < arrCmd.length; i++) + arrCmd[i] = (String) cmd.get(i); + osProcess.setCommand(arrCmd); + osProcess.setPipeStreams(false, false); + osProcess.setVisible(false); + osProcess.setLogger(logger); + osProcess.setDebug(false); + Runtime.getRuntime().addShutdownHook(new Thread() + { + public void run() + { + if (osProcess != null) + osProcess.kill(0); + } + }); + osProcess.start(); + logger.info("spawned system tray icon process with pid "+osProcess.getPid()); + return osProcess; + } + catch (Exception e) + { + logger.throwing("WRapperTRayIconFactory", "startTrayIconProcess", e); + } + return null; + } + + /** + * Gets the java. + * + * @return the java + */ + public static String getJava() + { + String result = System.getenv("java_exe"); + if (result == null) + { + result = System.getProperty("sun.boot.library.path"); + if (result != null) + result = result + "/javaw"; + else + result = "java"; + } + return result; + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/tray/WrapperTrayIconImpl.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/tray/WrapperTrayIconImpl.java new file mode 100644 index 0000000000..d0bb46ac07 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/tray/WrapperTrayIconImpl.java @@ -0,0 +1,1301 @@ +/* This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + *

    + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ +package org.rzo.yajsw.tray; + +import java.awt.AWTException; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Image; +import java.awt.RenderingHints; +import java.awt.SystemTray; +import java.awt.Toolkit; +import java.awt.TrayIcon; +import java.awt.event.ActionEvent; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; + +import javax.imageio.ImageIO; +import javax.swing.AbstractAction; +import javax.swing.ImageIcon; +import javax.swing.JMenuItem; +import javax.swing.JOptionPane; +import javax.swing.JPopupMenu; +import javax.swing.SwingUtilities; +import javax.swing.UIManager; + +import org.rzo.yajsw.boot.WrapperLoader; +import org.rzo.yajsw.config.YajswConfigurationImpl; +import org.rzo.yajsw.os.Mouse; +import org.rzo.yajsw.os.OperatingSystem; +import org.rzo.yajsw.os.Process; +import org.rzo.yajsw.tools.JCLParser; +import org.rzo.yajsw.util.DaemonThreadFactory; +import org.rzo.yajsw.wrapper.AbstractWrappedProcessMBean; +import org.rzo.yajsw.wrapper.WrappedProcess; + + +// TODO: Auto-generated Javadoc +/** + * The Class WrapperTrayIconImpl. + */ +public class WrapperTrayIconImpl implements WrapperTrayIcon +{ + volatile boolean _dialogDisplayed; + + /** The icon running. */ + Image iconRunning; + + /** The icon idle. */ + Image iconIdle; + + Image iconWaitForApp; + /** The icon else. */ + Image iconElse; + + /** The icon offline. */ + Image iconOffline; + + /** The ti. */ + TrayIcon ti; + Image tiImage; + + final JPopupMenu popup = new JPopupMenu(); + + + /** The current image. */ + Image currentImage = iconIdle; + + /** The tool tip prefix. */ + String toolTipPrefix; + + /** The current tool tip. */ + String currentToolTip; + + /** The tray. */ + final SystemTray tray = SystemTray.getSystemTray(); + + /** The init. */ + boolean init = false; + + /** The _console. */ + Console _console = null; + + /** The _process. */ + volatile AbstractWrappedProcessMBean _process; + protected static final Executor executor = Executors.newCachedThreadPool(new DaemonThreadFactory("console")); + + /** The stop. */ + volatile boolean stop = false; + + /** The _current state. */ + int _currentState = WrappedProcess.STATE_IDLE; + + /** The _stop item. */ + JMenuItem _stopItem = new JMenuItem(); + + /** The _close item. */ + JMenuItem _closeItem = new JMenuItem(); + + /** The _start item. */ + JMenuItem _startItem = new JMenuItem(); + + /** The _restart item. */ + JMenuItem _restartItem = new JMenuItem(); + + /** The _console item. */ + JMenuItem _consoleItem = new JMenuItem(); + + /** The _stop timer item. */ + JMenuItem _stopTimerItem = new JMenuItem(); + + /** The _thread dump item. */ + JMenuItem _threadDumpItem = new JMenuItem(); + JMenuItem _gcItem = new JMenuItem(); + JMenuItem _dumpHeapItem = new JMenuItem(); + + /** The _exit item. */ + JMenuItem _exitItem = new JMenuItem(); + + /** The _exit wrapper item. */ + JMenuItem _exitWrapperItem = new JMenuItem(); + + /** The _thread dump wrapper item. */ + JMenuItem _threadDumpWrapperItem = new JMenuItem(); + + /** The _close console item. */ + JMenuItem _closeConsoleItem = new JMenuItem(); + + /** The _start service item. */ + JMenuItem _startServiceItem = new JMenuItem(); + + /** The _response item. */ + JMenuItem _responseItem = new JMenuItem(); + + JMenuItem _updateItem = new JMenuItem(); + + /** The _inquire message. */ + String _inquireMessage = null; + + boolean _waitForAppReady = false; + + volatile boolean _trayDialog = false; + + private YajswConfigurationImpl _config; + + static Mouse m = OperatingSystem.instance().mouseInstance(); + + /** + * Instantiates a new wrapper tray icon impl. + * + * @param name + * the name + * @param icon + * the icon + */ + public WrapperTrayIconImpl(String name, String icon, YajswConfigurationImpl config) + { + try + { + Class cl = this.getClass().getClassLoader().loadClass("java.awt.GraphicsEnvironment"); + Method m = cl.getMethod("isHeadless", null); + Boolean b = (Boolean) m.invoke(null, null); + if (b) + { + System.out.println("SystemTray not supported on this platform: headless"); + return; + } + } + catch (Exception ex) + { + System.out.println("SystemTray not supported on this platform: "+ex.getMessage() + " error getting java.awt.GraphicsEnvironment"); + return; + } + if (!SystemTray.isSupported()) + { + System.out.println("SystemTray not supported on this platform"); + return; + } + + _config = config; + if (_config != null) + { + _waitForAppReady = _config.getBoolean("wrapper.ntservice.autoreport.waitready", false); + _trayDialog = _config.getBoolean("wrapper.tray.dialog", true); + String lookAndFeel = _config.getString("wrapper.tray.look_and_feel", null); + try + { + if (lookAndFeel != null && lookAndFeel.length() > 0) + { + UIManager.setLookAndFeel(lookAndFeel); + } + } + catch (Throwable ex) + { + ex.printStackTrace(); + } + } + + _dialogDisplayed = new Boolean(false); + + toolTipPrefix = name + " - "; + + InputStream f = null; + try + { + f = getImage(icon); + ti = new TrayIcon(createColorImage(f, null, null)); + ti.setImageAutoSize(true); + + Dimension d = ti.getSize(); + f = getImage(icon); + iconRunning = createColorImage(f, Color.GREEN, d); + f = getImage(icon); + iconIdle = createColorImage(f, Color.RED, d); + f = getImage(icon); + iconElse = createColorImage(f, Color.ORANGE, d); + f = getImage(icon); + iconOffline = createColorImage(f, Color.BLACK, d); + f = getImage(icon); + iconWaitForApp = createColorImage(f, Color.BLUE.brighter(), d); + } + catch (Exception ex) + { + System.out.println("System Tray: file type not supported -> abort"); + return; + } + + ti = new TrayIcon(iconIdle); + /* + * process.addStateChangeListener(new StateChangeListener() { public + * void stateChange(int newState, int oldState) { if (newState == + * WrappedProcess.STATE_SHUTDOWN) { synchronized (tray) { + * tray.remove(ti); } + * + * if (!_process.getType().endsWith("Service")) + * Runtime.getRuntime().halt(0); return; } showState(newState); } }); + */ + ti.setImageAutoSize(true); + + + try + { + SwingUtilities.windowForComponent(popup.getInvoker()).setAlwaysOnTop(true); + }catch(Throwable tr){ + + } + + _exitItem.setAction(new AbstractAction("Stop Tray", createImageIcon("/resources/exit.png")) + { + public void actionPerformed(ActionEvent e) + { + try + { + _dialogDisplayed = true; + closePopup(); + int userChoice = JOptionPane.OK_OPTION; + if (_trayDialog) + userChoice = JOptionPane.showConfirmDialog(null, _config.getString("wrapper.tray.text.dialog_exit_tray", "Terminate wrapper Tray ?"), + UIManager.getString("OptionPane.titleText"), + JOptionPane.YES_NO_OPTION); + + if(JOptionPane.OK_OPTION == userChoice) + { + closePopup(); + stop = true; + synchronized (tray) + { + tray.remove(ti); + } + System.exit(0); + } + } + finally + { + _dialogDisplayed = false; + } + + + } + + }); + _stopItem.setAction(new AbstractAction("Stop", createImageIcon("/resources/stop.png")) + { + public void actionPerformed(ActionEvent e) + { + if (_process != null) + { + SwingUtilities.invokeLater(new Runnable() + { + public void run() + { + try + { + _dialogDisplayed = true; + closePopup(); + int userChoice = JOptionPane.OK_OPTION; + if (_trayDialog) + userChoice = JOptionPane.showConfirmDialog(null, + _config.getString("wrapper.tray.text.dialog_stop", "Stop the application ?"), + UIManager.getString("OptionPane.titleText"), + JOptionPane.YES_NO_OPTION); + if(JOptionPane.OK_OPTION == userChoice) + { + executor.execute(new Runnable() + { + public void run() + { + try + { + _process.stop("TRAY"); + } + catch (Throwable ex) + { + ex.printStackTrace(); + } + + } + }); + } + } + finally + { + + _dialogDisplayed = false; + } + } + }); + + + } + + } + + }); + _closeItem.setAction(new AbstractAction("Close Popup", createImageIcon("/resources/close.png")) + { + public void actionPerformed(ActionEvent e) + { + closePopup(); + } + }); + + _startItem.setAction(new AbstractAction("Start", createImageIcon("/resources/start.png")) + { + public void actionPerformed(ActionEvent e) + { + System.out.println("start"); + if (_process != null) + try + { + _process.start(); + } + catch (Throwable ex) + { + ex.printStackTrace(); + } + closePopup(); + } + }); + + _restartItem.setAction(new AbstractAction("Restart", createImageIcon("/resources/restart.png")) + { + + public void actionPerformed(ActionEvent e) + { + if (_process != null) + { + SwingUtilities.invokeLater(new Runnable() + { + public void run() + { + try + { + _dialogDisplayed = true; + closePopup(); + int userChoice = JOptionPane.OK_OPTION; + if (_trayDialog) + userChoice = JOptionPane.showConfirmDialog(null, + _config.getString("wrapper.tray.text.dialog_restart", "Restart the application ?"), + UIManager.getString("OptionPane.titleText"), + JOptionPane.YES_NO_OPTION); + if(JOptionPane.OK_OPTION == userChoice) + { + executor.execute(new Runnable() + { + public void run() + { + try + { + _process.restart(); + } + catch (Throwable ex) + { + ex.printStackTrace(); + } + + } + }); + } + } + finally + { + + _dialogDisplayed = false; + } + } + }); + + + } + } + +}); + +_consoleItem.setAction(new AbstractAction("Console", createImageIcon("/resources/console.png")) + { + public void actionPerformed(ActionEvent e) + { + if (_process != null) + openConsole(); + closePopup(); + } + + }); + _threadDumpItem.setAction(new AbstractAction("Thread Dump", createImageIcon("/resources/lightning.png")) + { + public void actionPerformed(ActionEvent e) + { + if (_process != null) + try + { + _process.threadDump(); + } + catch (Throwable ex) + { + ex.printStackTrace(); + } + + closePopup(); + } + }); + + _gcItem.setAction(new AbstractAction("GC", createImageIcon("/resources/recycle.png")) + { + public void actionPerformed(ActionEvent e) + { + if (_process != null) + try + { + _process.gc(); + } + catch (Throwable ex) + { + ex.printStackTrace(); + } + + closePopup(); + } + }); + + _dumpHeapItem.setAction(new AbstractAction("Dump Heap", createImageIcon("/resources/document-save.png")) + { + public void actionPerformed(ActionEvent e) + { + if (_process != null) + try + { + String s = (String) JOptionPane.showInputDialog("Dump File Name (Empty == default) ?", ""); + _process.dumpHeap(s); + } + catch (Throwable ex) + { + ex.printStackTrace(); + } + + closePopup(); + } + }); + + + _stopTimerItem.setAction(new AbstractAction("Stop Timer/Condition", createImageIcon("/resources/clock_stop.png")) + { + public void actionPerformed(ActionEvent e) + { + if (_process != null) + { + try + { + _process.stopTimerCondition(); + if (_console != null) + { + _console.setTimer(_process.isTimerActive()); + _console.setCondition(_process.isConditionActive()); + } + } + catch (Throwable ex) + { + ex.printStackTrace(); + } + + } + closePopup(); + } + }); + _exitWrapperItem.setAction(new AbstractAction("Stop Wrapper", createImageIcon("/resources/exitWrapper.png")) + { + public void actionPerformed(ActionEvent e) + { + if (_process != null) + { + SwingUtilities.invokeLater(new Runnable() + { + public void run() + { + try + { + _dialogDisplayed = true; + closePopup(); + int userChoice = JOptionPane.OK_OPTION; + if (_trayDialog) + userChoice = JOptionPane.showConfirmDialog(null, + _config.getString("wrapper.tray.text.dialog_exit_wrapper", "Stop the wrapper ?"), + UIManager.getString("OptionPane.titleText"), + JOptionPane.YES_NO_OPTION); + if(JOptionPane.OK_OPTION == userChoice) + { + executor.execute(new Runnable() + { + public void run() + { + try + { + _process.stopWrapper(); + } + catch (Throwable ex) + { + ex.printStackTrace(); + } + + } + }); + } + } + finally + { + + _dialogDisplayed = false; + } + } + }); + + + } + + } + + }); + + _threadDumpWrapperItem.setAction(new AbstractAction("TDump Wrapper", createImageIcon("/resources/lightning.png")) + { + public void actionPerformed(ActionEvent e) + { + if (_process != null) + try + { + _process.wrapperThreadDump(); + } + catch (Throwable ex) + { + ex.printStackTrace(); + } + + closePopup(); + } + + }); + + _closeConsoleItem.setAction(new AbstractAction("Close Console") + { + public void actionPerformed(ActionEvent e) + { + closeConsole(); + closePopup(); + } + + }); + + _startServiceItem.setAction(new AbstractAction("Start Service", createImageIcon("/resources/startService.png")) + { + public void actionPerformed(ActionEvent e) + { + if (_process == null) + { + startService(); + } + closePopup(); + } + + }); + + _responseItem.setAction(new AbstractAction("Response", createImageIcon("/resources/Help16.gif")) + { + public void actionPerformed(ActionEvent e) + { + if (_process != null && _inquireMessage != null) + { + String message = _inquireMessage; + String s = (String) JOptionPane.showInputDialog(message, ""); + if (s != null && _process != null) + { + try + { + _process.writeInquireResponse(s); + _inquireMessage = null; + } + catch (Throwable ex) + { + ex.printStackTrace(); + } + + } + + } + closePopup(); + } + + }); + + _updateItem.setAction(new AbstractAction("Update", createImageIcon("/resources/update.png")) + { + public void actionPerformed(ActionEvent e) + { + if (_process != null) + { + String s = (String) JOptionPane.showInputDialog("Update configuration file", ""); + if (s != null && _process != null) + { + try + { + _process.update(s); + } + catch (Throwable ex) + { + ex.printStackTrace(); + } + + } + + } + closePopup(); + } + + }); + + + List menueList = _config == null ? Arrays.asList("start", "stop") : _config.getList("wrapper.tray.commands", Arrays.asList(new Object[]{"close", "start", "stop", "restart", "console", "response", "exitWrapper", "startService", "updateService", "exitTray"})); + + if (menueList.contains("start")) + popup.add(_startItem); + if (menueList.contains("stop")) + popup.add(_stopItem); + if (menueList.contains("restart")) + popup.add(_restartItem); + if (menueList.contains("console")) + popup.add(_consoleItem); + if (menueList.contains("response")) + popup.add(_responseItem); + // popup.add(_threadDumpWrapperItem); + if (menueList.contains("exitWrapper")) + popup.add(_exitWrapperItem); + if (menueList.contains("startService")) + popup.add(_startServiceItem); + if (menueList.contains("exitTray")) + popup.add(_exitItem); + if (menueList.contains("updateService")) + popup.add(_updateItem); + popup.add(_closeItem); + popup.validate(); + ti.addMouseListener(new MouseListener() + { + + public void mouseClicked(MouseEvent e) + { + System.out.println("mouse clicked"); + } + + public void mouseEntered(MouseEvent e) + { + System.out.println("mouse entered"); + + } + + public void mouseExited(MouseEvent e) + { + System.out.println("mouse exited"); + } + + public void mousePressed(MouseEvent e) + { + + if (!OperatingSystem.instance().getOperatingSystemName().toLowerCase().contains("mac")) + return; + System.out.println("mouse rleased"); + if(_dialogDisplayed == true) + { + + } + else + { + if(!_dialogDisplayed) + { + System.out.println("X"+e.getXOnScreen()+"/"+ popup.getWidth()); + System.out.println("Y"+e.getYOnScreen()+"/"+ popup.getHeight()); + int xPos = e.getXOnScreen() > popup.getWidth() ? e.getXOnScreen() - popup.getWidth() : e.getXOnScreen(); + int yPos = e.getYOnScreen() > popup.getHeight() ? e.getYOnScreen() - popup.getHeight() : e.getYOnScreen(); + popup.show(e.getComponent(), xPos, yPos); + if (m != null) + m.registerMouseUpListner(new Runnable() + { + + public void run() + { + closePopup(); + } + + }, executor); + } + } + + } + + public void mouseReleased(MouseEvent e) + { + System.out.println("mouse rleased"); + if(_dialogDisplayed == true) + { + + } + else + { + if(!_dialogDisplayed) + { + System.out.println("X"+e.getXOnScreen()+"/"+ popup.getWidth()); + System.out.println("Y"+e.getYOnScreen()+"/"+ popup.getHeight()); + int xPos = e.getXOnScreen() > popup.getWidth() ? e.getXOnScreen() - popup.getWidth() : e.getXOnScreen(); + int yPos = e.getYOnScreen() > popup.getHeight() ? e.getYOnScreen() - popup.getHeight() : e.getYOnScreen(); + popup.show(e.getComponent(), xPos, yPos); + if (m != null) + m.registerMouseUpListner(new Runnable() + { + + public void run() + { + closePopup(); + } + + }, executor); + } + } + + } + + }); + + + Runtime.getRuntime().addShutdownHook(new Thread() + { + public void run() + { + stop = true; + synchronized (tray) + { + tray.remove(ti); + } + } + }); + + try + { + tray.add(ti); + } + catch (AWTException e1) + { + // TODO Auto-generated catch block + e1.printStackTrace(); + System.exit(0); + } + + init = true; + + } + + private void startService() + { + try + { + + /* + WrappedService w = new WrappedService(); + if((!w.isRunning())&&(!w.isStarting())) + { + w.init(); + w.start(); + } + else + { + System.out.println("Already in Running || starting state."); + } + */ + // start in a separate process so that we can handle windows uac elevation if necessary + int myPid = OperatingSystem.instance().processManagerInstance().currentProcessId(); + Process me = OperatingSystem.instance().processManagerInstance().getProcess(myPid); + String cmd = me.getCommand(); + me.destroy(); + JCLParser parser = JCLParser.parse(cmd); + String[] startCmd = new String[5]; + startCmd[0] = parser.getJava(); + startCmd[1] = "-jar"; + startCmd[2] = new File(WrapperLoader.getWrapperJar()).getCanonicalPath(); + startCmd[3] = "-t"; + startCmd[4] = parser.getArgs().get(1); + Process startProcess = OperatingSystem.instance().processManagerInstance().createProcess(); + startProcess.setCommand(startCmd); + startProcess.setDebug(false); + startProcess.start(); + startProcess.waitFor(); + startProcess.destroy(); + } + catch (Throwable ex) + { + ex.printStackTrace(); + } + + + } + + private void closePopup() + { + executor.execute(new Runnable() + { + public void run() + { + try + { + // give event some time to get to java. + // in case we clicked in a java ui + Thread.sleep(50); + } + catch (InterruptedException e) + { + // TODO Auto-generated catch block + e.printStackTrace(); + } + SwingUtilities.invokeLater(new Runnable() + { + public void run() + { + popup.setVisible(false); + if (m != null) + m.unregisterMouseUpListner(); + } + }); + } + }); + + } + + private InputStream getImage(String icon) + { + InputStream f = null; + if (icon == null) + f = findFile("/resources/console.png"); + else + { + f = findFile(icon); + if (f == null) + { + try + { + System.out.println("System Tray: " + new File(icon).getCanonicalPath() + " not found -> default icon"); + } + catch (IOException e) + { + e.printStackTrace(); + } + f = findFile("/resources/console.png"); + } + } + if (f == null) + { + System.out.println("System Tray: no icon found -> abort"); + return null; + } + return f; + } + + /** + * Gets the state image. + * + * @param state + * the state + * + * @return the state image + */ + public Image getStateImage(int state) + { + switch (state) + { + case WrappedProcess.STATE_RUNNING: + return iconRunning; + case WrappedProcess.STATE_IDLE: + return iconIdle; + case WrappedProcess.STATE_APP_WAIT: + return iconWaitForApp; + default: + return iconElse; + } + } + + Color _currentUserColor = null; + Image _baseImage; + public Image getColorImage(Color color) + { + if (_currentUserColor == null || !_currentUserColor.equals(color)) + { + if (color != null) + { + return createColorImage(iconElse, color, ti.getSize()); + } + else + return getStateImage(_currentState); + } + return null; + } + + /** + * Gets the state tool tip. + * + * @param state + * the state + * + * @return the state tool tip + */ + public String getStateToolTip(int state) + { + switch (state) + { + case WrappedProcess.STATE_RUNNING: + return "Running"; + case WrappedProcess.STATE_IDLE: + return "Idle"; + case WrappedProcess.STATE_RESTART: + case WrappedProcess.STATE_RESTART_START: + case WrappedProcess.STATE_RESTART_STOP: + case WrappedProcess.STATE_RESTART_WAIT: + return "Restarting"; + case WrappedProcess.STATE_STARTING: + return "Starting"; + case WrappedProcess.STATE_USER_STOP: + case WrappedProcess.STATE_STOP: + return "Stopping"; + case WrappedProcess.STATE_APP_WAIT: + return "Waiting"; + default: + return "Other"; + } + } + + /* + * (non-Javadoc) + * + * @see org.rzo.yajsw.tray.WrapperTrayIcon#showState(int) + */ + synchronized public void showState(int state) + { + int oldState = _currentState; + + if (_waitForAppReady && state == WrappedProcess.STATE_RUNNING && (!_process.isAppReportedReady())) + { + state = WrappedProcess.STATE_APP_WAIT; + } + + _currentState = state; + String strState = getStateToolTip(state); + if (oldState != _currentState) + this.message("STATE CHANGED", getStateToolTip(oldState) + " -> " + getStateToolTip(_currentState)); + if (_console != null && _process != null) + { + _console.setState(strState); + _console.setAppRestartCount(_process.getTotalRestartCount(), _process.getRestartCount()); + _console.setAppPid(_process.getAppPid()); + _console.setAppStarted(_process.getAppStarted()); + _console.setAppStopped(_process.getAppStopped()); + _console.setExitCode(_process.getExitCode()); + _console.setTimer(_process.isTimerActive()); + _console.setCondition(_process.isConditionActive()); + } + + Image image = getStateImage(state); + if (image != currentImage) + { + _currentUserColor = null; + currentToolTip = toolTipPrefix + strState; + showImage(image); + } + + } + + private void showImage(Image image) + { + if (image != currentImage) + { + _currentUserColor = null; + ti.setImage(image); + currentImage = image; + ti.setToolTip(currentToolTip); + } + + } + + public void showColor(Color color) + { + Image image = getColorImage(color); + if (image != null) + showImage(image); + + } + + /** + * Returns an ImageIcon, or null if the path was invalid. + * + * @param path + * the path + * + * @return the image icon + */ + static ImageIcon createImageIcon(String path) + { + Image image = createImage(path); + if (image == null) + return null; + return new ImageIcon(image); + } + + static Image createImage(String path) + { + java.net.URL imgURL = WrapperTrayIconImpl.class.getResource(path); + if (imgURL != null) + { + return Toolkit.getDefaultToolkit().getImage(imgURL); + } + else + { + if (new File(path).exists()) + return Toolkit.getDefaultToolkit().getImage(path); + return null; + } + } + + private InputStream findFile(String path) + { + InputStream result = null; + try + { + result = getClass().getResourceAsStream(path); + } + catch (Exception e) + { + e.printStackTrace(); + } + if (result != null) + return result; + File f = null; + if (result == null) + f = new File(path); + if (f.exists()) + try + { + result = new FileInputStream(f); + return result; + } + catch (Exception e) + { + e.printStackTrace(); + } + return null; + + } + + private Image createColorImage(Image image, Color color, Dimension d) + { + if (d != null) + { + BufferedImage bufferedResizedImage = new BufferedImage(d.width, d.height, BufferedImage.TYPE_INT_RGB); + Graphics2D g2d = bufferedResizedImage.createGraphics(); + g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC); + g2d.drawImage(image, 0, 0, d.width, d.height, null); + g2d.dispose(); + image = bufferedResizedImage; + + } + if (color != null) + { + Graphics g = image.getGraphics(); + int w = image.getWidth(null); + int h = image.getHeight(null); + int rw = w / 2; + int rh = h / 2; + Color c = new Color(color.getRed(), color.getGreen(), color.getBlue(), 200); + g.setColor(c); + g.fillRoundRect(0, h - rh, rw, rh, rw, rh); + } + + return image; + + } + + private Image createColorImage(InputStream imageFile, Color color, Dimension d) throws Exception + { + BufferedImage image = ImageIO.read(imageFile); + imageFile.close(); + + + return createColorImage(image, color, d); + } + + /** + * The main method. + * + * @param args + * the arguments + * + * @throws InterruptedException + * the interrupted exception + */ + public static void main(String[] args) throws InterruptedException + { + WrapperTrayIconImpl t = new WrapperTrayIconImpl("test", null, null);// "tomcat.gif"); + //while (true) + { + Thread.sleep(2000); + t.showState(WrappedProcess.STATE_RUNNING); + Thread.sleep(2000); + t.showState(WrappedProcess.STATE_IDLE); + Thread.sleep(2000); + t.showState(WrappedProcess.STATE_RESTART); + } + } + + /* + * (non-Javadoc) + * + * @see org.rzo.yajsw.tray.WrapperTrayIcon#isInit() + */ + public boolean isInit() + { + return init; + } + + /** + * Open console. + */ + public void openConsole() + { + if (_console != null) + return; + _console = new Console(this); + this.showState(_currentState); + _console.setWrapperPid(_process.getWrapperPid()); + _console.setWrapperStarted(_process.getWrapperStarted()); + _console.setWrapperType(_process.getType()); + } + + /* + * (non-Javadoc) + * + * @see org.rzo.yajsw.tray.WrapperTrayIcon#closeConsole() + */ + public void closeConsole() + { + if (_console == null) + return; + _console.close(); + _console = null; + } + + /* + * (non-Javadoc) + * + * @see org.rzo.yajsw.tray.WrapperTrayIcon#error(java.lang.String, + * java.lang.String) + */ + public void error(String caption, String message) + { + ti.displayMessage(toolTipPrefix + caption, message, TrayIcon.MessageType.ERROR); + } + + /* + * (non-Javadoc) + * + * @see org.rzo.yajsw.tray.WrapperTrayIcon#info(java.lang.String, + * java.lang.String) + */ + public void info(String caption, String message) + { + ti.displayMessage(toolTipPrefix + caption, message, TrayIcon.MessageType.INFO); + } + + /* + * (non-Javadoc) + * + * @see org.rzo.yajsw.tray.WrapperTrayIcon#message(java.lang.String, + * java.lang.String) + */ + public void message(String caption, String message) + { + ti.displayMessage(toolTipPrefix + caption, message, TrayIcon.MessageType.NONE); + } + + /* + * (non-Javadoc) + * + * @see org.rzo.yajsw.tray.WrapperTrayIcon#warning(java.lang.String, + * java.lang.String) + */ + public void warning(String caption, String message) + { + ti.displayMessage(toolTipPrefix + caption, message, TrayIcon.MessageType.WARNING); + } + + /* + * (non-Javadoc) + * + * @seeorg.rzo.yajsw.tray.WrapperTrayIcon#setProcess(org.rzo.yajsw.wrapper. + * AbstractWrappedProcessMBean) + */ + public void setProcess(AbstractWrappedProcessMBean proxy) + { + _process = proxy; + if (_process == null) + { + Image image = iconOffline; + if (image != currentImage) + { + ti.setImage(image); + currentImage = image; + currentToolTip = toolTipPrefix + "OFFLINE"; + ti.setToolTip(currentToolTip); + message("STATE CHANGED", currentToolTip); + } + + // Enable stop menu. + if (SwingUtilities.isEventDispatchThread()) + { + _exitWrapperItem.setEnabled(true); + } + else + { + SwingUtilities.invokeLater(new Runnable() + { + public void run() + { + _exitWrapperItem.setEnabled(true); + } + + }); + } + + } + } + + /* + * (non-Javadoc) + * + * @see org.rzo.yajsw.tray.WrapperTrayIcon#isStop() + */ + public boolean isStop() + { + return stop; + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/tray/ahessian/client/AHessianClientPipelineFactory.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/tray/ahessian/client/AHessianClientPipelineFactory.java new file mode 100644 index 0000000000..7ed1e351c2 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/tray/ahessian/client/AHessianClientPipelineFactory.java @@ -0,0 +1,70 @@ +package org.rzo.yajsw.tray.ahessian.client; + +import java.util.concurrent.Executor; + +import org.jboss.netty.channel.ChannelPipeline; +import org.jboss.netty.channel.ChannelPipelineFactory; +import org.jboss.netty.util.Timer; +import org.rzo.netty.ahessian.application.jmx.remote.service.JmxSerializerFactory; +import org.rzo.netty.ahessian.io.InputStreamDecoder; +import org.rzo.netty.ahessian.io.OutputStreamEncoder; +import org.rzo.netty.ahessian.io.PullInputStreamConsumer; +import org.rzo.netty.ahessian.rpc.client.BootstrapProvider; +import org.rzo.netty.ahessian.rpc.client.HessianProxyFactory; +import org.rzo.netty.ahessian.rpc.client.ReconnectHandler; +import org.rzo.netty.ahessian.rpc.message.HessianRPCCallEncoder; +import org.rzo.netty.ahessian.rpc.message.HessianRPCReplyDecoder; +import org.rzo.netty.ahessian.rpc.message.OutputProducer; +import org.rzo.netty.ahessian.stopable.StopHandler; +import org.rzo.netty.ahessian.stopable.StopablePipeline; + +public class AHessianClientPipelineFactory implements ChannelPipelineFactory +{ + + Executor _executor; + HessianProxyFactory _factory; + BootstrapProvider _bootstrapProvider; + Timer _timer; + + AHessianClientPipelineFactory(Executor executor, HessianProxyFactory factory, BootstrapProvider bootstrapProvider, Timer timer) + { + _executor = executor; + _factory = factory; + _bootstrapProvider = bootstrapProvider; + _timer = timer; + } + + AHessianClientPipelineFactory(Executor executor, HessianProxyFactory factory, Timer timer) + { + this(executor, factory, null, timer); + } + + public ChannelPipeline getPipeline() throws Exception + { + ChannelPipeline pipeline = StopablePipeline.pipeline(); // Note the + // static + // import. + + // no auto reconnect: port may change in case of mcast discovery + if (_bootstrapProvider != null) + pipeline.addLast("reconnect", new ReconnectHandler(_bootstrapProvider, 1000, _timer)); + // pipeline.addLast("logger1",new OutLogger("1")); + + // InputStreamDecoder returns an input stream and calls the next handler + // in a separate thread + pipeline.addLast("inputStream", new InputStreamDecoder()); + + // pipeline.addLast("logger2",new OutLogger("2")); + pipeline.addLast("outputStream", new OutputStreamEncoder()); + + pipeline.addLast("hessianReplyDecoder", new PullInputStreamConsumer(new HessianRPCReplyDecoder(_factory, new JmxSerializerFactory()), _executor)); + pipeline.addLast("hessianCallEncoder", new HessianRPCCallEncoder(new JmxSerializerFactory())); + pipeline.addLast("outputProducer", new OutputProducer(_executor)); + // pipeline.addLast("logger3",new OutLogger("3")); + pipeline.addLast("hessianHandler", _factory); + pipeline.addLast("stopHandler", new StopHandler()); + + return pipeline; + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/tray/ahessian/client/AHessianJmxClient.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/tray/ahessian/client/AHessianJmxClient.java new file mode 100644 index 0000000000..14ac237221 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/tray/ahessian/client/AHessianJmxClient.java @@ -0,0 +1,182 @@ +package org.rzo.yajsw.tray.ahessian.client; + +import java.net.InetSocketAddress; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +import javax.management.MBeanServerConnection; + +import org.jboss.netty.bootstrap.ClientBootstrap; +import org.jboss.netty.channel.ChannelFuture; +import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory; +import org.jboss.netty.util.HashedWheelTimer; +import org.rzo.netty.ahessian.application.jmx.remote.service.AsyncMBeanServerConnection; +import org.rzo.netty.ahessian.application.jmx.remote.service.MBeanServerConnectionAsyncAdapter; +import org.rzo.netty.ahessian.rpc.client.BootstrapProvider; +import org.rzo.netty.ahessian.rpc.client.HessianProxyFactory; +import org.rzo.netty.ahessian.utils.MyReentrantLock; +import org.rzo.netty.mcast.discovery.DiscoveryClient; +import org.rzo.netty.mcast.discovery.DiscoveryListener; + +public class AHessianJmxClient implements BootstrapProvider +{ + boolean stop = false; + DiscoveryClient discovery = null; + ExecutorService executor = Executors.newCachedThreadPool(); + ClientBootstrap bootstrap = new ClientBootstrap(new NioClientSocketChannelFactory(executor, executor)); + MBeanServerConnection mbeanServer; + final Lock lock = new MyReentrantLock(); + final Condition connected = lock.newCondition(); + String currentHost = null; + HessianProxyFactory factory; + + public AHessianJmxClient(String discoveryName, int port) throws Exception + { + factory = new HessianProxyFactory(executor, "AHessianJMX", null); + + // in case we are disconnected: start the discovery + factory.setDisconnectedListener(new Runnable() + { + public void run() + { + try + { + close(); + if (discovery != null) + { + if (currentHost != null) + discovery.removeHost(currentHost); + discovery.start(); + } + } + catch (Exception e) + { + e.printStackTrace(); + } + } + }); + + factory.setConnectedListener(new Runnable() + { + + public void run() + { + // if (discovery == null) + doConnected(); + } + + }); + + bootstrap.setOption("reuseAddress", true); + + // if we do not have a port: use discovery + if (port == 0) + { + discovery = new DiscoveryClient(); + bootstrap.setPipelineFactory(new AHessianClientPipelineFactory(executor, factory, null)); + + discovery.setName(discoveryName); + discovery.addListener(new DiscoveryListener() + { + // we have discovered our mbean server + public void newHost(String name, String host) + { + try + { + // get the port - hostName should be the local host + String[] x = host.split("&"); + int port = Integer.parseInt(x[2]); + String hostName = x[1]; + // try to connect + ChannelFuture future = bootstrap.connect(new InetSocketAddress(hostName, port)); + // future.await(10000); + + // stop discovery + discovery.stop(); + // doConnected(); + currentHost = host; + } + catch (Exception ex) + { + ex.printStackTrace(); + } + } + }); + discovery.init(); + discovery.start(); + } + // if we have a port connect to that port + else + { + bootstrap.setOption("remoteAddress", new InetSocketAddress("localhost", port)); + bootstrap.setPipelineFactory(new AHessianClientPipelineFactory(executor, factory, this, new HashedWheelTimer())); + bootstrap.connect(); + } + } + + private void doConnected() + { + lock.lock(); + Map options = new HashMap(); + options.put("sync", true); + options.put("timeout", (long) 2000); + // we will be using a synchronous service + AsyncMBeanServerConnection asyncService = (AsyncMBeanServerConnection) factory.create(AsyncMBeanServerConnection.class, + AHessianJmxClient.class.getClassLoader(), options); + mbeanServer = new MBeanServerConnectionAsyncAdapter(asyncService); + connected.signal(); + lock.unlock(); + } + + public MBeanServerConnection getMBeanServer() + { + while (mbeanServer == null && !stop) + + { + lock.lock(); + try + { + connected.await(1000, TimeUnit.MILLISECONDS); + } + catch (InterruptedException e) + { + e.printStackTrace(); + } + lock.unlock(); + } + + return mbeanServer; + } + + public void close() + { + factory.setBlocked(true); + factory.invalidateAllPendingCalls(); + factory.invalidateProxies(); + mbeanServer = null; + if (factory.getChannel() != null && factory.getChannel().isConnected()) + factory.getChannel().close(); + } + + public void open() + { + factory.setBlocked(false); + } + + public void stop() + { + stop = true; + } + + public ClientBootstrap getBootstrap() + { + return bootstrap; + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/tray/ahessian/server/AHessianJmxServer.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/tray/ahessian/server/AHessianJmxServer.java new file mode 100644 index 0000000000..1a595562c9 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/tray/ahessian/server/AHessianJmxServer.java @@ -0,0 +1,54 @@ +package org.rzo.yajsw.tray.ahessian.server; + +import java.net.InetSocketAddress; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.management.MBeanServer; + +import org.jboss.netty.bootstrap.ServerBootstrap; +import org.jboss.netty.channel.Channel; +import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory; +import org.jboss.netty.handler.ipfilter.IpFilterRuleHandler; +import org.jboss.netty.handler.ipfilter.IpFilterRuleList; +import org.rzo.netty.mcast.discovery.DiscoveryServer; + +public class AHessianJmxServer +{ + public AHessianJmxServer(MBeanServer mbeanServer, String ipFilter, String serviceDiscoveryName, int port, Logger log) + { + Executor executor = Executors.newFixedThreadPool(10); + + // Configure the server. + ServerBootstrap bootstrap = new ServerBootstrap(new NioServerSocketChannelFactory(executor, executor)); + + bootstrap + .setPipelineFactory(new AHessianServerPipelineFactory(executor, new IpFilterRuleHandler(new IpFilterRuleList(ipFilter)), mbeanServer, log)); + + int serverPort = port; + // Bind and start to accept incoming connections. + Channel channel = bootstrap.bind(new InetSocketAddress(serverPort)); + if (serverPort == 0) + serverPort = ((InetSocketAddress) channel.getLocalAddress()).getPort(); + + log.info("ahessian jmx service bound to port " + serverPort); + + DiscoveryServer discovery = new DiscoveryServer(); + // allow discovery only from localhost. other computers will be ignored + discovery.setIpSet(new IpFilterRuleList("+n:localhost, -n:*")); + discovery.setName(serviceDiscoveryName); + discovery.setPort(serverPort); + try + { + discovery.init(); + } + catch (Exception e) + { + log.log(Level.SEVERE, "error starting tray icon server", e); + } + + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/tray/ahessian/server/AHessianServerPipelineFactory.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/tray/ahessian/server/AHessianServerPipelineFactory.java new file mode 100644 index 0000000000..1099a81436 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/tray/ahessian/server/AHessianServerPipelineFactory.java @@ -0,0 +1,67 @@ +package org.rzo.yajsw.tray.ahessian.server; + +import java.util.concurrent.Executor; +import java.util.logging.Logger; + +import javax.management.MBeanServer; +import javax.management.MBeanServerConnection; + +import org.jboss.netty.channel.ChannelPipeline; +import org.jboss.netty.channel.ChannelPipelineFactory; +import org.jboss.netty.handler.ipfilter.IpFilteringHandler; +import org.rzo.netty.ahessian.application.jmx.remote.service.JmxSerializerFactory; +import org.rzo.netty.ahessian.io.InputStreamDecoder; +import org.rzo.netty.ahessian.io.OutputStreamEncoder; +import org.rzo.netty.ahessian.io.PullInputStreamConsumer; +import org.rzo.netty.ahessian.rpc.message.HessianRPCCallDecoder; +import org.rzo.netty.ahessian.rpc.message.HessianRPCReplyEncoder; +import org.rzo.netty.ahessian.rpc.message.OutputProducer; +import org.rzo.netty.ahessian.rpc.server.ExecutorInvokeService; +import org.rzo.netty.ahessian.rpc.server.HessianRPCServiceHandler; +import org.rzo.netty.ahessian.stopable.StopHandler; +import org.rzo.netty.ahessian.stopable.StopablePipeline; + +public class AHessianServerPipelineFactory implements ChannelPipelineFactory +{ + + Executor _executor; + IpFilteringHandler _ipFilter; + MBeanServer _mbeanServer; + HessianRPCServiceHandler _factory; + Logger _log; + + AHessianServerPipelineFactory(Executor executor, IpFilteringHandler ipFilter, MBeanServer mbeanServer, Logger log) + { + AhessianLogging.setAhessianLogger(log); + _executor = executor; + _ipFilter = ipFilter; + _mbeanServer = mbeanServer; + _log = log; + _factory = new HessianRPCServiceHandler(_executor); + + // factory.addService("default", new ContinuationService(new + // ContinuationHalloWorldService(), HelloWorldServiceInterface.class, + // factory, _executor)); + _factory.addService("default", new ExecutorInvokeService(_mbeanServer, MBeanServerConnection.class, _factory, _executor)); + + } + + public ChannelPipeline getPipeline() throws Exception + { + ChannelPipeline pipeline = StopablePipeline.pipeline(); + // pipeline.addLast("logger1",new OutLogger("1")); + pipeline.addLast("ipfilter", _ipFilter); + pipeline.addLast("inputStream", new InputStreamDecoder()); + // pipeline.addLast("logger2",new OutLogger("2")); + pipeline.addLast("outputStream", new OutputStreamEncoder()); + pipeline.addLast("callDecoder", new PullInputStreamConsumer(new HessianRPCCallDecoder(new JmxSerializerFactory()), _executor)); + pipeline.addLast("replyEncoder", new HessianRPCReplyEncoder(new JmxSerializerFactory())); + // pipeline.addLast("logger3",new OutLogger("3")); + pipeline.addLast("outputProducer", new OutputProducer(_executor)); + pipeline.addLast("hessianRPCServer", _factory); + pipeline.addLast("stopHandler", new StopHandler()); + + return pipeline; + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/tray/ahessian/server/AhessianLogging.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/tray/ahessian/server/AhessianLogging.java new file mode 100644 index 0000000000..487e2b7cb1 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/tray/ahessian/server/AhessianLogging.java @@ -0,0 +1,23 @@ +package org.rzo.yajsw.tray.ahessian.server; + +import java.util.logging.Logger; + +import org.jboss.netty.logging.InternalLogger; +import org.jboss.netty.logging.InternalLoggerFactory; + +public class AhessianLogging +{ + public static void setAhessianLogger(final Logger log) + { + InternalLoggerFactory.setDefaultFactory(new InternalLoggerFactory() + { + + @Override + public InternalLogger newInstance(String name) + { + return (InternalLogger) new JdkLogger(log, "ahessian-jmx" ); + } + }); + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/tray/ahessian/server/JdkLogger.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/tray/ahessian/server/JdkLogger.java new file mode 100644 index 0000000000..ecb1dd386e --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/tray/ahessian/server/JdkLogger.java @@ -0,0 +1,95 @@ +/* + * Copyright 2009 Red Hat, Inc. + * + * Red Hat licenses this file to you under the Apache License, version 2.0 + * (the "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package org.rzo.yajsw.tray.ahessian.server; + +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.jboss.netty.logging.AbstractInternalLogger; + +/** + * java.util.logging + * logger. + * + * @author The Netty Project + * @author Trustin Lee + * + * @version $Rev: 2080 $, $Date: 2010-01-26 18:04:19 +0900 (Tue, 26 Jan 2010) $ + * + */ +class JdkLogger extends AbstractInternalLogger { + + private final Logger logger; + private final String loggerName; + + JdkLogger(Logger logger, String loggerName) { + this.logger = logger; + this.loggerName = loggerName; + } + + public void debug(String msg) { + logger.logp(Level.FINE, loggerName, null, msg); + } + + public void debug(String msg, Throwable cause) { + logger.logp(Level.FINE, loggerName, null, msg, cause); + } + + public void error(String msg) { + logger.logp(Level.SEVERE, loggerName, null, msg); + } + + public void error(String msg, Throwable cause) { + logger.logp(Level.SEVERE, loggerName, null, msg, cause); + } + + public void info(String msg) { + logger.logp(Level.INFO, loggerName, null, msg); + } + + public void info(String msg, Throwable cause) { + logger.logp(Level.INFO, loggerName, null, msg, cause); + } + + public boolean isDebugEnabled() { + return logger.isLoggable(Level.FINE); + } + + public boolean isErrorEnabled() { + return logger.isLoggable(Level.SEVERE); + } + + public boolean isInfoEnabled() { + return logger.isLoggable(Level.INFO); + } + + public boolean isWarnEnabled() { + return logger.isLoggable(Level.WARNING); + } + + public void warn(String msg) { + logger.logp(Level.WARNING, loggerName, null, msg); + } + + public void warn(String msg, Throwable cause) { + logger.logp(Level.WARNING, loggerName, null, msg, cause); + } + + @Override + public String toString() { + return loggerName; + } +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/updater/InstallAction.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/updater/InstallAction.java new file mode 100644 index 0000000000..d538386bb6 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/updater/InstallAction.java @@ -0,0 +1,112 @@ +package org.rzo.yajsw.updater; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +import org.rzo.yajsw.boot.WrapperLoader; +import org.rzo.yajsw.config.YajswConfigurationImpl; +import org.rzo.yajsw.os.JavaHome; +import org.rzo.yajsw.os.OperatingSystem; +import org.rzo.yajsw.os.Process; + + +public class InstallAction +{ + + + static YajswConfigurationImpl _currentConfig; + static String _newConfig; + static String _wrapperHome; + static Process _p; + + public static void setWrapperHome(String wrapperHome) + { + _wrapperHome = wrapperHome; + } + + public static void setCurrentConfig(YajswConfigurationImpl config) + { + _currentConfig = config; + } + + public static void setNewConfig(String newConfig) + { + _newConfig = newConfig; + } + + public static void run() + { + try + { + if (_p != null) + return; + if (_currentConfig == null || _newConfig == null || _wrapperHome == null) + return; + _p = OperatingSystem.instance().processManagerInstance().createProcess(); + _p.setCommand(getInstallerCommand()); + // daemonize ! + _p.setPipeStreams(false, false); + _p.setVisible(false); + // set working dir to wrapper home, so we know where we are running + // not required, but good + _p.setWorkingDir(WrapperLoader.getWrapperHome()); + _p.start(); + } + catch (Throwable ex) + { + ex.printStackTrace(); + } + } + + private static String[] getInstallerCommand() + { + List result = new ArrayList(); + // set java + JavaHome javaHome = OperatingSystem.instance().getJavaHome(_currentConfig); + String java = javaHome.findJava(_currentConfig.getString("wrapper.java.command"), _currentConfig.getString("wrapper.java.customProcName")); + result.add(java); + // set classpath + result.add("-classpath"); + result.add(getWrapperJar()+File.pathSeparator+getWrapperAppJar()); + // forward system props to sub process + for (Object obj : System.getProperties().keySet()) + { + String key = (String)obj; + result.add("-D"+key+"="+System.getProperty(key)); + } + // set main class + result.add(InstallerBooter.class.getName()); + // set main class args - current.conf new.conf + result.add(new File(_currentConfig.getCachedPath(false)).getAbsolutePath()); + result.add(_newConfig); + String[] arrResult = new String[result.size()]; + for (int i=0; i< arrResult.length; i++) + arrResult[i] = result.get(i); + return arrResult; + } + + private static String getWrapperJar() + { + return _wrapperHome+File.separator+"wrapper.jar"; + } + + private static String getWrapperAppJar() + { + return _wrapperHome+File.separator+"wrapperApp.jar"; + } + + // test + public static void main(String[] args) + { + UpdateAction.setUpdateConfig("z:/dev/yajsw/update.conf"); + System.setProperty("wrapper.config", "z:/dev/yajsw/conf/wrapper.helloworld.conf"); + YajswConfigurationImpl conf = new YajswConfigurationImpl(); + UpdateAction.setCurrentConfig(conf); + System.out.println("service "+conf.getString("wrapper.ntservice.name", "?")); + UpdateAction.run(); + } + + + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/updater/InstallerBooter.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/updater/InstallerBooter.java new file mode 100644 index 0000000000..fc72037a85 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/updater/InstallerBooter.java @@ -0,0 +1,54 @@ +/* This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + *

    + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ +package org.rzo.yajsw.updater; + +import java.io.File; +import java.lang.reflect.Method; +import java.net.URLClassLoader; + +import org.rzo.yajsw.boot.WrapperLoader; + +public class InstallerBooter +{ + + /** + * The main method. Loads the libs required by YAJSW and starts + * WrapperExe.main + * + * @param args + * the arguments + */ + public static void main(String[] args) + { + if (System.getProperty("java.io.tmpdir") != null) + { + File tmp = new File(System.getProperty("java.io.tmpdir")); + if (!tmp.exists()) + tmp.mkdirs(); + } + + URLClassLoader cl = WrapperLoader.getWrapperClassLoader(); + Thread.currentThread().setContextClassLoader(cl); + try + { + Class cls = Class.forName("org.rzo.yajsw.updater.InstallerMain", true, cl); + Method mainMethod = cls.getDeclaredMethod("main", new Class[] + { String[].class }); + mainMethod.invoke(null, new Object[] + { args }); + } + catch (Exception e) + { + e.printStackTrace(); + } + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/updater/InstallerMain.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/updater/InstallerMain.java new file mode 100644 index 0000000000..5115d39d1b --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/updater/InstallerMain.java @@ -0,0 +1,69 @@ +package org.rzo.yajsw.updater; + +import java.util.ArrayList; + +import org.rzo.yajsw.config.YajswConfigurationImpl; +import org.rzo.yajsw.os.ms.win.w32.WindowsXPProcess; +import org.rzo.yajsw.wrapper.WrappedService; + +import com.sun.jna.PlatformEx; + +public class InstallerMain +{ + + // args: currentApp.conf newApp.conf + public static void main(String[] args) throws Exception + { + if (args.length < 2) + { + System.err.println("missing new or current configuration -> abort"); + return; + } + uninstallService(args[0]); + try + { + Thread.sleep(2000); + } + catch (InterruptedException e) + { + e.printStackTrace(); + Thread.currentThread().interrupt(); + } + installService(args[1]); + } + + + private static void installService(String newAppConfig) + { + WrappedService service = new WrappedService(); + ArrayList list = new ArrayList(); + list.add(newAppConfig); + service.setConfFilesList(list); + service.init(); + + boolean result = service.install(); + if (PlatformEx.isWinVista() && service.requiresElevate()) + { + System.out.println("try uac elevate"); + WindowsXPProcess.elevateMe(); + return; + } + + if (result && System.getProperty("update.autostart") != null) + service.start(); + else + System.out.println("error installing"); + } + + private static void uninstallService(String currentConfig) + { + WrappedService service = new WrappedService(); + ArrayList list = new ArrayList(); + list.add(currentConfig); + service.init(); + service.stop(); + service.uninstall(); + } + + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/updater/UpdateAction.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/updater/UpdateAction.java new file mode 100644 index 0000000000..c6a4ae24c9 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/updater/UpdateAction.java @@ -0,0 +1,102 @@ +package org.rzo.yajsw.updater; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +import org.rzo.yajsw.boot.WrapperLoader; +import org.rzo.yajsw.config.YajswConfigurationImpl; +import org.rzo.yajsw.os.JavaHome; +import org.rzo.yajsw.os.OperatingSystem; +import org.rzo.yajsw.os.Process; + +public class UpdateAction +{ + static String _updateConfigFile; + static YajswConfigurationImpl _currentConfig; + static Process _p; + static boolean _autoStart = false; + + public static void setUpdateConfig(String updateConfigFile) + { + _updateConfigFile = updateConfigFile; + } + + public static void setCurrentConfig(YajswConfigurationImpl config) + { + _currentConfig = config; + } + + public static void setAutostart() + { + _autoStart = true; + } + + public static void run() + { + try + { + if (_p != null) + return; + if (_updateConfigFile == null || _updateConfigFile.length() == 0) + return; + _p = OperatingSystem.instance().processManagerInstance().createProcess(); + _p.setCommand(getUpdaterCommand()); + // daemonize ! + _p.setPipeStreams(false, false); + _p.setVisible(false); + // set working dir to wrapper home, so we know where we are running + // not required, but good + _p.setWorkingDir(WrapperLoader.getWrapperHome()); + _p.start(); + // wait, so that the service has been uninstalled and thus could not be restarted + // until it has been reinstalled + _p.waitFor(10000); + } + catch (Throwable ex) + { + ex.printStackTrace(); + } + } + + private static String[] getUpdaterCommand() + { + List result = new ArrayList(); + // set java + JavaHome javaHome = OperatingSystem.instance().getJavaHome(_currentConfig); + String java = javaHome.findJava(_currentConfig.getString("wrapper.java.command"), _currentConfig.getString("wrapper.java.customProcName")); + result.add(java); + // set classpath + result.add("-classpath"); + result.add(WrapperLoader.getWrapperJar()+File.pathSeparator+WrapperLoader.getWrapperAppJar()); + // forward system props to sub process + for (Object obj : System.getProperties().keySet()) + { + String key = (String)obj; + result.add("-D"+key+"="+System.getProperty(key)); + } + if (_autoStart) + result.add("-Dupdate.autostart=true"); + // set main class + result.add(UpdaterBooter.class.getName()); + // set main class args - update config file + result.add(_updateConfigFile); + result.add(new File(_currentConfig.getCachedPath(false)).getAbsolutePath()); + String[] arrResult = new String[result.size()]; + for (int i=0; i< arrResult.length; i++) + arrResult[i] = result.get(i); + return arrResult; + } + + // test + public static void main(String[] args) + { + UpdateAction.setUpdateConfig("z:/dev/yajsw/update.conf"); + System.setProperty("wrapper.config", "z:/dev/yajsw/conf/wrapper.helloworld.conf"); + YajswConfigurationImpl conf = new YajswConfigurationImpl(); + UpdateAction.setCurrentConfig(conf); + System.out.println("service "+conf.getString("wrapper.ntservice.name", "?")); + UpdateAction.run(); + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/updater/UpdaterBooter.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/updater/UpdaterBooter.java new file mode 100644 index 0000000000..d2f1e58a77 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/updater/UpdaterBooter.java @@ -0,0 +1,54 @@ +/* This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + *

    + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ +package org.rzo.yajsw.updater; + +import java.io.File; +import java.lang.reflect.Method; +import java.net.URLClassLoader; + +import org.rzo.yajsw.boot.WrapperLoader; + +public class UpdaterBooter +{ + + /** + * The main method. Loads the libs required by YAJSW and starts + * WrapperExe.main + * + * @param args + * the arguments + */ + public static void main(String[] args) + { + if (System.getProperty("java.io.tmpdir") != null) + { + File tmp = new File(System.getProperty("java.io.tmpdir")); + if (!tmp.exists()) + tmp.mkdirs(); + } + + URLClassLoader cl = WrapperLoader.getWrapperClassLoader(); + Thread.currentThread().setContextClassLoader(cl); + try + { + Class cls = Class.forName("org.rzo.yajsw.updater.UpdaterMain", true, cl); + Method mainMethod = cls.getDeclaredMethod("main", new Class[] + { String[].class }); + mainMethod.invoke(null, new Object[] + { args }); + } + catch (Exception e) + { + e.printStackTrace(); + } + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/updater/UpdaterMain.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/updater/UpdaterMain.java new file mode 100644 index 0000000000..953cfca70f --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/updater/UpdaterMain.java @@ -0,0 +1,94 @@ +package org.rzo.yajsw.updater; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; + +import org.apache.commons.configuration.FileOptionsProvider; +import org.apache.commons.configuration.FileSystem; +import org.apache.commons.configuration.VFSFileSystem; +import org.apache.commons.vfs2.FileObject; +import org.apache.commons.vfs2.FileSelectInfo; +import org.apache.commons.vfs2.FileSelector; +import org.apache.commons.vfs2.FileSystemException; +import org.rzo.yajsw.boot.WrapperLoader; +import org.rzo.yajsw.config.YajswConfigurationImpl; +import org.rzo.yajsw.os.ms.win.w32.WindowsXPProcess; +import org.rzo.yajsw.util.CommonsLoggingAdapter; +import org.rzo.yajsw.util.VFSUtils; +import org.rzo.yajsw.wrapper.WrappedService; + +import com.sun.jna.PlatformEx; + +public class UpdaterMain +{ + // args: update.conf currentApp.conf + public static void main(String[] args) throws Exception + { + if (args.length < 2) + { + System.err.println("missing update or current configuration -> abort"); + return; + } + System.setProperty("wrapper.config", args[0]); + YajswConfigurationImpl updateConfig = new YajswConfigurationImpl(); + + System.setProperty("wrapper.config", args[1]); + YajswConfigurationImpl currentAppConfig = new YajswConfigurationImpl(); + + // first uninstall service so it cannot be restarted + uninstallService(args[1]); + + String wrapperHome = updateWrapper(updateConfig); + + // spawn installer process with updated wrapper in classpath and current and new configurations + InstallAction.setCurrentConfig(currentAppConfig); + InstallAction.setNewConfig(updateConfig.getString("update.app.config")); + InstallAction.setWrapperHome(wrapperHome); + InstallAction.run(); + + } + + private static void uninstallService(String currentConfig) + { + WrappedService service = new WrappedService(); + ArrayList list = new ArrayList(); + list.add(currentConfig); + service.init(); + service.stop(); + service.uninstall(); + } + private static String updateWrapper(YajswConfigurationImpl updateConfig) throws Exception + { + String wrapperSrcFile = updateConfig.getString("update.wrapper.src", null); + String wrapperDestFile = updateConfig.getString("update.wrapper.dest", null); + if (wrapperSrcFile == null && wrapperDestFile == null) + return WrapperLoader.getWrapperHome(); + if (wrapperSrcFile == null || wrapperDestFile == null) + { + System.out.println("wrapper src or destination is empty -> not updating wrapper"); + return WrapperLoader.getWrapperHome(); + } + + FileObject wrapperSrc = VFSUtils.resolveFile((String)null, wrapperSrcFile); + FileObject wrapperDest = VFSUtils.resolveFile((String)null, wrapperDestFile); + wrapperDest.copyFrom(wrapperSrc, new FileSelector() + { + + public boolean includeFile(FileSelectInfo arg0) throws Exception + { + return true; + } + + public boolean traverseDescendents(FileSelectInfo arg0) throws Exception + { + return true; + } + + }); + return wrapperDest.getName().getPath(); + + } + + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/util/CaseInsensitiveMap.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/util/CaseInsensitiveMap.java new file mode 100644 index 0000000000..64f42d1b84 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/util/CaseInsensitiveMap.java @@ -0,0 +1,29 @@ +package org.rzo.yajsw.util; + +import java.util.HashMap; +import java.util.Map; + +public class CaseInsensitiveMap extends HashMap +{ + + public CaseInsensitiveMap(Map map) + { + super(); + for (Map.Entry entry : map.entrySet()) + { + put(entry.getKey(), entry.getValue()); + } + } + + @Override + public String put(String key, String value) + { + return super.put(key.toLowerCase(), value); + } + + @Override + public String get(Object key) + { + return super.get(((String) key).toLowerCase()); + } +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/util/CommonsLoggingAdapter.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/util/CommonsLoggingAdapter.java new file mode 100644 index 0000000000..c4ca68ccc3 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/util/CommonsLoggingAdapter.java @@ -0,0 +1,136 @@ +package org.rzo.yajsw.util; + +import org.apache.commons.logging.Log; +import org.jboss.netty.logging.InternalLogger; + +public class CommonsLoggingAdapter implements Log +{ + InternalLogger _log; + public CommonsLoggingAdapter(InternalLogger log) + { + _log = log; + } + + public void debug(Object arg0) + { + if (arg0 != null) + _log.debug(arg0.toString()); + else + _log.debug("null"); + } + + public void debug(Object arg0, Throwable arg1) + { + if (arg1 == null) + return; + if (arg0 != null) + _log.debug(arg0.toString(), arg1); + else + _log.debug("null", arg1); + } + + public void error(Object arg0) + { + if (arg0 != null) + _log.error(arg0.toString()); + else + _log.error("null"); + } + + public void error(Object arg0, Throwable arg1) + { + if (arg1 == null) + return; + if (arg0 != null) + _log.error(arg0.toString(), arg1); + else + _log.error("null", arg1); + } + + public void fatal(Object arg0) + { + error(arg0); + } + + public void fatal(Object arg0, Throwable arg1) + { + error(arg0, arg1); + } + + public void info(Object arg0) + { + if (arg0 != null) + _log.info(arg0.toString()); + else + _log.info("null"); + } + + public void info(Object arg0, Throwable arg1) + { + if (arg1 == null) + return; + if (arg0 != null) + _log.info(arg0.toString(), arg1); + else + _log.info("null", arg1); + } + + public boolean isDebugEnabled() + { + return false; + } + + public boolean isErrorEnabled() + { + return _log.isErrorEnabled(); + } + + public boolean isFatalEnabled() + { + return _log.isErrorEnabled(); + } + + public boolean isInfoEnabled() + { + return _log.isInfoEnabled(); + } + + public boolean isTraceEnabled() + { + return _log.isDebugEnabled(); + } + + public boolean isWarnEnabled() + { + return _log.isWarnEnabled(); + } + + public void trace(Object arg0) + { + debug(arg0); + } + + public void trace(Object arg0, Throwable arg1) + { + debug(arg0, arg1); + } + + public void warn(Object arg0) + { + if (arg0 != null) + _log.warn(arg0.toString()); + else + _log.warn("null"); + } + + public void warn(Object arg0, Throwable arg1) + { + if (arg1 == null) + return; + if (arg0 != null) + _log.warn(arg0.toString(), arg1); + else + _log.warn("null", arg1); + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/util/Cycler.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/util/Cycler.java new file mode 100644 index 0000000000..4d16f6f190 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/util/Cycler.java @@ -0,0 +1,112 @@ +package org.rzo.yajsw.util; + +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; + +public class Cycler +{ + Executor _executor; + long _period; + long _delay; + Runnable _runnable; + boolean _started = false; + boolean _terminated = true; + Runnable _worker; + Thread _cyclerThread = null; + + public Cycler(long period, long delay, Executor executor, Runnable runnable) + { + if (runnable == null || executor == null) + throw new NullPointerException(); + _period = checkDuration(period, 100); + _delay = checkDuration(delay, 0); + _executor = executor; + _runnable = runnable; + _worker = new Runnable() + { + public void run() + { + _cyclerThread = Thread.currentThread(); + _terminated = false; + if (_started) + try + { + Thread.sleep(_delay); + } + catch (InterruptedException ex) + { + // interrupted -> continue + } + while (_started) + { + if (_started) + try + { + _runnable.run(); + } + catch (Throwable ex) + { + ex.printStackTrace(); + } + if (_started) + try + { + Thread.sleep(_period); + } + catch (InterruptedException e) + { + // interrupted -> continue + } + + } + _terminated = true; + _cyclerThread = null; + } + }; + } + + public synchronized void start() + { + if (_started) + return; + _started = true; + if (_terminated) + _executor.execute(_worker); + } + + public synchronized void stop() + { + _started = false; + if (_cyclerThread != null) + _cyclerThread.interrupt(); + } + + private long checkDuration(long duration, long minValue) + { + long result = duration; + if (duration % 100 != 0) + result = (duration / 100) * 100; + if (result <= minValue) + result = minValue; + return result; + } + + public static void main(String[] args) throws InterruptedException + { + Cycler c = new Cycler(1000, 0, Executors.newCachedThreadPool(new DaemonThreadFactory("controller")), new Runnable() + { + public void run() + { + System.out.println(System.currentTimeMillis() + " running"); + } + }); + while (true) + { + c.start(); + Thread.sleep(1500); + c.stop(); + System.out.println(System.currentTimeMillis() + " stopped"); + } + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/util/DaemonThreadFactory.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/util/DaemonThreadFactory.java new file mode 100644 index 0000000000..c6f90c9fa0 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/util/DaemonThreadFactory.java @@ -0,0 +1,30 @@ +package org.rzo.yajsw.util; + +import java.util.concurrent.ThreadFactory; + +public class DaemonThreadFactory implements ThreadFactory +{ + final String _prefix; + volatile int _count = 0; + static ThreadGroup group = new ThreadGroup("yajsw"); + int _priority = Thread.NORM_PRIORITY; + + public DaemonThreadFactory(String prefix) + { + _prefix = "yajsw." + prefix + "-"; + } + + public DaemonThreadFactory(String prefix, int priority) + { + _prefix = "yajsw." + prefix + "-"; + _priority = priority; + } + + synchronized public Thread newThread(Runnable r) + { + Thread t = new Thread(group, r, _prefix + _count++); + t.setDaemon(true); + t.setPriority(_priority); + return t; + } +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/util/File.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/util/File.java new file mode 100644 index 0000000000..c6405554be --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/util/File.java @@ -0,0 +1,38 @@ +package org.rzo.yajsw.util; + +import java.util.Date; + +import org.rzo.yajsw.os.OperatingSystem; + +public class File extends java.io.File +{ + + public File(String pathname) + { + super(pathname); + } + + public long created() + { + return OperatingSystem.instance().fileManagerInstance().created(this); + } + + public long getFreeSpace() + { + return OperatingSystem.instance().fileManagerInstance().freeSpace(this); + } + + public long getTotalSpace() + { + return OperatingSystem.instance().fileManagerInstance().totalSpace(this); + } + + static public void main(String[] args) + { + File f = new File("E:"); + System.out.println(new Date(f.created())); + System.out.println(f.getFreeSpace()); + System.out.println(f.getTotalSpace()); + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/util/MyReentrantLock.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/util/MyReentrantLock.java new file mode 100644 index 0000000000..94262e2fe0 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/util/MyReentrantLock.java @@ -0,0 +1,36 @@ +package org.rzo.yajsw.util; + +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.ReentrantLock; + +public class MyReentrantLock extends ReentrantLock +{ + + final static long timeout = 100; // millis + @Override + synchronized public void lock() { + // To avoid a hang that seems to be caused by a lost-wakeup + // we repeatedly use tryAcquire in a loop so that we can + // poll the lock state + + boolean locked = false; + boolean interrupted = false; + + while(!locked) { + try { + locked = tryLock(timeout, TimeUnit.MILLISECONDS); + } + catch (InterruptedException ex) { + interrupted = true; + } + } + + if (interrupted) { + // re-assert interrupt state that occurred while we + // were acquiring the lock + Thread.currentThread().interrupt(); + } + } + + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/util/SimpleThreadFactory.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/util/SimpleThreadFactory.java new file mode 100644 index 0000000000..62de96b6f9 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/util/SimpleThreadFactory.java @@ -0,0 +1,29 @@ +package org.rzo.yajsw.util; + +import java.util.concurrent.ThreadFactory; + +public class SimpleThreadFactory implements ThreadFactory +{ + String _prefix; + int _count = 0; + static ThreadGroup group = new ThreadGroup("yajsw"); + int _priority = Thread.NORM_PRIORITY; + + public SimpleThreadFactory(String prefix) + { + _prefix = "yajsw." + prefix + "-"; + } + + public SimpleThreadFactory(String prefix, int priority) + { + _prefix = "yajsw." + prefix + "-"; + _priority = priority; + } + + public Thread newThread(Runnable r) + { + Thread t = new Thread(group, r, _prefix + _count++); + t.setPriority(_priority); + return t; + } +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/util/Utils.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/util/Utils.java new file mode 100644 index 0000000000..6d54c3ccea --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/util/Utils.java @@ -0,0 +1,49 @@ +package org.rzo.yajsw.util; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; + +import org.rzo.yajsw.wrapper.WrappedProcess; +import org.rzo.yajsw.wrapper.WrappedService; + +public class Utils +{ + WrappedProcess _process; + WrappedService _service; + + public Utils(WrappedProcess process) + { + _process = process; + } + + public Utils(WrappedService service) + { + _service = service; + } + + public String inquireCLI(String message) throws IOException + { + System.out.print(message + ":"); + return new BufferedReader(new InputStreamReader(System.in)).readLine(); + } + + public String inquireTrayIcon(String message) throws InterruptedException + { + String result = null; + if (_process == null) + { + System.out.println("ERROR in inquireTrayIcon: process == null"); + return null; + } + while (result == null) + { + result = _process.getTrayIcon().inquire(message); + if (result == null) + Thread.sleep(2000); + } + return result; + } + + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/util/VFSFileValidator.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/util/VFSFileValidator.java new file mode 100644 index 0000000000..93f0ad1bb5 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/util/VFSFileValidator.java @@ -0,0 +1,97 @@ +package org.rzo.yajsw.util; + +import java.util.List; +import java.util.ListIterator; + +import org.apache.commons.cli2.validation.FileValidator; +import org.apache.commons.cli2.validation.InvalidArgumentException; +import org.apache.commons.vfs2.FileObject; +import org.apache.commons.vfs2.FileType; + +public class VFSFileValidator extends FileValidator +{ + + String _base = null; + + public VFSFileValidator setBase(String base) + { + _base = base; + return this; + } + + public static VFSFileValidator getExistingInstance() + { + VFSFileValidator validator = null; + try + { + validator = new VFSFileValidator(); + } + catch (Exception e) + { + e.printStackTrace(); + } + validator.setExisting(true); + return validator; + } + + public static VFSFileValidator getExistingFileInstance() + { + VFSFileValidator validator = null; + try + { + validator = new VFSFileValidator(); + } + catch (Exception e) + { + // TODO Auto-generated catch block + e.printStackTrace(); + } + validator.setExisting(true); + validator.setFile(true); + return validator; + } + + public static VFSFileValidator getExistingDirectoryInstance() + { + VFSFileValidator validator = null; + try + { + validator = new VFSFileValidator(); + } + catch (Exception e) + { + e.printStackTrace(); + } + validator.setExisting(true); + validator.setDirectory(true); + return validator; + } + + public void validate(final List values) throws InvalidArgumentException + { + for (final ListIterator i = values.listIterator(); i.hasNext();) + { + final String name = (String) i.next(); + try + { + FileObject f = VFSUtils.resolveFile(_base, name); + + if ((isExisting() && !f.exists()) || (isFile() && !f.getType().equals(FileType.FILE)) + || (isDirectory() && !f.getType().equals(FileType.FILE)) || (isHidden() && !f.isHidden()) + || (isReadable() && !f.isReadable()) || (isWritable() && !f.isWriteable())) + { + + throw new InvalidArgumentException(name); + } + + i.set(name); + } + catch (Exception ex) + { + ex.printStackTrace(); + throw new InvalidArgumentException(name); + } + } + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/util/VFSUtils.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/util/VFSUtils.java new file mode 100644 index 0000000000..d26893f59e --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/util/VFSUtils.java @@ -0,0 +1,172 @@ +package org.rzo.yajsw.util; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; +import java.util.regex.Pattern; + +import org.apache.commons.vfs2.FileObject; +import org.apache.commons.vfs2.FileSelectInfo; +import org.apache.commons.vfs2.FileSelector; +import org.apache.commons.vfs2.FileSystemException; +import org.apache.commons.vfs2.FileSystemOptions; +import org.apache.commons.vfs2.FileType; +import org.apache.commons.vfs2.VFS; +import org.apache.commons.vfs2.impl.DefaultFileSystemManager; +import org.apache.commons.vfs2.provider.http.HttpFileSystemConfigBuilder; + + +public class VFSUtils +{ + static DefaultFileSystemManager fsManager = null; + static FileSystemOptions opts = new FileSystemOptions(); + + public static void init() throws FileSystemException + { + if (fsManager != null) + return; + + fsManager = (DefaultFileSystemManager) VFS.getManager(); + String httpProxy = System.getProperty("http.proxyHost"); + String httpPort = System.getProperty("http.proxyPort"); + if (httpProxy != null) + { + HttpFileSystemConfigBuilder.getInstance().setProxyHost(opts, httpProxy); + + int port = 8080; + if (httpPort != null) + try + { + port = Integer.parseInt(httpPort); + } + catch (Exception ex) + { + ex.printStackTrace(); + } + HttpFileSystemConfigBuilder.getInstance().setProxyPort(opts, port); + } + } + + public static FileObject resolveFile(String base, String file) throws FileSystemException + { + init(); + FileObject basef = null; + if (base != null) + basef = fsManager.resolveFile(new File("."), base); + return resolveFile(basef, file); + } + + public static FileObject resolveFile(FileObject basef, String file) throws FileSystemException + { + init(); + if (basef != null) + return fsManager.resolveFile(basef, file, opts); + else + return fsManager.resolveFile(file, opts); + } + + public static List resolveFiles(String value) throws Exception + { + init(); + return resolveFiles(fsManager.resolveFile(new File(".").getAbsolutePath()), value); + } + + public static List resolveFiles(FileObject basef, String value) throws Exception + { + init(); + return resolveFiles(basef, value, fsManager); + } + + public static List resolveFiles(FileObject basef, String value, DefaultFileSystemManager fsManager) + { + System.out.println("resolve files " + value); + try + { + ArrayList result = new ArrayList(); + + // if no wild card, just return the file + if (!(value.contains("?") || value.contains("*"))) + { + result.add(VFSUtils.resolveFile(basef, value)); + return result; + } + // if we have wild cards + { + // create a java pattern from the file pattern + String pattern = value.replaceAll("\\.", "\\\\."); + pattern = pattern.replaceAll("\\?", "."); + if (pattern.contains("/**/")) + pattern = pattern.replaceAll("/\\*\\*/", "/*/"); + pattern = pattern.replaceAll("\\*", ".*"); + pattern = basef.getName().getPath() + "/" + pattern; + final Pattern pat = Pattern.compile(pattern); + + // find prefix with no pattern + int istar = value.indexOf("*"); + if (istar <= 0) + istar = Integer.MAX_VALUE; + int iquest = value.indexOf("?"); + if (iquest <= 0) + iquest = Integer.MAX_VALUE; + int i = Math.min(istar, iquest); + String prefix = null; + if (i < Integer.MAX_VALUE) + { + prefix = value.substring(0, i); + } + + int depth = 0; + if (value.contains("**/")) + { + depth = Integer.MAX_VALUE; + } + else + while ((i = value.indexOf("*/")) != -1) + { + depth++; + value = value.substring(i + 2); + } + final int fdepth = depth; + FileSelector fs = new FileSelector() + { + public boolean includeFile(FileSelectInfo info) throws Exception + { + // files /x/x causes exceptions -> these are imaginary + // files -> ignore + if (info.getFile().getType() == FileType.IMAGINARY) + return false; + boolean result = pat.matcher(info.getFile().getName().getPath()).matches(); + System.out.println(info.getFile().getName().getPath() + " " + result); + return result; + } + + public boolean traverseDescendents(FileSelectInfo info) throws Exception + { + return info.getDepth() <= fdepth; + } + + }; + + FileObject nbase; + if (prefix != null) + nbase = basef.resolveFile(prefix); + else + nbase = basef; + + FileObject[] files = nbase.findFiles(fs); + if (files != null && files.length > 0) + return Arrays.asList(files); + else + return new ArrayList(); + } + } + catch (Exception ex) + { + ex.printStackTrace(); + } + return null; + } + + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/wrapper/AbstractWrappedProcess.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/wrapper/AbstractWrappedProcess.java new file mode 100644 index 0000000000..6858a90a06 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/wrapper/AbstractWrappedProcess.java @@ -0,0 +1,3435 @@ +package org.rzo.yajsw.wrapper; + +import java.awt.Color; +import java.io.BufferedReader; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.PrintStream; +import java.io.RandomAccessFile; +import java.lang.reflect.Method; +import java.nio.channels.FileChannel; +import java.nio.channels.FileLock; +import java.rmi.registry.LocateRegistry; +import java.rmi.registry.Registry; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.Map.Entry; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; +import java.util.logging.ConsoleHandler; +import java.util.logging.Handler; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.management.MBeanServer; +import javax.management.MBeanServerFactory; +import javax.management.ObjectName; +import javax.management.remote.JMXAuthenticator; +import javax.management.remote.JMXConnectorServer; +import javax.management.remote.JMXConnectorServerFactory; +import javax.management.remote.JMXPrincipal; +import javax.management.remote.JMXServiceURL; +import javax.security.auth.Subject; + +import org.apache.commons.collections.MultiMap; +import org.apache.commons.collections.map.MultiValueMap; +import org.apache.commons.configuration.BaseConfiguration; +import org.apache.commons.configuration.Configuration; +import org.apache.commons.vfs2.FileObject; +import org.jboss.netty.logging.InternalLogger; +import org.jboss.netty.logging.JdkLogger2Factory; +import org.rzo.yajsw.Constants; +import org.rzo.yajsw.YajswVersion; +import org.rzo.yajsw.action.Action; +import org.rzo.yajsw.action.ActionFactory; +import org.rzo.yajsw.cache.Cache; +import org.rzo.yajsw.condition.Condition; +import org.rzo.yajsw.config.YajswConfigurationImpl; +import org.rzo.yajsw.controller.Message; +import org.rzo.yajsw.controller.jvm.Controller; +import org.rzo.yajsw.io.CircularBuffer; +import org.rzo.yajsw.log.DateFileHandler; +import org.rzo.yajsw.log.MyFileHandler; +import org.rzo.yajsw.log.MyLogger; +import org.rzo.yajsw.log.PatternFormatter; +import org.rzo.yajsw.os.OperatingSystem; +import org.rzo.yajsw.os.Process; +import org.rzo.yajsw.os.StopableService; +import org.rzo.yajsw.os.ms.win.w32.ClusterNodeChangeListener; +import org.rzo.yajsw.os.posix.bsd.BSDProcess; +import org.rzo.yajsw.script.Script; +import org.rzo.yajsw.script.ScriptFactory; +import org.rzo.yajsw.timer.Timer; +import org.rzo.yajsw.timer.TimerFactory; +import org.rzo.yajsw.tray.WrapperTrayIconFactory; +import org.rzo.yajsw.tray.ahessian.server.AHessianJmxServer; +import org.rzo.yajsw.updater.UpdateAction; +import org.rzo.yajsw.util.CaseInsensitiveMap; +import org.rzo.yajsw.util.DaemonThreadFactory; +import org.rzo.yajsw.util.MyReentrantLock; +import org.rzo.yajsw.util.Utils; +import org.rzo.yajsw.util.VFSUtils; + +import com.sun.jna.Platform; +import com.sun.jna.PlatformEx; + +public abstract class AbstractWrappedProcess implements WrappedProcess, Constants, AbstractWrappedProcessMBean +{ + + /** The _os process. */ + volatile public Process _osProcess; + /** The _controller. */ + protected Controller _controller; + /** The _debug. */ + protected boolean _debug = false; + /** The _config. */ + protected YajswConfigurationImpl _config; + /** The _restarts. */ + // protected int _restarts; + /** The _gobler_err. */ + volatile protected Gobler _gobler_err; + /** The _gobler_in. */ + volatile protected Gobler _gobler_in; + /** The Constant executor. */ + protected static final Executor executor = Executors.newCachedThreadPool(new DaemonThreadFactory("wrappedProcess")); + + protected static final ThreadPoolExecutor scriptExecutor = (ThreadPoolExecutor) Executors.newCachedThreadPool(new DaemonThreadFactory("scriptExecutor")); + + /** The _first restart time. */ + protected long _firstRestartTime; + /** The _state. */ + protected volatile int _state = STATE_IDLE; + /** The _startup exit codes. */ + Set _startupExitCodes = new HashSet(); + /** The _shutdown exit codes. */ + Set _shutdownExitCodes = new HashSet(); + Set _stopExitCodes = new HashSet(); + /** The _exit code default restart. */ + boolean _exitCodeDefaultRestart = false; + /** The _local configuration. */ + protected Configuration _localConfiguration = new BaseConfiguration(); + /** The _app logger. */ + Logger _appLogger; + /** The _tmp path. */ + protected String _tmpPath; + /** The _wrapper logger. */ + Logger _wrapperLogger; + /** The _wrapper logger name. */ + String _wrapperLoggerName = "wrapper"; + /** The _app logger name. */ + String _appLoggerName; + String _appLoggerPid; + /** The _use system properties. */ + boolean _useSystemProperties = true; + /** The Constant PATHSEP. */ + protected static final String PATHSEP = System.getProperty("path.separator"); + /** The _restart count. */ + int _restartCount; + int _totalRestartCount = 0; + /** The _timer. */ + Timer _timer; + /** The _lock. */ + FileLock _lock; + /** The _lock file. */ + File _lockFile; + /** The _lock file channel. */ + FileChannel _lockFileChannel; + /** The _pid file. */ + File _pidFile; + /** The _successful invocation time. */ + long _successfulInvocationTime; + MultiMap _listeners = MultiValueMap.decorate(new HashMap(), HashSet.class); + MultiMap _userListeners = MultiValueMap.decorate(new HashMap(), HashSet.class); + + String _triggerLine; + Condition _condition; + Process _trayIconProcess; + volatile Date _appStarted; + volatile Date _appStopped; + Date _wrapperStarted; + volatile boolean _drainActive = false; + volatile int _exitCode = -99; + boolean _haltWrapperOnApp = false; + boolean _haltAppOnWrapper = false; + Object _cluster = null; + ClusterNodeChangeListener _clusterListener; + boolean _clusterTriggered = false; + Cache _cache = null; + volatile boolean _exiting = false; + volatile TrayIconProxy _trayIconMessages = null; + Script _restartDelayScript = null; + Object _service = null; + volatile boolean _stopRequested = false; + volatile boolean _startRequested = false; + MBeanServer _mbeanServer = null; + AHessianJmxServer _ahessianServer = null; + boolean _reconnecting = false; + boolean _init = false; + + /* + * bkowal - added constant. + */ + public static final int INFINITE_PROCESS_LOGGING = -1; + /* + * bkowal - switched from 40 to infinite constant + */ + public static final int MIN_PROCESS_LINES_TO_LOG = INFINITE_PROCESS_LOGGING; + + List _shutdownHooks = new ArrayList(); + + volatile boolean _stopper = false; + + Lock _stoppingHintLock = new MyReentrantLock(); + volatile long _stoppingHint = 0; + volatile long _stoppingHintSetTime = 0; + volatile boolean _appReportedReady = false; + volatile String _stopReason; + + JdkLogger2Factory _internalLoggerFactory = null; + + /** + * Inits the. + */ + public void init() + { + if (_init) + return; + Map utils = new HashMap(); + utils.put("util", new Utils(this)); + _config = new YajswConfigurationImpl(_localConfiguration, _useSystemProperties, utils); + getTmpPath(); + getWrapperLogger().warning("YAJSW: "+YajswVersion.YAJSW_VERSION); + getWrapperLogger().warning("OS : "+YajswVersion.OS_VERSION); + getWrapperLogger().warning("JVM : "+YajswVersion.JAVA_VERSION); + + if (!_config.isLocalFile()) + if (_cache == null) + { + _cache = new Cache(); + _cache.load(_config); + } + + String dbg = _config.getString("wrapper.debug"); + _debug = dbg == null ? false : dbg.equals("true"); + _successfulInvocationTime = _config.getLong("wrapper.successful_invocation_time", DEFAULT_SUCCESSFUL_INVOCATION_TIME) * 1000; + + String control = _config.getString("wrapper.control", DEFAULT_CONTROL); + if ("TIGHT".equals(control) || "WRAPPER".equals(control)) + _haltWrapperOnApp = true; + + if ("TIGHT".equals(control) || "APPLICATION".equals(control)) + _haltAppOnWrapper = true; + + for (Iterator it = _config.getKeys("wrapper.on_exit"); it.hasNext();) + { + String key = (String) it.next(); + String value = _config.getString(key); + if ("RESTART".equals(value)) + { + String postfix = key.substring(key.lastIndexOf(".") + 1); + if ("default".equals(postfix)) + _exitCodeDefaultRestart = true; + else + try + { + _startupExitCodes.add(Integer.parseInt(postfix)); + } + catch (Exception ex) + { + getWrapperLogger().info("error evaluating " + key + " " + ex.getMessage()); + } + + } + if ("SHUTDOWN".equals(value)) + { + String postfix = key.substring(key.lastIndexOf(".") + 1); + if ("default".equals(postfix)) + // do nothing + { + } + else + try + { + _shutdownExitCodes.add(Integer.parseInt(postfix)); + } + catch (Exception ex) + { + getWrapperLogger().info("error evaluating " + key + " " + ex.getMessage()); + } + + } + if ("STOP".equals(value)) + { + String postfix = key.substring(key.lastIndexOf(".") + 1); + if ("default".equals(postfix)) + // do nothing + { + } + else + try + { + _stopExitCodes.add(Integer.parseInt(postfix)); + } + catch (Exception ex) + { + getWrapperLogger().info("error evaluating " + key + " " + ex.getMessage()); + } + + } + } + + if (_timer == null) + _timer = TimerFactory.createTimer(_config, this); + _timer.init(); + if (_condition == null) + _condition = new Condition(_config, this, getInternalWrapperLogger()); + _condition.init(); + _restartCount = 0; + + // in case of active triggers control == LOOSE + if (_timer.isHasTrigger() || _condition.isHasTrigger()) + { + _haltWrapperOnApp = false; + // do not halt app on wrapper -> service stop takes too long + // _haltAppOnWrapper = false; + } + + // if we need the tray or jmx -> create and register a wrapper mbean + if (_config.getBoolean("wrapper.tray", false) || _config.getBoolean("wrapper.jmx", false)) + registerMBean(); + + // if we need a try -> start asynch hessian jmx remote service & create + // a tray proxy for displaying messages + if (_config.getBoolean("wrapper.tray", false)) + { + startAhessianService(); + _trayIconMessages = new TrayIconProxy(); + } + + // if we are not running as a sevice -> spawn the tray icon as a + // separate process + if ((!_reconnecting) && _config.getBoolean("wrapper.tray", false) && _trayIconProcess == null && !isService() && _config.getBoolean("wrapper.tray.spawn_process", true)) + { + _trayIconProcess = WrapperTrayIconFactory.startTrayIconProcess(_config, getWrapperLogger()); + } + + // if jmx is required -> start jmx rmi remote service + if (_config.getBoolean("wrapper.jmx", false)) + startJMXRmiService(); + + // -> redo if we reload the configuration + configStateChangeListeners(); + configShutdownHook(); + + String clusterScript = _config.getString("wrapper.windows.cluster.script", null); + configClusterScript(clusterScript); + cleanupTmp(); + _init = true; + + } + + private void cleanupTmp() + { + if (_config != null && _config.getBoolean("wrapper.cleanup_tmp", true) && (_tmpPath != null)) + { + File t = new File(_tmpPath); + if (t.exists()) + { + cleanupFolder(t); + } + } + } + + private void cleanupFolder(File t) + { + File[] files = t.listFiles(); + for (File f : files) + { + if (f.getName().startsWith("err_")) + f.delete(); + else if (f.getName().startsWith("in_")) + f.delete(); + else if (f.getName().startsWith("out_")) + f.delete(); + } + } + + private void configClusterScript(String clusterScript) + { + if (clusterScript != null && !"".equals(clusterScript)) + { + List args = _config.getList("wrapper.windows.cluster.script.args", new ArrayList()); + int timeout = _config.getInt("wrapper.windows.cluster.script.timeout", 0); + final Script script = ScriptFactory.createScript(clusterScript, "", this, args, getInternalWrapperLogger(), timeout, _config.getString("wrapper.script.encoding"), _config.getBoolean("wrapper.script.reload", false), _debug); + if (script == null) + return; + try + { + Class clazz = this.getClass().getClassLoader().loadClass("org.rzo.yajsw.os.ms.win.w32.Cluster"); + _cluster = clazz.newInstance(); + _clusterListener = new ClusterNodeChangeListener() + { + public void nodeChanged() + { + script.execute(); + } + }; + Method m = clazz.getMethod("addNodeChangeListener", ClusterNodeChangeListener.class); + m.invoke(_cluster, _clusterListener); + } + catch (Exception e) + { + e.printStackTrace(); + } + + } + } + + private void startCluster() + { + try + { + Class clazz = this.getClass().getClassLoader().loadClass("org.rzo.yajsw.os.ms.win.w32.Cluster"); + _clusterTriggered = true; + _clusterListener.nodeChanged(); + Method m = clazz.getMethod("start"); + m.invoke(_cluster); + } + catch (Exception e) + { + e.printStackTrace(); + } + + } + + private void configShutdownHook() + { + // on windows services: shutdown is handeled by the onStop method not by + // the hook + if (OperatingSystem.instance().getOperatingSystemName().toLowerCase().startsWith("windows") && isService()) + return; + if (_haltAppOnWrapper) + { + Thread hook = new Thread() + { + public void run() + { + getWrapperLogger().info("Shutting down Wrapper"); + if (!_exiting) + { + setExiting(); + AbstractWrappedProcess.this.stop("WRAPPER SHUTDOWN"); + AbstractWrappedProcess.this.shutdown(); + } + // give eventually running scripts time to terminate + try + { + Thread.sleep(5000); + } + catch (InterruptedException e) + { + e.printStackTrace(); + Thread.currentThread().interrupt(); + } + } + + }; + try + { + Runtime.getRuntime().addShutdownHook(hook); + // remember the hook so that we can remove it to avoid mem leaks + _shutdownHooks.add(hook); + } + catch (IllegalStateException ex) + { + // ignore if we are already shutting down the jvm. + // this may be the case if we are configuring a stopper process from within a shutdown hook. + } + } + + + } + + private void configStateChangeListeners() + { + Iterator listenersIterator = _config.getKeys("wrapper.script"); + // remove existing listeners. these may have changed due to config reload + List listeners = new ArrayList(); + for (Iterator it = listenersIterator; it.hasNext();) + listeners.add((String)it.next()); + Collections.sort(listeners, new AlphanumComparator()); + _listeners.clear(); + _listeners.putAll(_userListeners); + for (String key : listeners) + { + if (!key.endsWith(".args") && !key.endsWith(".encoding")&& !key.endsWith(".reload")&& !key.endsWith(".timeout")) + { + String value = _config.getString(key); + List args = _config.getList(key + ".args", new ArrayList()); + int timeout = _config.getInt(key + ".timeout", 0); + + String state = key.substring(key.lastIndexOf(".") + 1); + final Script script = ScriptFactory.createScript(value, state, this, args, getInternalWrapperLogger(), timeout, _config.getString("wrapper.script.encoding"), _config.getBoolean("wrapper.script.reload", false), _debug); + int iState = toIntState(state); + if (iState >= 0 && script != null) + addStateChangeListenerInternal(iState, new StateChangeListener() + { + + public void stateChange(int newState, int oldState) + { + script.executeWithTimeout(); + } + + }); + } + } + + if (_haltWrapperOnApp) + addStateChangeListenerInternal(STATE_IDLE, new StateChangeListener() + { + public void stateChange(int newState, int oldState) + { + if (_exiting) + return; + _exiting = true; + executor.execute(new Runnable() + { + + public void run() + { + if (_stopper) + return; + // if this is a service: do not exit here so that we + // can inform the service controller + if (!isService()) + System.exit(0); + else + { + Object service = getService(); + if (service != null) + { + // windows service + getWrapperLogger().info("calling onStop"); + ((StopableService) service).onStop(); + ((StopableService) service).waitOnStop(); + Runtime.getRuntime().halt(0); + } + else if (isService() && _haltWrapperOnApp) + { + // posix service + try + { + Thread.sleep(5000); + } + catch (InterruptedException e) + { + // TODO Auto-generated catch block + e.printStackTrace(); + } + Runtime.getRuntime().halt(0); + } + } + } + + }); + } + }); + + } + + private int toIntState(String state) + { + if ("START".equals(state)) + return STATE_STARTING; + else if ("RUN".equals(state)) + return STATE_RUNNING; + else if ("RESTART".equals(state)) + return STATE_RESTART; + else if ("STOP".equals(state)) + return STATE_USER_STOP; + else if ("ABORT".equals(state)) + return STATE_ABORT; + else if ("SHUTDOWN".equals(state)) + return STATE_SHUTDOWN; + else if ("IDLE".equals(state)) + return STATE_IDLE; + else + return -1; + } + + private void startJMXRmiService() + { + try + { + int port = _config.getInt("wrapper.jmx.rmi.port", Constants.DEFAULT_RMI_PORT); + if (port > 0) + { + Registry rmiRegistry = LocateRegistry.createRegistry(port); + JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:" + port + "/server"); + Map environment = null; + if (_config.getString("wrapper.jmx.rmi.user", null) != null) + { + final String myUser = _config.getString("wrapper.jmx.rmi.user"); + final String myPassword = _config.getString("wrapper.jmx.rmi.password", ""); + environment = new HashMap(); + JMXAuthenticator authenticator = new JMXAuthenticator() + { + + public Subject authenticate(Object credentials) + { + if (!(credentials instanceof String[])) + throw new SecurityException("Bad credentials"); + String[] creds = (String[]) credentials; + if (creds.length != 2) + throw new SecurityException("Bad credentials"); + + String user = creds[0]; + String password = creds[1]; + + if (password == null) + password = ""; + + if (!myUser.equals(user)) + throw new SecurityException("Unknown user " + user); + if (!myPassword.equals(password)) + throw new SecurityException("Bad password"); + + Set principals = new HashSet(); + principals.add(new JMXPrincipal(user)); + return new Subject(true, principals, Collections.EMPTY_SET, Collections.EMPTY_SET); + } + + }; + environment.put(JMXConnectorServer.AUTHENTICATOR, authenticator); + } + + JMXConnectorServer cs = JMXConnectorServerFactory.newJMXConnectorServer(url, environment, _mbeanServer); + cs.start(); + + } + } + catch (Exception ex) + { + ex.printStackTrace(); + } + + } + + private void startAhessianService() + { + if (_ahessianServer != null) + return; + String canonName; + try + { + canonName = new File(_config.getString("wrapper.config")).getCanonicalPath(); + _ahessianServer = new AHessianJmxServer(_mbeanServer, "+n:localhost, -n:*", canonName, _config.getInt("wrapper.tray.port", 0), getWrapperLogger()); + } + catch (IOException e) + { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + private void registerMBean() + { + ArrayList servers = MBeanServerFactory.findMBeanServer(null); + try + { + if (servers != null && servers.size() > 0) + _mbeanServer = (MBeanServer) servers.get(0); + if (_mbeanServer == null) + _mbeanServer = MBeanServerFactory.createMBeanServer(); + if (_mbeanServer != null) + { + String name = _config.getString("wrapper.console.title"); + if (name == null) + name = _config.getString("wrapper.ntservice.name"); + if (name == null) + name = "yajsw.noname"; + ObjectName oName = new ObjectName("org.rzo.yajsw", "name", name); + _mbeanServer.registerMBean(this, oName); + } + else + getWrapperLogger().severe("ERROR: no mbean server found "); + } + catch (Exception e) + { + e.printStackTrace(); + } + + } + + /** + * Gets the priority. + * + * @param priority + * the priority + * + * @return the priority + */ + protected int getPriority(String priority) + { + + if ("LOW".equals(priority)) + return Process.PRIORITY_LOW; + else if ("BELOW_NORMAL".equals(priority)) + return Process.PRIORITY_BELOW_NORMAL; + else if ("NORMAL".equals(priority)) + return Process.PRIORITY_NORMAL; + else if ("ABOVE_NORMAL".equals(priority)) + return Process.PRIORITY_ABOVE_NORMAL; + else if ("HIGH".equals(priority)) + return Process.PRIORITY_HIGH; + return Process.PRIORITY_UNDEFINED; + } + + /** + * Gets the affinity. + * + * @param affinity + * the affinity + * + * @return the affinity + */ + protected int getAffinity(String affinity) + { + try + { + return Integer.parseInt(affinity); + } + catch (Exception ex) + { + ex.printStackTrace(); + return 0; + } + } + + /** + * Exit code restart. + * + * @return true, if successful + */ + protected boolean exitCodeRestart() + { + if (_state == STATE_USER_STOP || _state == STATE_SHUTDOWN) + return false; + if (_startupExitCodes.contains(_osProcess.getExitCode())) + { + getWrapperLogger().info("restart process due to exit code rule"); + return true; + } + else if (_exitCodeDefaultRestart) + { + getWrapperLogger().info("restart process due to default exit code rule"); + return true; + } + + return false; + } + + /** + * Exit code shutdown. + * + * @return true, if successful + */ + protected boolean exitCodeShutdown() + { + if (_shutdownExitCodes.contains(_osProcess.getExitCode())) + { + getWrapperLogger().info("shutdown wrapper due to exit code rule"); + return true; + } + + return false; + } + + protected boolean exitCodeStop() + { + if (_stopExitCodes.contains(_osProcess.getExitCode())) + { + getWrapperLogger().info("stop process due to exit code rule"); + return true; + } + + return false; + } + + /** + * Sets the state. + * + * @param state + * the new state + */ + protected void setState(int state) + { + int oldState = _state; + if (_state != state) + { + if (_debug) + getWrapperLogger().info("set state " + getStringState(_state) + "->" + getStringState(state)); + _state = state; + // unset user color so we can display the state color + // if user wants other color for state, he can set it in a script. + if (_trayIconMessages != null) + { + _trayIconMessages.setUserColor(null); + if (state == STATE_IDLE) + _trayIconMessages.messages.clear(); + } + if (state == STATE_IDLE) + { + removeLockFile(); + } + Collection listeners = (Collection) _listeners.get(_state); + Collection listeners999 = (Collection) _listeners.get(999); + Collection allListeners = new HashSet(); + if (listeners != null) + allListeners.addAll(listeners); + if (listeners999 != null) + allListeners.addAll(listeners999); + + for (Iterator it = allListeners.iterator(); it.hasNext();) + { + StateChangeListener listener = (StateChangeListener) it.next(); + if (listener != null) + listener.stateChange(state, oldState); + } + if (_state == STATE_IDLE && _startRequested) + { + executor.execute(new Runnable() + { + public void run() + { + start(); + } + }); + + } + } + } + + /** + * String state. + * + * @param state + * the state + * + * @return the string + */ + static public String getStringState(int state) + { + switch (state) + { + case STATE_IDLE: + return "IDLE"; + case STATE_RESTART: + return "RESTART"; + case STATE_RESTART_START: + return "RESTART_START"; + case STATE_RESTART_STOP: + return "RESTART_STOP"; + case STATE_RESTART_WAIT: + return "RESTART_WAIT"; + case STATE_RUNNING: + return "RUNNING"; + case STATE_STARTING: + return "STARTING"; + case STATE_STOP: + return "STOP"; + case STATE_USER_STOP: + return "STATE_USER_STOP"; + case STATE_ABORT: + return "STATE_ABORT"; + case STATE_SHUTDOWN: + return "STATE_SHUTDOWN"; + default: + return "?"; + } + } + + /** The _start by timer. */ + boolean _startByTimer = false; + + /** + * Start by timer. + */ + public synchronized void startByTimer() + { + _startByTimer = true; + start(); + _startByTimer = false; + } + + public synchronized void start() + { + if (!_init) + init(); + if (_debug) + getWrapperLogger().info("start from Thread " + Thread.currentThread().getName()); + + _startRequested = true; + _stopRequested = false; + if (_state != STATE_IDLE) + { + getWrapperLogger().info("Process not IDLE -> Delaying start request"); + } + startInternal(); + } + + /** + * Start. + */ + public synchronized void startInternal() + { + setAppReportedReady(false); + if (!saveLockFile()) + return; + savePidFile(); + cleanupTmp(); + if (_timer.isHasTrigger() && !_timer.isTriggered()) + _timer.start(); + if (_condition.isHasTrigger() && !_condition.isTriggered()) + { + _condition.start(); + return; + } + if (!_timer.isStartImmediate() && !_startByTimer) + return; + + if (_cluster != null && !_clusterTriggered) + { + startCluster(); + return; + } + + _startRequested = false; + if (_state == STATE_IDLE) + setState(STATE_STARTING); + else if (_state == STATE_RESTART_WAIT) + setState(STATE_RESTART_START); + else + return; + + if (_shutdownHooks.isEmpty()) + configShutdownHook(); + + long startTimeout = _config.getLong("wrapper.startup.delay", 0); + if (_state == STATE_STARTING && startTimeout > 0) + { + try + { + getWrapperLogger().info("startup delay " + startTimeout + "sec"); + Thread.sleep(startTimeout * 1000); + if (_stopRequested) + { + setState(STATE_IDLE); + return; + } + } + catch (InterruptedException e) + { + e.printStackTrace(); + Thread.currentThread().interrupt(); + } + } + + if (_debug) + getWrapperLogger().info("starting Process"); + + if (_wrapperStarted == null) + _wrapperStarted = new Date(); + + if (_config.getBoolean("wrapper.restart.reload_configuration", DEFAULT_RELOAD_CONFIGURATION)) + { + reloadConfiguration(); + configStateChangeListeners(); + } + + if (_debug) + getWrapperLogger().info("starting controller"); + if (_controller != null) + { + // release resources if controller was running before this call to + // start + // otherwise resources will be released by gc -> finalize + _controller.reset(); + _controller.setDebug(isDebug()); + _controller.setLogger(getWrapperLogger()); + configController(); + if (!_controller.start()) + { + getWrapperLogger().info("could not start controller -> abort"); + setState(STATE_ABORT); + setState(STATE_IDLE); + return; + } + else if (_debug) + getWrapperLogger().info("controller started"); + + try + { + Thread.sleep(100); + } + catch (InterruptedException e) + { + e.printStackTrace(); + Thread.currentThread().interrupt(); + } + } + if (_osProcess == null) + { + createOSProcess(); + } + else + { + // release resources if _osProcess was running before this call to + // start + // otherwise resources will be released by gc -> finalize + if (_debug) + getWrapperLogger().info("_osProcess destroyed"); + _osProcess.destroy(); + } + configProcess(); + _firstRestartTime = System.currentTimeMillis(); + // _restartCount++; + Map triggerActions = getTriggerActions(); + Map regexTriggerActions = getRegexTriggerActions(); + Map missingTriggerActions = getMissingTriggerActions(); + Map missingRegexTriggerActions = getMissingRegexTriggerActions(); + _osProcess.setLogger(getWrapperLogger()); + _exitCode = -3; + if (_debug) + getWrapperLogger().info("spawning wrapped process"); + _controller.beginWaitForStartup(); + if (_osProcess.start()) + { + _controller.processStarted(); + _totalRestartCount++; + postStart(); + /* + * bkowal + * Suppress extraneous output unless debug is enabled. + */ + if (_debug) + { + getWrapperLogger().info("started process with pid " + _osProcess.getPid()); + } + if (pipeStreams()) + { + + _gobler_in = new Gobler(_osProcess.getInputStream(), getAppLogger(), triggerActions, regexTriggerActions, missingTriggerActions, + missingRegexTriggerActions, "OUTPUT " + _osProcess.getPid(), _osProcess.getPid()); + _gobler_err = new Gobler(_osProcess.getErrorStream(), getAppLogger(), triggerActions, regexTriggerActions, missingTriggerActions, + missingRegexTriggerActions, "ERROR " + _osProcess.getPid(), _osProcess.getPid()); + executor.execute(_gobler_err); + executor.execute(_gobler_in); + } + if (getState() != STATE_IDLE && getState() != STATE_RESTART) + { + _appStarted = new Date(); + setState(STATE_RUNNING); + updateAppLoggerName(); + } + } + else + { + getWrapperLogger().severe("failed to spawn wrapped process"); + _controller.processFailed(); + } + + // win 64 test + // WindowsXPProcess.getProcess(getPid()); + + } + + protected void reloadConfiguration() + { + { + Map utils = new HashMap(); + utils.put("util", new Utils(this)); + _config = new YajswConfigurationImpl(_localConfiguration, _useSystemProperties, utils); + + getWrapperLogger().info("reloaded configuration "); + } + } + + abstract void configController(); + + abstract void postStart(); + + void configProcess() + { + String priority = _config.getString("wrapper.priority"); + if (priority != null) + _osProcess.setPriority(getPriority(priority)); + + String affinity = _config.getString("wrapper.affinity"); + if (affinity != null) + _osProcess.setCpuAffinity(getAffinity(affinity)); + + String title = _config.getString("wrapper.console.title"); + if (title != null) + _osProcess.setTitle(title); + + _osProcess.setVisible(_config.getBoolean("wrapper.console.visible", Constants.DEFAULT_CONSOLE_VISIBLE) + && !_config.getBoolean("wrapper.service", false)); + _osProcess.setMinimized(_config.getBoolean("wrapper.console.minimized", Constants.DEFAULT_CONSOLE_MINIMIZED) + && !_config.getBoolean("wrapper.service", false)); + _osProcess.setUser(_config.getString("wrapper.app.account")); + _osProcess.setPassword(_config.getString("wrapper.app.password")); + + _osProcess.setDebug(_debug); + + String workingDir = _config.getString("wrapper.working.dir", "."); + if (workingDir != null) + { + File wd = new File(workingDir); + if (!wd.exists() || !wd.isDirectory()) + getWrapperLogger().warning("working directory " + workingDir + " not found"); + else + _osProcess.setWorkingDir(wd.getAbsolutePath()); + getWrapperLogger().info("working dir " + wd.getAbsolutePath()); + } + _osProcess.setEnvironment(getProcessEnvironment(_config)); + if (Platform.isWindows() && PlatformEx.isWinVista() && _config.getBoolean("wrapper.service", false) && _config.getBoolean("wrapper.ntservice.logon_active_session", false)) + { + _osProcess.setLogonActiveSession(true); + if (_debug) + getWrapperLogger().info("setLogonActiveSession"); + if (!_config.getBoolean("wrapper.ntservice.autoreport.startup", true)) + getWrapperLogger().warning("WARNING: do not set autoreport.startup & wrapper.ntservice.logon_active_session"); + if (_config.getString("wrapper.ntservice.account", null) != null) + getWrapperLogger().warning("WARNING: do not set wrapper.ntservice.account & wrapper.ntservice.logon_active_session"); + if (_config.getString("wrapper.app.account", null) != null) + getWrapperLogger().warning("WARNING: do not set wrapper.app.account & wrapper.ntservice.logon_active_session"); + } + String desktop = _config.getString("wrapper.ntservice.desktop", null); + if (Platform.isWindows() && PlatformEx.isWinVista() && _config.getBoolean("wrapper.service", false) && desktop != null) + _osProcess.setDesktop(desktop); + + + + } + + private List getProcessEnvironment(YajswConfigurationImpl config) + { + // if user did not set env properties: use default. + if (!config.getKeys("wrapper.app.env").hasNext()) + { + return null; + } + // get env. of this process from java + Map jEnv = (Map) (Platform.isWindows() ? new CaseInsensitiveMap(System.getenv()) : new HashMap(System.getenv())); + // overwrite with user settings + for (Iterator keys = config.getKeys("wrapper.app.env"); keys.hasNext();) + { + String key = (String) keys.next(); + String value = config.getString(key); + jEnv.put(key.substring("wrapper.app.env.".length()), value); + } + // change to string pair format + List result = new ArrayList(); + for (Entry entry : jEnv.entrySet()) + { + result.add(new String[]{entry.getKey(), entry.getValue()}); + } + return result; + } + +/* private List getProcessEnvironment(YajswConfigurationImpl config) + { + if (!config.getKeys("wrapper.app.env").hasNext()) + { + //getWrapperLogger().info("env: no yajsw env"); + return null; + } + List env = OperatingSystem.instance().processManagerInstance().getProcess( + OperatingSystem.instance().processManagerInstance().currentProcessId()).getEnvironment(); + for (Iterator keys = config.getKeys("wrapper.app.env"); keys.hasNext();) + { + String key = (String) keys.next(); + String value = config.getString(key); + String envKey = key.substring("wrapper.app.env.".length()); + //getWrapperLogger().info("env: "+envKey+"="+value); + updateEnvKey(envKey, value, env); + } + return env; + } + + private void updateEnvKey(String envKey, String value, List env) + { + String[] entry = findEnvEntry(envKey, env); + if (entry != null) + entry[1] = value; + else + env.add(new String[] + { envKey, value }); + } + + private String[] findEnvEntry(String envKey, List env) + { + for (String[] entry : env) + if (envKeyEqual(envKey, entry[0])) + return entry; + return null; + } + + private boolean envKeyEqual(String envKey1, String envKey2) + { + if (Platform.isWindows()) + return envKey1.toLowerCase().equals(envKey2.toLowerCase()); + else + return envKey1.equals(envKey2); + + } + */ + + /** + * Pipe streams. + * + * @return true, if successful + */ + protected boolean pipeStreams() + { + return _config.getBoolean("wrapper.console.pipestreams",false); + + } + + /** The _file handler. */ + Handler _fileHandler; + /** The _console handler. */ + Handler _consoleHandler; + + /** + * Restart log file. + */ + void restartLogFile() + { + if (_fileHandler == null) + return; + String rollMode = _config.getString("wrapper.logfile.rollmode", ""); + boolean append = !(rollMode.contains("WRAPPER") || rollMode.contains("JVM")); + if (!append && _appLogger != null) + { + _fileHandler.close(); + getFileHandler(); + } + } + + /** + * Gets the console handler. + * + * @return the console handler + */ + public Handler getConsoleHandler() + { + if (_consoleHandler != null) + return _consoleHandler; + + String consoleLogLevel = _config.getString("wrapper.console.loglevel", "INFO"); + if (consoleLogLevel.equals("NONE")) + return null; + + // per default java console handler uses err -> use out instead + _consoleHandler = new ConsoleHandler() + { + protected synchronized void setOutputStream(OutputStream out) throws SecurityException + { + super.setOutputStream(System.out); + } + }; + _consoleHandler.setFormatter(getConsoleFormatter()); + _consoleHandler.setLevel(getLogLevel(consoleLogLevel)); + + String encoding = _config.getString("wrapper.log.encoding"); + if (encoding != null) + try + { + _consoleHandler.setEncoding(encoding); + } + catch (Exception e) + { + e.printStackTrace(); + } + + + return _consoleHandler; + } + + /** + * Gets the file handler. + * + * @return the file handler + */ + public Handler getFileHandler() + { + if (_fileHandler != null) + return _fileHandler; + + String fileName = getLogFile(); + String fileLogLevel = _config.getString("wrapper.logfile.loglevel", "INFO"); + if ((fileName.equals("") || fileLogLevel.equals("NONE"))) + return null; + + /* parent folder will be created when creating the handler + File f = new File(fileName).getParentFile(); + if (!f.exists()) + try + { + f.mkdirs(); + } + catch (Exception ex) + { + ex.printStackTrace(); + } + */ + + try + { + String rollMode = _config.getString("wrapper.logfile.rollmode", ""); + boolean append = !(rollMode.contains("WRAPPER") || rollMode.contains("JVM")); + int count = _config.getInt("wrapper.logfile.maxfiles", 0); + int limit = getLogLimit(); + if (count == 0 && limit > 0) + count = 16192; + else if (count == 0) + count = 1; + boolean rollDate = "DATE".equals(rollMode); + String encoding = _config.getString("wrapper.log.encoding"); + int maxDays = _config.getInt("wrapper.logfile.maxdays", -1); + _fileHandler = fileName.contains("%d") ? new DateFileHandler(fileName, limit, count, append, rollDate, getFileFormatter(), getLogLevel(fileLogLevel), encoding, maxDays) : new MyFileHandler(fileName, limit, count, + append, getFileFormatter(), getLogLevel(fileLogLevel), encoding); + } + catch (Exception e) + { + e.printStackTrace(); + } + + return _fileHandler; + + } + + /** + * Update app logger name. + */ + private void updateAppLoggerName() + { + if (_appLogger == null) + return; + _appLoggerPid = getAppPid() + "/" + _restartCount; + ((MyLogger) _appLogger).setPID(_appLoggerPid); + _appLoggerName = getName() == null ? "" : getName(); + ((MyLogger) _appLogger).setName(_appLoggerName); + } + + /** + * Gets the app logger. + * + * @return the app logger + */ + protected Logger getAppLogger() + { + if (_appLogger != null) + return _appLogger; + + if (_appLogger == null) + { + _appLogger = new MyLogger(); + updateAppLoggerName(); + _appLogger.setUseParentHandlers(false); + if (_controller != null) + _controller.setLogger(_appLogger); + } + if (getFileHandler() != null) + _appLogger.addHandler(getFileHandler()); + if (getConsoleHandler() != null) + _appLogger.addHandler(getConsoleHandler()); + _appLogger.setLevel(Level.ALL); + return _appLogger; + } + + /** + * Gets the log level. + * + * @param logLevel + * the log level + * + * @return the log level + */ + private Level getLogLevel(String logLevel) + { + if (logLevel.equals("INFO")) + return Level.ALL; + else if (logLevel.equals("FATAL")) + return Level.SEVERE; + else if (logLevel.equals("ERROR")) + return Level.WARNING; + else if (logLevel.equals("STATUS")) + return Level.INFO; + + return Level.INFO; + } + + /** + * Gets the log limit. + * + * @return the log limit + */ + private int getLogLimit() + { + String res = _config.getString("wrapper.logfile.maxsize"); + String units = ""; + if (res == null) + return 0; + res = res.toLowerCase(); + if (res.endsWith("m")) + { + res = res.substring(0, res.length() - 1); + units = "m"; + } + else if (res.endsWith("k")) + { + res = res.substring(0, res.length() - 1); + units = "k"; + } + int result = Integer.parseInt(res); + if (units.equals("m")) + result = result * 1024 * 1024; + else if (units.equals("k")) + result = result * 1024; + return result; + } + + /** + * Gets the file formatter. + * + * @return the file formatter + */ + private PatternFormatter getFileFormatter() + { + String wFormat = _config.getString("wrapper.logfile.format", Constants.DEFAULT_LOG_FORMAT); + return getFormatter(wFormat); + + } + + /** + * Gets the console formatter. + * + * @return the console formatter + */ + private java.util.logging.Formatter getConsoleFormatter() + { + String wFormat = _config.getString("wrapper.console.format", Constants.DEFAULT_LOG_FORMAT); + return getFormatter(wFormat); + } + + /** + * Gets the formatter. + * + * @param wFormat + * the w format + * + * @return the formatter + */ + private PatternFormatter getFormatter(String wFormat) + { + PatternFormatter formatter = new PatternFormatter(); + String pattern = ""; + if (wFormat.contains("Z")) + { + formatter.setTimeFormat("yy-MM-dd HH:mm:ss.SS"); + } + else + { + formatter.setTimeFormat("yy-MM-dd HH:mm:ss"); + } + for (int i = 0; i < wFormat.length(); i++) + { + char c = wFormat.charAt(i); + if (i > 0 && c != '\r' && c != '\n') + pattern += "|"; + switch (c) + { + case 'L': + pattern += "%LEVEL%"; + break; + case 'P': + pattern += "%PARAM0%"; + break; + case 'N': + pattern += "%PARAM1%"; + break; + case 'T': + case 'Z': + pattern += "%TIME%"; + break; + case 'M': + pattern += "%MESSAGE%"; + break; + default: + ; + } + } + if (wFormat.endsWith("\r\n")) + pattern += "\r\n"; + else if (wFormat.endsWith("\r")) + pattern += "\r"; + else + pattern += System.getProperty("line.separator"); + formatter.setLogPattern(pattern); + return formatter; + } + + /** + * Gets the log file. + * + * @return the log file + */ + private String getLogFile() + { + String result = _config.getString("wrapper.logfile", "log/wrapper.log"); + File r = new File(result); + File f = null; + if (!r.isAbsolute()) + { + String wDir = _config.getString("wrapper.working.dir", "."); + f = new File(wDir, result); + } + else + f = new File(result); + // parent folder will be created in the handler + //if (!f.getParentFile().exists()) + // f.getParentFile().mkdirs(); + result = f.getAbsolutePath(); + if (result.contains("ROLLNUM")) + result = result.replace("ROLLNUM", "%g"); + //else + // result = result + ".%g"; + if (result.contains("YYYYMMDD")) + result = result.replace("YYYYMMDD", "%d"); + return result; + } + + protected Map getMissingTriggerActions() + { + Map result = new HashMap(); + for (Iterator it = _config.getKeys("wrapper.filter.missing.trigger"); it.hasNext();) + { + String tKey = (String) it.next(); + List lValue = _config.getList(tKey); + if (lValue == null || lValue.size() != 3) + { + getWrapperLogger().info("check parameters for " + tKey); + continue; + } + String tValue = (String) lValue.get(0); + // commons configuration does no accept , 1, 1 as valid list + // -> using * instead of space + if ("*".equals(tValue)) + tValue = ""; + int countValue = -1; + try + { + countValue = Integer.parseInt((String) lValue.get(1)); + } + catch (Exception ex) + { + getWrapperLogger().log(Level.SEVERE, "check parameters for " + tKey, ex); + } + long periodValue = -1; + try + { + periodValue = Long.parseLong((String) lValue.get(2)) * 1000; + } + catch (Exception ex) + { + getWrapperLogger().log(Level.SEVERE, "check parameters for " + tKey, ex); + } + String tName = tKey.substring("wrapper.filter.missing.trigger.".length()); + boolean autoStop = _config.getBoolean("wrapper.filter.missing.autostop."+tName, true); + String aKey = "wrapper.filter.missing.action." + tName; + String aValue = _config.getString(aKey, ""); + Object action = getTriggerAction(aValue); + String sKey = "wrapper.filter.missing.script." + tName; + String sValue = _config.getString(sKey, ""); + List args = _config.getList(sKey + ".args", null); + int timeout = _config.getInt(sKey + ".timeout", 0); + String[] strArgs = null; + if (args != null && args.size() > 0) + { + strArgs = new String[args.size()]; + for (int i = 0; i < strArgs.length; i++) + strArgs[i] = args.get(i).toString(); + } + Object script = getTriggerScript(sValue, tKey.substring(tKey.lastIndexOf('.')), strArgs, timeout); + if (action != null || script != null) + { + result.put(tValue, new MissingTriggerAction(executor, periodValue, countValue, new TriggerAction[] + { (TriggerAction) script, (TriggerAction) action }, autoStop, getWrapperLogger())); + } + } + return result; + } + + /** + * Gets the regex trigger actions. + * + * @return the regex trigger actions + */ + protected Map getMissingRegexTriggerActions() + { + Map result = new HashMap(); + for (Iterator it = _config.getKeys("wrapper.filter.missing.trigger-regex"); it.hasNext();) + { + String tKey = (String) it.next(); + List lValue = _config.getList(tKey); + if (lValue == null || lValue.size() != 3) + { + getWrapperLogger().info("check parameters for " + tKey); + continue; + } + String tValue = (String) lValue.get(0); + int countValue = -1; + try + { + countValue = Integer.parseInt((String) lValue.get(1)); + } + catch (Exception ex) + { + getWrapperLogger().log(Level.SEVERE, "check parameters for " + tKey, ex); + } + long periodValue = -1; + try + { + getWrapperLogger().info("check parameters for " + tKey); + periodValue = Long.parseLong((String) lValue.get(2)) * 1000; + } + catch (Exception ex) + { + getWrapperLogger().log(Level.SEVERE, "check parameters for " + tKey, ex); + } + String tName = tKey.substring("wrapper.filter.missing.trigger-regex.".length()); + boolean autoStop = _config.getBoolean("wrapper.filter.missing.autostop."+tName, true); + String aKey = "wrapper.filter.missing.action." + tName; + String aValue = _config.getString(aKey, ""); + Object action = getTriggerAction(aValue); + String sKey = "wrapper.filter.missing.script." + tName; + String sValue = _config.getString(sKey, ""); + List args = _config.getList(sKey + ".args", null); + int timeout = _config.getInt(sKey + ".timeout", 0); + String[] strArgs = null; + if (args != null && args.size() > 0) + { + strArgs = new String[args.size()]; + for (int i = 0; i < strArgs.length; i++) + strArgs[i] = args.get(i).toString(); + } + Object script = getTriggerScript(sValue, tKey.substring(tKey.lastIndexOf('.')), strArgs, timeout); + if (action != null || script != null) + { + result.put(tValue, new MissingTriggerAction(executor, periodValue, countValue, new TriggerAction[] + { (TriggerAction) script, (TriggerAction) action }, autoStop, getWrapperLogger())); + } + } + return result; + } + + /** + * Gets the trigger actions. + * + * @return the trigger actions + */ + protected Map getTriggerActions() + { + Map result = new HashMap(); + List configList = new ArrayList(); + for (Iterator it = _config.getKeys("wrapper.filter.trigger"); it.hasNext();) + { + configList.add(it.next()); + } + Collections.sort(configList, new AlphanumComparator()); + + for (Iterator it = configList.listIterator(); it.hasNext();) + { + String tKey = (String) it.next(); + String tValue = _config.getString(tKey); + if (tValue == null || tValue.length() == 0) + continue; + String tName = tKey.substring("wrapper.filter.trigger.".length()); + String aKey = "wrapper.filter.action." + tName; + String aValue = _config.getString(aKey, ""); + Object action = getTriggerAction(aValue); + String sKey = "wrapper.filter.script." + tName; + String sValue = _config.getString(sKey, ""); + List args = _config.getList(sKey + ".args", null); + int timeout = _config.getInt(sKey + ".timeout", 0); + String[] strArgs = null; + if (args != null && args.size() > 0) + { + strArgs = new String[args.size()]; + for (int i = 0; i < strArgs.length; i++) + strArgs[i] = args.get(i).toString(); + } + Object script = getTriggerScript(sValue, tKey.substring(tKey.lastIndexOf('.')), strArgs, timeout); + if (action != null && script != null) + { + addToActionMap(result, tValue, Arrays.asList(new Object[] + { script, action })); + } + else if (action != null) + addToActionMap(result, tValue, action); + else if (script != null) + addToActionMap(result, tValue, script); + else + addToActionMap(result, aKey, "RESTART"); + } + return result; + } + + private void addToActionMap(Map actionsMap, String key, Object value) + { + Object c = actionsMap.get(key); + if (c == null) + { + actionsMap.put(key, value); + } + else if (c instanceof Collection && value instanceof Collection) + { + ArrayList l = new ArrayList(); + l.addAll((Collection) c); + l.addAll((Collection) value); + actionsMap.put(key, l); + } + else if (c instanceof Collection && !(value instanceof Collection)) + { + ArrayList l = new ArrayList(); + l.addAll((Collection) c); + l.add(value); + actionsMap.put(key, l); + } + else if (!(c instanceof Collection) && value instanceof Collection) + { + ArrayList l = new ArrayList(); + l.add((Collection) c); + l.addAll((Collection) value); + actionsMap.put(key, l); + } + else + // c is not a collection && value is not a collection + { + actionsMap.put(key, Arrays.asList(new Object[] + { c, value })); + } + + } + + /** + * Gets the regex trigger actions. + * + * @return the regex trigger actions + */ + protected Map getRegexTriggerActions() + { + Map result = new HashMap(); + for (Iterator it = _config.getKeys("wrapper.filter.trigger-regex"); it.hasNext();) + { + String tKey = (String) it.next(); + String tValue = _config.getString(tKey); + if (tValue == null || tValue.length() == 0) + continue; + String tName = tKey.substring("wrapper.filter.trigger-regex.".length()); + String aKey = "wrapper.filter.action." + tName; + String aValue = _config.getString(aKey, ""); + Object action = getTriggerAction(aValue); + String sKey = "wrapper.filter.script." + tName; + String sValue = _config.getString(sKey, ""); + List args = _config.getList(sKey + ".args", null); + int timeout = _config.getInt(sKey + ".timeout", 0); + String[] strArgs = null; + if (args != null && args.size() > 0) + { + strArgs = new String[args.size()]; + for (int i = 0; i < strArgs.length; i++) + strArgs[i] = args.get(i).toString(); + } + + Object script = getTriggerScript(sValue, tKey.substring(tKey.lastIndexOf('.')), strArgs, timeout); + if (action != null && script != null) + { + addToActionMap(result, tValue, Arrays.asList(new Object[] + { script, action })); + } + else if (action != null) + addToActionMap(result, tValue, action); + else if (script != null) + addToActionMap(result, tValue, script); + else + addToActionMap(result, aKey, "RESTART"); + } + return result; + } + + /** + * Gets the trigger script. + * + * @param script + * the script + * @param key + * the key + * + * @return the trigger script + */ + private Object getTriggerScript(String script, String key, String[] args, int timeout) + { + if (script == null || "".equals(script)) + return null; + final Script s = ScriptFactory.createScript(script, key, this, args, getInternalWrapperLogger(), timeout, _config.getString("wrapper.script.encoding"), _config.getBoolean("wrapper.script.reload", false), _debug); + if (s == null) + { + this.getWrapperLogger().info("error initializing script " + script); + return null; + } + if (_debug) + this.getWrapperLogger().info("found script " + s.getScript()); + // final String id = key; + + return new TriggerAction() + { + public Object execute(final String line) + { + if (scriptExecutor.getActiveCount() > 20) + { + getInternalWrapperLogger().warn("executing too many scripts concurrently -> aborting script execution"); + return null; + } + scriptExecutor.execute(new Runnable() + { + + public void run() + { + AbstractWrappedProcess.this.getWrapperLogger().info("start script " + s.getScript()); + s.executeWithTimeout(new String(line)); + AbstractWrappedProcess.this.getWrapperLogger().info("end script " + s.getScript()); + } + }); + return null; + } + }; + } + + /** + * Gets the trigger action. + * + * @param value + * the value + * + * @return the trigger action + */ + private Object getTriggerAction(String value) + { + if ("RESTART".equals(value)) + return new TriggerAction() + { + public Object execute(String line) + { + if (allowRestart()) + restartInternal(); + else if (getState() == STATE_RUNNING) + stop("TRIGGER"); + return null; + } + }; + else if ("SHUTDOWN".equals(value)) + return new TriggerAction() + { + public Object execute(String line) + { + stop("TRIGGER"); + return null; + } + }; + + return null; + } + + /** + * Stop timer. + */ + public synchronized void stopTimer() + { + _timer.stop(); + } + + public synchronized void stopCondition() + { + _condition.stop(); + } + + public void stop() + { + stop("USER"); + } + + public void stop(String reason) + { + if (_debug) + getWrapperLogger().info("stop from Thread " + Thread.currentThread().getName()+ " reason: "+reason); + _stopReason = reason; + _startRequested = false; + if (_state != STATE_RUNNING && _state != STATE_IDLE) + { + getWrapperLogger().info("process not in state RUNNING -> Delaying stop"); + _stopRequested = true; + } + stopInternal(); + } + + /** + * Stop. + */ + public synchronized void stopInternal() + { + setAppReportedReady(false); + if (_state == STATE_RESTART) + { + _appStopped = new Date(); + setState(STATE_RESTART_STOP); + _stopReason = "RESTART"; + } + else if (_state == STATE_RUNNING) + { + _appStopped = new Date(); + setState(STATE_USER_STOP); + } + else + return; + + // if (_debug) + long shutdownWaitTime = _config.getInt("wrapper.shutdown.timeout", Constants.DEFAULT_SHUTDOWN_TIMEOUT) * 1000; + shutdownWaitTime += _config.getInt("wrapper.jvm_exit.timeout", Constants.DEFAULT_JVM_EXIT_TIMEOUT) * 1000; + if (shutdownWaitTime > Integer.MAX_VALUE) + shutdownWaitTime = Integer.MAX_VALUE; + /* + * bkowal + * Suppress extraneous output unless debug is enabled. + */ + if (_debug) + { + getWrapperLogger().info( + "stopping process with pid/timeout " + _osProcess.getPid() + " " + shutdownWaitTime); + } + + stopController((int) shutdownWaitTime, _stopReason); + stopOsProcess((int) shutdownWaitTime); + + _appStopped = new Date(); + if (_state == STATE_USER_STOP) + if (!_exiting) + setState(STATE_IDLE); + removeShutdownHooks(); + } + + private void removeShutdownHooks() + { + if (_exiting) + return; + for (Thread hook : _shutdownHooks) + { + Runtime.getRuntime().removeShutdownHook(hook); + } + _shutdownHooks.clear(); + } + + private void stopOsProcess(int shutdownWaitTime) + { + boolean externalStop = false; + String stopConfigName = _config.getString("wrapper.stop.conf"); + File stopConfigFile = null; + if (stopConfigName != null) + { + getWrapperLogger().info("using stop configuration " + stopConfigName); + stopConfigFile = new File(stopConfigName); + stopConfigName = stopConfigFile.getAbsolutePath(); + externalStop = stopConfigFile.isFile() && stopConfigFile.exists(); + if (!externalStop) + getWrapperLogger().severe("error accessing stop configuration "+stopConfigName); + } + WrappedProcess stopper = null; + if (externalStop) + { + getWrapperLogger().info("starting stop application"); + Configuration stopLocalConf = new BaseConfiguration(); + stopLocalConf.setProperty("wrapper.config", stopConfigName); + YajswConfigurationImpl stopConf = new YajswConfigurationImpl(stopLocalConf, _useSystemProperties); + stopper = WrappedProcessFactory.createProcess(stopConf); + stopper.getLocalConfiguration().setProperty("wrapper.config", stopConfigName); + stopper.setUseSystemProperties(_useSystemProperties); + stopper.setStopper(true); + // stopper.setDebug(true); + stopper.init(); + stopper.start(); + } + + // else normally process is stopped by the controller + // _osProcess.stop(shutdownWaitTime, 999); + + if (shutdownWaitTime > 0) + _osProcess.waitFor(shutdownWaitTime); + + long remainStopWait = getRemainStopWaitTime(); + while (_osProcess.isRunning() && remainStopWait > 0) + { + if (_debug) + getAppLogger().info("extending wait time " + remainStopWait); + + if (_service != null) + ((StopableService)_service).signalStopping(remainStopWait); + _osProcess.waitFor(remainStopWait); + remainStopWait = getRemainStopWaitTime(); + } + if (_osProcess.isRunning()) + { + getWrapperLogger().info("process did not stop after " + shutdownWaitTime + " sec. -> hard kill"); + } + _osProcess.kill(999); + + if (stopper != null && stopper.getState() != STATE_IDLE) + stopper.stop(); + + // give the OS some time to clean up + try + { + Thread.sleep(500); + } + catch (InterruptedException e) + { + e.printStackTrace(); + Thread.currentThread().interrupt(); + } + _osProcess.destroy(); + + /* + * bkowal + * Suppress extraneous output unless debug is enabled. + */ + if (_debug) + { + getWrapperLogger().info("process exit code: " + _osProcess.getExitCode()); + } + } + + private long getRemainStopWaitTime() + { + _stoppingHintLock.lock(); + + long d = _stoppingHintSetTime - System.currentTimeMillis(); + long result = _stoppingHint + d; + + _stoppingHintLock.unlock(); + return result; + } + + abstract void stopController(int timeout, String reason); + + /** + * Allow restart. + * + * @return true, if successful + */ + protected boolean allowRestart() + { + if (System.currentTimeMillis() - _firstRestartTime > _successfulInvocationTime) + _restartCount = 0; + if (_state == STATE_USER_STOP || _state == STATE_RESTART || _state == STATE_RESTART_STOP || _state == STATE_RESTART_WAIT) + return false; + if (_restartCount < _config.getInt("wrapper.max_failed_invocations", DEFAULT_MAX_FAILED_INVOCATIONS)) + return true; + getWrapperLogger().info("too many restarts "); + return false; + } + + /** + * Restart. + */ + public void restart() + { + if (_state != STATE_RUNNING) + return; + restartInternal(); + } + + /** + * Restart by timer. + */ + boolean _timerRestart = false; + + public void restartByTimer() + { + if (_timerRestart) + return; + _timerRestart = true; + stop("TIMER"); + try + { + Thread.sleep(getRestartDelay()); + } + catch (InterruptedException e) + { + if (_debug) + getWrapperLogger().log(Level.SEVERE, this.getClass().getName() + " restart", e); + Thread.currentThread().interrupt(); + } + + startByTimer(); + _timerRestart = false; + } + + /** + * Checks if is debug. + * + * @return true, if is debug + */ + boolean isDebug() + { + return _debug; + } + + /** + * Sets the debug. + * + * @param debug + * the new debug + */ + public void setDebug(boolean debug) + { + _debug = debug; + } + + /** + * Gets the pid. + * + * @return the pid + */ + public int getAppPid() + { + if (_osProcess != null) + return _osProcess.getPid(); + else + return -1; + } + + /** + * Save pid file. + */ + private void savePidFile() + { + String file = _config.getString("wrapper.pidfile"); + if (file != null) + { + try + { + _pidFile = new File(file); + if (!_pidFile.exists()) + _pidFile.createNewFile(); + FileWriter out = new FileWriter(_pidFile, false); + out.write("" + OperatingSystem.instance().processManagerInstance().currentProcessId()); + out.flush(); + out.close(); + Thread hook = new Thread() + { + public void run() + { + removePidFile(); + } + }; + Runtime.getRuntime().addShutdownHook(hook); + _shutdownHooks.add(hook); + if (_debug) + getWrapperLogger().info("created pid file " + _pidFile.getAbsolutePath()); + } + catch (Exception e) + { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + } + + /** + * Removes the pid file. + */ + private void removePidFile() + { + if (_pidFile != null) + { + try + { + _pidFile.delete(); + if (_config.getBoolean("wrapper.service", false)) + this.stop(); + + if (_debug) + getWrapperLogger().info("removed pid file " + _pidFile.getAbsolutePath()); + _pidFile = null; + } + catch (Exception e) + { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + } + + /** + * Save lock file. + * + * @return true, if successful + */ + protected boolean saveLockFile() + { + if (_lock != null) + return true; + String file = _config.getString("wrapper.lockfile", null); + if (file != null && !"".equals(file)) + { + try + { + _lockFile = new File(file); + // Check if the lock exist + if (_lockFile.exists()) + { + // if exist try to delete it + _lockFile.delete(); + } + // Try to get the lock + _lockFileChannel = new RandomAccessFile(_lockFile, "rw").getChannel(); + _lock = _lockFileChannel.tryLock(); + if (_lock == null) + { + // File is lock by other application + _lockFileChannel.close(); + getWrapperLogger().warning("Lock file " + file + " already locked by another application -> abort"); + return false; + } + if (_debug) + getWrapperLogger().info("created lock file " + _lockFile.getAbsolutePath()); + + } + catch (IOException e) + { + e.printStackTrace(); + return false; + } + } + return true; + } + + /** + * Removes the lock file. + */ + private void removeLockFile() + { + if (_lock != null) + { + // release and delete file lock + try + { + _lock.release(); + if (_debug) + getWrapperLogger().info("removed lock file " + _lockFile.getAbsolutePath()); + _lock = null; + _lockFileChannel.close(); + _lockFileChannel = null; + _lockFile.delete(); + _lockFile = null; + } + catch (IOException e) + { + e.printStackTrace(); + } + } + } + + /** + * File writer. + * + * @param file + * the file + * + * @return the file writer + */ + private FileWriter FileWriter(String file) + { + // TODO Auto-generated method stub + return null; + } + + /** + * Wait for. + */ + public void waitFor() + { + waitFor(Long.MAX_VALUE); + } + + /** + * Wait for. + * + * @param t + * the t + */ + public void waitFor(long t) + { + if (_state == STATE_IDLE) + return; + final Lock lock = new MyReentrantLock(); + final java.util.concurrent.locks.Condition isIdle = lock.newCondition(); + StateChangeListener listener = new StateChangeListener() + { + + public void stateChange(int newState, int oldState) + { + lock.lock(); + isIdle.signal(); + lock.unlock(); + } + + }; + this.addStateChangeListenerInternal(STATE_IDLE, listener); + if (_state != STATE_IDLE) + try + { + lock.lock(); + isIdle.await(t, TimeUnit.MILLISECONDS); + } + catch (InterruptedException e) + { + e.printStackTrace(); + Thread.currentThread().interrupt(); + } + lock.unlock(); + this.removeStateChangeListener(listener); + } + + /** + * Gets the local configuration. + * + * @return the local configuration + */ + public Configuration getLocalConfiguration() + { + return _localConfiguration; + } + + public void setLocalConfiguration(Configuration config) + { + _localConfiguration = config; + } + + /** + * Gets the exit code. + * + * @return the exit code + */ + public int getExitCode() + { + return _exitCode; + } + + /** + * Gets the wrapper logger. + * + * @return the wrapper logger + */ + public Logger getWrapperLogger() + { + if (_wrapperLogger != null) + return _wrapperLogger; + _wrapperLogger = new MyLogger(); + ((MyLogger) _wrapperLogger).setPID(_wrapperLoggerName); + ((MyLogger) _wrapperLogger).setName(getName()); + _wrapperLogger.setUseParentHandlers(false); + if (_controller != null) + _controller.setLogger(_appLogger); + if (getFileHandler() != null) + _wrapperLogger.addHandler(getFileHandler()); + if (getConsoleHandler() != null) + _wrapperLogger.addHandler(getConsoleHandler()); + return _wrapperLogger; + } + + public InternalLogger getInternalWrapperLogger() + { + if (_internalLoggerFactory == null) + _internalLoggerFactory = new JdkLogger2Factory(getWrapperLogger()); + return _internalLoggerFactory.newInstance(""); + } + + /** + * Sets the use system properties. + * + * @param useSystemProperties + * the new use system properties + */ + public void setUseSystemProperties(boolean useSystemProperties) + { + _useSystemProperties = useSystemProperties; + } + + /** + * Gets the tmp path. + * + * @return the tmp path + */ + public String getTmpPath() + { + if (_tmpPath == null) + { + _tmpPath = _config.getString("wrapper.tmp.path"); + if (_tmpPath == null || _tmpPath.startsWith("?")) + _tmpPath = System.getProperty("jna_tmpdir"); + if (_tmpPath == null || _tmpPath.startsWith("?")) + _tmpPath = System.getProperty("java.io.tmpdir"); + if (_tmpPath == null || _tmpPath.startsWith("?")) + _tmpPath = "tmp"; + File t = new File(_tmpPath); + if (!t.exists()) + t.mkdirs(); + } + return _tmpPath; + } + + /** + * Start drain. + */ + public synchronized void startDrain() + { + if (_gobler_err != null) + _gobler_err.setDrain(true); + if (_gobler_in != null) + _gobler_in.setDrain(true); + _drainActive = true; + } + + /** + * Stop drain. + */ + public synchronized void stopDrain() + { + if (_gobler_err != null) + _gobler_err.setDrain(false); + if (_gobler_in != null) + _gobler_in.setDrain(false); + _drainActive = false; + } + + /** + * Read drain line. + * + * @return the string + */ + public String readDrainLine() + { + String result = null; + if (!_drainActive) + return null; + if (_gobler_err != null) + try + { + if (!_gobler_err.isDrain()) + startDrain(); + result = _gobler_err.getDrainReader().readLine(); + } + catch (IOException e) + { + e.printStackTrace(); + } + if (result == null && _gobler_in != null) + try + { + if (!_gobler_in.isDrain()) + startDrain(); + result = _gobler_in.getDrainReader().readLine(); + } + catch (IOException e) + { + e.printStackTrace(); + } + return result; + + } + + public int getState() + { + return _state; + } + + /** + * Restart internal. + */ + public void restartInternal() + { + setAppReportedReady(false); + getWrapperLogger().info("restart internal " + getStringState()); + if (_state == STATE_RUNNING || _state == STATE_STARTING || _state == STATE_RESTART_START) + setState(STATE_RESTART); + else + return; + _restartCount++; + if (_debug) + { + getWrapperLogger().info("restarting " + _restartCount + " time"); + } + stopInternal(); + if (!_stopRequested) + { + setState(STATE_RESTART_WAIT); + try + { + Thread.sleep(getRestartDelay()); + } + catch (InterruptedException e) + { + if (_debug) + getWrapperLogger().log(Level.SEVERE, this.getClass().getName() + " restart", e); + Thread.currentThread().interrupt(); + } + if (!_stopRequested) + startInternal(); + else + { + getWrapperLogger().log(Level.SEVERE, "Process " + getName() + " stop requested, setting IDLE in restart internal"); + _stopRequested = false; + if (!_osProcess.isRunning()) + setState(STATE_IDLE); + } + } + else + { + if (_debug) + getWrapperLogger().info(" stop requested, setting IDLE in restart internal"); + _stopRequested = false; + if (!_osProcess.isRunning()) + setState(STATE_IDLE); + } + } + + private long getRestartDelay() + { + Script script = getRestartDelayScript(); + if (script != null) + { + Object time = script.execute(); + if (time instanceof Number) + return ((Number) time).longValue() * 1000; + } + return _config.getLong("wrapper.restart.delay", DEFAULT_RESTART_DELAY) * 1000; + } + + private Script getRestartDelayScript() + { + if (_restartDelayScript != null) + return _restartDelayScript; + String script = _config.getString("wrapper.restart.delay.script", null); + if (script != null) + { + List args = _config.getList("wrapper.restart.delay.script.args", null); + int timeout = _config.getInt("wrapper.restart.delay.script.timeout", 0); + _restartDelayScript = ScriptFactory.createScript(script, "", this, args, getInternalWrapperLogger(), 0, _config.getString("wrapper.script.encoding"), _config.getBoolean("wrapper.script.reload", false), _debug); + } + return _restartDelayScript; + } + + /** + * The Class Gobler. + */ + protected class Gobler implements Runnable + { + + /** The _input stream. */ + InputStream _inputStream; + + /** The _name. */ + String _name; + + /** The _gobler log. */ + Logger _goblerLog; + + /** The _actions regex. */ + Map _actionsRegex; + Map _missingActionsRegex; + + /** The _action triggers regex. */ + com.karneim.util.collection.regex.Pattern[] _actionTriggersRegex; + com.karneim.util.collection.regex.Pattern[] _missingActionTriggersRegex; + + /** The _actions. */ + Map _actions; + Map _missingActions; + + /** The _action triggers. */ + String[] _actionTriggers; + String[] _missingActionTriggers; + + /** The _drain. */ + volatile boolean _drain; + + /** The _drain buffer. */ + volatile CircularBuffer _drainBuffer; + + /** The _drain reader. */ + volatile BufferedReader _drainReader; + volatile int _pid; + + /** + * Sets the drain. + * + * @param drain + * the new drain + */ + public void setDrain(boolean drain) + { + if (drain && !_drain) + { + _drainBuffer = new CircularBuffer(16384, false); + _drainReader = new BufferedReader(_drainBuffer); + } + else if (!drain && _drain) + { + try + { + _drainReader.close(); + _drainBuffer = null; + } + catch (IOException e) + { + e.printStackTrace(); + } + } + + _drain = drain; + } + + /** + * Gets the drain reader. + * + * @return the drain reader + */ + public BufferedReader getDrainReader() + { + return _drainReader; + } + + /** + * Instantiates a new gobler. + * + * @param stream + * the stream + * @param log + * the log + * @param events + * the events + * @param eventsRegex + * the events regex + * @param name + * the name + */ + public Gobler(InputStream stream, Logger log, Map events, Map eventsRegex, Map missingEvents, Map missingEventsRegex, String name, int pid) + { + _inputStream = stream; + _name = name; + _goblerLog = log; + _actions = events; + if (events != null) + { + _actionTriggers = new String[events.size()]; + int i = 0; + for (Iterator it = events.keySet().iterator(); it.hasNext(); i++) + { + _actionTriggers[i] = (String) it.next(); + } + } + _actionsRegex = eventsRegex; + if (eventsRegex != null) + { + _actionTriggersRegex = new com.karneim.util.collection.regex.Pattern[eventsRegex.size()]; + int i = 0; + for (Iterator it = eventsRegex.keySet().iterator(); it.hasNext(); i++) + { + String s = (String) it.next(); + try + { + _actionTriggersRegex[i] = new com.karneim.util.collection.regex.Pattern(s); + } + catch (Throwable ex) + { + getWrapperLogger().log(Level.SEVERE, "error in regular expression " + s, ex); + } + } + } + _missingActions = missingEvents; + if (_missingActions != null) + { + _missingActionTriggers = new String[_missingActions.size()]; + int i = 0; + for (Iterator it = _missingActions.keySet().iterator(); it.hasNext(); i++) + { + _missingActionTriggers[i] = (String) it.next(); + } + } + _missingActionsRegex = missingEventsRegex; + if (_missingActionsRegex != null) + { + _missingActionTriggersRegex = new com.karneim.util.collection.regex.Pattern[_missingActionsRegex.size()]; + int i = 0; + for (Iterator it = missingEventsRegex.keySet().iterator(); it.hasNext(); i++) + { + String s = (String) it.next(); + try + { + _missingActionTriggersRegex[i] = new com.karneim.util.collection.regex.Pattern(s); + } + catch (Throwable ex) + { + getWrapperLogger().log(Level.SEVERE, "error in regular expression " + s, ex); + } + } + } + } + + /* + * (non-Javadoc) + * + * @see java.lang.Runnable#run() + */ + public void run() + { + try + { + if (_inputStream == null) + { + _goblerLog.info("cannot run stream gobler with a null stream"); + return; + } + String encoding = _config.getString("wrapper.log.encoding"); + InputStreamReader isr; + if (encoding == null) + isr = new InputStreamReader(_inputStream); + else + isr = new InputStreamReader(_inputStream, encoding); + + BufferedReader br = new BufferedReader(isr); + String line = null; + int k = 0; + if (_missingActions != null) + for (Iterator it = _missingActions.values().iterator(); it.hasNext();) + { + ((MissingTriggerAction) it.next()).start(); + } + if (_missingActionsRegex != null) + for (Iterator it = _missingActionsRegex.values().iterator(); it.hasNext();) + { + ((MissingTriggerAction) it.next()).start(); + } + while ((line = br.readLine()) != null) + { + if (_drain) + { + _drainBuffer.write(line); + } + + /* + * bkowal - added logic to check for the infinite indicator + */ + if (MIN_PROCESS_LINES_TO_LOG == INFINITE_PROCESS_LOGGING || + k <= MIN_PROCESS_LINES_TO_LOG) + { + _goblerLog.info(line); + k++; + } + else + { + _goblerLog.finest(line); + } + + + if (_actionTriggers != null) + for (int i = 0; i < _actionTriggers.length; i++) + { + if (line.contains(_actionTriggers[i])) + { + Object obj = _actions.get(_actionTriggers[i]); + if (obj instanceof TriggerAction) + { + TriggerAction action = (TriggerAction) obj; + getWrapperLogger().info("Trigger found: " + _actionTriggers[i] + " in line: "); + getWrapperLogger().info(line); + action.execute(new String(line)); + } + else if (obj instanceof Collection) + { + Collection c = (Collection) obj; + for (Iterator it = c.iterator(); it.hasNext();) + { + TriggerAction action = (TriggerAction) it.next(); + getWrapperLogger().info("Trigger found: " + action + " in line: "); + getWrapperLogger().info(line); + action.execute(new String(line)); + } + } + break; + } + } + + if (_actionTriggersRegex != null) + for (int i = 0; i < _actionTriggersRegex.length; i++) + { + if (_actionTriggersRegex[i].contains(line)) + { + Object obj = (TriggerAction) _actionsRegex.get(_actionTriggersRegex[i].getRegEx()); + if (obj instanceof TriggerAction) + { + TriggerAction action = (TriggerAction) obj; + getWrapperLogger().info("Trigger found: " + _actionTriggers[i] + " in line: "); + getWrapperLogger().info(line); + action.execute(new String(line)); + } + else if (obj instanceof Collection) + { + Collection c = (Collection) obj; + for (Iterator it = c.iterator(); it.hasNext();) + { + TriggerAction action = (TriggerAction) it.next(); + getWrapperLogger().info("Trigger found: " + _actionTriggers[i] + " in line: "); + getWrapperLogger().info(line); + action.execute(new String(line)); + } + } + break; + } + } + + if (_missingActionTriggers != null) + for (int i = 0; i < _missingActionTriggers.length; i++) + { + if ("".equals(_missingActionTriggers[i]) || line.contains(_missingActionTriggers[i])) + { + Object obj = (TriggerAction) _missingActions.get(_missingActionTriggers[i]); + if (obj instanceof TriggerAction) + { + TriggerAction action = (TriggerAction) obj; + if (_debug) + getWrapperLogger().info("found missing trigger : " + _missingActionTriggers[i]); + action.execute(new String(line)); + } + // break; + } + } + + if (_missingActionTriggersRegex != null) + for (int i = 0; i < _missingActionTriggersRegex.length; i++) + { + if (_missingActionTriggersRegex[i].contains(line)) + { + Object obj = (TriggerAction) _actionsRegex.get(_missingActionTriggersRegex[i].getRegEx()); + if (obj instanceof TriggerAction) + { + TriggerAction action = (TriggerAction) obj; + if (_debug) + getWrapperLogger().info("found missing trigger : " + _missingActionTriggers[i]); + action.execute(new String(line)); + } + // break; + } + } + + Thread.yield(); + } + } + catch (Exception ioe) + { + // ioe.printStackTrace(); + // _goblerLog.info("gobler execption " + _name + " " + + // ioe.getMessage()); + if (_debug) + _goblerLog.log(Level.INFO, " gobler terminated " + _name + " "+ioe); + } + if (AbstractWrappedProcess.this._osProcess != null && _pid == AbstractWrappedProcess.this._osProcess.getPid() + && AbstractWrappedProcess.this._osProcess.isRunning()) + AbstractWrappedProcess.this.executor.execute(new Runnable() + { + public void run() + { + if (AbstractWrappedProcess.this._state != STATE_RESTART_START && AbstractWrappedProcess.this._state != STATE_RESTART + && AbstractWrappedProcess.this._state != STATE_RESTART_STOP + && AbstractWrappedProcess.this._state != STATE_RESTART_WAIT) + { + _goblerLog.warning("yajsw panicking: gobler terminated but process is still running -> restart process"); + AbstractWrappedProcess.this.restartInternal(); + } + } + }); + + if (_debug) + _goblerLog.info("gobler terminated " + _name); + if (_missingActions != null) + for (Iterator it = _missingActions.values().iterator(); it.hasNext();) + { + ((MissingTriggerAction) it.next()).stop(); + } + if (_missingActionsRegex != null) + for (Iterator it = _missingActionsRegex.values().iterator(); it.hasNext();) + { + ((MissingTriggerAction) it.next()).stop(); + } + + } + + public boolean isDrain() + { + return _drain; + } + } + + private void addStateChangeListenerInternal(int state, StateChangeListener listener) + { + _listeners.put(state, listener); + } + + private void addStateChangeListenerInternal(StateChangeListener listener) + { + _listeners.put(999, listener); + } + + public void addStateChangeListener(int state, StateChangeListener listener) + { + _userListeners.put(state, listener); + } + + public void addStateChangeListener(StateChangeListener listener) + { + _userListeners.put(999, listener); + } + + public void removeStateChangeListener(StateChangeListener listener) + { + + } + + public void removeStateChangeListener(int state) + { + _listeners.remove(state); + } + + public int getRestartCount() + { + return _restartCount; + } + + public String getStringState() + { + return getStringState(_state); + } + + public String getName() + { + String result = ""; + if (_config == null) + return result; + if (_config.getBoolean("wrapper.service", false)) + result += "Service "; + String name = _config.getString("wrapper.console.title"); + if (name == null) + name = _config.getString("wrapper.ntservice.name"); + if (name == null) + name = _config.getString("wrapper.image"); + if (name == null) + name = _config.getString("wrapper.groovy"); + if (name == null) + name = _config.getString("wrapper.java.app.mainclass"); + if (name == null) + name = _config.getString("wrapper.java.app.jar"); + if (name == null) + name = ""; + result += name; + return result; + + } + + public OutputStream getOutputStream() + { + if (_osProcess != null) + return _osProcess.getOutputStream(); + return null; + } + + public Date getAppStarted() + { + return _appStarted; + } + + public Date getAppStopped() + { + return _appStopped; + } + + public Date getWrapperStarted() + { + return _wrapperStarted; + } + + public int getAppThreads() + { + if (_osProcess != null) + return _osProcess.getCurrentThreads(); + else + return -1; + } + + public long getAppVMemory() + { + if (_osProcess != null) + return _osProcess.getCurrentVirtualMemory(); + else + return -1; + } + + public long getAppPMemory() + { + if (_osProcess != null) + return _osProcess.getCurrentPhysicalMemory(); + else + return -1; + } + + public int getAppCpu() + { + if (_osProcess != null) + return _osProcess.getCurrentCpu(); + else + return -1; + } + + public int getAppHandles() + { + if (_osProcess != null) + return _osProcess.getCurrentHandles(); + else + return -1; + } + + public void addTriggerListener(TriggerListener listener) + { + // TODO ; + } + + public int getWrapperPid() + { + return OperatingSystem.instance().processManagerInstance().currentProcessId(); + } + + public boolean isTimerActive() + { + return (_timer != null) && _timer.isTriggered(); + } + + public boolean isConditionActive() + { + return (_condition != null) && _condition.isTriggered(); + } + + public void threadDump() + { + if (_osProcess != null && this instanceof WrappedJavaProcess && _osProcess.isRunning()) + ((WrappedJavaProcess) this).requestThreadDump(); + + } + + public void gc() + { + if (_osProcess != null && this instanceof WrappedJavaProcess && _osProcess.isRunning()) + ((WrappedJavaProcess) this).requestGc(); + + } + + public void dumpHeap(String fileName) + { + if (_osProcess != null && this instanceof WrappedJavaProcess && _osProcess.isRunning()) + ((WrappedJavaProcess) this).requestDumpHeap(fileName); + + } + + public void wrapperThreadDump() + { + Message m = new Message(Constants.WRAPPER_MSG_THREAD_DUMP, null); + Action a = ActionFactory.getAction(m); + try + { + ByteArrayOutputStream str = new ByteArrayOutputStream(); + PrintStream pr = new PrintStream(str); + a.execute(m, null, pr, null); + pr.flush(); + getWrapperLogger().info(str.toString()); + } + catch (IOException e) + { + e.printStackTrace(); + } + } + + public void stopTimerCondition() + { + stopTimer(); + stopCondition(); + } + + public boolean isOSProcessRunning() + { + if (_osProcess == null) + return false; + else + return _osProcess.isRunning(); + } + + public int getTotalRestartCount() + { + return _totalRestartCount; + } + + public boolean isService() + { + return _config.getBoolean("wrapper.service", false); + } + + public String getType() + { + if (_config.getBoolean("wrapper.service", false)) + return "Service"; + else if (_config.getBoolean("wrapper.console.visible", false)) + return "Console"; + else + return "Process"; + } + + public void shutdown() + { + setState(STATE_SHUTDOWN); + } + + public void osProcessTerminated() + { + Process process = _osProcess; + if (process != null) + _exitCode = process.getExitCode(); + } + + public boolean isHaltWrapperOnApp() + { + return _haltWrapperOnApp; + } + + public boolean isHaltAppOnWrapper() + { + return _haltAppOnWrapper; + } + + public void setExiting() + { + _exiting = true; + } + + public boolean isExiting() + { + return _exiting; + } + + public TrayIconProxy getTrayIcon() + { + return _trayIconMessages; + } + + public String[][] getTrayIconMessages() + { + if (_trayIconMessages == null) + return null; + String[][] result = _trayIconMessages.toArrayAndClear(); + if (_debug && result != null) + getWrapperLogger().info("sending tray icon messages: #"+result.length); + return result; + } + + public void stopWrapper() + { + if (_haltAppOnWrapper) + stop("WRAPPER SHUTDOWN"); + shutdown(); + try + { + Thread.sleep(5000); + } + catch (InterruptedException e) + { + e.printStackTrace(); + } + System.exit(0); + } + + public boolean hasOutput() + { + return getOutputStream() != null; + } + + public void writeOutput(String txt) + { + ((PrintStream) getOutputStream()).println(txt); + } + + public void writeInquireResponse(String s) + { + if (_trayIconMessages != null) + { + _trayIconMessages._inquireResponse = s; + _trayIconMessages._inquireMessage = null; + } + } + + public String getInquireMessage() + { + if (_trayIconMessages != null) + return _trayIconMessages._inquireMessage; + else + return null; + } + + public void setService(Object service) + { + _service = service; + } + + public Object getService() + { + return _service; + } + + public void setProperty(String key, String value) + { + getWrapperLogger().info("set property " + key + " " + value); + _localConfiguration.setProperty(key, value); + } + + public void resetCache() + { + getWrapperLogger().info("reset cache "); + _cache = null; + } + + public long getMaxStartTime() + { + if (_config == null) + return 0; + long startDelay = _config.getLong("wrapper.startup.delay", 0); + long startupTimeout = _config.getInt("wrapper.startup.timeout", DEFAULT_STARTUP_TIMEOUT) * 1000; + if (startDelay < Integer.MAX_VALUE && startupTimeout < Integer.MAX_VALUE) + return startDelay + startupTimeout; + else + return Integer.MAX_VALUE; + } + + public boolean isReconnecting() + { + return _reconnecting; + } + + void setReconnecting(boolean reconnecting) + { + _reconnecting = reconnecting; + } + + public void setStopper(boolean b) + { + _stopper = b; + } + + public void monitorConf() + { + boolean monitor = _config.getBoolean("wrapper.monitor.config", false); + if (!monitor) + return; + if (_config.isStopper()) + return; + final long fileTime = _config.getConfigFileTime(); + if (fileTime <= 0) + { + getWrapperLogger().info("wrapper.monitor.config: cannot start: could not get file time"); + return; + } + + _localConfiguration.setProperty("wrapper.restart.reload_configuration", true); + + executor.execute(new Runnable() + { + public void run() + { + getWrapperLogger().info("wrapper.monitor.config: start"); + while (getState() == STATE_RUNNING) + { + long t = _config.getConfigFileTime(); + if (t > fileTime) + { + getWrapperLogger().info("wrapper.monitor.config: config file changed: "+new Date(fileTime)+" -> "+new Date(t)+" Restarting Application"); + executor.execute(new Runnable() + { + public void run() + { + restart(); + } + }); + break; + } + try + { + Thread.sleep(5000); + } + catch (Exception ex) + { + break; + } + } + getWrapperLogger().info("wrapper.monitor.config: end"); + } + }); + + } + + public Object getCluster() + { + return _cluster; + } + + public Configuration getYajswConfig() { + return _config; + } + + public void signalStopping(long waitHint) + { + if (_debug) + getAppLogger().info("received signalStopping hint " + waitHint); + + if (_state == STATE_RUNNING) + this.stop("APPLICATION"); + _stoppingHintLock.lock(); + _stoppingHint = waitHint; + _stoppingHintSetTime = System.currentTimeMillis(); + _stoppingHintLock.unlock(); + } + + public boolean isAppReportedReady() + { + //System.out.println("isAppReportedReady "+_appReportedReady); + return _appReportedReady; + } + + public void setAppReportedReady(boolean appReportedReady) + { + _appReportedReady = appReportedReady; + } + + public Color getUserTrayColor() + { + if (_trayIconMessages == null) + return null; + return _trayIconMessages.getUserColor(); + } + + protected void createOSProcess() + { + if (_config.getBoolean("wrapper.fork_hack", false)) + _osProcess = new BSDProcess(); + else + _osProcess = OperatingSystem.instance().processManagerInstance().createProcess(); + + } + + public void update() + { + update(null); + } + + public void update(String updateConfFile) + { + update(updateConfFile, true); + } + + public void update(String updateConfFile, boolean autostart) + { + getInternalWrapperLogger().info("service update invoked: "+ updateConfFile == null ? "null" : updateConfFile); + if (!isService()) + { + getInternalWrapperLogger().warn("service update can only be invoked for a service -> abort "); + return; + } + // if we have defined a default file ignore input + String internUpdateConfFile = _config.getString("wrapper.update.conf", updateConfFile); + if (internUpdateConfFile == null || internUpdateConfFile.length() == 0) + { + getInternalWrapperLogger().warn("missing update configuration file -> abort"); + return; + } + // is this a pattern: choose newest version + try + { + List files = VFSUtils.resolveFiles(internUpdateConfFile); + FileObject found = null; + for (FileObject f : files) + { + if (found == null) + found = f; + else if (f.getContent().getLastModifiedTime() > found.getContent().getLastModifiedTime()) + found = f; + } + if (found == null) + { + getInternalWrapperLogger().warn("did not find matching file for "+internUpdateConfFile +" -> abort"); + return; + } + else + internUpdateConfFile = found.getName().getPath(); + + } + catch (Exception e) + { + getInternalWrapperLogger().warn("error in update() ", e); + return; + } + + UpdateAction.setUpdateConfig(internUpdateConfFile); + UpdateAction.setCurrentConfig(_config); + if (autostart) + UpdateAction.setAutostart(); + getInternalWrapperLogger().info("spawing update process for configuration "+internUpdateConfFile); + UpdateAction.run(); + } + + + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/wrapper/AbstractWrappedProcessMBean.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/wrapper/AbstractWrappedProcessMBean.java new file mode 100644 index 0000000000..aebc5edffa --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/wrapper/AbstractWrappedProcessMBean.java @@ -0,0 +1,113 @@ +package org.rzo.yajsw.wrapper; + +import java.awt.Color; +import java.util.Date; + +public interface AbstractWrappedProcessMBean +{ + /** + * Start. + */ + public void start(); + + /** + * Stop. + */ + public void stop(); + public void stop(String reason); + + /** + * Restart. + */ + public void restart(); + + /** + * Gets the pid. + * + * @return the pid + */ + public int getAppPid(); + + /** + * Gets the exit code. + * + * @return the exit code + */ + public int getExitCode(); + + public String getStringState(); + + public void threadDump(); + + public void gc(); + + public void wrapperThreadDump(); + + public String getType(); + + public String getName(); + + public void waitFor(); + + public void stopTimerCondition(); + + public boolean isTimerActive(); + + public boolean isConditionActive(); + + public int getTotalRestartCount(); + + public int getRestartCount(); + + public Date getAppStarted(); + + public Date getAppStopped(); + + public int getWrapperPid(); + + public Date getWrapperStarted(); + + public int getAppCpu(); + + public int getAppHandles(); + + public long getAppVMemory(); + public long getAppPMemory(); + + public int getAppThreads(); + + public void startDrain(); + + public String readDrainLine(); + + public void stopDrain(); + + public int getState(); + + public String[][] getTrayIconMessages(); + + public void stopWrapper(); + + public boolean hasOutput(); + + public void writeOutput(String txt); + + public void writeInquireResponse(String s); + + public String getInquireMessage(); + + public void init(); + + public void setProperty(String key, String value); + + public void resetCache(); + + public boolean isAppReportedReady(); + + public void dumpHeap(String s); + + public Color getUserTrayColor(); + + public void update(String updateConfFile); + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/wrapper/AlphanumComparator.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/wrapper/AlphanumComparator.java new file mode 100644 index 0000000000..5e86e3b662 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/wrapper/AlphanumComparator.java @@ -0,0 +1,160 @@ +/* + * The Alphanum Algorithm is an improved sorting algorithm for strings + * containing numbers. Instead of sorting numbers in ASCII order like + * a standard sort, this algorithm sorts numbers in numeric order. + * + * The Alphanum Algorithm is discussed at http://www.DaveKoelle.com + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ +package org.rzo.yajsw.wrapper; + +import java.util.Comparator; + +// TODO: Auto-generated Javadoc +/** + * This is an updated version with enhancements made by Daniel Migowski, Andre + * Bogus, and David Koelle + * + * To convert to use Templates (Java 1.5+): - Change "implements Comparator" to + * "implements Comparator" - Change "compare(Object o1, Object o2)" to + * "compare(String s1, String s2)" - Remove the type checking and casting in + * compare(). + * + * To use this class: Use the static "sort" method from the + * java.util.Collections class: Collections.sort(your list, new + * AlphanumComparator()); + */ +public class AlphanumComparator implements Comparator +{ + + /** + * Checks if is digit. + * + * @param ch + * the ch + * + * @return true, if is digit + */ + private final boolean isDigit(char ch) + { + return ch >= 48 && ch <= 57; + } + + /** + * Length of string is passed in for improved efficiency (only need to + * calculate it once) *. + * + * @param s + * the s + * @param slength + * the slength + * @param marker + * the marker + * + * @return the chunk + */ + private final String getChunk(String s, int slength, int marker) + { + StringBuilder chunk = new StringBuilder(); + char c = s.charAt(marker); + chunk.append(c); + marker++; + if (isDigit(c)) + { + while (marker < slength) + { + c = s.charAt(marker); + if (!isDigit(c)) + break; + chunk.append(c); + marker++; + } + } + else + { + while (marker < slength) + { + c = s.charAt(marker); + if (isDigit(c)) + break; + chunk.append(c); + marker++; + } + } + return chunk.toString(); + } + + /* + * (non-Javadoc) + * + * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object) + */ + public int compare(Object o1, Object o2) + { + if (!(o1 instanceof String) || !(o2 instanceof String)) + { + return 0; + } + String s1 = (String) o1; + String s2 = (String) o2; + + int thisMarker = 0; + int thatMarker = 0; + int s1Length = s1.length(); + int s2Length = s2.length(); + + while (thisMarker < s1Length && thatMarker < s2Length) + { + String thisChunk = getChunk(s1, s1Length, thisMarker); + thisMarker += thisChunk.length(); + + String thatChunk = getChunk(s2, s2Length, thatMarker); + thatMarker += thatChunk.length(); + + // If both chunks contain numeric characters, sort them numerically + int result = 0; + if (isDigit(thisChunk.charAt(0)) && isDigit(thatChunk.charAt(0))) + { + // Simple chunk comparison by length. + int thisChunkLength = thisChunk.length(); + result = thisChunkLength - thatChunk.length(); + // If equal, the first different number counts + if (result == 0) + { + for (int i = 0; i < thisChunkLength; i++) + { + result = thisChunk.charAt(i) - thatChunk.charAt(i); + if (result != 0) + { + return result; + } + } + } + } + else + { + result = thisChunk.compareTo(thatChunk); + } + + if (result != 0) + return result; + } + + return s1Length - s2Length; + } +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/wrapper/FileUtils.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/wrapper/FileUtils.java new file mode 100644 index 0000000000..286e8036dd --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/wrapper/FileUtils.java @@ -0,0 +1,223 @@ +/* This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + *

    + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ +package org.rzo.yajsw.wrapper; + +import java.io.File; +import java.io.FileFilter; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; +import java.util.logging.Logger; + +import org.apache.commons.configuration.AbstractConfiguration; +import org.apache.commons.configuration.BaseConfiguration; +import org.apache.commons.configuration.CompositeConfiguration; +import org.apache.commons.configuration.ConfigurationConverter; +import org.apache.commons.configuration.EnvironmentConfiguration; +import org.apache.commons.io.filefilter.WildcardFileFilter; + +// TODO: Auto-generated Javadoc +/** + * The Class FileUtils. + */ +public class FileUtils +{ + + /** The log. */ + static Logger log = Logger.getLogger(FileUtils.class.getName()); + + /** + * Gets the files. + * + * @param workingDir + * the working dir + * @param pattern + * the pattern + * + * @return the files + */ + public static Collection getFiles(String workingDir, String pattern) + { + ArrayList result = new ArrayList(); + + // check if we have a non patterned file name + File res = new File(pattern); + if (res.exists() && res.isAbsolute()) + { + result.add(res); + return result; + } + // pk-20080626: added working dir + // res = new File(workingDir, pattern); + File workingDirectory = new File(workingDir); + res = new File(workingDirectory.getAbsolutePath(), pattern); + // System.out.println("FileUtils: filename="+res+", exists:"+res.exists()); + if (res.exists()) + { + result.add(res); + return result; + } + + // so this must be a pattern try to figure out the files + // does not work -> without separator + //String[] s = pattern.split("[" + File.separator + "|/]"); + String[] s = pattern.split("[\\\\|/]"); + String[] sh; + if (s.length == 1) + { + sh = new String[2]; + sh[0] = "."; + sh[1] = s[0]; + } + else + sh = s; + + Collection paths = new HashSet(); + paths.add(sh[0]); + for (int i = 1; i < sh.length; i++) + { + String file = sh[i]; + if (file.trim().length() == 0) + continue; + Collection newPaths = new HashSet(); + for (Iterator it = paths.iterator(); it.hasNext();) + { + String pathStr = (String) it.next(); + if (pathStr.endsWith(":")) + pathStr += "/"; + File path = new File(pathStr); + if ((!path.isDirectory()) || (!path.exists()) || (!(path.isAbsolute()))) + path = new File(workingDir, pathStr); + Collection files = getWildcardFiles(path.getAbsolutePath(), file); + for (Iterator it2 = files.iterator(); it2.hasNext();) + { + File f = (File) it2.next(); + if (f.isDirectory()) + newPaths.add(f.getPath()); + else if (f.isFile()) + result.add(f); + } + } + paths = newPaths; + } + + /* + * String file = s[s.length-1]; String path = pattern.substring(0, + * pattern.lastIndexOf(file)); + * + * if (path == null || path.equals("")) path = "."; File fPath = null; + * try { fPath = new File(path); if (!fPath.isDirectory()) { + * log.warning( + * "classpath directory "+fPath.getCanonicalPath()+" not found"); return + * result; } } catch (Exception ex) { + * log.warning("classpath directory "+path+" error" + ex.getMessage()); + * return result; } FileFilter fileFilter = new + * WildcardFileFilter(file); File[] thisFiles = + * fPath.listFiles(fileFilter); for (int i=0; i< thisFiles.length; i++) + * { File f = thisFiles[i]; if (f.exists()) result.add(f); else + * log.warning("classpath file "+f.getName() +"not found"); } + */ + if (result.size() == 0) + log.warning("No files found for " + pattern); + return result; + } + + /** + * Gets the wildcard files. + * + * @param path + * the path + * @param file + * the file + * + * @return the wildcard files + */ + private static Collection getWildcardFiles(String path, String file) + { + ArrayList result = new ArrayList(); + file = file.trim(); + if (file.equals(".") || file.equals("..")) + { + result.add(new File(path+"/"+file)); + return result; + } + File fPath = new File(path); + try + { + if (!fPath.isDirectory()) + { + log.warning("classpath directory " + fPath.getCanonicalPath() + " not found"); + return result; + } + } + catch (Exception ex) + { + log.warning("classpath directory " + path + " error" + ex.getMessage()); + return result; + } + FileFilter fileFilter = new WildcardFileFilter(file); + File[] thisFiles = fPath.listFiles(fileFilter); + for (int i = 0; i < thisFiles.length; i++) + { + File f = thisFiles[i]; + if (f.exists()) + result.add(f); + else + log.warning("classpath file " + f.getName() + "not found"); + } + return result; + } + + /** + * The main method. + * + * @param args + * the arguments + */ + public static void main(String[] args) + { + System.out.println(getFiles(".", "z:\\dev\\yajsw\\..\\yajsw\\*.jar").size()); + try + { + // String + // fileName=FilenameUtils.separatorsToSystem("C:\\init\\MOBILEguard\\yajsw/lib/jvmstat/*.jar"); + // System.out.println("FileName: "+fileName); + CompositeConfiguration compConfig = new CompositeConfiguration(); + AbstractConfiguration configuraton = new BaseConfiguration(); + compConfig.addConfiguration(new EnvironmentConfiguration()); + configuraton.setProperty("wrapper.java.classpath.1", "${VERSANT_ROOT}/lib/jvi.*jar"); + configuraton.setProperty("wrapper.java.classpath.2", "${GROOVY_HOME}/lib/*.jar"); + compConfig.addConfiguration(configuraton); + System.out.println("Configuration: " + ConfigurationConverter.getProperties(compConfig)); + System.out.println("subset: " + ConfigurationConverter.getProperties(compConfig.subset("wrapper.java"))); + + // Collection files=FileUtils.getFiles("../..", + // "C:/versant/7_0_1/lib/jvi*.jar"); + // Collection collection= + // org.apache.commons.io.FileUtils.listFiles(new File("C:/"), + // new WildcardFileFilter("jvi*.jar"), new + // WildcardFileFilter("*jar")); + // File[] files= new + // File("C:").listFiles((FilenameFilter)FileFilterUtils.nameFileFilter("C:/versant/7_0_1/lib/jvi*.jar")); + + // + // FileUtils.getFiles("C:/versant/7_0_1/lib/", "jvi*.jar"); + // System.out.println("FileList="+ + // FileUtils.getFiles("C:/versant/7_0_1/lib/", "jvi*.jar")); + // java.util.Arrays.asList(files)); + } + catch (Exception ex) + { + ex.printStackTrace(); + } + } +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/wrapper/MissingTriggerAction.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/wrapper/MissingTriggerAction.java new file mode 100644 index 0000000000..212f8bf817 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/wrapper/MissingTriggerAction.java @@ -0,0 +1,68 @@ +package org.rzo.yajsw.wrapper; + +import java.util.concurrent.Executor; +import java.util.logging.Logger; + +import org.rzo.yajsw.util.Cycler; + +class MissingTriggerAction implements TriggerAction +{ + volatile private Cycler _cycler; + volatile private int _counter = 0; + private int _count; + private TriggerAction[] _actions; + Executor _executor; + + MissingTriggerAction(Executor executor, long period, int count, TriggerAction[] actions, final boolean autoStop, final Logger logger) + { + _count = count; + _executor = executor; + _actions = actions; + _cycler = new Cycler(period, period, executor, new Runnable() + { + public void run() + { + // System.out.println("missing trigger "+_counter + " "+_count); + if (_counter < _count) + { + if (autoStop) + _cycler.stop(); + for (final TriggerAction action : _actions) + if (action != null) + { + // run the action in a separate thread, because on + // restart the cycler thread will be interrupted + _executor.execute(new Runnable() + { + public void run() + { + // TODO add logger + logger.info("missing trigger executed, found # " + _counter + " triggers during check period"); + action.execute(""); + } + }); + } + } + else + _counter = 0; + } + }); + } + + void start() + { + _cycler.start(); + } + + void stop() + { + _cycler.stop(); + } + + public Object execute(String line) + { + _counter++; + return null; + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/wrapper/StateChangeListener.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/wrapper/StateChangeListener.java new file mode 100644 index 0000000000..7409decc25 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/wrapper/StateChangeListener.java @@ -0,0 +1,6 @@ +package org.rzo.yajsw.wrapper; + +public interface StateChangeListener +{ + public void stateChange(int newState, int oldState); +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/wrapper/TrayIconMessage.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/wrapper/TrayIconMessage.java new file mode 100644 index 0000000000..da1f70b1c2 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/wrapper/TrayIconMessage.java @@ -0,0 +1,24 @@ +package org.rzo.yajsw.wrapper; + +import org.rzo.yajsw.wrapper.TrayIconProxy.Types; + +public class TrayIconMessage +{ + Types _type; + String _caption; + String _message; + + public TrayIconMessage(Types type, String caption, String message) + { + _type = type; + _caption = caption; + _message = message; + } + + public String[] toStringArray() + { + return new String[] + { _type.toString(), _caption, _message }; + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/wrapper/TrayIconProxy.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/wrapper/TrayIconProxy.java new file mode 100644 index 0000000000..6079e1b80d --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/wrapper/TrayIconProxy.java @@ -0,0 +1,97 @@ +package org.rzo.yajsw.wrapper; + +import java.awt.Color; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class TrayIconProxy +{ + List messages = Collections.synchronizedList(new ArrayList()); + volatile String _inquireMessage; + volatile String _inquireResponse; + volatile Color _userColor; + + public enum Types + { + ERROR, INFO, WARNING, MESSAGE + }; + + public void error(String caption, String message) + { + synchronized (messages) + { + messages.add(new TrayIconMessage(Types.ERROR, caption, message)); + } + } + + public void info(String caption, String message) + { + synchronized (messages) + { + messages.add(new TrayIconMessage(Types.INFO, caption, message)); + } + } + + public void warning(String caption, String message) + { + synchronized (messages) + { + messages.add(new TrayIconMessage(Types.WARNING, caption, message)); + } + } + + public void message(String caption, String message) + { + synchronized (messages) + { + messages.add(new TrayIconMessage(Types.MESSAGE, caption, message)); + } + } + + public String inquire(String message) + { + String result = null; + if (_inquireResponse != null) + { + result = _inquireResponse; + System.out.println("got response for inquire "); + _inquireResponse = null; + } + else + { + System.out.println("waiting for inquire " + message); + _inquireMessage = message; + result = null; + } + return result; + } + + public String[][] toArrayAndClear() + { + String[][] result = null; + synchronized (messages) + { + if (messages.size() == 0) + return null; + result = new String[messages.size()][]; + int i = 0; + for (TrayIconMessage message : messages) + result[i++] = message.toStringArray(); + messages.clear(); + } + return result; + } + + public void setUserColor(Color color) + { + _userColor = color; + } + + public Color getUserColor() + { + return _userColor; + } + + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/wrapper/TriggerAction.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/wrapper/TriggerAction.java new file mode 100644 index 0000000000..2c6bbdce9e --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/wrapper/TriggerAction.java @@ -0,0 +1,16 @@ +package org.rzo.yajsw.wrapper; + +/** + * The Interface TriggerAction. + */ +public interface TriggerAction +{ + + /** + * Execute. + * + * @param line + * the line + */ + public Object execute(String line); +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/wrapper/TriggerListener.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/wrapper/TriggerListener.java new file mode 100644 index 0000000000..a4a7f755bc --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/wrapper/TriggerListener.java @@ -0,0 +1,6 @@ +package org.rzo.yajsw.wrapper; + +public interface TriggerListener +{ + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/wrapper/WrappedGroovyProcess.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/wrapper/WrappedGroovyProcess.java new file mode 100644 index 0000000000..8b9d4d974b --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/wrapper/WrappedGroovyProcess.java @@ -0,0 +1,16 @@ +package org.rzo.yajsw.wrapper; + +public class WrappedGroovyProcess extends WrappedJavaProcess +{ + + protected String getMainClass() + { + return _config.getString("wrapper.java.mainclass", "org.rzo.yajsw.app.WrapperGroovyMain"); + } + + public String getType() + { + return "Groovy-" + super.getType(); + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/wrapper/WrappedJavaProcess.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/wrapper/WrappedJavaProcess.java new file mode 100644 index 0000000000..db5f72eedd --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/wrapper/WrappedJavaProcess.java @@ -0,0 +1,1132 @@ +/* This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + *

    + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ +package org.rzo.yajsw.wrapper; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.HashMap; +import java.util.Random; +import java.util.Map.Entry; +import java.util.jar.Attributes; +import java.util.jar.JarFile; +import java.util.jar.Manifest; +import java.util.logging.Level; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.rzo.yajsw.Constants; +import org.rzo.yajsw.app.WrapperMainServiceWin; +import org.rzo.yajsw.boot.WrapperLoader; +import org.rzo.yajsw.controller.AbstractController.ControllerListener; +import org.rzo.yajsw.controller.jvm.JVMController; +import org.rzo.yajsw.os.JavaHome; +import org.rzo.yajsw.os.OperatingSystem; +import org.apache.commons.lang.StringUtils; + +// TODO: Auto-generated Javadoc +/** + * The Class WrappedJavaProcess. + */ +public class WrappedJavaProcess extends AbstractWrappedProcess +{ + /* Constants used to set and determine the order of jvm parameters. */ + private static final String LAST_KEYWORD = "LAST"; + + private static final String _PERIOD_SEPARATOR_ = "."; + + /* + * bkowal - define the parameter order parameter name + */ + private static final String ORDER_PATTERN = "wrapper.jvm.parameter.order"; + + private static final String NUMERIC_ORDER_PATTERN = ORDER_PATTERN + + _PERIOD_SEPARATOR_ + "([1-9][0-9]*)"; + + private static final Pattern numericOrderPattern = Pattern + .compile(NUMERIC_ORDER_PATTERN); + + private static final String LAST_ORDER_PATTERN = ORDER_PATTERN + + _PERIOD_SEPARATOR_ + LAST_KEYWORD; + + private static final Pattern lastOrderPattern = Pattern + .compile(LAST_ORDER_PATTERN); + /* End of Constants. */ + + /** The _key. */ + String _key; + + /** The _tee name. */ + String _teeName; + + /** The _java pid file. */ + File _javaPidFile; + + boolean _initController = false; + + Runnable _serviceStartupListener = null; + + public void init() + { + super.init(); + _key = "" + new Random(System.currentTimeMillis()).nextLong(); + _localConfiguration.setProperty("wrapper.key", _key); + if (_controller == null) + { + _controller = new JVMController(this); + configController(); + } + + } + + protected boolean pipeStreams() + { + return super.pipeStreams() || _teeName != null; + + } + + + protected void setState(int state) + { + super.setState(state); + if (state == STATE_IDLE) + { + removeJavaPidFile(); + } + + } + + /** + * Config process. + */ + void configProcess() + { + // _osProcess.destroy(); + + _osProcess.setTmpPath(_tmpPath); + + if (!super.pipeStreams()) + { + // _osProcess.setPipeStreams(true, false); + _teeName = _key + "$" + System.currentTimeMillis(); + _localConfiguration.setProperty("wrapper.teeName", _teeName); + _osProcess.setTeeName(_teeName); + _osProcess.setPipeStreams(false, false); + } + else + { + _osProcess.setPipeStreams(true, true); + _osProcess.setTeeName(null); + + if (!_haltAppOnWrapper) + getWrapperLogger().log(Level.WARNING, "WARNING: application streams are piped, but wrapper.control setting may cause zombie processes. Please set to TIGHT"); + } + + JavaHome javaHome = OperatingSystem.instance().getJavaHome(_config); + javaHome.setLogger(getInternalWrapperLogger()); + String java = javaHome.findJava(_config.getString("wrapper.java.command"), _config.getString("wrapper.java.customProcName")); + if (java == null) + getWrapperLogger().log(Level.SEVERE, "ERROR: could not get java command"); + List jvmOptions = jvmOptions(); + List wrapperOptions = wrapperOptions(); + String mainClass = getMainClass(); + List command = new ArrayList(); + command.add(java); + command.addAll(jvmOptions); + command.addAll(wrapperOptions); + command.add(mainClass); + // clean spaces ### + List cleanCommand = new ArrayList(); + for (int i = 0; i < command.size(); i++) + { + String text = command.get(i).toString(); + if (text.trim().equals("")) + { + continue; + } + cleanCommand.add(text); + } + String[] arrCmd = new String[cleanCommand.size()]; + for (int i = 0; i < arrCmd.length; i++) + arrCmd[i] = (String) cleanCommand.get(i); + _osProcess.setCommand(arrCmd); + + super.configProcess(); + } + + protected String getMainClass() + { + return _config.getString("wrapper.java.mainclass", "org.rzo.yajsw.app.WrapperJVMMain"); + } + + private String getDOption(String key, String value) + { + if (value != null && !value.contains(" ")) + return "-D"+key+"="+value; + else + return "-D"+key+"=\""+value+"\""; + } + + /** + * Wrapper options. + * + * @return the string + */ + private List wrapperOptions() + { + ArrayList result = new ArrayList(); + JVMController controller = (JVMController) _controller; + + result.add(getDOption("wrapper.port", ""+controller.getPort())); + result.add(getDOption("wrapper.key", controller.getKey())); + if (_teeName != null) + result.add(getDOption("wrapper.teeName", _teeName)); + result.add(getDOption("wrapper.tmp.path", _tmpPath)); + result.add(getDOption("jna_tmpdir", _tmpPath)); + + for (Iterator it = _config.getSystemConfiguration().getKeys("wrapper"); it.hasNext();) + { + String key = (String) it.next(); + if (("wrapper.service".equals(key) || "wrapper.console.visible".equals(key)) && _config.getBoolean("wrapper.service", false)) + continue; + if ("wrapper.config".equals(key)) + { + result.add(checkValue(getDOption(key, _config.getCachedPath()))); + } + else + { + String opt = getDOption(key, _config.getProperty(key).toString()); + if (!result.contains(opt)) + result.add(checkValue(opt)); + } + } + + String gcPattern = _config.getString("wrapper.java.monitor.gc", null); + if ((gcPattern != null) && (gcPattern.length() > 0)) + { + gcPattern = gcPattern.replaceAll(",","\\\\,"); + result.add(getDOption("wrapper.java.monitor.gc", gcPattern)); + } + + String preScript = _config.getString("wrapper.app.pre.script", null); + if (preScript != null & !"".equals(preScript)) + try + { + File f = new File(preScript); + if (!f.exists()) + getWrapperLogger().warning("app.pre.script not found: " + preScript); + else + { + preScript = checkValue(f.getCanonicalPath()); + result.add(getDOption("wrapper.app.pre.script", preScript)); + } + } + catch (Exception ex) + { + getWrapperLogger().log(Level.SEVERE, "WrappedJavaProcess wrapperOptions", ex); + } + + return result; + } + + /** + * Jvm options. + * + * @return the string + */ + private List jvmOptions() + { + ArrayList result = new ArrayList(); + StringBuffer sb = new StringBuffer(); + sb.append(WrapperLoader.getWrapperAppJar().trim()); + StringBuilder appCp = getAppClassPath(_config.getString("wrapper.working.dir", "."), _config.getKeys("wrapper.java.classpath")); + if (appCp != null && appCp.length() > 0) + { + sb.append(PATHSEP); + sb.append(appCp); + } + // Save the classpath + final String classpath = checkValue(sb.toString()); + + boolean hasXrs = false; + boolean hasXmx = false; + boolean hasXms = false; + for (Iterator it = _config.getKeys("wrapper.java.additional"); it.hasNext();) + { + String key = (String) it.next(); + String value = _config.getString(key); + if (value == null) + { + continue; + } + // exclude jvm parameters that could not be resolved. + if (value.contains("?unresolved?")) + { + getWrapperLogger().warning("JVM Parameter: '" + key + "' COULD NOT BE RESOLVED!!!"); + continue; + } + result.add(checkValue(value)); + hasXrs |= value.contains("-Xrs"); + hasXmx |= value.contains("-Xmx"); + hasXms |= value.contains("-Xms"); + } + sb = new StringBuffer(); + if (_config.getKeys("wrapper.java.library.path").hasNext()) + { + for (Iterator it = _config.getKeys("wrapper.java.library.path"); it.hasNext();) + { + String key = (String) it.next(); + if (_config.getString(key) == null) + continue; + sb.append(checkValue(_config.getString(key))); + if (it.hasNext()) + sb.append(PATHSEP); + } + result.add(getDOption("java.library.path", sb.toString())); + } + + if (_config.getBoolean("wrapper.service", false) && !hasXrs) + { + result.add("-Xrs"); + } + if (_config.getBoolean("wrapper.service", false)) + { + result.add("-Dwrapper.service=true"); + result.add("-Dwrapper.console.visible=false"); + } + else if (_config.getBoolean("wrapper.console.visible", Constants.DEFAULT_CONSOLE_VISIBLE)) + result.add("-Dwrapper.console.visible=true"); + + if (_config.containsKey("wrapper.java.initmemory") || _config.containsKey("wrapper.java.initmemory.relative") + || _config.containsKey("wrapper.java.maxmemory") || _config.containsKey("wrapper.java.maxmemory.relative")) + { + long xmx = 0; + long xmxr = 0; + long xms = 0; + long xmsr = 0; + OperatingSystem.instance().systemInformation().setLogger(this.getWrapperLogger()); + long totalRAM = 0; + if (!hasXms) + { + try + { + xms = _config.getLong("wrapper.java.initmemory", 0); + xmsr = _config.getLong("wrapper.java.initmemory.relative", 0); + } + catch (Exception ex) + { + getWrapperLogger().info("error in wrapper.java.initmemory " + ex.getMessage()); + } + if (xmsr > 0) + totalRAM = OperatingSystem.instance().systemInformation().totalRAM(); + if (xmsr > 0 && totalRAM > 0) + xms = (totalRAM * xmsr) / 100 / (1024 * 1024); + if (xms > 0) + { + result.add("-Xms" + xms + "m"); + } + } + if (!hasXmx) + { + try + { + xmx = _config.getLong("wrapper.java.maxmemory", 0); + xmxr = _config.getLong("wrapper.java.maxmemory.relative", 0); + } + catch (Exception ex) + { + getWrapperLogger().info("error in wrapper.java.maxmemory " + ex.getMessage()); + } + if (xmxr > 0 && totalRAM == 0) + totalRAM = OperatingSystem.instance().systemInformation().totalRAM(); + if (xmxr > 0 && totalRAM > 0) + xmx = (totalRAM * xmxr) / 100 / (1024 * 1024); + if (xmx > 0) + { + if (xmx < xms) + xmx = xms; + if (xmx < 3) + xmx = 3; + result.add("-Xmx" + xmx + "m"); + } + } + } + int port = _config.getInt("wrapper.java.debug.port", -1); + if (port != -1) + { + result.add("-Xdebug"); + /* + * bkowal - no longer suspend execution when debugging + */ + result.add("-Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=" + port); + } + String preMainScript = _config.getString("wrapper.app.pre_main.script", null); + if (preMainScript != null && preMainScript.length() > 0) + result.add("-Dwrapper.app.pre_main.script=" + preMainScript); + // if we are running as service "remember" the system properties and env vars we have used + if (_config.getBoolean("wrapper.service", false)) + { + for (Entry e : _config.getEnvLookupSet().entrySet()) + { + String opt = getDOption(e.getKey(), e.getValue()); + if (opt != null && !result.contains(opt)) + result.add(opt); + } + } + //TODO ??? there seems to be a bug in windows or java socket ??? + // when piping stdout/stderr streams netty client connect hangs + // it does not hang when jmxremote is set, or in debug mode. + // probably due to open of socket in launcher before java code is executed + // jmxremote causes problems with jboss7 loading java.util.Log + /* + if (Platform.isWindows() && _config.getBoolean("wrapper.console.pipestreams")) + if (!result.contains("-Dcom.sun.management.jmxremote")) + result.add("-Dcom.sun.management.jmxremote"); + */ + + return orderParameters(result, classpath); + } + + /* + * List was left as a generic to match the code that would use it. + */ + private List orderParameters(List parameters, String classpath) + { + Map orderedParametersMap = new HashMap(); + + /* + * bkowal - loop through the parameters and re-arrange the parameters + * in the specified order. + */ + Iterator paramOrderIterator = + _config.getKeys("wrapper.jvm.parameter.order"); + while (paramOrderIterator.hasNext()) + { + String paramOrderKey = paramOrderIterator.next(); + + Matcher numericMatcher = + numericOrderPattern.matcher(paramOrderKey); + Matcher lastMatcher = + lastOrderPattern.matcher(paramOrderKey); + + String index = null; + /* determine the order parameter type */ + if (numericMatcher.matches()) + { + index = numericMatcher.group(1); + } + if (lastMatcher.matches()) + { + index = LAST_KEYWORD; + } + + if (index == null) + { + getWrapperLogger().warning("Invalid Parameter Order Specifier: '" + + paramOrderKey + "'; SKIPPING!!!"); + continue; + } + + String parameter = _config.getString(paramOrderKey); + orderedParametersMap.put(index, parameter); + } + + /* Determine the total number of ordered parameters. */ + int numberOrdered = orderedParametersMap.size(); + + /* Ensure that there are actually parameters that we will be ordering. */ + if (numberOrdered <= 0 || numberOrdered > parameters.size()) + { + // Respect the original YAJSW JVM parameter order. + parameters.add(0, "-classpath"); + parameters.add(1, classpath); + return parameters; + } + + String lastParameter = null; + int classpathIndex = -1; + if (orderedParametersMap.containsKey(LAST_KEYWORD)) + { + numberOrdered -= 1; + // extract and save off the "LAST" parameter + String parameter = orderedParametersMap.get(LAST_KEYWORD); + if (parameter.equals("-classpath")) + { + // the end of the list. + classpathIndex = parameters.size() - 1; + } + else + { + lastParameter = lookupParameter(parameters, parameter); + // remove the parameter from the list. + parameters.remove(lastParameter); + } + } + + // loop through the parameters that will need to be ordered. + for (int i = 1; i <= numberOrdered; i++) + { + String parameter = + orderedParametersMap.get(Integer.toString(i)); + if (parameter == null) + { + continue; + } + + if (parameter.equals("-classpath")) + { + classpathIndex = i - 1; + continue; + } + String orderedParameter = + lookupParameter(parameters, parameter); + if (orderedParameter == null) + { + continue; + } + // remove the parameter from the list. + parameters.remove(orderedParameter); + // add the parameter at the requested location. + parameters.add(i - 1, orderedParameter); + } + + if (lastParameter != null) + { + parameters.add(lastParameter); + } + + if (classpathIndex > 0) + { + parameters.add(classpathIndex, "-classpath"); + parameters.add(classpathIndex + 1, classpath); + } + else + { + // Respect the original YAJSW JVM parameter order. + parameters.add(0, "-classpath"); + parameters.add(1, classpath); + } + + return parameters; + } + + private String lookupParameter(List parameters, String parameter) + { + for (Object _parameter : parameters) + { + if (_parameter.toString().contains(parameter)) + { + return _parameter.toString(); + } + } + + getWrapperLogger().warning("Parameter Not Found: '" + + parameter + "'; UNABLE TO ADD TO ORDERED PARAMETERS!!!"); + return null; + } + + // call to java "-Ddir=c:\" will cause a parse exception in the java + // launcher + private String checkValue(String value) + { + value = value.trim(); + if (value.endsWith("\\") && !value.endsWith("\\\\")) + value += "\\"; + return value; + } + + /** + * Gets the app class path. + * + * @param workingDir + * the working dir + * @param config + * the config + * + * @return the app class path + */ + private StringBuilder getAppClassPath(String workingDir, Iterator keys) + { + List configList = new ArrayList(); + for (Iterator it = keys; it.hasNext();) + { + configList.add(it.next()); + } + + Collections.sort(configList, new AlphanumComparator()); + List files = new ArrayList(); + String jar = _config.getString("wrapper.java.app.jar", null); + if (jar != null) + { + Collection jars = FileUtils.getFiles(workingDir, jar); + files.addAll(jars); + files.addAll(classpathFromJar(jars, workingDir)); + } + for (Iterator it = configList.listIterator(); it.hasNext();) + { + String file = _config.getString((String) it.next()); + if (file == null) + continue; + files.addAll(FileUtils.getFiles(workingDir, file)); + } + StringBuilder sb = new StringBuilder(); + for (Iterator it = files.iterator(); it.hasNext();) + { + try + { + sb.append(((File) it.next()).getCanonicalPath()); + } + catch (IOException e) + { + // TODO Auto-generated catch block + e.printStackTrace(); + } + if (it.hasNext()) + sb.append(PATHSEP); + + } + + /* + * bkowal - recursively search the directories specified + * using the wrapper.search.java.classpath jvm parameter and add entries + * to the classpath. + */ + final String[] jarPattern = new String[] { "jar" }; + List containingDirectories = new ArrayList(); + Iterator searchLocationIterator = _config.getKeys("wrapper.search.java.classpath"); + while (searchLocationIterator.hasNext()) + { + String key = searchLocationIterator.next(); + String location = _config.getString(key); + + File locationDirectory = new File(location); + if (locationDirectory.exists() == false || + locationDirectory.isDirectory() == false) + { + getWrapperLogger().warning(location + + " either does not exist or is not a directory; skipping!"); + continue; + } + Iterator foundFilesIterator = + org.apache.commons.io.FileUtils.iterateFiles(locationDirectory, jarPattern, true); + while (foundFilesIterator.hasNext()) + { + File foundFile = (File) foundFilesIterator.next(); + String containingDirectory = org.apache.commons.io.FilenameUtils + .getFullPath(foundFile.getAbsolutePath()); + if (containingDirectories.contains(containingDirectory) == false) { + containingDirectories.add(containingDirectory); + sb.append(PATHSEP); + sb.append(containingDirectory + "*"); + } + } + } + return sb; + } + + private Collection classpathFromJar(Collection jars, String workingDir) + { + Collection result = new ArrayList(); + URL url = null; + for (Object jar : jars) + { + try + { + url = ((File)jar).toURI().toURL(); + } + catch (MalformedURLException e2) + { + e2.printStackTrace(); + continue; + } + Manifest manifest; + try + { + manifest = new JarFile((File)jar).getManifest(); + } + catch (IOException e1) + { + e1.printStackTrace(); + continue; + } + Attributes attr = manifest.getMainAttributes(); + + String cl = attr.getValue("Class-Path"); + ClassLoader loader = null; + if (cl != null) + { + String[] clArr = cl.split(" "); + for (int i = 0; i < clArr.length; i++) + { + String file = clArr[i]; + Collection myFile; + try + { + myFile = FileUtils.getFiles(workingDir, file); + result.addAll(myFile); + } + catch (Exception e) + { + e.printStackTrace(); + } + } + } + } + return result; + } + + /** + * Config controller. + */ + void configController() + { + + JVMController controller = (JVMController) _controller; + + if (_config.getBoolean("wrapper.java.monitor.gc.restart", false)) + { + long max = _config.getLong("wrapper.java.monitor.gc.threshold", -1); + controller.setMaxFullGCTimeRestart(max); + } + + if (_config.getBoolean("wrapper.java.monitor.heap.restart", false)) + { + long max = _config.getLong("wrapper.java.monitor.heap.threshold.percent", -1); + controller.setMaxHeapRestart(max); + } + + controller.setLogger(getWrapperLogger()); + controller.setKey(_config.getString("wrapper.key")); + if (_config.containsKey("wrapper.port")) + { + controller.setMinPort(_config.getInt("wrapper.port")); + controller.setMaxPort(_config.getInt("wrapper.port")); + } + else + { + controller.setMinPort(_config.getInt("wrapper.port.min", Constants.DEFAULT_PORT)); + controller.setMaxPort(_config.getInt("wrapper.port.max", 65535)); + } + + controller.setStartupTimeout(_config.getInt("wrapper.startup.timeout", DEFAULT_STARTUP_TIMEOUT) * 1000); + controller.setPingTimeout(_config.getInt("wrapper.ping.timeout", DEFAULT_PING_TIMEOUT) * 1000); + if (!_initController) + { + ControllerListener restartHandler = new ControllerListener() + { + public void fire() + { + if (_state == STATE_RESTART_STOP || _state == STATE_RESTART || _state == STATE_RESTART_START || _state == STATE_RESTART_WAIT) + return; + if (allowRestart() && exitCodeRestart() && !exitCodeShutdown() && !exitCodeStop()) + { + restartInternal(); + } + else + { + if (_debug) + { + getWrapperLogger().info("giving up after " + _restartCount + " retries"); + } + if (_state != STATE_USER_STOP) + setState(STATE_ABORT); + if (!_exiting) + stop(); + setState(STATE_IDLE); + if (exitCodeShutdown()) + stopWrapper(); + + } + + } + + }; + ControllerListener killedRestartHandler = new ControllerListener() + { + public void fire() + { + if (_state == STATE_RESTART_STOP || _state == STATE_RESTART || _state == STATE_RESTART_WAIT) + return; + if (allowRestart() && exitCodeRestart() && !exitCodeShutdown()&& !exitCodeStop()) + { + restartInternal(); + } + else + { + if (_debug) + { + getWrapperLogger().info("giving up after " + _restartCount + " retries"); + } + if (_state != STATE_USER_STOP) + setState(STATE_ABORT); + if (!_exiting) + stop(); + setState(STATE_IDLE); + if (exitCodeShutdown()) + stopWrapper(); + + } + + } + + }; + controller.addListener(JVMController.STATE_STARTUP_TIMEOUT, restartHandler); + controller.addListener(JVMController.STATE_THRESHOLD, restartHandler); + controller.addListener(JVMController.STATE_PING_TIMEOUT, restartHandler); + controller.addListener(JVMController.STATE_PROCESS_KILLED, killedRestartHandler); + + if (!_config.getBoolean("wrapper.ntservice.autoreport.startup", true)) + if (getService() instanceof WrapperMainServiceWin) + setServiceStartupListener(new Runnable() + { + + public void run() + { + ((WrapperMainServiceWin) getService()).notifyStartup(); + } + + }); + + controller.setServiceStartupListener(_serviceStartupListener); + + controller.init(); + _initController = true; + } + } + + void postStart() + { + saveJavaPidFile(); + + } + + // test main + /** + * The main method. + * + * @param args + * the arguments + */ + public static void main(String[] args) + { + WrappedProcess[] w = new WrappedProcess[20]; + for (int i = 0; i < w.length; i++) + { + w[i] = new WrappedJavaProcess(); + w[i].getLocalConfiguration().setProperty("wrapper.config", "conf/wrapper.helloworld.conf"); + w[i].getLocalConfiguration().setProperty("wrapper.debug", "true"); + w[i].setUseSystemProperties(false); + w[i].init(); + } + boolean done = false; + while (!done) + { + // done = true; + for (int i = 0; i < w.length; i++) + { + System.out.println("starting " + i); + w[i].start(); + } + try + { + Thread.sleep(5000); + } + catch (InterruptedException e) + { + e.printStackTrace(); + Thread.currentThread().interrupt(); + } + for (int i = 0; i < w.length; i++) + { + System.out.println("stopping " + i); + w[i].stop(); + } + try + { + Thread.sleep(1000); + } + catch (InterruptedException e) + { + e.printStackTrace(); + Thread.currentThread().interrupt(); + } + } + } + + /** + * Save java pid file. + */ + void saveJavaPidFile() + { + String file = _config.getString("wrapper.java.pidfile"); + if (file != null) + { + try + { + _javaPidFile = new File(file); + if (!_javaPidFile.exists()) + _javaPidFile.createNewFile(); + FileWriter out = new FileWriter(_javaPidFile, false); + out.write("" + getAppPid()); + out.flush(); + out.close(); + if (_debug) + getWrapperLogger().info("created jva.pid file " + _javaPidFile.getAbsolutePath()); + } + catch (Exception e) + { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + } + + /** + * Removes the java pid file. + */ + void removeJavaPidFile() + { + if (_javaPidFile != null) + { + try + { + _javaPidFile.delete(); + + if (_debug) + getWrapperLogger().info("removed java.pid file " + _javaPidFile.getAbsolutePath()); + _javaPidFile = null; + } + catch (Exception e) + { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + } + + /** + * Reconnect. + * + * @param pid + * the pid + * + * @return true, if successful + */ + public boolean reconnect(int pid) + { + if (_state != STATE_IDLE) + return false; + + _osProcess = OperatingSystem.instance().processManagerInstance().getProcess(pid); + if (_osProcess == null) + return false; + String cmd = _osProcess.getCommand(); + if (cmd == null) + return false; + String key = getPropertyFromCommandLine("wrapper.key=[^ \"]*", cmd); + if (key == null) + return false; + String port = getPropertyFromCommandLine("wrapper.port=[^ \"]*", cmd); + if (port == null) + return false; + String configFile = getPropertyFromCommandLine("wrapper.config=[^ \"]*", cmd); + String teeName = getPropertyFromCommandLine("wrapper.teeName=[^ \"]*", cmd); + String tmpPath = getPropertyFromCommandLine("wrapper.tmpPath=[^ \"]*", cmd); + + _localConfiguration.setProperty("wrapper.key", key); + _localConfiguration.setProperty("wrapper.port", port); + if (teeName != null) + _localConfiguration.setProperty("wrapper.teeName", teeName); + _localConfiguration.setProperty("wrapper.tmpPath", tmpPath); + if (configFile != null) + _localConfiguration.setProperty("wrapper.config", configFile); + + setReconnecting(true); + + super.init(); + _osProcess.setTeeName(teeName); + _osProcess.setTmpPath(tmpPath); + _osProcess.reconnectStreams(); + + if (_controller == null) + _controller = new JVMController(this); + + JVMController controller = (JVMController) _controller; + + // controller.setDebug(true); + configController(); + + _firstRestartTime = System.currentTimeMillis(); + + // controller.setDebug(true); + controller.start(); + controller.processStarted(); + setState(STATE_RUNNING); + + boolean result = controller.waitFor(_config.getInt("wrapper.ping.timeout", DEFAULT_PING_TIMEOUT) * 1000); + if (result) + { + // wait for stream to be available + for (int i = 0; i < 5 && _osProcess.getInputStream() == null; i++) + try + { + Thread.sleep(200); + } + catch (InterruptedException e) + { + e.printStackTrace(); + Thread.currentThread().interrupt(); + } + Map triggerActions = getTriggerActions(); + Map regexTriggerActions = getRegexTriggerActions(); + Map missingTriggerActions = getMissingTriggerActions(); + Map missingRegexTriggerActions = getMissingRegexTriggerActions(); + + _gobler_in = new Gobler(_osProcess.getInputStream(), getAppLogger(), triggerActions, regexTriggerActions, missingTriggerActions, + missingRegexTriggerActions, "OUTPUT " + _osProcess.getPid(), _osProcess.getPid()); + _gobler_err = new Gobler(_osProcess.getErrorStream(), getAppLogger(), triggerActions, regexTriggerActions, missingTriggerActions, + missingRegexTriggerActions, "ERROR " + _osProcess.getPid(), _osProcess.getPid()); + executor.execute(_gobler_err); + executor.execute(_gobler_in); + setState(STATE_RUNNING); + saveJavaPidFile(); + saveLockFile(); + } + return result; + } + + /** + * Gets the property from command line. + * + * @param pattern + * the pattern + * @param cmd + * the cmd + * + * @return the property from command line + */ + private String getPropertyFromCommandLine(String pattern, String cmd) + { + String result = null; + Pattern p = Pattern.compile(pattern); + Matcher m = p.matcher(cmd); + if (m.find()) + result = m.group(); + if (result != null && result.length() > 0) + return result.substring(result.indexOf("=") + 1).replaceAll("'", ""); + return null; + } + + /** + * Gets the tee name. + * + * @return the tee name + */ + public String getTeeName() + { + return _teeName; + } + + /** + * Request thread dump. + */ + /** + * Request thread dump. + */ + public void requestThreadDump() + { + if (_controller != null) + { + JVMController controller = (JVMController) _controller; + controller.requestThreadDump(); + } + } + + public void requestGc() + { + if (_controller != null) + { + JVMController controller = (JVMController) _controller; + controller.requestGc(); + } + } + + public void requestDumpHeap(String fileName) + { + if (_controller != null) + { + JVMController controller = (JVMController) _controller; + controller.requestDumpHeap(fileName); + } + } + + void stopController(int timeout, String reason) + { + JVMController controller = (JVMController) _controller; + controller.stop(JVMController.STATE_USER_STOP, reason); + } + + public String getType() + { + return "Java-" + super.getType(); + } + + public void setServiceStartupListener(Runnable serviceStartupListener) + { + _serviceStartupListener = serviceStartupListener; + } + + protected void reloadConfiguration() + { + super.reloadConfiguration(); + } + + public float getHeapPercent() + { + if (_controller == null) + return -1; + return ((JVMController)_controller).getHeap(); + } + + public long getMinorGCTime() + { + if (_controller == null) + return -1; + return ((JVMController)_controller).getMinGC(); + } + + public long getFullGCTime() + { + if (_controller == null) + return -1; + return ((JVMController)_controller).getFullGC(); + } + + public long getHeapInBytes() + { + if (_controller == null) + { + return -1; + } + return ((JVMController) _controller).getHeapInBytes(); + } + + + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/wrapper/WrappedProcess.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/wrapper/WrappedProcess.java new file mode 100644 index 0000000000..acfd01a7b8 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/wrapper/WrappedProcess.java @@ -0,0 +1,219 @@ +package org.rzo.yajsw.wrapper; + +import java.io.OutputStream; +import java.util.Date; +import java.util.logging.Logger; + +import org.apache.commons.configuration.Configuration; +import org.jboss.netty.logging.InternalLogger; + +public interface WrappedProcess +{ + /** The Constant STATE_IDLE. */ + static final int STATE_IDLE = 0; + + /** The Constant STATE_STARTING. */ + static final int STATE_STARTING = 1; + + /** The Constant STATE_RESTART. */ + static final int STATE_RESTART = 2; + + /** The Constant STATE_STOP. */ + static final int STATE_STOP = 3; + + /** The Constant STATE_RUNNING. */ + static final int STATE_RUNNING = 4; + + /** The Constant STATE_RESTART_START. */ + static final int STATE_RESTART_START = 5; + + /** The Constant STATE_RESTART_STOP. */ + static final int STATE_RESTART_STOP = 6; + + /** The Constant STATE_RESTART_WAIT. */ + static final int STATE_RESTART_WAIT = 7; + + /** The Constant STATE_USER_STOP. */ + static final int STATE_USER_STOP = 8; + + /** The Constant STATE_ABORT. */ + static final int STATE_ABORT = 9; + + static final int STATE_SHUTDOWN = 10; + + // used only within tray icon to show that app has not yet reported that it is up and running + static final int STATE_APP_WAIT = 11; + + /** + * Inits the. + */ + public void init(); + + /** + * Start. + */ + public void start(); + + /** + * Stop. + */ + public void stop(); + public void stop(String reason); + + /** + * Restart. + */ + public void restart(); + + /** + * Gets the pid. + * + * @return the pid + */ + public int getAppPid(); + + /** + * Reconnect. + * + * @param pid + * the pid + * + * @return true, if successful + */ + public boolean reconnect(int pid); + + /** + * Gets the local configuration. + * + * @return the local configuration + */ + public Configuration getLocalConfiguration(); + + /** + * Gets the exit code. + * + * @return the exit code + */ + public int getExitCode(); + + /** + * Sets the use system properties. + * + * @param useSystemProperties + * the new use system properties + */ + public void setUseSystemProperties(boolean useSystemProperties); + + public int getState(); + + public void stopTimer(); + + public void restartInternal(); + + public void startByTimer(); + + public void restartByTimer(); + + public void setDebug(boolean b); + + public void addStateChangeListener(int state, StateChangeListener listener); + + public void addStateChangeListener(StateChangeListener listener); + + public void addTriggerListener(TriggerListener listener); + + public int getRestartCount(); + + public String getStringState(); + + public String getName(); + + public void startDrain(); + + public String readDrainLine(); + + public void stopDrain(); + + public OutputStream getOutputStream(); + + public Date getAppStarted(); + + public Date getAppStopped(); + + public Date getWrapperStarted(); + + public int getAppThreads(); + + public long getAppVMemory(); + public long getAppPMemory(); + + public int getAppCpu(); + + public int getAppHandles(); + + public int getWrapperPid(); + + public boolean isTimerActive(); + + public boolean isConditionActive(); + + public void threadDump(); + + public void gc(); + + public void dumpHeap(String fileName); + + public void stopTimerCondition(); + + public boolean isOSProcessRunning(); + + public void waitFor(); + + public void waitFor(long duration); + + public int getTotalRestartCount(); + + public String getType(); + + public Logger getWrapperLogger(); + + public void removeStateChangeListener(StateChangeListener listener); + + public void shutdown(); + + public void setLocalConfiguration(Configuration config); + + public void osProcessTerminated(); + + public boolean isHaltWrapperOnApp(); + + public boolean isHaltAppOnWrapper(); + + public void removeStateChangeListener(int state); + + public void setExiting(); + + public boolean isExiting(); + + public TrayIconProxy getTrayIcon(); + + public void setService(Object service); + + public long getMaxStartTime(); + + public void setStopper(boolean b); + + public Configuration getYajswConfig(); + + public void signalStopping(long valueOf); + + public boolean isAppReportedReady(); + + public void setAppReportedReady(boolean appReportedReady); + + public InternalLogger getInternalWrapperLogger(); + + public void update(String conf, boolean autostart); + + +} \ No newline at end of file diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/wrapper/WrappedProcessFactory.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/wrapper/WrappedProcessFactory.java new file mode 100644 index 0000000000..60d01482d0 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/wrapper/WrappedProcessFactory.java @@ -0,0 +1,46 @@ +package org.rzo.yajsw.wrapper; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.commons.configuration.Configuration; +import org.apache.commons.configuration.MapConfiguration; +import org.rzo.yajsw.config.YajswConfiguration; +import org.rzo.yajsw.config.YajswConfigurationImpl; + +public class WrappedProcessFactory +{ + public static WrappedProcess createProcess(YajswConfiguration config) + { + if (config.getString("wrapper.image") != null) + return new WrappedRuntimeProcess(); + else if (config.getString("wrapper.groovy") != null) + return new WrappedGroovyProcess(); + return new WrappedJavaProcess(); + } + + public static WrappedProcess createProcess(Map map, boolean useSystemProperties) + { + Configuration localConf = new MapConfiguration(map); + YajswConfiguration conf = new YajswConfigurationImpl(localConf, true); + WrappedProcess process = createProcess(conf); + process.setLocalConfiguration(localConf); + process.setUseSystemProperties(useSystemProperties); + process.init(); + return process; + } + + public static WrappedProcessList createProcessList(Map map, List confFiles, boolean useSystemProperties) + { + WrappedProcessList list = new WrappedProcessList(); + for (Object conf : confFiles) + { + Map sConf = new HashMap(map); + sConf.put("wrapper.config", conf); + list.add(createProcess(sConf, useSystemProperties)); + } + return list; + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/wrapper/WrappedProcessList.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/wrapper/WrappedProcessList.java new file mode 100644 index 0000000000..695801dbb4 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/wrapper/WrappedProcessList.java @@ -0,0 +1,64 @@ +package org.rzo.yajsw.wrapper; + +import java.util.ArrayList; + +public class WrappedProcessList extends ArrayList +{ + public void startAll() + { + for (WrappedProcess p : this) + { + p.start(); + } + } + + public void stopAll(String reason) + { + for (WrappedProcess p : this) + { + p.stop(reason); + } + } + + public void onStopWrapper() + { + for (WrappedProcess p : this) + { + if (p.isHaltAppOnWrapper()) + p.stop(); + } + } + + public void initAll() + { + for (WrappedProcess p : this) + { + p.init(); + } + } + + public void restartAll() + { + for (WrappedProcess p : this) + { + p.restart(); + } + } + + public void removeStateChangeListener(int state) + { + for (WrappedProcess p : this) + { + p.removeStateChangeListener(state); + } + } + + public void shutdown() + { + for (WrappedProcess p : this) + { + p.shutdown(); + } + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/wrapper/WrappedRuntimeProcess.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/wrapper/WrappedRuntimeProcess.java new file mode 100644 index 0000000000..64a3cb5a80 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/wrapper/WrappedRuntimeProcess.java @@ -0,0 +1,142 @@ +package org.rzo.yajsw.wrapper; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.apache.commons.configuration.Configuration; +import org.rzo.yajsw.controller.AbstractController.ControllerListener; +import org.rzo.yajsw.controller.runtime.RuntimeController; +import org.rzo.yajsw.os.OperatingSystem; + +public class WrappedRuntimeProcess extends AbstractWrappedProcess +{ + + @Override + void configProcess() + { + super.configProcess(); + List command = new ArrayList(); + String c = _config.getString("wrapper.image", null); + if (c == null || c.length() == 0) + { + getInternalWrapperLogger().error("wrapper.image not set -> abort"); + return; + } + // TODO check if c exists - search in PATH + command.add(c); + for (Iterator it = _config.getKeys("wrapper.app.parameter"); it.hasNext();) + { + String p = _config.getString((String)it.next()); + if (p != null) + { + p = p.trim(); + if (p.length() > 0) + command.add(p); + } + } + String[] arrCmd = new String[command.size()]; + for (int i = 0; i < arrCmd.length; i++) + arrCmd[i] = (String) command.get(i); + + _osProcess.setCommand(arrCmd); + //_osProcess.setPipeStreams(true, false); + // set this to false at your own risk. + boolean pipeStreams = _config.getBoolean("wrapper.console.pipestreams",true); + _osProcess.setPipeStreams(pipeStreams, pipeStreams); + } + + @Override + void postStart() + { + } + + @Override + void stopController(int timeout, String reason) + { + _controller.stop(RuntimeController.STATE_USER_STOPPED, reason); + _osProcess.stop(timeout, 999); + } + + public boolean reconnect(int pid) + { + try + { + _osProcess = OperatingSystem.instance().processManagerInstance().getProcess(pid); + if (!_osProcess.stop(10, 0)) + { + getWrapperLogger().severe("native processes must be restarted to consume the out and err streams. stopping of process failed"); + } + + // this.start(); + } + catch (Throwable ex) + { + getWrapperLogger().severe( + "native processes must be restarted to consume the out and err streams. stopping of process failed: " + ex.getMessage()); + ex.printStackTrace(); + } + return true; + } + + public void init() + { + super.init(); + if (_controller == null) + { + _controller = new RuntimeController(this); + configController(); + } + } + + private final ControllerListener listenerStopped = new ControllerListener() + { + public void fire() + { + getWrapperLogger().info("listener stopped"); + if (_osProcess.isRunning()) + stop(); + if (allowRestart() && exitCodeRestart() && !exitCodeShutdown()) + { + restartInternal(); + } + else + { + setState(STATE_IDLE); + if (_debug) + { + getWrapperLogger().info("giving up after " + _restartCount + " retries"); + } + } + + } + + }; + + void configController() + { + _controller.setLogger(getWrapperLogger()); + _controller.addListener(RuntimeController.STATE_STOPPED, listenerStopped); + + } + + public String getType() + { + return "Native-" + super.getType(); + } + + public static void main(String[] args) + { + WrappedRuntimeProcess p = new WrappedRuntimeProcess(); + Configuration c = p.getLocalConfiguration(); + c.setProperty("wrapper.image", "notepad");// "test.bat");//notepad");//"c:/temp/test.bat");// + c.setProperty("wrapper.working.dir", "c:/"); + p.init(); + p.start(); + p.waitFor(10000); + System.out.println("stopping"); + p.stop(); + System.out.println("stopped " + p.getExitCode()); + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/wrapper/WrappedService.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/wrapper/WrappedService.java new file mode 100644 index 0000000000..d57081f401 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/wrapper/WrappedService.java @@ -0,0 +1,632 @@ +package org.rzo.yajsw.wrapper; + +import java.io.File; +import java.io.IOException; +import java.net.MalformedURLException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.Map.Entry; +import java.util.jar.Attributes; +import java.util.jar.JarFile; +import java.util.jar.Manifest; +import java.util.logging.Logger; + +import org.apache.commons.configuration.BaseConfiguration; +import org.apache.commons.configuration.Configuration; +import org.apache.commons.configuration.ConfigurationUtils; +import org.jboss.netty.logging.SimpleLoggerFactory; +import org.rzo.yajsw.Constants; +import org.rzo.yajsw.boot.WrapperServiceBooter; +import org.rzo.yajsw.cache.Cache; +import org.rzo.yajsw.config.YajswConfigurationImpl; +import org.rzo.yajsw.os.JavaHome; +import org.rzo.yajsw.os.OperatingSystem; +import org.rzo.yajsw.os.Service; +import org.rzo.yajsw.os.ms.win.w32.WindowsXPService; +import org.rzo.yajsw.os.posix.PosixService; +import org.rzo.yajsw.util.Utils; + +public class WrappedService +{ + + /** The _config. */ + YajswConfigurationImpl _config; + + /** The _debug. */ + boolean _debug = false; + + /** The Constant PATHSEP. */ + static final String PATHSEP = System.getProperty("path.separator"); + + /** The _log. */ + Logger _log = Logger.getLogger(this.getClass().getName()); + + /** The _local configuration. */ + protected Configuration _localConfiguration = new BaseConfiguration(); + + /** The _use system properties. */ + boolean _useSystemProperties = true; + + Service _osService; + + Cache _cache = null; + + volatile boolean _init = false; + + private List _confFilesList; + + /* + * (non-Javadoc) + * + * @see jnacontrib.win32.Win32Service#init() + */ + public void init() + { + if (_init) + return; + Map utils = new HashMap(); + utils.put("util", new Utils(this)); + if (_confFilesList != null && !_confFilesList.isEmpty() && _localConfiguration != null && !_localConfiguration.containsKey("wrapper.config")) + { + _localConfiguration.setProperty("wrapper.config", _confFilesList.get(0)); + } + + _config = new YajswConfigurationImpl(_localConfiguration, _useSystemProperties, utils); + if (_confFilesList != null && !_confFilesList.isEmpty()) + { + _config.setProperty("wrapperx.config", _confFilesList); + } + + if (!_config.isLocalFile()) + if (_cache == null) + { + _cache = new Cache(); + _cache.load(_config); + } + + String dbg = _config.getString("wrapper.debug"); + _debug = dbg == null ? false : dbg.equals("true"); + _osService = OperatingSystem.instance().serviceManagerInstance().createService(); + _osService.setLogger(_log); + _osService.setName(_config.getString("wrapper.ntservice.name")); + + String displayName = _config.getString("wrapper.ntservice.displayname"); + String description = _config.getString("wrapper.ntservice.description"); + + Set dependeciesSet = new HashSet(); + for (Iterator it = _config.getKeys("wrapper.ntservice.dependency"); it.hasNext();) + { + String value = _config.getString((String) it.next()); + if (value != null && value.length() > 0) + dependeciesSet.add(value); + } + String[] dependencies = new String[dependeciesSet.size()]; + int i = 0; + for (Iterator it = dependeciesSet.iterator(); it.hasNext(); i++) + dependencies[i] = (String) it.next(); + + String account = _config.getString("wrapper.ntservice.account"); + String password = _config.getString("wrapper.ntservice.password"); + String startType = _config.getString("wrapper.ntservice.starttype", Constants.DEFAULT_SERVICE_START_TYPE); + + String[] command = getCommand(); + _osService.setAccount(account); + _osService.setCommand(command); + _osService.setDependencies(dependencies); + _osService.setDescription(description); + _osService.setDisplayName(displayName); + _osService.setPassword(password); + _osService.setConfig(_config); + _osService.setStartType(startType); + _osService.setFailureActions(OperatingSystem.instance().getServiceFailureActions(_config)); + _osService.init(); + + _init = true; + + } + + /** + * Install. + * + * @return true, if successful + */ + public boolean install() + { + int i = 0; + return _osService.install(); + } + + public boolean uninstall() + { + if (_osService == null) + return false; + stop(); + return _osService.uninstall(); + } + + /** + * Adds the classpath from manifest. + * + * @param classpath + * the classpath + * @param f + * the f + */ + protected void addClasspathFromManifest(ArrayList classpath, File f) + { + try + { + + Manifest manifest = new JarFile(f).getManifest(); + Attributes attr = manifest.getMainAttributes(); + + String cl = attr.getValue("Class-Path"); + if (cl == null) + return; + String[] clArr = cl.split(" "); + for (int i = 0; i < clArr.length; i++) + { + String file = clArr[i]; + File myFile = new File(f.getParent(), file).getCanonicalFile(); + // System.out.println("adding classpath from manifest "+file); + if (myFile.exists() && !classpath.contains(myFile)) + classpath.add(myFile); + else if (!myFile.exists()) + System.out.println("file not found " + myFile); + } + } + catch (MalformedURLException e) + { + // TODO Auto-generated catch block + e.printStackTrace(); + } + catch (IOException e) + { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + } + + /** + * Wrapper options. + * + * @return the string + */ + protected ArrayList wrapperOptions() + { + int additionalCount = 1; + boolean xrsFound = false; + ArrayList result = new ArrayList(); + + // first add lookup vars eg ${lookup} + if (_config.getBoolean("wrapper.save_interpolated", true)) + { + + for (Entry e : _config.getEnvLookupSet().entrySet()) + { + result.add("\"-D" + e.getKey() + "=" + e.getValue()+"\""); + } + for (Iterator it = _config.getLookupSet().iterator(); it.hasNext();) + { + String key = (String) it.next(); + if (!"wrapper.working.dir".equals(key)) + result.add("\"-D" + key + "=" + _config.getString(key)+"\""); + } + } + + for (Iterator it = _config.getSystemConfiguration().getKeys("wrapper"); it.hasNext();) + { + String key = (String) it.next(); + if (key.equals("wrapper.config")) + result.add("-D" + key + "=" + _config.getCachedPath()); + else + result.add("-D" + key + "=" + _config.getString(key)); + if (key.startsWith("wrapper.additional.")) + { + additionalCount++; + if (_config.getString(key).equals("-Xrs")) + xrsFound = true; + } + } + + // if we have a list of configurations + if (_confFilesList != null && _confFilesList.size() > 1) + { + String confList = ""; + // for each configuration + for (int i = 0; i < _confFilesList.size(); i++) + { + // load it + Configuration localConfiguration = ConfigurationUtils.cloneConfiguration(_localConfiguration); + String conf = _confFilesList.get(i); + localConfiguration.setProperty("wrapper.config", conf); + Map utils = new HashMap(); + utils.put("util", new Utils(this)); + YajswConfigurationImpl config = new YajswConfigurationImpl(localConfiguration, _useSystemProperties, utils); + Cache cache = null; + // check if we need to download files from remote location + if (!config.isLocalFile()) + { + cache = new Cache(); + cache.load(config); + } + // add configuration file name to list + confList += config.getCachedPath(); + if (i < (_confFilesList.size() - 1)) + confList += ","; + } + // TODO lookup is currently done only for first configuration + // add list to service parameters + result.add("-D" + "wrapperx.config" + "=" + confList); + } + + if (!xrsFound) + { + result.add("-Dwrapper.additional." + (additionalCount) + "x=-Xrs"); + } + if (_config.getString("wrapper.stop.conf") != null) + try + { + result.add("-Dwrapper.stop.conf=" + new File(_config.getString("wrapper.stop.conf")).getCanonicalPath()); + } + catch (IOException e) + { + e.printStackTrace(); + } + if (_config.getString("wrapper.groovy") != null) + try + { + result.add("-Dwrapper.groovy=" + new File(_config.getString("wrapper.groovy")).getCanonicalPath()); + } + catch (IOException e) + { + e.printStackTrace(); + } + String tmpDir = _config.getString("wrapper.tmp.path", null); + if (tmpDir == null) + tmpDir = System.getProperty("java.io.tmpdir"); + File tmpFile = new File(tmpDir); + result.add("\"-Djna_tmpdir=" + tmpFile.getAbsolutePath()+"\""); + + return result; + } + + // test main + /** + * The main method. + * + * @param args + * the arguments + */ + public Configuration getLocalConfiguration() + { + + return _localConfiguration; + } + + public void setLocalConfiguration(Configuration config) + { + _localConfiguration = config; + } + + public boolean isInstalled() + { + if (_osService != null) + return _osService.isInstalled(state()); + return false; + } + + public boolean isRunning() + { + if (_osService != null) + return _osService.isRunning(state()); + return false; + } + + public boolean isStarting() + { + if (_osService != null) + return _osService.isStarting(state()); + return false; + } + + public void setUseSystemProperties(boolean useSystem) + { + _useSystemProperties = useSystem; + } + + public boolean start() + { + // System.out.println("OsService.start() "+_osService); + if (_osService != null) + return _osService.start(); + return false; + } + + public boolean stop() + { + if (_osService != null) + return _osService.stop(); + return false; + } + + /** + * Gets the java command. + * + * @return the java command + */ + String[] getCommand() + { + // interpolate all configuration properties, so that we may evaluate all required environment variables + for (Iterator it = _config.getKeys(); it.hasNext(); ) + { + String key = (String) it.next(); + if (key.startsWith("wrapper.")) + try + { + _config.getList(key); + } + catch (Exception ex) + { + + } + } + JavaHome javaHome = OperatingSystem.instance().getJavaHome(_config); + javaHome.setLogger(SimpleLoggerFactory.getInstance(this.getClass().getName())); + String java = javaHome.findJava(_config.getString("wrapper.ntservice.java.command", _config.getString("wrapper.java.command")), _config.getString("wrapper.ntservice.java.customProcName")); + if (java == null) + { + _log.warning("no java exe found. check configuration file. -> using default \"java\""); + java = "java"; + } + ArrayList jvmOptions = jvmOptions(); + ArrayList wrapperOptions = wrapperOptions(); + String mainClass = getServiceMainClass(); + String workingDir = _config.getString("wrapper.working.dir"); + if (workingDir != null) + { + File wd = new File(workingDir); + if (!wd.exists() || !wd.isDirectory()) + _log.warning("working directory " + workingDir + " not found"); + } + String[] result = new String[jvmOptions.size() + wrapperOptions.size() + 2]; + result[0] = java; + result[result.length - 1] = mainClass; + int i = 1; + for (Iterator it = jvmOptions.listIterator(); it.hasNext(); i++) + result[i] = (String) it.next(); + for (Iterator it = wrapperOptions.listIterator(); it.hasNext(); i++) + result[i] = (String) it.next(); + return result; + } + + private String getServiceMainClass() + { + return WrapperServiceBooter.class.getName(); + } + + /** + * Jvm options. + * + * @return the string + */ + private ArrayList jvmOptions() + { + ArrayList result = new ArrayList(); + result.add("-classpath"); + ArrayList classpath = new ArrayList(); + String[] files = _config.getString("java.class.path").split(PATHSEP); + for (int i = 0; i < files.length; i++) + { + File f = null; + try + { + f = new File(files[i]).getCanonicalFile(); + } + catch (IOException e) + { + // TODO Auto-generated catch block + e.printStackTrace(); + } + if (f != null && f.exists() && !classpath.contains(f.getAbsolutePath())) + { + // System.out.println("adding classpath :"+f+":"); + classpath.add(f); + //if (f.getName().endsWith(".jar")) + // addClasspathFromManifest(classpath, f); + } + } + StringBuffer sb = new StringBuffer(); + for (Iterator it = classpath.iterator(); it.hasNext();) + { + String canonicalFileName = null; + try + { + canonicalFileName = ((File) it.next()).getCanonicalPath(); + } + catch (IOException e) + { + // TODO Auto-generated catch block + e.printStackTrace(); + } + sb.append(canonicalFileName); + if (it.hasNext()) + sb.append(PATHSEP); + } + result.add(sb.toString()); + result.add("-Xrs"); + result.add("-Dwrapper.service=true"); + String wDir = _config.getString("wrapper.working.dir"); + if (wDir != null) + { + File f = new File(wDir); + if (f.exists() && f.isDirectory()) + try + { + result.add("-Dwrapper.working.dir=" + f.getCanonicalPath()); + } + catch (IOException e) + { + e.printStackTrace(); + } + } + if (_config.getBoolean("wrapper.ntservice.debug", false)) + { + result.add("-Xdebug"); + result.add("-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=1044"); + } + for (Iterator it = _config.getKeys("wrapper.ntservice.additional"); it.hasNext();) + { + String key = (String) it.next(); + String value = _config.getString(key); + result.add(value); + } + return result; + } + + public String getServiceName() + { + return _config.getString("wrapper.ntservice.name"); + } + + public static void main(String[] args) + { + System.setProperty("wrapper.java.app.mainclass", "test.HelloWorld"); + System.setProperty("wrapper.ntservice.name", "JavaServiceWrapper_1207751158998"); + System.setProperty("wrapper.filter.trigger.1", "999"); + System.setProperty("wrapper.java.additional.1", "-Xrs"); + System.setProperty("wrapper.on_exit.default", "RESTART"); + WrappedService w = new WrappedService(); + + System.out.println("init"); + w.init(); + System.out.println("install"); + w.install(); + for (int i = 0; i < 10; i++) + { + System.out.println("start"); + w.start(); + try + { + Thread.sleep(10000); + } + catch (InterruptedException e) + { + // TODO Auto-generated catch block + e.printStackTrace(); + } + System.out.println("stop"); + w.stop(); + } + System.out.println("uninstall"); + w.uninstall(); + + } + + public int state() + { + if (_osService != null) + return _osService.state(); + return Service.STATE_UNKNOWN; + } + + public boolean isInstalled(int state) + { + if (_osService == null) + return false; + return _osService.isInstalled(state); + } + + public boolean isRunning(int state) + { + if (_osService == null) + return false; + return _osService.isRunning(state); + } + + public boolean isInteractive(int state) + { + if (_osService == null) + return false; + return _osService.isInteractive(state); + } + + public boolean isAutomatic(int state) + { + if (_osService == null) + return false; + return _osService.isAutomatic(state); + } + + public boolean isManual(int state) + { + if (_osService == null) + return false; + return _osService.isManual(state); + } + + public boolean isDisabled(int state) + { + if (_osService == null) + return false; + return _osService.isDisabled(state); + } + + public boolean isPaused(int state) + { + if (_osService == null) + return false; + return _osService.isPaused(state); + } + + public boolean isStateUnknown(int state) + { + if (_osService == null) + return false; + return _osService.isStateUnknown(state); + } + + public void stopProcess() + { + if (_osService instanceof PosixService) + { + ((PosixService) _osService).stopProcess(); + } + else + stop(); + } + + public void startProcess() + { + if (_osService instanceof PosixService) + { + ((PosixService) _osService).startProcess(); + } + else + start(); + } + + public String getConfigLocalPath() + { + return _config.getCachedPath(false); + } + + public void setConfFilesList(List confFiles) + { + _confFilesList = confFiles; + } + + public boolean requiresElevate() + { + if (_osService != null && _osService instanceof WindowsXPService) + return ((WindowsXPService)_osService).requestElevation(); + return false; + } + +} diff --git a/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/wrapper/WrappedServiceFactory.java b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/wrapper/WrappedServiceFactory.java new file mode 100644 index 0000000000..a54a4230bd --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/org/rzo/yajsw/wrapper/WrappedServiceFactory.java @@ -0,0 +1,30 @@ +package org.rzo.yajsw.wrapper; + +import java.util.List; +import java.util.Map; + +import org.apache.commons.configuration.Configuration; +import org.apache.commons.configuration.MapConfiguration; + +public class WrappedServiceFactory +{ + public static Object createService(Map map) + { + Configuration localConf = new MapConfiguration(map); + WrappedService service = new WrappedService(); + service.setLocalConfiguration(localConf); + service.init(); + return service; + } + + public static Object createServiceList(Map map, List confFiles) + { + Configuration localConf = new MapConfiguration(map); + WrappedService service = new WrappedService(); + service.setLocalConfiguration(localConf); + service.setConfFilesList(confFiles); + service.init(); + return service; + } + +} diff --git a/javaUtilities/yajsw/src/main/java/resources/About16.gif b/javaUtilities/yajsw/src/main/java/resources/About16.gif new file mode 100644 index 0000000000..04da95eb83 Binary files /dev/null and b/javaUtilities/yajsw/src/main/java/resources/About16.gif differ diff --git a/javaUtilities/yajsw/src/main/java/resources/Help16.gif b/javaUtilities/yajsw/src/main/java/resources/Help16.gif new file mode 100644 index 0000000000..dc5c2d310d Binary files /dev/null and b/javaUtilities/yajsw/src/main/java/resources/Help16.gif differ diff --git a/javaUtilities/yajsw/src/main/java/resources/clock_stop.png b/javaUtilities/yajsw/src/main/java/resources/clock_stop.png new file mode 100644 index 0000000000..6fe8a6f951 Binary files /dev/null and b/javaUtilities/yajsw/src/main/java/resources/clock_stop.png differ diff --git a/javaUtilities/yajsw/src/main/java/resources/close.png b/javaUtilities/yajsw/src/main/java/resources/close.png new file mode 100644 index 0000000000..8b0fef9a83 Binary files /dev/null and b/javaUtilities/yajsw/src/main/java/resources/close.png differ diff --git a/javaUtilities/yajsw/src/main/java/resources/console.png b/javaUtilities/yajsw/src/main/java/resources/console.png new file mode 100644 index 0000000000..c5b797a7df Binary files /dev/null and b/javaUtilities/yajsw/src/main/java/resources/console.png differ diff --git a/javaUtilities/yajsw/src/main/java/resources/delete.png b/javaUtilities/yajsw/src/main/java/resources/delete.png new file mode 100644 index 0000000000..08f249365a Binary files /dev/null and b/javaUtilities/yajsw/src/main/java/resources/delete.png differ diff --git a/javaUtilities/yajsw/src/main/java/resources/document-save.png b/javaUtilities/yajsw/src/main/java/resources/document-save.png new file mode 100644 index 0000000000..22ff495710 Binary files /dev/null and b/javaUtilities/yajsw/src/main/java/resources/document-save.png differ diff --git a/javaUtilities/yajsw/src/main/java/resources/edit-clear.png b/javaUtilities/yajsw/src/main/java/resources/edit-clear.png new file mode 100644 index 0000000000..e6c8e8b9f3 Binary files /dev/null and b/javaUtilities/yajsw/src/main/java/resources/edit-clear.png differ diff --git a/javaUtilities/yajsw/src/main/java/resources/else.png b/javaUtilities/yajsw/src/main/java/resources/else.png new file mode 100644 index 0000000000..cbd2cc8007 Binary files /dev/null and b/javaUtilities/yajsw/src/main/java/resources/else.png differ diff --git a/javaUtilities/yajsw/src/main/java/resources/exit.png b/javaUtilities/yajsw/src/main/java/resources/exit.png new file mode 100644 index 0000000000..ab6808fba5 Binary files /dev/null and b/javaUtilities/yajsw/src/main/java/resources/exit.png differ diff --git a/javaUtilities/yajsw/src/main/java/resources/exitWrapper.png b/javaUtilities/yajsw/src/main/java/resources/exitWrapper.png new file mode 100644 index 0000000000..b4486a8bc3 Binary files /dev/null and b/javaUtilities/yajsw/src/main/java/resources/exitWrapper.png differ diff --git a/javaUtilities/yajsw/src/main/java/resources/idle.png b/javaUtilities/yajsw/src/main/java/resources/idle.png new file mode 100644 index 0000000000..6b6169a9d5 Binary files /dev/null and b/javaUtilities/yajsw/src/main/java/resources/idle.png differ diff --git a/javaUtilities/yajsw/src/main/java/resources/lightning.png b/javaUtilities/yajsw/src/main/java/resources/lightning.png new file mode 100644 index 0000000000..9680afd12f Binary files /dev/null and b/javaUtilities/yajsw/src/main/java/resources/lightning.png differ diff --git a/javaUtilities/yajsw/src/main/java/resources/pause.png b/javaUtilities/yajsw/src/main/java/resources/pause.png new file mode 100644 index 0000000000..c8b4fe2251 Binary files /dev/null and b/javaUtilities/yajsw/src/main/java/resources/pause.png differ diff --git a/javaUtilities/yajsw/src/main/java/resources/recycle.png b/javaUtilities/yajsw/src/main/java/resources/recycle.png new file mode 100644 index 0000000000..a528c67d81 Binary files /dev/null and b/javaUtilities/yajsw/src/main/java/resources/recycle.png differ diff --git a/javaUtilities/yajsw/src/main/java/resources/restart.png b/javaUtilities/yajsw/src/main/java/resources/restart.png new file mode 100644 index 0000000000..101cca7d9c Binary files /dev/null and b/javaUtilities/yajsw/src/main/java/resources/restart.png differ diff --git a/javaUtilities/yajsw/src/main/java/resources/running.png b/javaUtilities/yajsw/src/main/java/resources/running.png new file mode 100644 index 0000000000..e3cdb02e55 Binary files /dev/null and b/javaUtilities/yajsw/src/main/java/resources/running.png differ diff --git a/javaUtilities/yajsw/src/main/java/resources/start.png b/javaUtilities/yajsw/src/main/java/resources/start.png new file mode 100644 index 0000000000..a7de0feb09 Binary files /dev/null and b/javaUtilities/yajsw/src/main/java/resources/start.png differ diff --git a/javaUtilities/yajsw/src/main/java/resources/startService.png b/javaUtilities/yajsw/src/main/java/resources/startService.png new file mode 100644 index 0000000000..7153e9a441 Binary files /dev/null and b/javaUtilities/yajsw/src/main/java/resources/startService.png differ diff --git a/javaUtilities/yajsw/src/main/java/resources/stop.png b/javaUtilities/yajsw/src/main/java/resources/stop.png new file mode 100644 index 0000000000..ede2815e59 Binary files /dev/null and b/javaUtilities/yajsw/src/main/java/resources/stop.png differ diff --git a/javaUtilities/yajsw/src/main/java/resources/tick.png b/javaUtilities/yajsw/src/main/java/resources/tick.png new file mode 100644 index 0000000000..a9925a06ab Binary files /dev/null and b/javaUtilities/yajsw/src/main/java/resources/tick.png differ diff --git a/javaUtilities/yajsw/src/main/java/resources/update.png b/javaUtilities/yajsw/src/main/java/resources/update.png new file mode 100644 index 0000000000..58f19c68b0 Binary files /dev/null and b/javaUtilities/yajsw/src/main/java/resources/update.png differ diff --git a/javaUtilities/yajsw/src/main/java/test/HelloWorld.java b/javaUtilities/yajsw/src/main/java/test/HelloWorld.java new file mode 100644 index 0000000000..4986abf835 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/test/HelloWorld.java @@ -0,0 +1,436 @@ +package test; + +import java.awt.AWTException; +import java.awt.BorderLayout; +import java.awt.Image; +import java.awt.SystemTray; +import java.awt.Toolkit; +import java.awt.TrayIcon; +import java.awt.image.ImageProducer; +import java.awt.image.MemoryImageSource; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileWriter; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; +import java.util.Vector; + +import javax.swing.JFrame; +import javax.swing.JLabel; + +import org.apache.commons.collections.map.CaseInsensitiveMap; +import org.rzo.yajsw.app.WrapperJVMMain; + +public class HelloWorld +{ + Map m = new CaseInsensitiveMap(); + static Map outOfMem = new HashMap(); + + static class MyWriter implements Runnable + { + public void run() + { + Thread.currentThread().setName("writer"); + + int i = 0; + while (i < 10) + { + System.out.println(i++); + try + { + Thread.sleep(100); + } + catch (InterruptedException e) + { + e.printStackTrace(); + } + } + } + + } + + public static void simulateDeadlock() + { + // These are the two resource objects we'll try to get locks for + final Object resource1 = "resource1"; + final Object resource2 = "resource2"; + // Here's the first thread. It tries to lock resource1 then resource2 + Thread t1 = new Thread() + { + public void run() + { + Thread.currentThread().setName("simulate deadlock"); + + // Lock resource 1 + synchronized (resource1) + { + System.out.println("Thread 1: locked resource 1"); + + // Pause for a bit, simulating some file I/O or something. + // Basically, we just want to give the other thread a chance + // to + // run. Threads and deadlock are asynchronous things, but + // we're + // trying to force deadlock to happen here... + try + { + Thread.sleep(50); + } + catch (InterruptedException e) + { + } + + // Now wait 'till we can get a lock on resource 2 + synchronized (resource2) + { + System.out.println("Thread 1: locked resource 2"); + } + } + } + }; + + // Here's the second thread. It tries to lock resource2 then resource1 + Thread t2 = new Thread() + { + public void run() + { + Thread.currentThread().setName("simulate deadlock 2"); + // This thread locks resource 2 right away + synchronized (resource2) + { + System.out.println("Thread 2: locked resource 2"); + + // Then it pauses, for the same reason as the first thread + // does + try + { + Thread.sleep(50); + } + catch (InterruptedException e) + { + } + + // Then it tries to lock resource1. But wait! Thread 1 + // locked + // resource1, and won't release it 'till it gets a lock on + // resource2. This thread holds the lock on resource2, and + // won't + // release it 'till it gets resource1. We're at an impasse. + // Neither + // thread can run, and the program freezes up. + synchronized (resource1) + { + System.out.println("Thread 2: locked resource 1"); + } + } + } + }; + + // Start the two threads. If all goes as planned, deadlock will occur, + // and the program will never exit. + t1.start(); + t2.start(); + } + + // test for application main. + public static void main(final String[] args) throws Exception + { + final FileWriter fw = new FileWriter("test.txt"); + Runtime.getRuntime().addShutdownHook(new Thread() + { + + public void run() + { + Thread.currentThread().setName("shutdown hook"); + if (WrapperJVMMain.WRAPPER_MANAGER != null) + System.out.println("stop reason: " + WrapperJVMMain.WRAPPER_MANAGER.getStopReason()); + if (args.length > 0 && args[0].equals("exception")) + { + System.out.println("Exception 1"); + System.out.println("Exception 2"); + System.out.println("Exception 3"); + } + + int i = 1; + // while (i>0) + // System.out.println("asdfasd"); + // Runtime.getRuntime().halt(0); + System.out.println("You wanna quit, hey?"); + try + { + fw.close(); + if (args.length > 0 && args[0].equals("signalStopping")) + { + System.out.println("+ sleeping"); + if (WrapperJVMMain.WRAPPER_MANAGER != null) + WrapperJVMMain.WRAPPER_MANAGER.signalStopping(35000); + Thread.sleep(60000); + System.out.println("- sleeping"); + } + else if (args.length > 0 && args[0].equals("sleepStop")) + { + Thread.sleep(180000); + Runtime.getRuntime().halt(0); + } + } + catch (Exception e) + { + // TODO Auto-generated catch block + e.printStackTrace(); + } + // while(true); + } + + }); + + System.out.println("java.library.path: "+System.getProperty("java.library.path")); + + if (args.length >= 1 && "crash".equals(args[0])) + { + Thread.sleep(5000); + Runtime.getRuntime().halt(99); + } + if (args.length >= 1 && "outofmem-thread".equals(args[0])) + { + int x = 0; + while (true) + { + x++; + new Thread(new Runnable() + { + + public void run() + { + try + { + // System.out.println("thread up"); + Thread.sleep(Long.MAX_VALUE); + System.out.println("thread down"); + } + catch (InterruptedException e) + { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + }).start(); + if (x % 100 == 0) + System.out.println("outofmem-thread " + x); + // Thread.sleep(10); + } + } + if (args.length >= 1 && "outofmem-heap".equals(args[0])) + { + new Thread(new Runnable() + { + + public void run() + { + int i = 0; + while (true) + { + i++; + outOfMem.put(i, "aaaaaaaaaaaaaaaaaaaaa" + i); + + if (i % 1000 == 0) + System.out.println("outofmem-heap " + i); + // Thread.sleep(10); + } + } + }).start(); + } + + if (args.length >= 1 && "appready".equals(args[0])) + { + Thread.sleep(5000); + System.out.println("calling report service startup"); + if (WrapperJVMMain.WRAPPER_MANAGER != null) + WrapperJVMMain.WRAPPER_MANAGER.reportServiceStartup(); + else + System.out.println("missing wrapper manager"); + } + + System.out.println("myenv " + System.getProperty("myenv")); + if (WrapperJVMMain.WRAPPER_MANAGER != null) + System.out.println("wrapper property: " + WrapperJVMMain.WRAPPER_MANAGER.getProperties().getProperty("wrapper.debug")); + /* + * try { Process p = Runtime.getRuntime().exec("../set.bat"); + * BufferedReader in1 = new BufferedReader(new + * InputStreamReader(p.getInputStream())); String line; while ((line = + * in1.readLine()) != null) System.out.println(line); } catch (Exception + * ex) { ex.printStackTrace(); } DocumentBuilderFactory factory = + * DocumentBuilderFactory.newInstance(); + * System.out.println(factory.getClass()); + */ + // try + // { + // Configuration config = new BaseConfiguration(); + // } + // catch (Throwable ex) + // { + // System.out.println("all ok we cannot access commons configuration"); + // ex.printStackTrace(); + // } + System.out.println("args:"); + for (int i = 0; i < args.length; i++) + System.out.println(args[i]); + final Vector v = new Vector(); + new File("test.txt").delete(); + final BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); + new Thread(new Runnable() + { + public void run() + { + Thread.currentThread().setName("input reader"); + try + { + int i = 0; + byte[] buf = new byte[256]; + while (true) + { + i++; + String line = in.readLine(); + System.out.println("in > " + line); + if (line.contains("exit 0")) + { + System.out.println("exiting 0"); + System.exit(0); + } + if (line.contains("exit 1")) + { + System.out.println("exiting 1"); + System.exit(1); + } + if (line.contains("exit 257")) + { + System.out.println("exiting 1"); + System.exit(257); + } + } + } + catch (Exception ex) + { + ex.printStackTrace(); + } + System.out.println("terminated"); + } + }).start(); + + ArrayList list = new ArrayList(); + + // System.out.println(Scheduler.class.getClassLoader()); + // System.out.println(Configuration.class.getClassLoader()); + // System.out.flush(); + int i = 0; + // org.rzo.yajsw.WrapperMain.WRAPPER_MANAGER.threadDump(); + try + { + // Thread.sleep(10000); + } + catch (Exception e2) + { + // TODO Auto-generated catch block + e2.printStackTrace(); + } + new Thread(new MyWriter()).start(); + new Thread(new MyWriter()).start(); + new Thread(new MyWriter()).start(); + // System.out.println(new BufferedReader(new + // InputStreamReader(System.in)).readLine()); + // for (; i < 10;) + if (args.length > 0 && "reportStartup".equals(args[0])) + if (WrapperJVMMain.WRAPPER_MANAGER != null) + WrapperJVMMain.WRAPPER_MANAGER.reportServiceStartup(); + + if (args.length >= 1 && "deadlock".equals(args[0])) + simulateDeadlock(); + if (args.length >= 1 && "tray".equals(args[0])) + startTray(); + + while (true) + { + i++; + System.out.println("a" + i); + System.out.flush(); + // simulate jvm crash + // while (i>3) + // list.add("asfdasffsadfdsdfsaadfsasdasf"); + + // if (i ==20) + // org.rzo.yajsw.app.WrapperJVMMain.WRAPPER_MANAGER.restart(); + + if (fw != null) + try + { + // v.add(new byte[1000]); + // fw.write("" + i + "\n"); + // fw.flush(); + } + catch (Throwable e1) + { + // TODO Auto-generated catch block + e1.printStackTrace(); + System.exit(0); + } + if (i % 2 == 0) + try + { + // WrapperJVMMain.WRAPPER_MANAGER.stop(); + Thread.sleep(500); + // System.out.println("Exception"); + // System.out.flush(); + // Runtime.getRuntime().halt(0); + } + catch (Exception e) + { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + /* + * WrapperManager.instance.restart(); try { Thread.sleep(10000); } catch + * (InterruptedException e) { // TODO Auto-generated catch block + * e.printStackTrace(); } + */ + // System.exit(0); + // System.out.println("hello world. short test"); + } + + private static void startTray() + { + SystemTray tray = SystemTray.getSystemTray(); + int w = 80; + int[] pix = new int[w * w]; + for (int i = 0; i < w * w; i++) + pix[i] = (int) (Math.random() * 255); + ImageProducer producer = new MemoryImageSource(w, w, pix, 0, w); + Image image = Toolkit.getDefaultToolkit().createImage(producer); + TrayIcon trayIcon = new TrayIcon(image); + trayIcon.setImageAutoSize(true); + startWindow(); + try + { + tray.add(trayIcon); + System.out.println("installed tray"); + } + catch (AWTException e) + { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + private static void startWindow() + { + JFrame frame = new JFrame("Hellow World"); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + frame.getContentPane().add(new JLabel("hellow world test"), BorderLayout.CENTER); + frame.pack(); + frame.setVisible(true); + } + +} diff --git a/javaUtilities/yajsw/src/main/java/test/HelloWorld2.java b/javaUtilities/yajsw/src/main/java/test/HelloWorld2.java new file mode 100644 index 0000000000..6c2c309432 --- /dev/null +++ b/javaUtilities/yajsw/src/main/java/test/HelloWorld2.java @@ -0,0 +1,21 @@ +package test; + + +public class HelloWorld2 extends HelloWorld +{ + + public static void main(String[] args) + { + try + { + Thread.sleep(30000); + } + catch (InterruptedException e) + { + // TODO Auto-generated catch block + e.printStackTrace(); + } + System.exit(99); + } + +} diff --git a/javaUtilities/yajsw/src/srvmgr/java/org/rzo/yajsw/srvmgr/client/AsyncServiceManagerServer.java b/javaUtilities/yajsw/src/srvmgr/java/org/rzo/yajsw/srvmgr/client/AsyncServiceManagerServer.java new file mode 100644 index 0000000000..a97c7ee3df --- /dev/null +++ b/javaUtilities/yajsw/src/srvmgr/java/org/rzo/yajsw/srvmgr/client/AsyncServiceManagerServer.java @@ -0,0 +1,16 @@ +package org.rzo.yajsw.srvmgr.client; + + + +public interface AsyncServiceManagerServer +{ + public Object getServiceList(); + public Object getService(String name); + public Object start(String name); + public Object stop(String name); + public Object yajswInstall(String configuration); + public Object yajswUninstall(String name); + public Object yajswReloadConsole(String name, String newConfig); + public Object isServiceManager(); + +} diff --git a/javaUtilities/yajsw/src/srvmgr/java/org/rzo/yajsw/srvmgr/client/ClientBooter.java b/javaUtilities/yajsw/src/srvmgr/java/org/rzo/yajsw/srvmgr/client/ClientBooter.java new file mode 100644 index 0000000000..87ed8d4fcb --- /dev/null +++ b/javaUtilities/yajsw/src/srvmgr/java/org/rzo/yajsw/srvmgr/client/ClientBooter.java @@ -0,0 +1,28 @@ +package org.rzo.yajsw.srvmgr.client; + +import java.lang.reflect.Method; +import java.net.URLClassLoader; + +import org.rzo.yajsw.boot.WrapperLoader; + +public class ClientBooter +{ + public static void main(String[] args) + { + URLClassLoader cl = WrapperLoader.getWrapperClassLoader(); + Thread.currentThread().setContextClassLoader(cl); + try + { + Class cls = Class.forName("org.rzo.yajsw.srvmgr.client.ClientMain", true, cl); + Method mainMethod = cls.getDeclaredMethod("main", new Class[] + { String[].class }); + mainMethod.invoke(null, new Object[] + { args }); + } + catch (Exception e) + { + e.printStackTrace(); + } + } + +} diff --git a/javaUtilities/yajsw/src/srvmgr/java/org/rzo/yajsw/srvmgr/client/ClientMain.java b/javaUtilities/yajsw/src/srvmgr/java/org/rzo/yajsw/srvmgr/client/ClientMain.java new file mode 100644 index 0000000000..8d803185b1 --- /dev/null +++ b/javaUtilities/yajsw/src/srvmgr/java/org/rzo/yajsw/srvmgr/client/ClientMain.java @@ -0,0 +1,679 @@ +package org.rzo.yajsw.srvmgr.client; + + +import org.rzo.netty.ahessian.rpc.client.HessianProxyFactory; +import org.rzo.netty.mcast.discovery.DiscoveryClient; +import org.rzo.netty.mcast.discovery.DiscoveryListener; +import org.rzo.netty.mcast.discovery.DiscoveryServer; + +import java.awt.BorderLayout; +import java.awt.Dialog.ModalityType; +import java.awt.event.ActionEvent; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.UnknownHostException; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.Timer; +import java.util.TimerTask; +import java.util.Vector; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import javax.swing.AbstractAction; +import javax.swing.DefaultComboBoxModel; +import javax.swing.JDialog; +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.WindowConstants; + +import org.jboss.netty.bootstrap.ClientBootstrap; +import org.jboss.netty.channel.Channel; +import org.jboss.netty.channel.ChannelFuture; +import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory; +import org.jboss.netty.channel.socket.oio.OioClientSocketChannelFactory; + +import org.rzo.yajsw.os.ServiceInfo; +import org.rzo.yajsw.srvmgr.server.ServiceManagerServer; + +public class ClientMain +{ + static ServicesTable servicesTable; + static HostsTable hosts; + static HiddenTable hidden; + static HashMapproxies = new HashMap(); + static final JFrame frame = new JFrame("Services Manager"); + static Set configurations = new HashSet(); + static ExecutorService executor = Executors.newCachedThreadPool(); + static DiscoveryClient discovery = new DiscoveryClient(); + + + public static void main(String[] args) throws Exception + { + ExecutorService executor = Executors.newCachedThreadPool(); + ServicesForm form = new ServicesForm(); + + servicesTable = new ServicesTable(form._SERVICES_TABLE); + hosts = new HostsTable(form._HOSTS_TABLE); + hidden = new HiddenTable(form._HIDDEN_TABLE); + loadData(); + + new Timer("hosts updater", true).schedule(new TimerTask() + { + + @Override + public void run() + { + updateHosts(); + } + + }, 0, 500); + + + form._NEW_HOST_BUTTON.setAction(new AbstractAction("+") + { + public void actionPerformed(ActionEvent e) + { + doNewHost(); + } + }); + + + form._DELETE_HOST_BUTTON.setAction(new AbstractAction("-") + { + public void actionPerformed(ActionEvent e) + { + doDeleteHost(); + } + }); + + + + form._ADD_HOSTS_BUTTON.setAction(new AbstractAction(">>") + { + public void actionPerformed(ActionEvent e) + { + for (Host host : hosts.getSelection()) + { + Host newHost = new Host(host.getName(), host.getPort()); + newHost.setState(host.getState()); + newHost.setIncluded(true); + hosts.updateObject(newHost); + servicesTable.addService(host.getName(), proxies.get(host.getName())); + } + saveData(); + } + }); + + form._REMOVE_HOSTS_BUTTON.setAction(new AbstractAction("<<") + { + public void actionPerformed(ActionEvent e) + { + for (Host host : hosts.getSelection()) + { + Host newHost = new Host(host.getName(), host.getPort()); + newHost.setState(host.getState()); + newHost.setIncluded(false); + hosts.updateObject(newHost); + servicesTable.removeService(host.getName()); + } + saveData(); + } + }); + + form._ADD_HIDDEN_BUTTON.setAction(new AbstractAction("<<") + { + public void actionPerformed(ActionEvent e) + { + for (ServiceInfo service : servicesTable.getSelection()) + { + hidden.updateObject(service); + } + saveData(); + } + }); + + form._REMOVE_HIDDEN_BUTTON.setAction(new AbstractAction(">>") + { + public void actionPerformed(ActionEvent e) + { + for (ServiceInfo service : hidden.getSelection()) + { + hidden.removeObject(service); + } + saveData(); + } + }); + + + form._START_BUTTON.setAction(new AbstractAction("Start") + { + public void actionPerformed(ActionEvent e) + { + for (ServiceInfo service : servicesTable.getSelection()) + try { + proxies.get(service.getHost()).start(service.getName()); + } + catch (Exception ex) + { + ex.printStackTrace(); + } + } + }); + + form._STOP_BUTTON.setAction(new AbstractAction("Stop") + { + public void actionPerformed(ActionEvent e) + { + for (ServiceInfo service : servicesTable.getSelection()) + try { + proxies.get(service.getHost()).stop(service.getName()); + } + catch (Exception ex) + { + ex.printStackTrace(); + } + } + }); + + form._INSTALL_BUTTON.setAction(new AbstractAction("Install") + { + public void actionPerformed(ActionEvent e) + { + doInstall(); + } + }); + + form._UNINSTALL_BUTTON.setAction(new AbstractAction("Uninstall") + { + public void actionPerformed(ActionEvent e) + { + doUninstall(); + } + }); + + form._RELOAD_CONSOLE.setAction(new AbstractAction("Reload Console App") + { + public void actionPerformed(ActionEvent e) + { + doReloadConsole(); + } + }); + + frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); + frame.setSize(910, 690); + frame.setLocationRelativeTo(null); + frame.getContentPane().add(form); + frame.setVisible(true); + + discovery.setName("serviceManagerServer"); + discovery.addListener(new DiscoveryListener() + { + + public void newHost(String serviceName, String host) + { + try + { + String[] x = host.split(":"); + int port = Integer.parseInt(x[1]); + String name = InetAddress.getByName(x[0]).getHostName(); + synchronized(hosts) + { + Host newHost = new Host(name, port); + Host oldHost = hosts.getObject(newHost); + if (oldHost != null) + { + newHost.setIncluded(oldHost.isIncluded()); + hosts.removeObject(oldHost); + } + newHost.setState("CONNECTED"); + hosts.updateObject(newHost); + + saveData(); + } + } + catch (Exception ex) + { + ex.printStackTrace(); + } + + } + + }); + discovery.init(); + discovery.start(); + + + + } + + protected static void doDeleteHost() + { + synchronized(hosts) + { + for (Host host : hosts.getSelection()) + { + hosts.removeObject(host); + servicesTable.removeService(host.getName()); + } + saveData(); + } + } + + protected static void doNewHost() + { + final NewHostDialog newDialog = new NewHostDialog(); + + final JDialog dialog = new JDialog(frame); + final Vector listData = new Vector(); + newDialog._OK_BUTTON.setAction(new AbstractAction("ADD") + { + + public void actionPerformed(ActionEvent e) + { + String name = newDialog._HOST.getText(); + String stPort = newDialog._PORT.getText(); + if (name == null || name.length() == 0 || stPort == null || stPort.length() == 0) + return; + try + { + int port = Integer.parseInt(stPort); + synchronized(hosts) + { + hosts.updateObject(new Host(name, port)); + saveData(); + } + } + catch (Exception ex) + { + ex.printStackTrace(); + } + } + + }); + newDialog._CANCEL_BUTTON.setAction(new AbstractAction("CLOSE") + { + + public void actionPerformed(ActionEvent e) + { + dialog.setVisible(false); + } + + }); + dialog.add(newDialog); + dialog.setLocationRelativeTo(frame); + dialog.setSize(350, 180); + dialog.setModalityType(ModalityType.APPLICATION_MODAL); + dialog.setVisible(true); + + } + + protected static void doUninstall() + { + final UninstallDialog uninstall = new UninstallDialog(); + + final JDialog dialog = new JDialog(frame); + + final List selected = servicesTable.getSelection(); + uninstall._SERVICES.setText(""); + for (ServiceInfo info : selected) + { + uninstall._SERVICES.setText(uninstall._SERVICES.getText()+info.getHost()+"/"+info.getDisplayName()+ " "); + } + + uninstall._OK_BUTTON.setAction(new AbstractAction("UNINSTALL") + { + + public void actionPerformed(ActionEvent e) + { + uninstall._OK_BUTTON.setEnabled(false); + uninstall._SERVICES.setText(""); + for (ServiceInfo info : selected) + { + AsyncServiceManagerServer proxy = proxies.get(info.getHost()); + if (proxy != null) + { + boolean result = false; + try + { + result = ((Boolean) ((Future)proxy.yajswUninstall(info.getName())).get(10, TimeUnit.SECONDS)).booleanValue(); + } + catch (Exception e1) + { + // TODO Auto-generated catch block + e1.printStackTrace(); + } + if (result) + uninstall._SERVICES.setText(uninstall._SERVICES.getText() + "success"); + else + uninstall._SERVICES.setText(uninstall._SERVICES.getText() + "error"); + } + else + uninstall._SERVICES.setText(uninstall._SERVICES.getText() + "no connection"); + + } + + } + + }); + uninstall._CANCEL_BUTTON.setAction(new AbstractAction("CLOSE") + { + + public void actionPerformed(ActionEvent e) + { + dialog.setVisible(false); + } + + }); + + + dialog.add(uninstall); + dialog.setLocationRelativeTo(frame); + dialog.setSize(500, 170); + dialog.setModalityType(ModalityType.APPLICATION_MODAL); + dialog.setVisible(true); + } + + protected static void doReloadConsole() + { + final ReloadConsoleDialog reloadConsole = new ReloadConsoleDialog(); + + final JDialog dialog = new JDialog(frame); + if (servicesTable.getSelection().size() == 0) + return; + final ServiceInfo selected = servicesTable.getSelection().get(0); + reloadConsole._SERVICE.setText(selected.getName()); + reloadConsole._CONFIGURATION.setModel(new DefaultComboBoxModel(new Vector(configurations))); + + reloadConsole._OK_BUTTON.setAction(new AbstractAction("Reload") + { + + public void actionPerformed(ActionEvent e) + { + reloadConsole._OK_BUTTON.setEnabled(false); + AsyncServiceManagerServer proxy = proxies.get(selected.getHost()); + boolean result = false; + try + { + result = ((Boolean)((Future)proxy.yajswReloadConsole(selected.getName(), (String) reloadConsole._CONFIGURATION.getSelectedItem())).get(10, TimeUnit.SECONDS)).booleanValue(); + } + catch (Exception e1) + { + // TODO Auto-generated catch block + e1.printStackTrace(); + } + if (result) + reloadConsole._MESSAGE.setText("success"); + else + reloadConsole._MESSAGE.setText("error"); + if (!configurations.contains(reloadConsole._CONFIGURATION.getSelectedItem())) + { + configurations.add((String) reloadConsole._CONFIGURATION.getSelectedItem()); + saveData(); + } + } + + }); + reloadConsole._CANCEL_BUTTON.setAction(new AbstractAction("CLOSE") + { + + public void actionPerformed(ActionEvent e) + { + dialog.setVisible(false); + } + + }); + + + dialog.add(reloadConsole); + dialog.setLocationRelativeTo(frame); + dialog.setSize(550, 200); + dialog.setModalityType(ModalityType.APPLICATION_MODAL); + dialog.setVisible(true); + } + + protected static void doInstall() + { + final InstallDialog install = new InstallDialog(); + + final JDialog dialog = new JDialog(frame); + final Vector listData = new Vector(); + for (Host host : hosts.getObjectList()) + { + listData.add(host.getName()); + } + install._HOSTS_LIST.setListData(listData); + for (int i=0; i selected = servicesTable.getSelection(); + if (selected.size() >0) + { + ServiceInfo selection = selected.get(0); + } + install._CONFIGURATION.setModel(new DefaultComboBoxModel(new Vector(configurations))); + install._OK_BUTTON.setAction(new AbstractAction("INSTALL") + { + + public void actionPerformed(ActionEvent e) + { + int[] selInd = install._HOSTS_LIST.getSelectedIndices(); + install._OK_BUTTON.setEnabled(false); + install._MESSAGE.setText("installing"); + for (int i : selInd) + { + install._MESSAGE.setText(install._MESSAGE.getText()+ " - " + listData.get(i) + ": "); + AsyncServiceManagerServer proxy = proxies.get(listData.get(i)); + if (proxy != null) + { + boolean result = false; + try + { + result = ((Boolean)((Future)proxy.yajswInstall((String) install._CONFIGURATION.getSelectedItem())).get(10, TimeUnit.SECONDS)).booleanValue(); + } + catch (Exception e1) + { + // TODO Auto-generated catch block + e1.printStackTrace(); + } + if (result) + install._MESSAGE.setText(install._MESSAGE.getText() + "success"); + else + install._MESSAGE.setText(install._MESSAGE.getText() + "error"); + } + else + install._MESSAGE.setText(install._MESSAGE.getText() + "no connection"); + if (!configurations.contains(install._CONFIGURATION.getSelectedItem())) + { + configurations.add((String) install._CONFIGURATION.getSelectedItem()); + } + } + saveData(); + } + + }); + install._CANCEL_BUTTON.setAction(new AbstractAction("CLOSE") + { + + public void actionPerformed(ActionEvent e) + { + dialog.setVisible(false); + } + + }); + dialog.add(install); + dialog.setLocationRelativeTo(frame); + dialog.setSize(570, 320); + dialog.setModalityType(ModalityType.APPLICATION_MODAL); + dialog.setVisible(true); + + } + + private static void saveData() + { + Map data = new HashMap(); + data.put("hosts", hosts.getObjectList()); + data.put("hidden", hidden.getObjectList()); + data.put("configurations", configurations); + File f = new File("ServiceManager.ser"); + try + { + if (!f.exists()) + f.createNewFile(); + ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(f)); + out.writeObject(data); + out.close(); + } + catch (Exception e) + { + e.printStackTrace(); + } + } + + private static void loadData() + { + File f = new File("ServiceManager.ser"); + try + { + if (!f.exists()) + return; + ObjectInputStream in = new ObjectInputStream(new FileInputStream(f)); + Map data = (Map) in.readObject(); + for (Iterator it = ((Collection)data.get("hosts")).iterator(); it.hasNext(); ) + hosts.updateObject((Host)it.next()); + for (Iterator it = ((Collection)data.get("hidden")).iterator(); it.hasNext(); ) + hidden.updateObject((ServiceInfo)it.next()); + configurations = (Set) data.get("configurations"); + in.close(); + } + catch (Exception e) + { + e.printStackTrace(); + } + + } + + private static void updateHosts() + { + boolean changed = false; + synchronized(hosts) + { + for (Host host : hosts.getObjectList()) + { + AsyncServiceManagerServer proxy = proxies.get(host.getName()); + boolean connected = false; + if (proxy != null) + { + try + { + connected = ((Boolean)((Future)proxy.isServiceManager()).get(10, TimeUnit.SECONDS)).booleanValue(); + } + catch (Exception e) + { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + else + { + ClientBootstrap bootstrap = new ClientBootstrap( + new NioClientSocketChannelFactory( + executor, + executor)); + + HessianProxyFactory factory = new HessianProxyFactory(executor, host.getName()+":"+host.getPort()); + bootstrap.setPipelineFactory( + new RPCClientPipelineFactory(executor, factory)); + + // Start the connection attempt. + ChannelFuture future = bootstrap.connect(new InetSocketAddress(host.getName(), host.getPort())); + try + { + future.await(10000); + connected = future.isSuccess(); + + + if (connected) + { + Map options = new HashMap(); + proxy = (AsyncServiceManagerServer) factory.create(AsyncServiceManagerServer.class, ClientMain.class.getClassLoader(), options); + connected = ((Boolean)((Future)proxy.isServiceManager()).get(10, TimeUnit.SECONDS)).booleanValue(); + if (connected) + { + proxies.put(host.getName(), proxy); + Host newHost = new Host(host.getName(), host.getPort()); + newHost.setIncluded(host.isIncluded()); + newHost.setState("CONNECTED"); + hosts.updateObject(newHost); + if (host.isIncluded()) + servicesTable.addService(host.getName(), proxy); + } + else + future.getChannel().close(); + } + } + catch (Exception e) + { + System.out.println("error accessing "+host.getName()); + connected = false; + if (future != null) + future.getChannel().close(); + } + + } + + if (!connected) + { + disconnect(host, proxies.remove(host.getName())); + changed = true; + } + else if (proxy == null && !"DISCONNECTED".equals(host.getState())) + { + Host newHost = new Host(host.getName(), host.getPort()); + newHost.setIncluded(host.isIncluded()); + newHost.setState("DISCONNECTED"); + hosts.updateObject(newHost); + } + } + } + } + + private static void disconnect(Host host, AsyncServiceManagerServer proxy) + { + if (proxy != null) + servicesTable.removeService(host.getName()); + Host newHost = new Host(host.getName(), host.getPort()); + newHost.setIncluded(host.isIncluded()); + newHost.setState("DISCONNECTED"); + hosts.updateObject(newHost); + try + { + discovery.removeHost(InetAddress.getByName(host.getName()).getHostAddress()+":"+host.getPort()); + } + catch (Exception e) + { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + + +} diff --git a/javaUtilities/yajsw/src/srvmgr/java/org/rzo/yajsw/srvmgr/client/HiddenTable.java b/javaUtilities/yajsw/src/srvmgr/java/org/rzo/yajsw/srvmgr/client/HiddenTable.java new file mode 100644 index 0000000000..4a89ff7613 --- /dev/null +++ b/javaUtilities/yajsw/src/srvmgr/java/org/rzo/yajsw/srvmgr/client/HiddenTable.java @@ -0,0 +1,84 @@ +package org.rzo.yajsw.srvmgr.client; + +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTable; + +import org.rzo.yajsw.os.ServiceInfo; +import org.rzo.yajsw.util.BeanTableFormat; +import org.rzo.yajsw.util.ObservableList; +import org.rzo.yajsw.util.ObservableObject; + +import ca.odell.glazedlists.BasicEventList; +import ca.odell.glazedlists.EventList; +import ca.odell.glazedlists.GlazedLists; +import ca.odell.glazedlists.ObservableElementList; +import ca.odell.glazedlists.SortedList; +import ca.odell.glazedlists.swing.EventSelectionModel; +import ca.odell.glazedlists.swing.EventTableModel; +import ca.odell.glazedlists.swing.TableComparatorChooser; + +public class HiddenTable +{ + String[] propertyNames = {"displayName"}; + String[] columnLabels = {"Name"}; + + ObservableList list; + EventSelectionModel _selection; + + + HiddenTable(JTable jTable) + { + ObservableElementList.Connector connector = GlazedLists.beanConnector(ObservableObject.class); + SortedList servicesEventList = + new SortedList( + new ObservableElementList ( + GlazedLists.threadSafeList( + new BasicEventList() + ), connector + )); + EventTableModel servicesTableModel = new EventTableModel(servicesEventList, + new BeanTableFormat(propertyNames, columnLabels)); + list = new ObservableList(servicesEventList, propertyNames, new String[]{"name"}); + jTable.setModel(servicesTableModel); + _selection = new EventSelectionModel(servicesEventList); + jTable.setSelectionModel(_selection); + } + + public void updateObject(ServiceInfo service) + { + list.updateObject(service); + } + + public void removeObject(ServiceInfo service) + { + list.removeObject(service); + } + + public boolean containsObject(ServiceInfo service) + { + return list.containsObject(service); + } + + public List getSelection() + { + List result = new ArrayList(); + EventList selection = _selection.getSelected(); + for (ObservableObject x : selection) + result.add((ServiceInfo) x.getRoot()); + return result; + } + + public Collection getObjectList() + { + return list.getObjectList(); + } + + +} diff --git a/javaUtilities/yajsw/src/srvmgr/java/org/rzo/yajsw/srvmgr/client/Host.java b/javaUtilities/yajsw/src/srvmgr/java/org/rzo/yajsw/srvmgr/client/Host.java new file mode 100644 index 0000000000..8e3b93dd7e --- /dev/null +++ b/javaUtilities/yajsw/src/srvmgr/java/org/rzo/yajsw/srvmgr/client/Host.java @@ -0,0 +1,70 @@ +package org.rzo.yajsw.srvmgr.client; + +import java.io.Serializable; + +public class Host implements Serializable, Comparable +{ + String _name; + int _port; + boolean _included = false; + String _state = "UNKNOWN"; + + Host() + { + _state = "UNKOWN"; + } + + public Host(String name, int port) + { + _name = name; + _port = port; + _state = "UNKOWN"; + } + + public String getName() + { + return _name; + } + + public void setName(String name) + { + //_name = name; + } + + public boolean isIncluded() + { + return _included; + } + + public String getState() + { + return _state; + } + + public void setIncluded(boolean included) + { + _included = included; + } + + public void setState(String state) + { + _state = state; + } + + public int compareTo(Host o) + { + return _name.compareTo(o.getName()); + } + + public int getPort() + { + return _port; + } + + public void setPort(int port) + { + _port = port; + } + + +} diff --git a/javaUtilities/yajsw/src/srvmgr/java/org/rzo/yajsw/srvmgr/client/HostsTable.java b/javaUtilities/yajsw/src/srvmgr/java/org/rzo/yajsw/srvmgr/client/HostsTable.java new file mode 100644 index 0000000000..c60f13fe84 --- /dev/null +++ b/javaUtilities/yajsw/src/srvmgr/java/org/rzo/yajsw/srvmgr/client/HostsTable.java @@ -0,0 +1,96 @@ +package org.rzo.yajsw.srvmgr.client; + +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Timer; +import java.util.TimerTask; + +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTable; + +import org.rzo.yajsw.os.ServiceInfo; +import org.rzo.yajsw.srvmgr.server.ServiceManagerServer; +import org.rzo.yajsw.util.BeanTableFormat; +import org.rzo.yajsw.util.ObservableList; +import org.rzo.yajsw.util.ObservableObject; + +import ca.odell.glazedlists.BasicEventList; +import ca.odell.glazedlists.EventList; +import ca.odell.glazedlists.GlazedLists; +import ca.odell.glazedlists.ObservableElementList; +import ca.odell.glazedlists.SortedList; +import ca.odell.glazedlists.swing.EventSelectionModel; +import ca.odell.glazedlists.swing.EventTableModel; +import ca.odell.glazedlists.swing.TableComparatorChooser; + +public class HostsTable +{ + String[] propertyNames = {"name", "state", "included"}; + String[] columnLabels = {"Name", "State", "In"}; + + ObservableList list; + EventSelectionModel _selection; + + + HostsTable(JTable jTable) + { + ObservableElementList.Connector connector = GlazedLists.beanConnector(ObservableObject.class); + SortedList servicesEventList = + new SortedList( + new ObservableElementList ( + GlazedLists.threadSafeList( + new BasicEventList() + ), connector + )); + EventTableModel servicesTableModel = new EventTableModel(servicesEventList, + new BeanTableFormat(propertyNames, columnLabels)); + list = new ObservableList(servicesEventList, propertyNames, new String[]{"name"}); + //JTable jTable = new JTable(servicesTableModel); + jTable.setModel(servicesTableModel); + _selection = new EventSelectionModel(servicesEventList); + jTable.setSelectionModel(_selection); + TableComparatorChooser tableSorter = TableComparatorChooser.install(jTable, servicesEventList, TableComparatorChooser.SINGLE_COLUMN); + Dimension size = jTable.getPreferredScrollableViewportSize(); +// jTable.setPreferredScrollableViewportSize +// (new Dimension(jTable.getPreferredSize().width, size.height)); +// JScrollPane servicesListScrollPane = new JScrollPane(jTable); +// this.setLayout(new BorderLayout()); +// this.add(servicesListScrollPane, BorderLayout.CENTER); + } + + public void updateObject(Host host) + { + list.updateObject(host); + } + + public void removeObject(Host host) + { + list.removeObject(host); + } + + public List getSelection() + { + List result = new ArrayList(); + EventList selection = _selection.getSelected(); + for (ObservableObject x : selection) + result.add((Host) x.getRoot()); + return result; + } + + public Host getObject(Host host) + { + return list.getObject(host); + } + + public Collection getObjectList() + { + return list.getObjectList(); + } + + + +} diff --git a/javaUtilities/yajsw/src/srvmgr/java/org/rzo/yajsw/srvmgr/client/InstallDialog.java b/javaUtilities/yajsw/src/srvmgr/java/org/rzo/yajsw/srvmgr/client/InstallDialog.java new file mode 100644 index 0000000000..c3fe8f83f6 --- /dev/null +++ b/javaUtilities/yajsw/src/srvmgr/java/org/rzo/yajsw/srvmgr/client/InstallDialog.java @@ -0,0 +1,213 @@ +package org.rzo.yajsw.srvmgr.client; + +import com.jeta.open.i18n.I18NUtils; +import com.jgoodies.forms.layout.CellConstraints; +import com.jgoodies.forms.layout.FormLayout; +import java.awt.BorderLayout; +import java.awt.ComponentOrientation; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import javax.swing.Box; +import javax.swing.ImageIcon; +import javax.swing.JButton; +import javax.swing.JComboBox; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.JPanel; +import javax.swing.JScrollPane; + + +public class InstallDialog extends JPanel +{ + JButton _CANCEL_BUTTON = new JButton(); + JButton _OK_BUTTON = new JButton(); + JComboBox _CONFIGURATION = new JComboBox(); + JLabel _MESSAGE = new JLabel(); + JList _HOSTS_LIST = new JList(); + + /** + * Default constructor + */ + public InstallDialog() + { + initializePanel(); + } + + /** + * Main method for panel + */ + public static void main(String[] args) + { + JFrame frame = new JFrame(); + frame.setSize(600, 400); + frame.setLocation(100, 100); + frame.getContentPane().add(new InstallDialog()); + frame.setVisible(true); + + frame.addWindowListener( new WindowAdapter() + { + public void windowClosing( WindowEvent evt ) + { + System.exit(0); + } + }); + } + + /** + * Adds fill components to empty cells in the first row and first column of the grid. + * This ensures that the grid spacing will be the same as shown in the designer. + * @param cols an array of column indices in the first row where fill components should be added. + * @param rows an array of row indices in the first column where fill components should be added. + */ + void addFillComponents( Container panel, int[] cols, int[] rows ) + { + Dimension filler = new Dimension(10,10); + + boolean filled_cell_11 = false; + CellConstraints cc = new CellConstraints(); + if ( cols.length > 0 && rows.length > 0 ) + { + if ( cols[0] == 1 && rows[0] == 1 ) + { + /** add a rigid area */ + panel.add( Box.createRigidArea( filler ), cc.xy(1,1) ); + filled_cell_11 = true; + } + } + + for( int index = 0; index < cols.length; index++ ) + { + if ( cols[index] == 1 && filled_cell_11 ) + { + continue; + } + panel.add( Box.createRigidArea( filler ), cc.xy(cols[index],1) ); + } + + for( int index = 0; index < rows.length; index++ ) + { + if ( rows[index] == 1 && filled_cell_11 ) + { + continue; + } + panel.add( Box.createRigidArea( filler ), cc.xy(1,rows[index]) ); + } + + } + + /** + * Helper method to load an image file from the CLASSPATH + * @param imageName the package and name of the file to load relative to the CLASSPATH + * @return an ImageIcon instance with the specified image file + * @throws IllegalArgumentException if the image resource cannot be loaded. + */ + public ImageIcon loadImage( String imageName ) + { + try + { + ClassLoader classloader = getClass().getClassLoader(); + java.net.URL url = classloader.getResource( imageName ); + if ( url != null ) + { + ImageIcon icon = new ImageIcon( url ); + return icon; + } + } + catch( Exception e ) + { + e.printStackTrace(); + } + throw new IllegalArgumentException( "Unable to load image: " + imageName ); + } + + /** + * Method for recalculating the component orientation for + * right-to-left Locales. + * @param orientation the component orientation to be applied + */ + public void applyComponentOrientation( ComponentOrientation orientation ) + { + // Not yet implemented... + // I18NUtils.applyComponentOrientation(this, orientation); + super.applyComponentOrientation(orientation); + } + + public JPanel createPanel() + { + JPanel jpanel1 = new JPanel(); + FormLayout formlayout1 = new FormLayout("FILL:DEFAULT:NONE,FILL:DEFAULT:NONE,FILL:4DLU:NONE,FILL:143PX:NONE,FILL:249PX:NONE,FILL:DEFAULT:NONE","CENTER:DEFAULT:NONE,CENTER:DEFAULT:NONE,CENTER:DEFAULT:NONE,CENTER:DEFAULT:NONE,CENTER:111PX:NONE,CENTER:4DLU:NONE,CENTER:DEFAULT:NONE,CENTER:12DLU:NONE,CENTER:DEFAULT:NONE,CENTER:DEFAULT:NONE,CENTER:DEFAULT:NONE"); + CellConstraints cc = new CellConstraints(); + jpanel1.setLayout(formlayout1); + + JLabel jlabel1 = new JLabel(); + jlabel1.setFont(new Font("Tahoma",Font.BOLD,11)); + jlabel1.setText("YAJSW Configuration"); + jpanel1.add(jlabel1,cc.xy(2,7)); + + jpanel1.add(createPanel1(),cc.xywh(2,9,4,1)); + JLabel jlabel2 = new JLabel(); + jlabel2.setFont(new Font("Tahoma",Font.BOLD,12)); + jlabel2.setText("Install/Reinstall YAJSW Service"); + jlabel2.setHorizontalAlignment(JLabel.CENTER); + jpanel1.add(jlabel2,cc.xywh(2,2,4,1)); + + _CONFIGURATION.setEditable(true); + _CONFIGURATION.setName("CONFIGURATION"); + _CONFIGURATION.setRequestFocusEnabled(false); + jpanel1.add(_CONFIGURATION,cc.xywh(4,7,2,1)); + + _MESSAGE.setName("MESSAGE"); + jpanel1.add(_MESSAGE,cc.xywh(2,10,4,1)); + + _HOSTS_LIST.setName("HOSTS_LIST"); + JScrollPane jscrollpane1 = new JScrollPane(); + jscrollpane1.setViewportView(_HOSTS_LIST); + jscrollpane1.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED); + jscrollpane1.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); + jpanel1.add(jscrollpane1,cc.xywh(4,4,1,2)); + + JLabel jlabel3 = new JLabel(); + jlabel3.setFont(new Font("Tahoma",Font.BOLD,11)); + jlabel3.setText("Hosts"); + jpanel1.add(jlabel3,cc.xy(2,4)); + + addFillComponents(jpanel1,new int[]{ 1,2,3,4,5,6 },new int[]{ 1,2,3,4,5,6,7,8,9,10,11 }); + return jpanel1; + } + + public JPanel createPanel1() + { + JPanel jpanel1 = new JPanel(); + FormLayout formlayout1 = new FormLayout("FILL:308PX:NONE,FILL:89PX:NONE,FILL:22PX:NONE,FILL:87PX:NONE","CENTER:DEFAULT:NONE"); + CellConstraints cc = new CellConstraints(); + jpanel1.setLayout(formlayout1); + + _CANCEL_BUTTON.setActionCommand("Cancel"); + _CANCEL_BUTTON.setName("CANCEL_BUTTON"); + _CANCEL_BUTTON.setText("CLOSE"); + jpanel1.add(_CANCEL_BUTTON,cc.xy(4,1)); + + _OK_BUTTON.setActionCommand("OK"); + _OK_BUTTON.setName("OK_BUTTON"); + _OK_BUTTON.setText("INSTALL"); + jpanel1.add(_OK_BUTTON,cc.xy(2,1)); + + addFillComponents(jpanel1,new int[]{ 1,3 },new int[]{ 1 }); + return jpanel1; + } + + /** + * Initializer + */ + protected void initializePanel() + { + setLayout(new BorderLayout()); + add(createPanel(), BorderLayout.CENTER); + } + + +} diff --git a/javaUtilities/yajsw/src/srvmgr/java/org/rzo/yajsw/srvmgr/client/NewHostDialog.java b/javaUtilities/yajsw/src/srvmgr/java/org/rzo/yajsw/srvmgr/client/NewHostDialog.java new file mode 100644 index 0000000000..ab2a2ec921 --- /dev/null +++ b/javaUtilities/yajsw/src/srvmgr/java/org/rzo/yajsw/srvmgr/client/NewHostDialog.java @@ -0,0 +1,205 @@ +package org.rzo.yajsw.srvmgr.client; + +import com.jeta.open.i18n.I18NUtils; +import com.jgoodies.forms.layout.CellConstraints; +import com.jgoodies.forms.layout.FormLayout; +import java.awt.BorderLayout; +import java.awt.ComponentOrientation; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import javax.swing.Box; +import javax.swing.ImageIcon; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JTextField; + + +public class NewHostDialog extends JPanel +{ + JTextField _HOST = new JTextField(); + JLabel _MESSAGE = new JLabel(); + JTextField _PORT = new JTextField(); + JButton _CANCEL_BUTTON = new JButton(); + JButton _OK_BUTTON = new JButton(); + + /** + * Default constructor + */ + public NewHostDialog() + { + initializePanel(); + } + + /** + * Main method for panel + */ + public static void main(String[] args) + { + JFrame frame = new JFrame(); + frame.setSize(600, 400); + frame.setLocation(100, 100); + frame.getContentPane().add(new NewHostDialog()); + frame.setVisible(true); + + frame.addWindowListener( new WindowAdapter() + { + public void windowClosing( WindowEvent evt ) + { + System.exit(0); + } + }); + } + + /** + * Adds fill components to empty cells in the first row and first column of the grid. + * This ensures that the grid spacing will be the same as shown in the designer. + * @param cols an array of column indices in the first row where fill components should be added. + * @param rows an array of row indices in the first column where fill components should be added. + */ + void addFillComponents( Container panel, int[] cols, int[] rows ) + { + Dimension filler = new Dimension(10,10); + + boolean filled_cell_11 = false; + CellConstraints cc = new CellConstraints(); + if ( cols.length > 0 && rows.length > 0 ) + { + if ( cols[0] == 1 && rows[0] == 1 ) + { + /** add a rigid area */ + panel.add( Box.createRigidArea( filler ), cc.xy(1,1) ); + filled_cell_11 = true; + } + } + + for( int index = 0; index < cols.length; index++ ) + { + if ( cols[index] == 1 && filled_cell_11 ) + { + continue; + } + panel.add( Box.createRigidArea( filler ), cc.xy(cols[index],1) ); + } + + for( int index = 0; index < rows.length; index++ ) + { + if ( rows[index] == 1 && filled_cell_11 ) + { + continue; + } + panel.add( Box.createRigidArea( filler ), cc.xy(1,rows[index]) ); + } + + } + + /** + * Helper method to load an image file from the CLASSPATH + * @param imageName the package and name of the file to load relative to the CLASSPATH + * @return an ImageIcon instance with the specified image file + * @throws IllegalArgumentException if the image resource cannot be loaded. + */ + public ImageIcon loadImage( String imageName ) + { + try + { + ClassLoader classloader = getClass().getClassLoader(); + java.net.URL url = classloader.getResource( imageName ); + if ( url != null ) + { + ImageIcon icon = new ImageIcon( url ); + return icon; + } + } + catch( Exception e ) + { + e.printStackTrace(); + } + throw new IllegalArgumentException( "Unable to load image: " + imageName ); + } + + /** + * Method for recalculating the component orientation for + * right-to-left Locales. + * @param orientation the component orientation to be applied + */ + public void applyComponentOrientation( ComponentOrientation orientation ) + { + // Not yet implemented... + // I18NUtils.applyComponentOrientation(this, orientation); + super.applyComponentOrientation(orientation); + } + + public JPanel createPanel() + { + JPanel jpanel1 = new JPanel(); + FormLayout formlayout1 = new FormLayout("FILL:DEFAULT:NONE,FILL:DEFAULT:NONE,FILL:4DLU:NONE,FILL:143PX:NONE,FILL:120PX:NONE,FILL:DEFAULT:NONE","CENTER:DEFAULT:NONE,CENTER:DEFAULT:NONE,CENTER:DEFAULT:NONE,CENTER:DEFAULT:NONE,CENTER:4DLU:NONE,CENTER:DEFAULT:NONE,CENTER:12DLU:NONE,CENTER:DEFAULT:NONE,CENTER:DEFAULT:NONE,CENTER:DEFAULT:NONE"); + CellConstraints cc = new CellConstraints(); + jpanel1.setLayout(formlayout1); + + JLabel jlabel1 = new JLabel(); + jlabel1.setFont(new Font("Tahoma",Font.BOLD,11)); + jlabel1.setText("Port"); + jpanel1.add(jlabel1,cc.xy(2,6)); + + JLabel jlabel2 = new JLabel(); + jlabel2.setFont(new Font("Tahoma",Font.BOLD,12)); + jlabel2.setText("Add Host"); + jlabel2.setHorizontalAlignment(JLabel.CENTER); + jpanel1.add(jlabel2,cc.xywh(2,2,4,1)); + + JLabel jlabel3 = new JLabel(); + jlabel3.setFont(new Font("Tahoma",Font.BOLD,11)); + jlabel3.setText("Host"); + jpanel1.add(jlabel3,cc.xy(2,4)); + + _HOST.setName("HOST"); + jpanel1.add(_HOST,cc.xywh(4,4,2,1)); + + _MESSAGE.setName("MESSAGE"); + jpanel1.add(_MESSAGE,cc.xywh(2,9,4,1)); + + _PORT.setName("PORT"); + jpanel1.add(_PORT,cc.xywh(4,6,2,1)); + + jpanel1.add(createPanel1(),cc.xywh(2,8,4,1)); + addFillComponents(jpanel1,new int[]{ 1,2,3,4,5,6 },new int[]{ 1,2,3,4,5,6,7,8,9,10 }); + return jpanel1; + } + + public JPanel createPanel1() + { + JPanel jpanel1 = new JPanel(); + FormLayout formlayout1 = new FormLayout("FILL:93PX:NONE,FILL:89PX:NONE,FILL:22PX:NONE,FILL:87PX:NONE","CENTER:DEFAULT:NONE"); + CellConstraints cc = new CellConstraints(); + jpanel1.setLayout(formlayout1); + + _CANCEL_BUTTON.setActionCommand("Cancel"); + _CANCEL_BUTTON.setName("CANCEL_BUTTON"); + _CANCEL_BUTTON.setText("CANCEL"); + jpanel1.add(_CANCEL_BUTTON,cc.xy(4,1)); + + _OK_BUTTON.setActionCommand("OK"); + _OK_BUTTON.setName("OK_BUTTON"); + _OK_BUTTON.setText("ADD HOST"); + jpanel1.add(_OK_BUTTON,cc.xy(2,1)); + + addFillComponents(jpanel1,new int[]{ 1,3 },new int[]{ 1 }); + return jpanel1; + } + + /** + * Initializer + */ + protected void initializePanel() + { + setLayout(new BorderLayout()); + add(createPanel(), BorderLayout.CENTER); + } + + +} diff --git a/javaUtilities/yajsw/src/srvmgr/java/org/rzo/yajsw/srvmgr/client/RPCClientPipelineFactory.java b/javaUtilities/yajsw/src/srvmgr/java/org/rzo/yajsw/srvmgr/client/RPCClientPipelineFactory.java new file mode 100644 index 0000000000..17c9d27b92 --- /dev/null +++ b/javaUtilities/yajsw/src/srvmgr/java/org/rzo/yajsw/srvmgr/client/RPCClientPipelineFactory.java @@ -0,0 +1,59 @@ +package org.rzo.yajsw.srvmgr.client; + +import static org.jboss.netty.channel.Channels.pipeline; + +import java.util.concurrent.Executor; + +import org.rzo.netty.ahessian.rpc.client.HessianProxyFactory; +import org.rzo.netty.ahessian.rpc.client.ReconnectHandler; +import org.rzo.netty.ahessian.rpc.message.HessianRPCCallEncoder; +import org.rzo.netty.ahessian.rpc.message.HessianRPCReplyDecoder; +import org.rzo.netty.ahessian.rpc.message.OutputProducer; +import org.rzo.netty.ahessian.auth.ClientAuthFilter; +import org.rzo.netty.ahessian.auth.EncryptedAuthToken; +import org.rzo.netty.ahessian.auth.ServerAuthFilter; +import org.rzo.netty.ahessian.io.InputStreamDecoder; +import org.rzo.netty.ahessian.io.OutputStreamEncoder; +import org.rzo.netty.ahessian.io.PullInputStreamConsumer; +import org.rzo.netty.ahessian.log.OutLogger; +import org.rzo.netty.ahessian.session.MixinPipeline; + +import org.jboss.netty.channel.ChannelPipeline; +import org.jboss.netty.channel.ChannelPipelineFactory; + + + +public class RPCClientPipelineFactory implements ChannelPipelineFactory +{ + + private HessianProxyFactory _factory; + private Executor _executor; + private Host _host; + + public RPCClientPipelineFactory (Executor executor, HessianProxyFactory factory) + { + _factory = factory; + _executor = executor; + } + + public ChannelPipeline getPipeline() throws Exception + { + ChannelPipeline pipeline = pipeline(); // Note the static import. + // InputStreamDecoder returns an input stream and calls the next handler in a separate thread + + pipeline.addLast("inputStream", new InputStreamDecoder()); + + //pipeline.addLast("logger2",new OutLogger("2")); + pipeline.addLast("outputStream", new OutputStreamEncoder()); + + pipeline.addLast("hessianReplyDecoder", new PullInputStreamConsumer(new HessianRPCReplyDecoder(_factory), _executor)); + pipeline.addLast("hessianCallEncoder", new HessianRPCCallEncoder()); + pipeline.addLast("outputProducer", new OutputProducer(_executor)); + //pipeline.addLast("logger3",new OutLogger("3")); + pipeline.addLast("hessianHandler", _factory); + + return pipeline; + } + + +} diff --git a/javaUtilities/yajsw/src/srvmgr/java/org/rzo/yajsw/srvmgr/client/ReloadConsoleDialog.java b/javaUtilities/yajsw/src/srvmgr/java/org/rzo/yajsw/srvmgr/client/ReloadConsoleDialog.java new file mode 100644 index 0000000000..2f1b41dd35 --- /dev/null +++ b/javaUtilities/yajsw/src/srvmgr/java/org/rzo/yajsw/srvmgr/client/ReloadConsoleDialog.java @@ -0,0 +1,209 @@ +package org.rzo.yajsw.srvmgr.client; + +import com.jeta.open.i18n.I18NUtils; +import com.jgoodies.forms.layout.CellConstraints; +import com.jgoodies.forms.layout.FormLayout; +import java.awt.BorderLayout; +import java.awt.ComponentOrientation; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import javax.swing.Box; +import javax.swing.ImageIcon; +import javax.swing.JButton; +import javax.swing.JComboBox; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JTextField; + + +public class ReloadConsoleDialog extends JPanel +{ + JButton _CANCEL_BUTTON = new JButton(); + JButton _OK_BUTTON = new JButton(); + JComboBox _CONFIGURATION = new JComboBox(); + JTextField _SERVICE = new JTextField(); + JLabel _MESSAGE = new JLabel(); + + /** + * Default constructor + */ + public ReloadConsoleDialog() + { + initializePanel(); + } + + /** + * Main method for panel + */ + public static void main(String[] args) + { + JFrame frame = new JFrame(); + frame.setSize(600, 400); + frame.setLocation(100, 100); + frame.getContentPane().add(new ReloadConsoleDialog()); + frame.setVisible(true); + + frame.addWindowListener( new WindowAdapter() + { + public void windowClosing( WindowEvent evt ) + { + System.exit(0); + } + }); + } + + /** + * Adds fill components to empty cells in the first row and first column of the grid. + * This ensures that the grid spacing will be the same as shown in the designer. + * @param cols an array of column indices in the first row where fill components should be added. + * @param rows an array of row indices in the first column where fill components should be added. + */ + void addFillComponents( Container panel, int[] cols, int[] rows ) + { + Dimension filler = new Dimension(10,10); + + boolean filled_cell_11 = false; + CellConstraints cc = new CellConstraints(); + if ( cols.length > 0 && rows.length > 0 ) + { + if ( cols[0] == 1 && rows[0] == 1 ) + { + /** add a rigid area */ + panel.add( Box.createRigidArea( filler ), cc.xy(1,1) ); + filled_cell_11 = true; + } + } + + for( int index = 0; index < cols.length; index++ ) + { + if ( cols[index] == 1 && filled_cell_11 ) + { + continue; + } + panel.add( Box.createRigidArea( filler ), cc.xy(cols[index],1) ); + } + + for( int index = 0; index < rows.length; index++ ) + { + if ( rows[index] == 1 && filled_cell_11 ) + { + continue; + } + panel.add( Box.createRigidArea( filler ), cc.xy(1,rows[index]) ); + } + + } + + /** + * Helper method to load an image file from the CLASSPATH + * @param imageName the package and name of the file to load relative to the CLASSPATH + * @return an ImageIcon instance with the specified image file + * @throws IllegalArgumentException if the image resource cannot be loaded. + */ + public ImageIcon loadImage( String imageName ) + { + try + { + ClassLoader classloader = getClass().getClassLoader(); + java.net.URL url = classloader.getResource( imageName ); + if ( url != null ) + { + ImageIcon icon = new ImageIcon( url ); + return icon; + } + } + catch( Exception e ) + { + e.printStackTrace(); + } + throw new IllegalArgumentException( "Unable to load image: " + imageName ); + } + + /** + * Method for recalculating the component orientation for + * right-to-left Locales. + * @param orientation the component orientation to be applied + */ + public void applyComponentOrientation( ComponentOrientation orientation ) + { + // Not yet implemented... + // I18NUtils.applyComponentOrientation(this, orientation); + super.applyComponentOrientation(orientation); + } + + public JPanel createPanel() + { + JPanel jpanel1 = new JPanel(); + FormLayout formlayout1 = new FormLayout("FILL:DEFAULT:NONE,FILL:DEFAULT:NONE,FILL:4DLU:NONE,FILL:143PX:NONE,FILL:249PX:NONE,FILL:DEFAULT:NONE","CENTER:DEFAULT:NONE,CENTER:DEFAULT:NONE,CENTER:DEFAULT:NONE,CENTER:DEFAULT:NONE,CENTER:4DLU:NONE,CENTER:DEFAULT:NONE,CENTER:12DLU:NONE,CENTER:DEFAULT:NONE,CENTER:DEFAULT:NONE,CENTER:DEFAULT:NONE"); + CellConstraints cc = new CellConstraints(); + jpanel1.setLayout(formlayout1); + + JLabel jlabel1 = new JLabel(); + jlabel1.setFont(new Font("Tahoma",Font.BOLD,11)); + jlabel1.setText("YAJSW Configuration"); + jpanel1.add(jlabel1,cc.xy(2,6)); + + jpanel1.add(createPanel1(),cc.xywh(2,8,4,1)); + JLabel jlabel2 = new JLabel(); + jlabel2.setFont(new Font("Tahoma",Font.BOLD,12)); + jlabel2.setText("Reload YAJSW Console Application"); + jlabel2.setHorizontalAlignment(JLabel.CENTER); + jpanel1.add(jlabel2,cc.xywh(2,2,4,1)); + + _CONFIGURATION.setEditable(true); + _CONFIGURATION.setName("CONFIGURATION"); + _CONFIGURATION.setRequestFocusEnabled(false); + jpanel1.add(_CONFIGURATION,cc.xywh(4,6,2,1)); + + JLabel jlabel3 = new JLabel(); + jlabel3.setFont(new Font("Tahoma",Font.BOLD,11)); + jlabel3.setText("Console"); + jpanel1.add(jlabel3,cc.xy(2,4)); + + _SERVICE.setEnabled(false); + _SERVICE.setName("SERVICE"); + jpanel1.add(_SERVICE,cc.xy(4,4)); + + _MESSAGE.setName("MESSAGE"); + jpanel1.add(_MESSAGE,cc.xywh(2,9,4,1)); + + addFillComponents(jpanel1,new int[]{ 1,2,3,4,5,6 },new int[]{ 1,2,3,4,5,6,7,8,9,10 }); + return jpanel1; + } + + public JPanel createPanel1() + { + JPanel jpanel1 = new JPanel(); + FormLayout formlayout1 = new FormLayout("FILL:308PX:NONE,FILL:89PX:NONE,FILL:22PX:NONE,FILL:87PX:NONE","CENTER:DEFAULT:NONE"); + CellConstraints cc = new CellConstraints(); + jpanel1.setLayout(formlayout1); + + _CANCEL_BUTTON.setActionCommand("Cancel"); + _CANCEL_BUTTON.setName("CANCEL_BUTTON"); + _CANCEL_BUTTON.setText("CLOSE"); + jpanel1.add(_CANCEL_BUTTON,cc.xy(4,1)); + + _OK_BUTTON.setActionCommand("OK"); + _OK_BUTTON.setName("OK_BUTTON"); + _OK_BUTTON.setText("INSTALL"); + jpanel1.add(_OK_BUTTON,cc.xy(2,1)); + + addFillComponents(jpanel1,new int[]{ 1,3 },new int[]{ 1 }); + return jpanel1; + } + + /** + * Initializer + */ + protected void initializePanel() + { + setLayout(new BorderLayout()); + add(createPanel(), BorderLayout.CENTER); + } + + +} diff --git a/javaUtilities/yajsw/src/srvmgr/java/org/rzo/yajsw/srvmgr/client/ServicesForm.java b/javaUtilities/yajsw/src/srvmgr/java/org/rzo/yajsw/srvmgr/client/ServicesForm.java new file mode 100644 index 0000000000..2c9fd3d2ed --- /dev/null +++ b/javaUtilities/yajsw/src/srvmgr/java/org/rzo/yajsw/srvmgr/client/ServicesForm.java @@ -0,0 +1,276 @@ +package org.rzo.yajsw.srvmgr.client; + +import com.jeta.open.i18n.I18NUtils; +import com.jgoodies.forms.layout.CellConstraints; +import com.jgoodies.forms.layout.FormLayout; +import java.awt.BorderLayout; +import java.awt.ComponentOrientation; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import javax.swing.Box; +import javax.swing.ImageIcon; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTable; + + +public class ServicesForm extends JPanel +{ + JButton _ADD_HOSTS_BUTTON = new JButton(); + JButton _REMOVE_HOSTS_BUTTON = new JButton(); + JButton _ADD_HIDDEN_BUTTON = new JButton(); + JButton _REMOVE_HIDDEN_BUTTON = new JButton(); + JTable _HIDDEN_TABLE = new JTable(); + JButton _START_BUTTON = new JButton(); + JButton _STOP_BUTTON = new JButton(); + JButton _INSTALL_BUTTON = new JButton(); + JButton _UNINSTALL_BUTTON = new JButton(); + JButton _RELOAD_CONSOLE = new JButton(); + JTable _HOSTS_TABLE = new JTable(); + JButton _DELETE_HOST_BUTTON = new JButton(); + JButton _NEW_HOST_BUTTON = new JButton(); + JTable _SERVICES_TABLE = new JTable(); + + /** + * Default constructor + */ + public ServicesForm() + { + initializePanel(); + } + + /** + * Main method for panel + */ + public static void main(String[] args) + { + JFrame frame = new JFrame(); + frame.setSize(600, 400); + frame.setLocation(100, 100); + frame.getContentPane().add(new ServicesForm()); + frame.setVisible(true); + + frame.addWindowListener( new WindowAdapter() + { + public void windowClosing( WindowEvent evt ) + { + System.exit(0); + } + }); + } + + /** + * Adds fill components to empty cells in the first row and first column of the grid. + * This ensures that the grid spacing will be the same as shown in the designer. + * @param cols an array of column indices in the first row where fill components should be added. + * @param rows an array of row indices in the first column where fill components should be added. + */ + void addFillComponents( Container panel, int[] cols, int[] rows ) + { + Dimension filler = new Dimension(10,10); + + boolean filled_cell_11 = false; + CellConstraints cc = new CellConstraints(); + if ( cols.length > 0 && rows.length > 0 ) + { + if ( cols[0] == 1 && rows[0] == 1 ) + { + /** add a rigid area */ + panel.add( Box.createRigidArea( filler ), cc.xy(1,1) ); + filled_cell_11 = true; + } + } + + for( int index = 0; index < cols.length; index++ ) + { + if ( cols[index] == 1 && filled_cell_11 ) + { + continue; + } + panel.add( Box.createRigidArea( filler ), cc.xy(cols[index],1) ); + } + + for( int index = 0; index < rows.length; index++ ) + { + if ( rows[index] == 1 && filled_cell_11 ) + { + continue; + } + panel.add( Box.createRigidArea( filler ), cc.xy(1,rows[index]) ); + } + + } + + /** + * Helper method to load an image file from the CLASSPATH + * @param imageName the package and name of the file to load relative to the CLASSPATH + * @return an ImageIcon instance with the specified image file + * @throws IllegalArgumentException if the image resource cannot be loaded. + */ + public ImageIcon loadImage( String imageName ) + { + try + { + ClassLoader classloader = getClass().getClassLoader(); + java.net.URL url = classloader.getResource( imageName ); + if ( url != null ) + { + ImageIcon icon = new ImageIcon( url ); + return icon; + } + } + catch( Exception e ) + { + e.printStackTrace(); + } + throw new IllegalArgumentException( "Unable to load image: " + imageName ); + } + + /** + * Method for recalculating the component orientation for + * right-to-left Locales. + * @param orientation the component orientation to be applied + */ + public void applyComponentOrientation( ComponentOrientation orientation ) + { + // Not yet implemented... + // I18NUtils.applyComponentOrientation(this, orientation); + super.applyComponentOrientation(orientation); + } + + public JPanel createPanel() + { + JPanel jpanel1 = new JPanel(); + FormLayout formlayout1 = new FormLayout("FILL:DEFAULT:NONE,FILL:DEFAULT:NONE,FILL:DEFAULT:NONE,FILL:DEFAULT:NONE,FILL:DEFAULT:NONE,FILL:214PX:NONE,FILL:4DLU:NONE,FILL:56PX:NONE,FILL:4DLU:NONE,FILL:PREF:NONE,FILL:DEFAULT:NONE","CENTER:DEFAULT:NONE,CENTER:DEFAULT:NONE,CENTER:2DLU:NONE,FILL:PREF:NONE,CENTER:DEFAULT:NONE,CENTER:DEFAULT:NONE,CENTER:2DLU:NONE,CENTER:DEFAULT:NONE,CENTER:DEFAULT:NONE,CENTER:DEFAULT:NONE,CENTER:DEFAULT:NONE,CENTER:60PX:NONE,CENTER:DEFAULT:NONE,CENTER:DEFAULT:NONE,CENTER:DEFAULT:NONE,CENTER:66PX:NONE,CENTER:DEFAULT:NONE,CENTER:DEFAULT:NONE,CENTER:2DLU:NONE,CENTER:95PX:NONE,CENTER:DEFAULT:NONE,CENTER:DEFAULT:NONE,CENTER:DEFAULT:NONE,CENTER:138PX:NONE,CENTER:DEFAULT:NONE"); + CellConstraints cc = new CellConstraints(); + jpanel1.setLayout(formlayout1); + + _ADD_HOSTS_BUTTON.setActionCommand("+"); + _ADD_HOSTS_BUTTON.setName("ADD_HOSTS_BUTTON"); + _ADD_HOSTS_BUTTON.setText(">>"); + _ADD_HOSTS_BUTTON.setToolTipText("Add Services from Host"); + _ADD_HOSTS_BUTTON.setHorizontalTextPosition(JButton.CENTER); + jpanel1.add(_ADD_HOSTS_BUTTON,cc.xy(8,13)); + + _REMOVE_HOSTS_BUTTON.setActionCommand("-"); + _REMOVE_HOSTS_BUTTON.setName("REMOVE_HOSTS_BUTTON"); + _REMOVE_HOSTS_BUTTON.setText("<<"); + jpanel1.add(_REMOVE_HOSTS_BUTTON,cc.xy(8,15)); + + _ADD_HIDDEN_BUTTON.setActionCommand("+"); + _ADD_HIDDEN_BUTTON.setName("ADD_HIDDEN_BUTTON"); + _ADD_HIDDEN_BUTTON.setText("<<"); + jpanel1.add(_ADD_HIDDEN_BUTTON,cc.xy(8,21)); + + _REMOVE_HIDDEN_BUTTON.setActionCommand("-"); + _REMOVE_HIDDEN_BUTTON.setName("REMOVE_HIDDEN_BUTTON"); + _REMOVE_HIDDEN_BUTTON.setText(">>"); + jpanel1.add(_REMOVE_HIDDEN_BUTTON,cc.xy(8,23)); + + JLabel jlabel1 = new JLabel(); + jlabel1.setFont(new Font("Tahoma",Font.BOLD,11)); + jlabel1.setText("Hidden Services"); + jlabel1.setHorizontalAlignment(JLabel.CENTER); + jpanel1.add(jlabel1,cc.xywh(2,18,5,1)); + + _HIDDEN_TABLE.setName("HIDDEN_TABLE"); + JScrollPane jscrollpane1 = new JScrollPane(); + jscrollpane1.setViewportView(_HIDDEN_TABLE); + jscrollpane1.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED); + jscrollpane1.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); + jpanel1.add(jscrollpane1,cc.xywh(2,20,5,5)); + + jpanel1.add(createPanel1(),new CellConstraints(10,4,1,3,CellConstraints.FILL,CellConstraints.FILL)); + JLabel jlabel2 = new JLabel(); + jlabel2.setFont(new Font("Tahoma",Font.BOLD,11)); + jlabel2.setText("Services"); + jlabel2.setHorizontalAlignment(JLabel.CENTER); + jpanel1.add(jlabel2,cc.xy(10,2)); + + _HOSTS_TABLE.setName("HOSTS_TABLE"); + JScrollPane jscrollpane2 = new JScrollPane(); + jscrollpane2.setViewportView(_HOSTS_TABLE); + jscrollpane2.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED); + jscrollpane2.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); + jpanel1.add(jscrollpane2,cc.xywh(2,4,5,13)); + + JLabel jlabel3 = new JLabel(); + jlabel3.setFont(new Font("Tahoma",Font.BOLD,11)); + jlabel3.setText("Hosts"); + jlabel3.setHorizontalAlignment(JLabel.CENTER); + jpanel1.add(jlabel3,cc.xywh(2,2,5,1)); + + _DELETE_HOST_BUTTON.setActionCommand("-"); + _DELETE_HOST_BUTTON.setName("DELETE_HOST_BUTTON"); + _DELETE_HOST_BUTTON.setText("-"); + jpanel1.add(_DELETE_HOST_BUTTON,cc.xy(8,11)); + + _NEW_HOST_BUTTON.setActionCommand("+"); + _NEW_HOST_BUTTON.setName("NEW_HOST_BUTTON"); + _NEW_HOST_BUTTON.setText("+"); + jpanel1.add(_NEW_HOST_BUTTON,cc.xy(8,9)); + + _SERVICES_TABLE.setName("SERVICES_TABLE"); + JScrollPane jscrollpane3 = new JScrollPane(); + jscrollpane3.setViewportView(_SERVICES_TABLE); + jscrollpane3.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED); + jscrollpane3.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); + jpanel1.add(jscrollpane3,cc.xywh(10,8,1,17)); + + addFillComponents(jpanel1,new int[]{ 1,2,3,4,5,6,7,8,9,10,11 },new int[]{ 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 }); + return jpanel1; + } + + public JPanel createPanel1() + { + JPanel jpanel1 = new JPanel(); + FormLayout formlayout1 = new FormLayout("FILL:DEFAULT:NONE,FILL:DEFAULT:NONE,FILL:DEFAULT:NONE,FILL:39PX:NONE,FILL:DEFAULT:NONE,FILL:DEFAULT:NONE,FILL:DEFAULT:NONE,FILL:68PX:NONE,FILL:DEFAULT:NONE,FILL:10PX:NONE","CENTER:DEFAULT:NONE"); + CellConstraints cc = new CellConstraints(); + jpanel1.setLayout(formlayout1); + + _START_BUTTON.setActionCommand("Start"); + _START_BUTTON.setName("START_BUTTON"); + _START_BUTTON.setText("Start"); + jpanel1.add(_START_BUTTON,cc.xy(1,1)); + + _STOP_BUTTON.setActionCommand("Stop"); + _STOP_BUTTON.setName("STOP_BUTTON"); + _STOP_BUTTON.setText("Stop"); + jpanel1.add(_STOP_BUTTON,cc.xy(3,1)); + + _INSTALL_BUTTON.setActionCommand("Install"); + _INSTALL_BUTTON.setName("INSTALL_BUTTON"); + _INSTALL_BUTTON.setText("Install"); + jpanel1.add(_INSTALL_BUTTON,cc.xy(5,1)); + + _UNINSTALL_BUTTON.setActionCommand("Uninstall"); + _UNINSTALL_BUTTON.setName("UNINSTALL_BUTTON"); + _UNINSTALL_BUTTON.setText("Uninstall"); + jpanel1.add(_UNINSTALL_BUTTON,cc.xy(7,1)); + + _RELOAD_CONSOLE.setActionCommand("ReloadConsoleApp"); + _RELOAD_CONSOLE.setName("RELOAD_CONSOLE"); + _RELOAD_CONSOLE.setText("ReloadConsoleApp"); + jpanel1.add(_RELOAD_CONSOLE,cc.xy(9,1)); + + addFillComponents(jpanel1,new int[]{ 2,4,6,8,10 },new int[0]); + return jpanel1; + } + + /** + * Initializer + */ + protected void initializePanel() + { + setLayout(new BorderLayout()); + add(createPanel(), BorderLayout.CENTER); + } + + +} diff --git a/javaUtilities/yajsw/src/srvmgr/java/org/rzo/yajsw/srvmgr/client/ServicesTable.java b/javaUtilities/yajsw/src/srvmgr/java/org/rzo/yajsw/srvmgr/client/ServicesTable.java new file mode 100644 index 0000000000..938f139bcb --- /dev/null +++ b/javaUtilities/yajsw/src/srvmgr/java/org/rzo/yajsw/srvmgr/client/ServicesTable.java @@ -0,0 +1,150 @@ +package org.rzo.yajsw.srvmgr.client; + +import java.beans.PropertyChangeListener; +import java.beans.PropertyChangeSupport; +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Timer; +import java.util.TimerTask; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTable; +import javax.swing.SwingUtilities; + +import org.rzo.netty.ahessian.rpc.client.AsyncHessianProxy; +import org.rzo.yajsw.os.ServiceInfo; +import org.rzo.yajsw.os.ServiceInfoImpl; +import org.rzo.yajsw.srvmgr.server.ServiceManagerServer; +import org.rzo.yajsw.util.BeanTableFormat; +import org.rzo.yajsw.util.ObservableList; +import org.rzo.yajsw.util.ObservableObject; + +import ca.odell.glazedlists.BasicEventList; +import ca.odell.glazedlists.EventList; +import ca.odell.glazedlists.GlazedLists; +import ca.odell.glazedlists.ObservableElementList; +import ca.odell.glazedlists.SortedList; +import ca.odell.glazedlists.gui.TableFormat; +import ca.odell.glazedlists.swing.EventSelectionModel; +import ca.odell.glazedlists.swing.EventTableModel; +import ca.odell.glazedlists.swing.TableComparatorChooser; + +public class ServicesTable +{ + + String[] propertyNames = {"host", "displayName", "account", "pid", "wrapped", "wrapperAppPid"}; + String[] columnLabels = {"Host", "Name", "Account", "Pid", "YAJSW", "App Pid"}; + final Map _managers = Collections.synchronizedMap(new HashMap()); + EventSelectionModel _selection; + + + ServicesTable(JTable jTable) + { + ObservableElementList.Connector connector = GlazedLists.beanConnector(ObservableObject.class); + SortedList servicesEventList = + new SortedList( + new ObservableElementList ( + GlazedLists.threadSafeList( + new BasicEventList() + ), connector + )); + EventTableModel servicesTableModel = new EventTableModel(servicesEventList, + new BeanTableFormat(propertyNames, columnLabels)); + final ObservableList list = new ObservableList(servicesEventList, propertyNames, new String[]{"host", "name"}); + jTable.setModel(servicesTableModel); + _selection = new EventSelectionModel(servicesEventList); + jTable.setSelectionModel(_selection); + TableComparatorChooser tableSorter = TableComparatorChooser.install(jTable, servicesEventList, TableComparatorChooser.SINGLE_COLUMN); + new Timer("services table updated", true).schedule(new TimerTask() + { + + @Override + public void run() + { + List allServices = new ArrayList(); + HashSet managers; + synchronized (_managers) + { + managers = new HashSet(_managers.keySet()); + } + + for (String managerName : managers) + { + Map list = null; + try + { + AsyncServiceManagerServer manager = _managers.get(managerName); + if (manager == null) + continue; + list = (Map) ((Future)manager.getServiceList()).get(10, TimeUnit.SECONDS); + for (ServiceInfo info : list.values()) + { + ((ServiceInfoImpl)info).setHost(managerName); + } + } + catch (Exception e) + { + e.printStackTrace(); + } + if (list == null || list.isEmpty()) + { + System.out.println("error getting services"); + } + else + { + for (Object service : list.values()) + if (!ClientMain.hidden.containsObject((ServiceInfo) service)) + allServices.add((ServiceInfo) service); + } + } + + list.update(allServices); + } + + }, + 0, + 500); + } + + public void addService(String hostName, AsyncServiceManagerServer manager) + { + if (manager == null) + return; + + synchronized(_managers) + { + if (manager == null) + return; + + if (_managers.get(hostName) == null) + _managers.put(hostName, manager); + } + } + + public void removeService(String managerName) + { + _managers.remove(managerName); + } + + public List getSelection() + { + List result = new ArrayList(); + EventList selection = _selection.getSelected(); + for (ObservableObject x : selection) + result.add((ServiceInfo) x.getRoot()); + return result; + } + + +} diff --git a/javaUtilities/yajsw/src/srvmgr/java/org/rzo/yajsw/srvmgr/client/UninstallDialog.java b/javaUtilities/yajsw/src/srvmgr/java/org/rzo/yajsw/srvmgr/client/UninstallDialog.java new file mode 100644 index 0000000000..503c381b34 --- /dev/null +++ b/javaUtilities/yajsw/src/srvmgr/java/org/rzo/yajsw/srvmgr/client/UninstallDialog.java @@ -0,0 +1,192 @@ +package org.rzo.yajsw.srvmgr.client; + +import com.jeta.open.i18n.I18NUtils; +import com.jgoodies.forms.layout.CellConstraints; +import com.jgoodies.forms.layout.FormLayout; +import java.awt.BorderLayout; +import java.awt.ComponentOrientation; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import javax.swing.Box; +import javax.swing.ImageIcon; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; + + +public class UninstallDialog extends JPanel +{ + JLabel _SERVICES = new JLabel(); + JButton _CANCEL_BUTTON = new JButton(); + JButton _OK_BUTTON = new JButton(); + + /** + * Default constructor + */ + public UninstallDialog() + { + initializePanel(); + } + + /** + * Main method for panel + */ + public static void main(String[] args) + { + JFrame frame = new JFrame(); + frame.setSize(600, 400); + frame.setLocation(100, 100); + frame.getContentPane().add(new UninstallDialog()); + frame.setVisible(true); + + frame.addWindowListener( new WindowAdapter() + { + public void windowClosing( WindowEvent evt ) + { + System.exit(0); + } + }); + } + + /** + * Adds fill components to empty cells in the first row and first column of the grid. + * This ensures that the grid spacing will be the same as shown in the designer. + * @param cols an array of column indices in the first row where fill components should be added. + * @param rows an array of row indices in the first column where fill components should be added. + */ + void addFillComponents( Container panel, int[] cols, int[] rows ) + { + Dimension filler = new Dimension(10,10); + + boolean filled_cell_11 = false; + CellConstraints cc = new CellConstraints(); + if ( cols.length > 0 && rows.length > 0 ) + { + if ( cols[0] == 1 && rows[0] == 1 ) + { + /** add a rigid area */ + panel.add( Box.createRigidArea( filler ), cc.xy(1,1) ); + filled_cell_11 = true; + } + } + + for( int index = 0; index < cols.length; index++ ) + { + if ( cols[index] == 1 && filled_cell_11 ) + { + continue; + } + panel.add( Box.createRigidArea( filler ), cc.xy(cols[index],1) ); + } + + for( int index = 0; index < rows.length; index++ ) + { + if ( rows[index] == 1 && filled_cell_11 ) + { + continue; + } + panel.add( Box.createRigidArea( filler ), cc.xy(1,rows[index]) ); + } + + } + + /** + * Helper method to load an image file from the CLASSPATH + * @param imageName the package and name of the file to load relative to the CLASSPATH + * @return an ImageIcon instance with the specified image file + * @throws IllegalArgumentException if the image resource cannot be loaded. + */ + public ImageIcon loadImage( String imageName ) + { + try + { + ClassLoader classloader = getClass().getClassLoader(); + java.net.URL url = classloader.getResource( imageName ); + if ( url != null ) + { + ImageIcon icon = new ImageIcon( url ); + return icon; + } + } + catch( Exception e ) + { + e.printStackTrace(); + } + throw new IllegalArgumentException( "Unable to load image: " + imageName ); + } + + /** + * Method for recalculating the component orientation for + * right-to-left Locales. + * @param orientation the component orientation to be applied + */ + public void applyComponentOrientation( ComponentOrientation orientation ) + { + // Not yet implemented... + // I18NUtils.applyComponentOrientation(this, orientation); + super.applyComponentOrientation(orientation); + } + + public JPanel createPanel() + { + JPanel jpanel1 = new JPanel(); + FormLayout formlayout1 = new FormLayout("FILL:DEFAULT:NONE,FILL:DEFAULT:NONE,FILL:8DLU:NONE","CENTER:DEFAULT:NONE,CENTER:DEFAULT:NONE,CENTER:DEFAULT:NONE,CENTER:DEFAULT:NONE,CENTER:DEFAULT:NONE,CENTER:DEFAULT:NONE,CENTER:DEFAULT:NONE,CENTER:DEFAULT:NONE,CENTER:DEFAULT:NONE"); + CellConstraints cc = new CellConstraints(); + jpanel1.setLayout(formlayout1); + + JLabel jlabel1 = new JLabel(); + jlabel1.setFont(new Font("Tahoma",Font.BOLD,11)); + jlabel1.setText("The following services will be removed. Note: only YAJSW can be removed"); + jpanel1.add(jlabel1,cc.xy(2,4)); + + _SERVICES.setName("SERVICES"); + _SERVICES.setText("Host/service name, Host/Service name"); + jpanel1.add(_SERVICES,cc.xy(2,6)); + + jpanel1.add(createPanel1(),cc.xy(2,8)); + JLabel jlabel2 = new JLabel(); + jlabel2.setFont(new Font("Tahoma",Font.BOLD,12)); + jlabel2.setText("Uninstall Services"); + jlabel2.setHorizontalAlignment(JLabel.CENTER); + jpanel1.add(jlabel2,cc.xy(2,2)); + + addFillComponents(jpanel1,new int[]{ 1,2,3 },new int[]{ 1,2,3,4,5,6,7,8,9 }); + return jpanel1; + } + + public JPanel createPanel1() + { + JPanel jpanel1 = new JPanel(); + FormLayout formlayout1 = new FormLayout("FILL:280PX:NONE,FILL:DEFAULT:NONE,FILL:DEFAULT:NONE,FILL:DEFAULT:NONE","CENTER:DEFAULT:NONE"); + CellConstraints cc = new CellConstraints(); + jpanel1.setLayout(formlayout1); + + _CANCEL_BUTTON.setActionCommand("Cancel"); + _CANCEL_BUTTON.setName("CANCEL_BUTTON"); + _CANCEL_BUTTON.setText("Cancel"); + jpanel1.add(_CANCEL_BUTTON,cc.xy(4,1)); + + _OK_BUTTON.setActionCommand("OK"); + _OK_BUTTON.setName("OK_BUTTON"); + _OK_BUTTON.setText("OK"); + jpanel1.add(_OK_BUTTON,cc.xy(2,1)); + + addFillComponents(jpanel1,new int[]{ 1,3 },new int[]{ 1 }); + return jpanel1; + } + + /** + * Initializer + */ + protected void initializePanel() + { + setLayout(new BorderLayout()); + add(createPanel(), BorderLayout.CENTER); + } + + +} diff --git a/javaUtilities/yajsw/src/srvmgr/java/org/rzo/yajsw/srvmgr/hub/HubBooter.java b/javaUtilities/yajsw/src/srvmgr/java/org/rzo/yajsw/srvmgr/hub/HubBooter.java new file mode 100644 index 0000000000..14f5b52849 --- /dev/null +++ b/javaUtilities/yajsw/src/srvmgr/java/org/rzo/yajsw/srvmgr/hub/HubBooter.java @@ -0,0 +1,28 @@ +package org.rzo.yajsw.srvmgr.hub; + +import java.lang.reflect.Method; +import java.net.URLClassLoader; + +import org.rzo.yajsw.boot.WrapperLoader; + +public class HubBooter +{ + public static void main(String[] args) + { + URLClassLoader cl = WrapperLoader.getWrapperClassLoader(); + Thread.currentThread().setContextClassLoader(cl); + try + { + Class cls = Class.forName("org.rzo.yajsw.srvmgr.client.ClientMain", true, cl); + Method mainMethod = cls.getDeclaredMethod("main", new Class[] + { String[].class }); + mainMethod.invoke(null, new Object[] + { args }); + } + catch (Exception e) + { + e.printStackTrace(); + } + } + +} diff --git a/javaUtilities/yajsw/src/srvmgr/java/org/rzo/yajsw/srvmgr/hub/HubMain.java b/javaUtilities/yajsw/src/srvmgr/java/org/rzo/yajsw/srvmgr/hub/HubMain.java new file mode 100644 index 0000000000..3ed86ef129 --- /dev/null +++ b/javaUtilities/yajsw/src/srvmgr/java/org/rzo/yajsw/srvmgr/hub/HubMain.java @@ -0,0 +1,384 @@ +package org.rzo.yajsw.srvmgr.hub; + + +import org.rzo.netty.ahessian.rpc.client.HessianProxyFactory; +import org.rzo.netty.mcast.discovery.DiscoveryClient; +import org.rzo.netty.mcast.discovery.DiscoveryListener; +import org.rzo.netty.mcast.discovery.DiscoveryServer; + +import java.awt.BorderLayout; +import java.awt.Dialog.ModalityType; +import java.awt.event.ActionEvent; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.UnknownHostException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.Timer; +import java.util.TimerTask; +import java.util.Vector; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import javax.swing.AbstractAction; +import javax.swing.DefaultComboBoxModel; +import javax.swing.JDialog; +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.WindowConstants; + +import org.jboss.netty.bootstrap.ClientBootstrap; +import org.jboss.netty.channel.Channel; +import org.jboss.netty.channel.ChannelFuture; +import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory; +import org.jboss.netty.channel.socket.oio.OioClientSocketChannelFactory; + +import org.rzo.yajsw.os.ServiceInfo; +import org.rzo.yajsw.os.ServiceInfoImpl; +import org.rzo.yajsw.srvmgr.client.AsyncServiceManagerServer; +import org.rzo.yajsw.srvmgr.client.Host; +import org.rzo.yajsw.srvmgr.client.RPCClientPipelineFactory; +import org.rzo.yajsw.srvmgr.server.ServiceManagerServer; + +public class HubMain +{ + static List servicesList = new ArrayList(); + static Map hostsList = new HashMap(); + static Set hiddenList = new HashSet(); + static HashMapproxies = new HashMap(); + static Set configurations = new HashSet(); + static ExecutorService executor = Executors.newCachedThreadPool(); + static DiscoveryClient discovery = new DiscoveryClient(); + static HubServiceServer hubServiceServer; + + + public static void main(String[] args) throws Exception + { + ExecutorService executor = Executors.newCachedThreadPool(); + + loadData(); + + new Timer("hosts updater", true).schedule(new TimerTask() + { + + @Override + public void run() + { + updateHosts(); + updateServices(); + } + + + }, 0, 500); + + + discovery.setName("serviceManagerServer"); + discovery.addListener(new DiscoveryListener() + { + + public void newHost(String serviceName, String host) + { + try + { + String[] x = host.split(":"); + int port = Integer.parseInt(x[1]); + String name = InetAddress.getByName(x[0]).getHostName(); + synchronized(hostsList) + { + System.out.println("new host "+name+":"+port); + Host newHost = new Host(name, port); + Host oldHost = hostsList.get(newHost.getName()); + if (oldHost != null) + { + newHost.setIncluded(oldHost.isIncluded()); + } + hostsList.put(newHost.getName(), newHost); + + saveData(); + } + } + catch (Exception ex) + { + ex.printStackTrace(); + } + + } + + }); + discovery.init(); + discovery.start(); + + + hubServiceServer = new HubServiceServer(Integer.parseInt(args[0]), null, proxies); + + } + + protected static void doDeleteHost(String host) + { + synchronized(hostsList) + { + synchronized(servicesList) + { + hostsList.remove(host); + for (ServiceInfo service : new ArrayList(servicesList)) + if (host.equals(service.getHost())) + servicesList.remove(service); + saveData(); + } + } + } + + + protected static void doNewHost(String host, int port) + { + synchronized(hostsList) + { + Host newHost = new Host(host, port); + Host oldHost = hostsList.get(newHost.getName()); + if (oldHost != null) + { + newHost.setIncluded(oldHost.isIncluded()); + } + newHost.setState("CONNECTED"); + hostsList.put(newHost.getName(), newHost); + + saveData(); + } + + + } + + private static void saveData() + { + Map data = new HashMap(); + data.put("hosts", hostsList); + data.put("hidden", hiddenList); + data.put("configurations", configurations); + File f = new File("ServiceManager.ser"); + try + { + if (!f.exists()) + f.createNewFile(); + ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(f)); + out.writeObject(data); + out.close(); + } + catch (Exception e) + { + e.printStackTrace(); + } + } + + private static void loadData() + { + File f = new File("ServiceManagerHub.ser"); + try + { + if (!f.exists()) + return; + ObjectInputStream in = new ObjectInputStream(new FileInputStream(f)); + Map data = (Map) in.readObject(); + for (Iterator it = ((Collection)data.get("hosts")).iterator(); it.hasNext(); ) + { + Host host = (Host) it.next(); + hostsList.put(host.getName(), host); + } + for (Iterator it = ((Collection)data.get("hidden")).iterator(); it.hasNext(); ) + hiddenList.add((String)it.next()); + configurations = (Set) data.get("configurations"); + in.close(); + } + catch (Exception e) + { + e.printStackTrace(); + } + + } + + private static void updateServices() + { + synchronized(hostsList) + { + synchronized(proxies) + { + + synchronized(servicesList) + { + servicesList.clear(); + for (String host : hostsList.keySet()) + { + AsyncServiceManagerServer proxy = proxies.get(host); + Collection services = null; + try + { + services = (Collection) ((Map) proxy.getServiceList()).values(); + } + catch (Exception ex) + { + ex.printStackTrace(); + } + if (services != null) + for (ServiceInfo service : services) + { + synchronized(hiddenList) + { + if (! hiddenList.contains(service.getName())) + { + ((ServiceInfoImpl)service).setHost(host); + servicesList.add(service); + } + } + } + } + } + } + } + System.out.println("update services: #"+servicesList.size()); + } + + + private static void updateHosts() + { + boolean changed = false; + synchronized(hostsList) + { + for (Host host : hostsList.values()) + { + AsyncServiceManagerServer proxy = null; + synchronized(proxies) + { + proxy = proxies.get(host.getName()); + } + boolean connected = false; + if (proxy != null) + { + try + { + connected = ((Boolean)proxy.isServiceManager()).booleanValue(); + } + catch (Exception e) + { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + else + { + ClientBootstrap bootstrap = new ClientBootstrap( + new NioClientSocketChannelFactory( + executor, + executor)); + + HessianProxyFactory factory = new HessianProxyFactory(executor, host.getName()+":"+host.getPort()); + bootstrap.setPipelineFactory( + new RPCClientPipelineFactory(executor, factory)); + + // Start the connection attempt. + ChannelFuture future = bootstrap.connect(new InetSocketAddress(host.getName(), host.getPort())); + try + { + future.await(10000); + connected = future.isSuccess(); + + + if (connected) + { + Map options = new HashMap(); + options.put("sync", true); + options.put("timeout", new Long(10000)); + proxy = (AsyncServiceManagerServer) factory.create(AsyncServiceManagerServer.class, HubMain.class.getClassLoader(), options); + connected = ((Boolean)proxy.isServiceManager()).booleanValue(); + if (connected) + { + synchronized(proxies) + { + proxies.put(host.getName(), proxy); + } + Host newHost = new Host(host.getName(), host.getPort()); + newHost.setIncluded(true); + newHost.setState("CONNECTED"); + hostsList.remove(host.getName()); + hostsList.put(newHost.getName(), newHost); + //if (host.isIncluded()) + // TODO servicesList.addService(host.getName(), proxy); + } + else + future.getChannel().close(); + } + } + catch (Exception e) + { + System.out.println("error accessing "+host.getName()); + e.printStackTrace(); + + connected = false; + if (future != null) + future.getChannel().close(); + } + + } + + if (!connected) + { + synchronized(proxies) + { + disconnect(host, proxies.remove(host.getName())); + } + changed = true; + } + else if (proxy == null && !"DISCONNECTED".equals(host.getState())) + { + Host newHost = new Host(host.getName(), host.getPort()); + newHost.setIncluded(host.isIncluded()); + newHost.setState("DISCONNECTED"); + hostsList.put(newHost.getName(), newHost); + } + } + } + } + + private static void removeServices(String host) + { + + } + + private static void disconnect(Host host, AsyncServiceManagerServer proxy) + { + if (proxy != null) + removeServices(host.getName()); + host.setState("DISCONNECTED"); + try + { + discovery.removeHost(InetAddress.getByName(host.getName()).getHostAddress()+":"+host.getPort()); + } + catch (Exception e) + { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + + + + +} diff --git a/javaUtilities/yajsw/src/srvmgr/java/org/rzo/yajsw/srvmgr/hub/HubService.java b/javaUtilities/yajsw/src/srvmgr/java/org/rzo/yajsw/srvmgr/hub/HubService.java new file mode 100644 index 0000000000..83a488c506 --- /dev/null +++ b/javaUtilities/yajsw/src/srvmgr/java/org/rzo/yajsw/srvmgr/hub/HubService.java @@ -0,0 +1,16 @@ +package org.rzo.yajsw.srvmgr.hub; + +import java.util.List; + +import org.rzo.yajsw.os.ServiceInfo; +import org.rzo.yajsw.srvmgr.client.Host; + +public interface HubService +{ + + public List getHosts(); + public List getServices(); + public void start(String serviceName, String hostName); + public void stop(String serviceName, String hostName); + public void hide(String serviceName, String hostName); +} diff --git a/javaUtilities/yajsw/src/srvmgr/java/org/rzo/yajsw/srvmgr/hub/HubServiceServer.java b/javaUtilities/yajsw/src/srvmgr/java/org/rzo/yajsw/srvmgr/hub/HubServiceServer.java new file mode 100644 index 0000000000..f11821ee9c --- /dev/null +++ b/javaUtilities/yajsw/src/srvmgr/java/org/rzo/yajsw/srvmgr/hub/HubServiceServer.java @@ -0,0 +1,110 @@ +package org.rzo.yajsw.srvmgr.hub; + +import java.net.InetSocketAddress; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; + +import org.jboss.netty.bootstrap.ServerBootstrap; +import org.jboss.netty.channel.Channel; +import org.jboss.netty.channel.socket.oio.OioServerSocketChannelFactory; +import org.rzo.netty.ahessian.rpc.server.HessianRPCServiceHandler; +import org.rzo.netty.ahessian.rpc.server.ImmediateInvokeService; +import org.rzo.yajsw.os.ServiceInfo; +import org.rzo.yajsw.srvmgr.client.AsyncServiceManagerServer; +import org.rzo.yajsw.srvmgr.client.Host; + +public class HubServiceServer implements HubService +{ + HashMap _proxies; + + Comparator hostsComparator = new Comparator() + { + public int compare(Host o1, Host o2) + { + return o1.getName().compareTo(o2.getName()); + } + }; + + Comparator servicesComparator = new Comparator() + { + public int compare(ServiceInfo o1, ServiceInfo o2) + { + return o1.getName().compareTo(o2.getName()); + } + }; + + HubServiceServer(int port, String acl, HashMapproxies) + { + _proxies = proxies; + Executor executor = Executors.newFixedThreadPool(200); + + // Configure the server. + ServerBootstrap bootstrap = new ServerBootstrap( + new OioServerSocketChannelFactory( + executor, + executor)); + + HessianRPCServiceHandler factory = new HessianRPCServiceHandler(executor); + factory.addService("default", new ImmediateInvokeService(this, HubService.class, factory)); + + + bootstrap.setPipelineFactory( + new RPCServerPipelineFactory(executor, factory, acl)); + + // Bind and start to accept incoming connections. + Channel channel = bootstrap.bind(new InetSocketAddress(port)); + } + + public List getHosts() + { + List result; + synchronized(HubMain.hostsList) + { + result = new ArrayList(HubMain.hostsList.values()); + } + Collections.sort(result, hostsComparator); + System.out.println("getHosts #"+result.size()); + return result; + } + + public List getServices() + { + List result; + synchronized(HubMain.servicesList) + { + result = new ArrayList(HubMain.servicesList); + } + Collections.sort(result, servicesComparator); + System.out.println("getServices #"+result.size()); + return result; + } + + public void hide(String serviceName, String hostName) + { + synchronized(HubMain.hiddenList) + { + System.out.println("hiding "+serviceName); + HubMain.hiddenList.add(serviceName); + } + } + + public void start(String serviceName, String hostName) + { + AsyncServiceManagerServer proxy = _proxies.get(hostName); + if (proxy != null) + proxy.start(serviceName); + } + + public void stop(String serviceName, String hostName) + { + AsyncServiceManagerServer proxy = _proxies.get(hostName); + if (proxy != null) + proxy.stop(serviceName); + } + +} diff --git a/javaUtilities/yajsw/src/srvmgr/java/org/rzo/yajsw/srvmgr/hub/RPCServerPipelineFactory.java b/javaUtilities/yajsw/src/srvmgr/java/org/rzo/yajsw/srvmgr/hub/RPCServerPipelineFactory.java new file mode 100644 index 0000000000..e168d5c6bf --- /dev/null +++ b/javaUtilities/yajsw/src/srvmgr/java/org/rzo/yajsw/srvmgr/hub/RPCServerPipelineFactory.java @@ -0,0 +1,57 @@ +package org.rzo.yajsw.srvmgr.hub; + +import static org.jboss.netty.channel.Channels.pipeline; +import org.rzo.netty.ahessian.rpc.message.HessianRPCCallDecoder; +import org.rzo.netty.ahessian.rpc.message.HessianRPCReplyEncoder; +import org.rzo.netty.ahessian.rpc.message.OutputProducer; +import org.rzo.netty.ahessian.rpc.server.HessianRPCServiceHandler; +import org.rzo.netty.ahessian.auth.EncryptedAuthToken; +import org.rzo.netty.ahessian.auth.ServerAuthFilter; +import org.rzo.netty.ahessian.io.InputStreamDecoder; +import org.rzo.netty.ahessian.io.OutputStreamEncoder; +import org.rzo.netty.ahessian.io.PushInputStreamConsumer; +import org.rzo.netty.ahessian.log.OutLogger; + +import java.util.concurrent.Executor; + +import org.jboss.netty.channel.ChannelPipeline; +import org.jboss.netty.channel.ChannelPipelineFactory; +import org.jboss.netty.handler.ipfilter.IpFilterRuleHandler; +import org.jboss.netty.handler.ipfilter.IpFilterRuleList; + +import org.rzo.netty.ahessian.rpc.server.HessianSkeleton; + +public class RPCServerPipelineFactory implements ChannelPipelineFactory +{ + Executor _executor; + HessianRPCServiceHandler _handler; + String _acl = null; + + RPCServerPipelineFactory(Executor executor, HessianRPCServiceHandler handler, String acl) + { + _executor = executor; + _handler = handler; + _acl = acl; + } + + public ChannelPipeline getPipeline() throws Exception + { + ChannelPipeline pipeline = pipeline(); // Note the static import. + if (_acl != null) + { + + pipeline.addFirst("firewall", new IpFilterRuleHandler(new IpFilterRuleList(_acl))); + } + pipeline.addLast("logger",new OutLogger("server")); + pipeline.addLast("inputStream", new InputStreamDecoder()); + pipeline.addLast("outputStream", new OutputStreamEncoder()); + pipeline.addLast("callDecoder", new PushInputStreamConsumer(new HessianRPCCallDecoder(), _executor)); + pipeline.addLast("replyEncoder", new HessianRPCReplyEncoder()); + pipeline.addLast("outputProducer", new OutputProducer(_executor)); + pipeline.addLast("hessianRPCServer", _handler); + + //bootstrap.getPipeline().addLast("logger4",new OutLogger("4")); + return pipeline; + } + +} diff --git a/javaUtilities/yajsw/src/srvmgr/java/org/rzo/yajsw/srvmgr/server/AbstractServiceManagerServer.java b/javaUtilities/yajsw/src/srvmgr/java/org/rzo/yajsw/srvmgr/server/AbstractServiceManagerServer.java new file mode 100644 index 0000000000..0f7e608902 --- /dev/null +++ b/javaUtilities/yajsw/src/srvmgr/java/org/rzo/yajsw/srvmgr/server/AbstractServiceManagerServer.java @@ -0,0 +1,15 @@ +package org.rzo.yajsw.srvmgr.server; + +public abstract class AbstractServiceManagerServer implements ServiceManagerServer +{ + public void addClientServer(String server) + { + + } + public void removeClientServer(String server) + { + + } + + +} diff --git a/javaUtilities/yajsw/src/srvmgr/java/org/rzo/yajsw/srvmgr/server/RPCServerPipelineFactory.java b/javaUtilities/yajsw/src/srvmgr/java/org/rzo/yajsw/srvmgr/server/RPCServerPipelineFactory.java new file mode 100644 index 0000000000..5f60ea8922 --- /dev/null +++ b/javaUtilities/yajsw/src/srvmgr/java/org/rzo/yajsw/srvmgr/server/RPCServerPipelineFactory.java @@ -0,0 +1,56 @@ +package org.rzo.yajsw.srvmgr.server; + +import static org.jboss.netty.channel.Channels.pipeline; +import org.rzo.netty.ahessian.rpc.message.HessianRPCCallDecoder; +import org.rzo.netty.ahessian.rpc.message.HessianRPCReplyEncoder; +import org.rzo.netty.ahessian.rpc.message.OutputProducer; +import org.rzo.netty.ahessian.rpc.server.HessianRPCServiceHandler; +import org.rzo.netty.ahessian.auth.EncryptedAuthToken; +import org.rzo.netty.ahessian.auth.ServerAuthFilter; +import org.rzo.netty.ahessian.io.InputStreamDecoder; +import org.rzo.netty.ahessian.io.OutputStreamEncoder; +import org.rzo.netty.ahessian.io.PushInputStreamConsumer; +import org.rzo.netty.ahessian.log.OutLogger; + +import java.util.concurrent.Executor; + +import org.jboss.netty.channel.ChannelPipeline; +import org.jboss.netty.channel.ChannelPipelineFactory; +import org.jboss.netty.handler.ipfilter.IpFilterRuleHandler; +import org.jboss.netty.handler.ipfilter.IpFilterRuleList; + +import org.rzo.netty.ahessian.rpc.server.HessianSkeleton; + +public class RPCServerPipelineFactory implements ChannelPipelineFactory +{ + Executor _executor; + HessianRPCServiceHandler _handler; + String _acl = null; + + RPCServerPipelineFactory(Executor executor, HessianRPCServiceHandler handler, String acl) + { + _executor = executor; + _handler = handler; + _acl = acl; + } + + public ChannelPipeline getPipeline() throws Exception + { + ChannelPipeline pipeline = pipeline(); // Note the static import. + if (_acl != null) + { + + pipeline.addFirst("firewall", new IpFilterRuleHandler(new IpFilterRuleList(_acl))); + } + pipeline.addLast("inputStream", new InputStreamDecoder()); + pipeline.addLast("outputStream", new OutputStreamEncoder()); + pipeline.addLast("callDecoder", new PushInputStreamConsumer(new HessianRPCCallDecoder(), _executor)); + pipeline.addLast("replyEncoder", new HessianRPCReplyEncoder()); + pipeline.addLast("outputProducer", new OutputProducer(_executor)); + pipeline.addLast("hessianRPCServer", _handler); + + //bootstrap.getPipeline().addLast("logger4",new OutLogger("4")); + return pipeline; + } + +} diff --git a/javaUtilities/yajsw/src/srvmgr/java/org/rzo/yajsw/srvmgr/server/ServerBooter.java b/javaUtilities/yajsw/src/srvmgr/java/org/rzo/yajsw/srvmgr/server/ServerBooter.java new file mode 100644 index 0000000000..089b3c9c14 --- /dev/null +++ b/javaUtilities/yajsw/src/srvmgr/java/org/rzo/yajsw/srvmgr/server/ServerBooter.java @@ -0,0 +1,28 @@ +package org.rzo.yajsw.srvmgr.server; + +import java.lang.reflect.Method; +import java.net.URLClassLoader; + +import org.rzo.yajsw.boot.WrapperLoader; + +public class ServerBooter +{ + public static void main(String[] args) + { + URLClassLoader cl = WrapperLoader.getWrapperClassLoader(); + Thread.currentThread().setContextClassLoader(cl); + try + { + Class cls = Class.forName("org.rzo.yajsw.srvmgr.server.ServerMain", true, cl); + Method mainMethod = cls.getDeclaredMethod("main", new Class[] + { String[].class }); + mainMethod.invoke(null, new Object[] + { args }); + } + catch (Exception e) + { + e.printStackTrace(); + } + } + +} diff --git a/javaUtilities/yajsw/src/srvmgr/java/org/rzo/yajsw/srvmgr/server/ServerMain.java b/javaUtilities/yajsw/src/srvmgr/java/org/rzo/yajsw/srvmgr/server/ServerMain.java new file mode 100644 index 0000000000..a7f417fa1f --- /dev/null +++ b/javaUtilities/yajsw/src/srvmgr/java/org/rzo/yajsw/srvmgr/server/ServerMain.java @@ -0,0 +1,88 @@ +package org.rzo.yajsw.srvmgr.server; + +import org.rzo.netty.ahessian.rpc.server.ContinuationService; +import org.rzo.netty.ahessian.rpc.server.HessianRPCServiceHandler; +import org.rzo.netty.ahessian.rpc.server.ImmediateInvokeService; +import org.rzo.netty.mcast.discovery.DiscoveryServer; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.net.InetSocketAddress; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; + +import org.jboss.netty.bootstrap.ServerBootstrap; +import org.jboss.netty.channel.Channel; +import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory; +import org.jboss.netty.channel.socket.oio.OioServerSocketChannelFactory; +import org.rzo.yajsw.srvmgr.server.ms.win.WinServiceManagerServer; + +public class ServerMain +{ + public static void main(String[] args) throws IOException, IOException, ClassNotFoundException, Exception + { + int serverPort = 0; + try + { + serverPort = Integer.parseInt(args[0]); + } + catch (Exception ex) + { + // no port -> bind to 0 port and annouce port per multicast + } + String acl = null; + int aclParPos = 1; + if (serverPort == 0) + aclParPos = 0; + + if (args.length == aclParPos+1) + acl = args[aclParPos]; + List clientHosts = new ArrayList(); + File f = new File("serviceManagerServer.ser"); + if (f.exists()) + { + ObjectInputStream in = new ObjectInputStream(new FileInputStream(f)); + clientHosts = (List) in.readObject(); + in.close(); + } + + + Executor executor = Executors.newFixedThreadPool(200); + + // Configure the server. + ServerBootstrap bootstrap = new ServerBootstrap( + new OioServerSocketChannelFactory( + executor, + executor)); + + HessianRPCServiceHandler factory = new HessianRPCServiceHandler(executor); + factory.addService("default", new ImmediateInvokeService(getServiceManagerServer(), ServiceManagerServer.class, factory)); + + + bootstrap.setPipelineFactory( + new RPCServerPipelineFactory(executor, factory, acl)); + + // Bind and start to accept incoming connections. + Channel channel = bootstrap.bind(new InetSocketAddress(serverPort)); + if (serverPort == 0) + serverPort = ((InetSocketAddress)channel.getLocalAddress()).getPort(); + + System.out.println("bound to port "+serverPort); + + DiscoveryServer discovery = new DiscoveryServer(); + discovery.setName("serviceManagerServer"); + discovery.setPort(serverPort); + discovery.init(); + } + + private static ServiceManagerServer getServiceManagerServer() + { + return new WinServiceManagerServer(); + } + +} diff --git a/javaUtilities/yajsw/src/srvmgr/java/org/rzo/yajsw/srvmgr/server/ServiceManagerServer.java b/javaUtilities/yajsw/src/srvmgr/java/org/rzo/yajsw/srvmgr/server/ServiceManagerServer.java new file mode 100644 index 0000000000..e07c687b2e --- /dev/null +++ b/javaUtilities/yajsw/src/srvmgr/java/org/rzo/yajsw/srvmgr/server/ServiceManagerServer.java @@ -0,0 +1,21 @@ +package org.rzo.yajsw.srvmgr.server; + +import java.util.List; +import java.util.Map; + +import org.rzo.yajsw.os.Service; +import org.rzo.yajsw.os.ServiceInfo; + +public interface ServiceManagerServer +{ + + public Map getServiceList(); + public ServiceInfo getService(String name); + public boolean start(String name); + public boolean stop(String name); + public boolean yajswInstall(String configuration); + public boolean yajswUninstall(String name); + public boolean yajswReloadConsole(String name, String newConfig); + public boolean isServiceManager(); + +} diff --git a/javaUtilities/yajsw/src/srvmgr/java/org/rzo/yajsw/srvmgr/server/ms/win/WinServiceManagerServer.java b/javaUtilities/yajsw/src/srvmgr/java/org/rzo/yajsw/srvmgr/server/ms/win/WinServiceManagerServer.java new file mode 100644 index 0000000000..1e8d1058d0 --- /dev/null +++ b/javaUtilities/yajsw/src/srvmgr/java/org/rzo/yajsw/srvmgr/server/ms/win/WinServiceManagerServer.java @@ -0,0 +1,303 @@ +package org.rzo.yajsw.srvmgr.server.ms.win; + +import java.io.File; +import java.io.IOException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.management.MBeanServerConnection; +import javax.management.MBeanServerInvocationHandler; +import javax.management.ObjectName; +import javax.management.remote.JMXConnector; +import javax.management.remote.JMXConnectorFactory; +import javax.management.remote.JMXServiceURL; + +import org.apache.commons.configuration.BaseConfiguration; +import org.apache.commons.configuration.Configuration; +import org.rzo.yajsw.Constants; +import org.rzo.yajsw.config.YajswConfigurationImpl; +import org.rzo.yajsw.os.OperatingSystem; +import org.rzo.yajsw.os.Service; +import org.rzo.yajsw.os.Process; +import org.rzo.yajsw.os.ServiceInfo; +import org.rzo.yajsw.os.ServiceInfoImpl; +import org.rzo.yajsw.os.ms.win.w32.WindowsXPProcess; +import org.rzo.yajsw.os.ms.win.w32.WindowsXPProcess.MyKernel32.PROCESSENTRY32; +import org.rzo.yajsw.srvmgr.server.ServiceManagerServer; +import org.rzo.yajsw.tools.JCLParser; +import org.rzo.yajsw.wrapper.AbstractWrappedProcessMBean; +import org.rzo.yajsw.wrapper.WrappedService; + +import com.sun.jna.Native; + +public class WinServiceManagerServer implements ServiceManagerServer +{ + Map _services; + long _lastListGet = 0; + long _listTTL = 5000; + Map _processes = new HashMap(); + + JMXConnector jmxc; + + + public ServiceInfo getService(String name) + { + // TODO Auto-generated method stub + return null; + } + + public synchronized Map getServiceList() + { + if (_services == null || (System.currentTimeMillis() - _listTTL) > _lastListGet) + { + _services = OperatingSystem.instance().serviceManagerInstance().getServiceList(); + setServicesAppPid(); + _services.putAll(getYAJSWConsoles()); + _lastListGet = System.currentTimeMillis(); + } + return _services; + } + + private void setServicesAppPid() + { + for (ServiceInfo service : _services.values()) + { + if (service.getWrapped() != null && !"-".equals(service.getWrapped())) + ((ServiceInfoImpl)service).setWrapperAppPid(getAppPid(service.getPid())); + } + } + + private int getAppPid(int pid) + { + int result = 0; + List pids = OperatingSystem.instance().processManagerInstance().getProcessTree(pid); + if (pids != null && !pids.isEmpty()) + { + for (Integer pp : pids) + { + if (pp != pid) + { + Process ppp = OperatingSystem.instance().processManagerInstance().getProcess(pp); + if (ppp == null) + { + System.out.println("no access right to pid "+pp); + continue; + } + if (ppp.getCommand().trim().endsWith("Main")||ppp.getCommand().trim().endsWith("Main\"")) + result = pp; + } + } + } + return result; +} + + private Map getYAJSWConsoles() + { + Map result = new HashMap(); + Map processes = WindowsXPProcess.getProcessMaps(0)[0]; + for (Integer pid : processes.keySet()) + { + Process p = _processes.get(pid); + if (p == null) + { + p = WindowsXPProcess.getProcess(pid); + if (p == null) + continue; + _processes.put(pid, p); + } + String cmd = p.getCommand(); + if (cmd != null && cmd.contains("wrapper.jar\" -c")) + { + JCLParser jp = JCLParser.parse(cmd); + ServiceInfoImpl service = new ServiceInfoImpl(); + service.setCommand(cmd); + String confFile = jp.getArgs().get(1); + if (confFile == null) + continue; + File f = new File(confFile); + if (!f.exists()) + f = new File(p.getWorkingDir(), confFile); + service.setDisplayName(f.getAbsolutePath()); + service.setName(jp.getArgs().get(1)); + service.setPid(pid); + service.setWrapperAppPid(getAppPid(pid)); + service.setAccount(p.getUser()); + result.put(service.getName(), service); + } + Set newPids = new HashSet(processes.keySet()); + Set toRemove = new HashSet(_processes.keySet()); + toRemove.removeAll(newPids); + for (Integer x : toRemove) + _processes.remove(x); + } + + return result; + } + + public boolean start(String name) + { + if (isService(name)) + return OperatingSystem.instance().serviceManagerInstance().getService(name).start(); + else + return startYAJSW(name); + } + + private boolean startYAJSW(String name) + { + AbstractWrappedProcessMBean proxy = getJmxProxy(name); + if (proxy == null) + return false; + try + { + proxy.start(); + } + catch (Exception ex) + { + ex.printStackTrace(); + } + try + { + jmxc.close(); + } + catch (IOException e) + { + // TODO Auto-generated catch block + e.printStackTrace(); + } + return true; + } + + private boolean isService(String name) + { + ServiceInfo s = _services.get(name); + return !"Console".equals(s.getWrapped()); + } + + public boolean stop(String name) + { + if (isService(name)) + return OperatingSystem.instance().serviceManagerInstance().getService(name).stop(); + else + return stopYAJSW(name); + } + + private boolean stopYAJSW(String name) + { + AbstractWrappedProcessMBean proxy = getJmxProxy(name); + if (proxy == null) + return false; + try + { + proxy.stop(); + } + catch (Exception ex) + { + ex.printStackTrace(); + } + try + { + jmxc.close(); + } + catch (IOException e) + { + // TODO Auto-generated catch block + e.printStackTrace(); + } + return true; + } + + private AbstractWrappedProcessMBean getJmxProxy(String name) + { + AbstractWrappedProcessMBean proxy = null; + try + { + String config = name; + Process p = _processes.get(_services.get(name).getPid()); + File f = new File(p.getWorkingDir(), config); + if (!f.exists()) + { + System.out.println("file not found "+f.getAbsolutePath()); + //return null; + } + Configuration localConf = new BaseConfiguration(); + localConf.addProperty("wrapper.config", config); + YajswConfigurationImpl _config = new YajswConfigurationImpl(localConf, true); + int port = _config.getInt("wrapper.jmx.rmi.port", Constants.DEFAULT_RMI_PORT); + String xname = _config.getString("wrapper.console.title"); + if (xname == null) + xname = _config.getString("wrapper.ntservice.name"); + if (xname == null) + xname = "yajsw.noname"; + ObjectName oName = new ObjectName("org.rzo.yajsw", "name", xname); + JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:"+port+"/server"); + jmxc = JMXConnectorFactory.connect(url, null); + MBeanServerConnection mbsc = jmxc.getMBeanServerConnection(); + proxy = (AbstractWrappedProcessMBean) + MBeanServerInvocationHandler.newProxyInstance( + mbsc, + oName, + AbstractWrappedProcessMBean.class, + false); + } + catch (Exception ex) + { + ex.printStackTrace(); + } + return proxy; + + } + + public boolean yajswInstall(String configuration) + { + WrappedService w = new WrappedService(); + Configuration c = w.getLocalConfiguration(); + c.setProperty("wrapper.config", configuration); + w.init(); + return w.install(); + } + + public boolean yajswUninstall(String name) + { + if (_services.get(name) != null && "Service".equals(_services.get(name).getWrapped())) + { + Service service = OperatingSystem.instance().serviceManagerInstance().getService(name); + service.stop(); + return service.uninstall(); + } + else + return false; + } + + public boolean yajswReloadConsole(String name, String newConfig) + { + AbstractWrappedProcessMBean proxy = getJmxProxy(name); + if (proxy == null) + return false; + try + { + proxy.setProperty("wrapper.config", newConfig); + proxy.resetCache(); + proxy.init(); + } + catch (Exception ex) + { + ex.printStackTrace(); + return false; + } + return true; + + } + + public boolean isServiceManager() + { + return true; + } + + +} diff --git a/javaUtilities/yajsw/src/srvmgr/java/org/rzo/yajsw/util/BeanTableFormat.java b/javaUtilities/yajsw/src/srvmgr/java/org/rzo/yajsw/util/BeanTableFormat.java new file mode 100644 index 0000000000..26d0792b92 --- /dev/null +++ b/javaUtilities/yajsw/src/srvmgr/java/org/rzo/yajsw/util/BeanTableFormat.java @@ -0,0 +1,48 @@ +package org.rzo.yajsw.util; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.commons.beanutils.PropertyUtils; + +import ca.odell.glazedlists.gui.TableFormat; + +public class BeanTableFormat implements TableFormat +{ + String[] _columnNames; + Map _propertyNames = new HashMap(); + + public BeanTableFormat(String[] propertyNames, String[] columnNames) + { + _columnNames = columnNames; + for (int i=0; i +{ + EventList _list; + String[] _idProperty; + String[] _propertyNames; + + Map _currentList = new HashMap(); + + public ObservableList(EventList list, String[] propertyNames, String[] idProperty) + { + _list = list; + _idProperty = idProperty; + _propertyNames = propertyNames; + } + + private String getId(Object obj) + { + String result = ""; + try + { + for (String prop : _idProperty) + result += PropertyUtils.getSimpleProperty(obj, prop) + "$$"; + return result; + } + catch (Exception e) + { + e.printStackTrace(); + } + return null; + } + + public void update(Collection newList) + { + Set updated = new HashSet(); + for (Object obj : newList) + { + updated.add(updateObject(obj)); + } + Set toRemove = new HashSet(_currentList.keySet()); + toRemove.removeAll(updated); + for (Object id : toRemove) + { + _list.remove(_currentList.remove(id)); + } + + } + + public Object updateObject(Object obj) + { + Object id = getId(obj); + ObservableObject current = _currentList.get(id); + if (current == null) + { + current = new ObservableObject(obj, _propertyNames); + _currentList.put(id, current); + _list.add(current); + } + else + current.update(obj); + return id; + } + + public void removeObject(E obj) + { + Object id = getId(obj); + ObservableObject current = _currentList.remove(id); + if (current != null) + _list.remove(current); + + } + + public boolean containsObject(E obj) + { + return _currentList.containsKey(getId(obj)); + } + + public E getObject(E obj) + { + Object id = getId(obj); + ObservableObject current = _currentList.get(id); + if (current == null) + return null; + return (E) current.getRoot(); + } + + public Collection getObjectList() + { + List result = new ArrayList(); + for (ObservableObject obj : _currentList.values()) + { + result.add((E) obj.getRoot()); + } + return result; + } + + +} diff --git a/javaUtilities/yajsw/src/srvmgr/java/org/rzo/yajsw/util/ObservableObject.java b/javaUtilities/yajsw/src/srvmgr/java/org/rzo/yajsw/util/ObservableObject.java new file mode 100644 index 0000000000..06348de7e5 --- /dev/null +++ b/javaUtilities/yajsw/src/srvmgr/java/org/rzo/yajsw/util/ObservableObject.java @@ -0,0 +1,68 @@ +package org.rzo.yajsw.util; + +import java.beans.PropertyChangeListener; +import java.beans.PropertyChangeSupport; +import java.lang.reflect.InvocationTargetException; + +import org.apache.commons.beanutils.PropertyUtils; +import org.rzo.yajsw.os.ServiceInfo; + +public class ObservableObject implements Comparable +{ + Object _root; + String[] _propertyNames; + String _idName; + + public ObservableObject(Object obj, String[] propertyNames) + { + _root = obj; + _propertyNames = propertyNames; + } + + private final PropertyChangeSupport support = new PropertyChangeSupport(this); + public void addPropertyChangeListener(PropertyChangeListener l) { + support.addPropertyChangeListener(l); + } + + public void removePropertyChangeListener(PropertyChangeListener l) { + support.removePropertyChangeListener(l); + } + + public void update(Object obj) + { + for (String field : _propertyNames) + update(field, obj); + } + + public void update(String field, Object obj) + { + Object newValue; + try + { + newValue = PropertyUtils.getSimpleProperty(obj, field); + Object oldValue = PropertyUtils.getSimpleProperty(_root, field); + if (oldValue != null && !oldValue.equals(newValue)) + { + PropertyUtils.setSimpleProperty(_root, field, newValue); + support.firePropertyChange(field, oldValue, newValue); + } + } + catch (Exception e) + { + e.printStackTrace(); + } + } + + public int compareTo(ObservableObject o) + { + Comparable t1 = (Comparable) _root; + Comparable t2 = (Comparable) o._root; + return t1.compareTo(t2); + } + + public Object getRoot() + { + return _root; + } + +} diff --git a/javaUtilities/yajsw/src/test/groovy/test/config/YajswConfigurationImplTest.groovy b/javaUtilities/yajsw/src/test/groovy/test/config/YajswConfigurationImplTest.groovy new file mode 100644 index 0000000000..93b6420270 --- /dev/null +++ b/javaUtilities/yajsw/src/test/groovy/test/config/YajswConfigurationImplTest.groovy @@ -0,0 +1,166 @@ +/** + * + */ + package test.config + + +import org.testng.annotations.* +import org.testng.TestNG +import org.testng.TestListenerAdapter +import static org.testng.AssertJUnit.*; +import org.apache.commons.configuration.*; +import org.rzo.yajsw.config.*; +import org.apache.commons.configuration.Configuration; + +/** +* +*/ +public class YajswConfigurationImplTest { + + /** + * Main entry point to run YajswConfigurationImplTest as + * simple Groovy class + */ + public static void main(String[] args){ + def testng = new TestNG() + testng.setTestClasses(YajswConfigurationImplTest) + testng.addListener(new TestListenerAdapter()) + testng.run() + } + + void createConfigurationFile(String file, Map co) + { + MapConfiguration mc = new MapConfiguration(co); + PropertiesConfiguration prop = new PropertiesConfiguration(); + ConfigurationUtils.copy(mc, prop); + prop.setFileName(file); + prop.save(); + } + + Configuration createLocalConfiguration(Map conf) + { + return new MapConfiguration(conf); + } + + + /** + * + */ + @Test + final void testWithSystemEnv(){ + YajswConfigurationImpl conf = new YajswConfigurationImpl(null, true); + assertTrue(conf.getString("java.home") != null) + } + /** + * + */ + @Test + final void testNoSystemEnv(){ + YajswConfigurationImpl conf = new YajswConfigurationImpl(null, false); + assertTrue(conf.getString("java.home") == null) + } + + /** + * + */ + @Test + final void testOverrideOrder(){ + // only conf file + createConfigurationFile('test.conf', ["test" : "3"]) + Configuration local = createLocalConfiguration(['wrapper.config': 'test.conf']) + YajswConfigurationImpl conf = new YajswConfigurationImpl(local, false); + assertTrue(conf.getInt("test") == 3) + + // local overrides file + local = createLocalConfiguration(['wrapper.config': 'test.conf', 'test': "2"]) + conf = new YajswConfigurationImpl(local, false); + assertTrue(conf.getInt("test") == 2) + + // local overrides system and file + System.setProperty("test", "1") + conf = new YajswConfigurationImpl(local, true); + assertTrue(conf.getInt("test") == 2) + + // system overrides file + local = createLocalConfiguration(['wrapper.config': 'test.conf']) + conf = new YajswConfigurationImpl(local, true); + assertTrue(conf.getInt("test") == 1) +} + + @Test + final void testInterpolation(){ + // simple interpolation + Configuration local = createLocalConfiguration(['test': '${java.home}/test']) + YajswConfigurationImpl conf = new YajswConfigurationImpl(local, true); + assertTrue(conf.getString("test").equals(conf.getString("java.home")+"/test")) + + // groovy interpolation + local = createLocalConfiguration(['test': '${"${java.home}".toLowerCase()}']) + conf = new YajswConfigurationImpl(local, true); + assertTrue(conf.getString("test").equals(conf.getString("java.home").toLowerCase())) + assertTrue(conf.lookupSet.contains("test") && conf.lookupSet.size() == 1) + } + + @Test + final void testGetString() + { + YajswConfigurationImpl conf = new YajswConfigurationImpl(null, true); + String result = conf.getString("java.home"); + println result + assertTrue(!result.contains("\\")); + } + + @Test + final void testIsLocalFile() + { + YajswConfigurationImpl conf = new YajswConfigurationImpl(null, true); + assertTrue(conf.isLocalFile()); + + createConfigurationFile('test.conf', ["test" : "3"]) + Configuration local = createLocalConfiguration(['wrapper.config': 'test.conf']); + conf = new YajswConfigurationImpl(local, true); + assertTrue(conf.isLocalFile()); + + local = createLocalConfiguration(['wrapper.config': 'http://eprognos.dhis.org/webdav/wrapper.tomcat.conf']); + conf = new YajswConfigurationImpl(local, true); + assertFalse(conf.isLocalFile()); + + local = createLocalConfiguration(['wrapper.config': 'http://java.sun.com/javase/technologies/desktop/javawebstart/apps/draw.jnlp']); + conf = new YajswConfigurationImpl(local, true); + assertFalse(conf.isLocalFile()); + } + + @Test + final void testGetCachedPath() + { + // no config file + YajswConfigurationImpl conf = new YajswConfigurationImpl(null, true); + String result = conf.getCachedPath(true); + assertNull(result); + + // local config file + createConfigurationFile('test.conf', ["test" : "3"]) + Configuration local = createLocalConfiguration(['wrapper.config': 'test.conf']); + conf = new YajswConfigurationImpl(local, true); + result = conf.getCachedPath(true); + assertNotNull(result) + assertEquals(result, new File('test.conf').canonicalPath); + + // remote config file + local = createLocalConfiguration(['wrapper.config': 'http://eprognos.dhis.org/webdav/wrapper.tomcat.conf']); + conf = new YajswConfigurationImpl(local, true); + result = conf.getCachedPath(true); + assertNotNull(result) + assertEquals(new PropertiesConfiguration(result).getString("wrapper.java.app.mainclass"), 'org.apache.catalina.startup.Bootstrap'); + + // remote jnlp file + local = createLocalConfiguration(['wrapper.config': 'http://java.sun.com/javase/technologies/desktop/javawebstart/apps/draw.jnlp']); + conf = new YajswConfigurationImpl(local, true); + result = conf.getCachedPath(true); + assertNotNull(result) + assertEquals(new PropertiesConfiguration(result).getString("wrapper.java.app.mainclass"), 'Draw'); + + } + + +} \ No newline at end of file diff --git a/javaUtilities/yajsw/src/test/groovy/test/controller/JVMControllerTest.groovy b/javaUtilities/yajsw/src/test/groovy/test/controller/JVMControllerTest.groovy new file mode 100644 index 0000000000..9000bfbadf --- /dev/null +++ b/javaUtilities/yajsw/src/test/groovy/test/controller/JVMControllerTest.groovy @@ -0,0 +1,192 @@ +/** + * + */ + package test.controller + + +import org.testng.annotations.* +import org.testng.TestNG +import org.testng.TestListenerAdapter +import static org.testng.AssertJUnit.*; + + import org.rzo.yajsw.controller.*; + import org.rzo.yajsw.controller.jvm.*; +import org.rzo.yajsw.wrapper.*; +import org.rzo.yajsw.Constants; + + +import java.util.concurrent.*; +import java.lang.Runnable; +import java.util.Random; + +import org.jmock.*; +import org.jmock.internal.*; + + +/** +* +*/ +public class JVMControllerTest { + + Random random = new Random(System.currentTimeMillis()) + //JUnit4GroovyMockery mockery = new JUnit4GroovyMockery(); + Mockery mockery = new Mockery(); + def String key = ""+0 + def message = new Message((byte)Constants.WRAPPER_MSG_KEY, key); + + @BeforeClass + void initTest() + { + mockery.addExpectation(new InvocationExpectation()) // allow all invocations + } + + + /** + * Main entry point to run JVMControllerTest as + * simple Groovy class + */ + public static void main(String[] args){ + def testng = new TestNG() + testng.setTestClasses(JVMControllerTest) + testng.addListener(new TestListenerAdapter()) + testng.run() + } + + JVMController createController() + { + WrappedJavaProcess process = new WrappedJavaProcess(); + JVMController controller = new JVMController(process); + controller.setStartupTimeout(100000) + controller.setPingTimeout(100000) + controller.setKey(key) + + return controller; + } + + void mockProcessLogon(JVMController controller) + { + controller._acceptor.getHandler().messageReceived(ioSession, message); + } + + + /** + * + */ + final void testRepeatedStartStop() + { + JVMController controller = createController(); + controller.init() + (1..100).each + { + assertTrue("could not start controller", controller.start()); + Thread.sleep((long)(random.nextInt(500) /10)*10) // sleep on windows < 10 has issues on windows + assertEquals("wrong state after start", controller.stateAsStr(JVMController.STATE_WAITING), controller.stateAsStr(controller.getState())); + mockProcessLogon(controller); + controller.processStarted(); + Thread.sleep((long)(random.nextInt(500) /10)*10+100) + assertEquals("wrong state after processStarted", controller.stateAsStr(JVMController.STATE_PROCESS_KILLED), controller.stateAsStr(controller.getState())); + controller.stop(JVMController.STATE_USER_STOP); + //Thread.sleep((long)(random.nextInt(500) /10)*10) + assertEquals("wrong state after stop", controller.stateAsStr(JVMController.STATE_USER_STOP), controller.stateAsStr(controller.getState())); + controller.reset(); + Thread.sleep((long)(random.nextInt(500) /10)*10) + assertEquals("wrong state after reset", controller.stateAsStr(JVMController.STATE_UNKNOWN), controller.stateAsStr(controller.getState())); + } + } + + @Test + final void testMultithreadRepeatedStartStop() + { + def futures = [] + ExecutorService pool = Executors.newCachedThreadPool(); + (1..10).each + { + futures.add( pool.execute( { testRepeatedStartStop() } as Runnable )) + } + pool.shutdown(); + assertTrue("start/stop test hangs", pool.awaitTermination(120, TimeUnit.SECONDS)) + } + + @Test + final void testPortRange() + { + JVMController controller = createController(); + controller.setMaxPort(15003) + controller.setMinPort(15003) + controller.init(); + + assertTrue("could not start controller", controller.start()); + assertEquals("wrong state", controller.stateAsStr(JVMController.STATE_WAITING), controller.stateAsStr(controller.getState())); + controller.stop(JVMController.STATE_USER_STOP); + Thread.sleep((long)(random.nextInt(500) /10)*10) + assertEquals("wrong state", controller.stateAsStr(JVMController.STATE_USER_STOP), controller.stateAsStr(controller.getState())); + controller.reset(); + Thread.sleep((long)(random.nextInt(500) /10)*10) + assertEquals("wrong state", controller.stateAsStr(JVMController.STATE_UNKNOWN), controller.stateAsStr(controller.getState())); + + ServerSocket socket = new ServerSocket() + socket.bind(new InetSocketAddress(15003)) + assertFalse("started controller although port is not available", controller.start()); + assertNotSame("state after erronous start should not be WAITING", controller.getState(), JVMController.STATE_WAITING); + + + controller.setMaxPort(15004) + assertTrue("could not start controller", controller.start()); + assertEquals("wrong state", controller.stateAsStr(JVMController.STATE_WAITING), controller.stateAsStr(controller.getState())); + controller.stop(JVMController.STATE_USER_STOP); + Thread.sleep((long)(random.nextInt(500) /10)*10) + assertEquals("wrong state", controller.stateAsStr(JVMController.STATE_USER_STOP), controller.stateAsStr(controller.getState())); + controller.reset(); + Thread.sleep((long)(random.nextInt(500) /10)*10) + assertEquals("wrong state", controller.stateAsStr(JVMController.STATE_UNKNOWN), controller.stateAsStr(controller.getState())); + + socket.close() + } + + @Test + final void testStartupTimeout() + { + JVMController controller = createController(); + controller.setKey(key) + controller.setStartupTimeout(1000) + controller.setPingTimeout(100000) + controller.init(); + + + // test timeout event + assertTrue("could not start controller", controller.start()); + assertEquals("wrong state", controller.stateAsStr(JVMController.STATE_WAITING), controller.stateAsStr(controller.getState())); + Thread.sleep((long)(2000)) + assertEquals("wrong state", controller.stateAsStr(JVMController.STATE_STARTUP_TIMEOUT), controller.stateAsStr(controller.getState())); + controller.stop(JVMController.STATE_USER_STOP); + controller.reset(); + Thread.sleep((long)(random.nextInt(500) /10)*10) + assertEquals("wrong state", controller.stateAsStr(JVMController.STATE_UNKNOWN), controller.stateAsStr(controller.getState())); + + // test stop before timeout + assertTrue("could not start controller", controller.start()); + assertEquals("wrong state", controller.stateAsStr(JVMController.STATE_WAITING), controller.stateAsStr(controller.getState())); + Thread.sleep((long)(500)) + assertEquals("wrong state", controller.stateAsStr(JVMController.STATE_WAITING), controller.stateAsStr(controller.getState())); + controller.stop(JVMController.STATE_USER_STOP); + assertEquals("wrong state", controller.stateAsStr(JVMController.STATE_USER_STOP), controller.stateAsStr(controller.getState())); + controller.reset(); + assertEquals("wrong state", controller.stateAsStr(JVMController.STATE_UNKNOWN), controller.stateAsStr(controller.getState())); + + // test process started before timeout + assertTrue("could not start controller", controller.start()); + Thread.sleep((long)500) + assertEquals("wrong state", controller.stateAsStr(JVMController.STATE_WAITING), controller.stateAsStr(controller.getState())); + mockProcessLogon(controller); + // simulate process log on + Thread.sleep((long)500) + assertEquals("wrong state", controller.stateAsStr(JVMController.STATE_LOGGED_ON), controller.stateAsStr(controller.getState())); + controller.stop(JVMController.STATE_USER_STOP); + Thread.sleep((long)500) + assertEquals("wrong state", controller.stateAsStr(JVMController.STATE_USER_STOP), controller.stateAsStr(controller.getState())); + controller.reset(); + Thread.sleep((long)500) + assertEquals("wrong state", controller.stateAsStr(JVMController.STATE_UNKNOWN), controller.stateAsStr(controller.getState())); + } + +} \ No newline at end of file diff --git a/javaUtilities/yajsw/src/test/groovy/test/controller/TestJVMController2.groovy b/javaUtilities/yajsw/src/test/groovy/test/controller/TestJVMController2.groovy new file mode 100644 index 0000000000..33bdec2ac6 --- /dev/null +++ b/javaUtilities/yajsw/src/test/groovy/test/controller/TestJVMController2.groovy @@ -0,0 +1,128 @@ +/** + * + */ + package test.controller + + +import org.testng.annotations.* +import org.testng.TestNG +import org.testng.TestListenerAdapter +import static org.testng.AssertJUnit.*; + + import org.rzo.yajsw.controller.*; + import org.rzo.yajsw.controller.jvm.*; +import org.rzo.yajsw.wrapper.*; +import org.rzo.yajsw.Constants; +import org.rzo.yajsw.nettyutils.*; +import org.rzo.yajsw.app.WrapperManagerImpl; + +import java.util.concurrent.*; + +import org.jboss.netty.bootstrap.*; +import org.jboss.netty.channel.*; +import org.jboss.netty.channel.socket.nio.*; + + +/** +* +*/ +public class TestJVMController2 { + + /** + * Main entry point to run TestJVMController2 as + * simple Groovy class + */ + + def String key = ""+1234 + def Executor executor = Executors.newCachedThreadPool(); + + def int sleep = 10000 + + + + public static void main(String[] args){ + def testng = new TestNG() + testng.setTestClasses(TestJVMController2) + testng.addListener(new TestListenerAdapter()) + testng.run() + } + + JVMController createController() + { + WrappedJavaProcess process = new WrappedJavaProcess(); + JVMController controller = new JVMController(process); + controller.setStartupTimeout(100000) + controller.setPingTimeout(100000) + controller.setKey(key) + + return controller; + } + + + void simConnect() + { + def ClientBootstrap connector; + connector = new ClientBootstrap( + new NioClientSocketChannelFactory( + executor, + executor)); + connector.setOption("remoteAddress", new InetSocketAddress("127.0.0.1", 15003)); + connector.setOption("connectTimeoutMillis", 10 * 1000); + connector.getPipeline().addLast("log", new LoggingFilter(null)); + connector.getPipeline().addLast("messageEncoder", new MessageEncoder()); + connector.getPipeline().addLast("messageDecoder", new MessageDecoder()); + + ChannelFuture future1 = connector.connect(); + future1.await(); + println "connected ? "+ future1.isSuccess() + future1.getChannel().write(new Message(Constants.WRAPPER_MSG_KEY, key)); + } + + + + + + /** + * + */ + //@Test + final void testController(){ + JVMController controller = createController(); + controller.init(); + (1..10).each + { + controller.start(); + Thread.sleep(sleep); + simConnect(); + simConnect(); + simConnect(); + simConnect(); + Thread.sleep(sleep); + Thread.sleep(sleep); + Thread.sleep(sleep); + Thread.sleep(sleep); + controller.reset(); + Thread.sleep(sleep); + } + } + + @Test + final void testJVMMain() + { + JVMController controller = createController(); + controller.init(); + controller.start(); + + def WrapperManagerImpl wm = new WrapperManagerImpl(); + wm.setPort(15003) + wm.setKey(key) + wm.setPingInterval(5000); + wm.start() + + Thread.sleep(60000) + + //controller.reset(); + + } + +} \ No newline at end of file diff --git a/javaUtilities/yajsw/src/test/groovy/test/io/TestCyclicBufferFile.groovy b/javaUtilities/yajsw/src/test/groovy/test/io/TestCyclicBufferFile.groovy new file mode 100644 index 0000000000..3b301b0bc2 --- /dev/null +++ b/javaUtilities/yajsw/src/test/groovy/test/io/TestCyclicBufferFile.groovy @@ -0,0 +1,100 @@ + package test.io + + +import org.testng.annotations.* +import org.testng.TestNG +import org.testng.TestListenerAdapter +import static org.testng.AssertJUnit.*; + +import org.rzo.yajsw.io.*; + +import java.util.concurrent.*; +import java.lang.Runnable; + +import java.io.*; + + +/** +* +*/ +public class TestCyclicBufferFile { + + /** + * Main entry point to run TestCyclicBufferFile as + * simple Groovy class + */ + public static void main(String[] args){ + def testng = new TestNG() + testng.setTestClasses(TestCyclicBufferFile) + testng.addListener(new TestListenerAdapter()) + testng.run() + } + + + void readx(CyclicBufferFileInputStream reader, int sizex) + { + InputStreamReader isr = new InputStreamReader(reader); + BufferedReader br = new BufferedReader(isr); + String line = "" + int i=1; + try + { + for (; (line = br.readLine()) != null && iTeeInputStream as + * simple Groovy class + */ + public static void main(String[] args){ + def testng = new TestNG() + testng.setTestClasses(TeeInputStream) + testng.addListener(new TestListenerAdapter()) + testng.run() + } + + InputStream createInput(String x) + { + File f = new File("in$x") + //f.delete(); + f.createNewFile() + f.write((1..100).join("\n")) + + return new FileInputStream("in$x") + } + + + /** + * + */ + // TODO hangs @Test + final void testTeeInputStream(){ + InputStream in1 = createInput("1") + InputStream in2 = createInput("2") + TeeInputStream inx = new TeeInputStream(); + inx.connect(in1); + inx.connect(in2); + while (true) + { + println inx.read() + } + } + +} \ No newline at end of file diff --git a/javaUtilities/yajsw/src/test/groovy/test/io/TestTeeOutputStream.groovy b/javaUtilities/yajsw/src/test/groovy/test/io/TestTeeOutputStream.groovy new file mode 100644 index 0000000000..2b837ec0cd --- /dev/null +++ b/javaUtilities/yajsw/src/test/groovy/test/io/TestTeeOutputStream.groovy @@ -0,0 +1,69 @@ +/** + * + */ + package test.io + + +import org.testng.annotations.* +import org.testng.TestNG +import org.testng.TestListenerAdapter +import static org.testng.AssertJUnit.*; + +import org.rzo.yajsw.io.*; + +/** +* +*/ +public class TestTeeOutputStream { + + /** + * Main entry point to run TestTeeOutputStream as + * simple Groovy class + */ + public static void main(String[] args){ + def testng = new TestNG() + testng.setTestClasses(TestTeeOutputStream) + testng.addListener(new TestListenerAdapter()) + testng.run() + } + + + /** + * + */ + @Test + final void testTeeOutputStream(){ + new File("out1.txt").delete() + FileOutputStream out1 = new FileOutputStream("out1.txt"); + new File("out2.txt").delete() + FileOutputStream out2 = new FileOutputStream("out2.txt"); + TeeOutputStream tee = new TeeOutputStream(); + tee.connect(out1); + tee.connect(out2); + + (1..100).each + { + tee.write("$it\n" as byte[]) + } + tee.close(); + out1.close(); + out2.close(); + File f1 = new File("out1.txt") + int count = 1 + f1.eachLine + { + assertEquals(it, ""+count++) + } + File f2 = new File("out2.txt") + count = 1 + f2.eachLine + { + assertEquals(it, ""+count++) + } + + + + + } + +} \ No newline at end of file diff --git a/javaUtilities/yajsw/src/test/groovy/test/os/TestJavaHome.groovy b/javaUtilities/yajsw/src/test/groovy/test/os/TestJavaHome.groovy new file mode 100644 index 0000000000..0b1126cc5f --- /dev/null +++ b/javaUtilities/yajsw/src/test/groovy/test/os/TestJavaHome.groovy @@ -0,0 +1,83 @@ +/** + * + */ + package test.os + + +import org.testng.annotations.* +import org.testng.TestNG +import org.testng.TestListenerAdapter +import static org.testng.AssertJUnit.*; + + import org.apache.commons.configuration.*; + import org.rzo.yajsw.os.*; + + +/** +* +*/ +public class TestJavaHome { + + /** + * Main entry point to run TestJavaHome as + * simple Groovy class + * + * Test requires jdk jre 1.5 + 1.6 installed. + */ + public static void main(String[] args){ + def testng = new TestNG() + testng.setTestClasses(TestJavaHome) + testng.addListener(new TestListenerAdapter()) + testng.run() + } + + boolean expectedJVM(String cmd, String version) + { + return cmd.contains(version) + } + + + /** + * + */ + @Test + final void testJavaHomeMin(){ + BaseConfiguration conf = new BaseConfiguration(); + conf.setProperty("wrapper.java.command.minVersion", "1.6.0") + JavaHome jvm = OperatingSystem.instance().getJavaHome(conf) + assertTrue(expectedJVM("${jvm.findJava()}", "1.6")) + } + + @Test + final void testJavaHomeMax(){ + BaseConfiguration conf = new BaseConfiguration(); + conf.setProperty("wrapper.java.command.maxVersion", "1.5.0") + JavaHome jvm = OperatingSystem.instance().getJavaHome(conf) + assertTrue(expectedJVM("${jvm.findJava()}", "1.5")) + } + + @Test + final void testJavaHomeJre(){ + BaseConfiguration conf = new BaseConfiguration(); + conf.setProperty("wrapper.java.command.jreOnly", "true") + JavaHome jvm = OperatingSystem.instance().getJavaHome(conf) + assertTrue(expectedJVM("${jvm.findJava()}", "jre")) + } + + @Test + final void testJavaHomeJdk(){ + BaseConfiguration conf = new BaseConfiguration(); + conf.setProperty("wrapper.java.command.jdkOnly", "true") + JavaHome jvm = OperatingSystem.instance().getJavaHome(conf) + assertTrue(expectedJVM("${jvm.findJava()}", "jdk")) + } + + @Test + final void testJavaHomeJavaw(){ + BaseConfiguration conf = new BaseConfiguration(); + conf.setProperty("wrapper.java.command.javaw", "true") + JavaHome jvm = OperatingSystem.instance().getJavaHome(conf) + assertTrue(expectedJVM("${jvm.findJava()}", "javaw")) + } + +} \ No newline at end of file diff --git a/javaUtilities/yajsw/src/test/groovy/test/os/TestOsProcess.groovy b/javaUtilities/yajsw/src/test/groovy/test/os/TestOsProcess.groovy new file mode 100644 index 0000000000..a5f7045258 --- /dev/null +++ b/javaUtilities/yajsw/src/test/groovy/test/os/TestOsProcess.groovy @@ -0,0 +1,189 @@ +/** + * + */ + package test.os + + +import org.testng.annotations.* +import org.testng.TestNG +import org.testng.TestListenerAdapter +import static org.testng.AssertJUnit.*; + +import org.rzo.yajsw.os.*; +import com.sun.jna.Platform; + +import java.util.concurrent.*; +import java.lang.Runnable; + + +/** +* +*/ +public class TestOsProcess { + + /** + * Main entry point to run TestOsProcess as + * simple Groovy class + */ + public static void main(String[] args){ + def testng = new TestNG() + testng.setTestClasses(TestOsProcess) + testng.addListener(new TestListenerAdapter()) + testng.run() + } + + + /** + * + */ + final Process createPing(){ + String strCmd = "ping localhost" + (Platform.isWindows() ? " -t" : ""); + //String strCmd = "notepad" + String[] cmd = strCmd.split(); + Process process = OperatingSystem.instance().processManagerInstance().createProcess(); + process.setCommand(cmd); + process.setTitle('ping') + return process; + } + + @Test + final void stopPing() + { + Process process = createPing(); + assertTrue(process.start()); + assertTrue(process.isRunning()); + assertTrue(!process.isTerminated()); + long time = System.currentTimeMillis(); + assertTrue(process.stop(5000, 9)); + long duration = System.currentTimeMillis() - time; + assertTrue(process.isTerminated()); + assertTrue(!process.isRunning()); + assertTrue(duration < 7000) + assertEquals(process.getExitCode(), 9); + } + + @Test + final void killPing() + { + Process process = createPing(); + assertTrue(process.start()); + assertTrue(process.isRunning()); + assertTrue(!process.isTerminated()); + long time = System.currentTimeMillis(); + assertTrue(process.kill(9)); + long duration = System.currentTimeMillis() - time; + assertTrue(process.isTerminated()); + assertTrue(!process.isRunning()); + assertTrue(duration >= 0 && duration < 1000) + assertEquals(process.getExitCode(), 9); + } + + @Test + final void multiKillPing() + { + Process process = createPing(); + (1..10).each + { + assertTrue(process.start()); + assertTrue(process.isRunning()); + assertTrue(!process.isTerminated()); + long time = System.currentTimeMillis(); + assertTrue(process.kill(9)); + long duration = System.currentTimeMillis() - time; + assertTrue(process.isTerminated()); + assertTrue(!process.isRunning()); + assertTrue(duration >= 0 && duration < 5000) + assertEquals(process.getExitCode(), 9); + process.destroy(); + } + } + + @Test + final void multiThreadMultiKillPing() + { + def futures = [] + ExecutorService pool = Executors.newCachedThreadPool(); + (1..50).each + { + futures.add( pool.execute( { multiKillPing() } as Runnable )) + } + pool.shutdown(); + assertTrue("start/stop test hangs", pool.awaitTermination(120, TimeUnit.SECONDS)) + + } + + @Test + final void performancePing() + { + Process process = createPing(); + assertTrue(process.start()); + assertTrue(process.getCurrentCpu() >= 0) + assertTrue(process.getCurrentHandles() >= 1) + assertTrue(process.getCurrentPageFaults() >= 0) + assertTrue(process.getCurrentPhysicalMemory() >= 1) + assertTrue(process.getCurrentThreads() >= 1) + assertTrue(process.getCurrentVirtualMemory() >= 1) + assertTrue(process.kill(9)); + } + + @Test + final void priorityPing() + { + Process process = createPing(); + process.setPriority(Process.PRIORITY_BELOW_NORMAL); + assertTrue(process.start()); + assertTrue(process.kill(9)); + } + + @Test + final void badUserPing() + { + Process process = createPing(); + process.setUser("no good user"); + assertTrue(!process.start()); + process.kill(9) + } + + @Test + final void currentUserPing() + { + //TODO + } + + @Test + final void pipeStreamsPing() + { + Process process = createPing(); + process.setPipeStreams(true, false) + assertTrue(process.start()); + BufferedReader input = new BufferedReader(new InputStreamReader(process.getInputStream())) + Thread.sleep(1000) + String line = input.readLine() + line = input.readLine() + line = input.readLine() + assertTrue("expecting ping in $line", line.toLowerCase().startsWith("ping")); + assertTrue(process.kill(9)); + } + + @Test + final void getPingProcess() + { + Process process = createPing(); + assertTrue(process.start()); + int pid = process.getPid(); + Process ping = OperatingSystem.instance().processManagerInstance().getProcess(pid) + assertEquals(ping.getCommand(), process.getCommand()) + assertTrue(ping.getUser() != null) + assertTrue(ping.getWorkingDir() != null) + assertEquals(ping.getTitle(), process.getTitle()) + assertTrue(ping.kill(9)); + assertEquals(process.getExitCode(), 9); + } + + + + + + + +} \ No newline at end of file diff --git a/javaUtilities/yajsw/src/test/groovy/test/os/TestOsService.groovy b/javaUtilities/yajsw/src/test/groovy/test/os/TestOsService.groovy new file mode 100644 index 0000000000..290664c7a2 --- /dev/null +++ b/javaUtilities/yajsw/src/test/groovy/test/os/TestOsService.groovy @@ -0,0 +1,123 @@ +/** + * + */ + package test.os + + +import org.testng.annotations.* +import org.testng.TestNG +import org.testng.TestListenerAdapter +import static org.testng.AssertJUnit.*; + + import org.rzo.yajsw.os.*; + import com.sun.jna.Platform; + + import org.apache.commons.configuration.*; + + + +/** +* +*/ +public class TestOsService { + + /** + * Main entry point to run TestOsService as + * simple Groovy class + */ + public static void main(String[] args){ + def testng = new TestNG() + testng.setTestClasses(TestOsService) + testng.addListener(new TestListenerAdapter()) + testng.run() + } + + Service createPing() + { + Service service = OperatingSystem.instance().serviceManagerInstance().createService() + String strCmd = "ping localhost" + (Platform.isWindows() ? " -t" : ""); + String[] cmd = strCmd.split(); + service.setCommand(cmd) + service.setName("ping") + service.setDescription("ping") + service.setDisplayName("ping") + service.setConfig(new BaseConfiguration()) + return service + } + + + + + /** + * + */ + @Test + final void installUninstall(){ + Service service = createPing() + service.init() + // make sure it is not already installed + service.uninstall() + assertTrue(service.install()) + assertTrue(service.isInstalled(service.state())) + assertTrue(service.uninstall()) + assertTrue(!service.isInstalled(service.state())) + } + + @Test + final void multiInstallUninstall() + { + Service service = createPing() + service.init() + service.uninstall() + (1..10).each + { + assertTrue(service.install()) + assertTrue(service.isInstalled(service.state())) + assertTrue(service.uninstall()) + assertTrue(!service.isInstalled(service.state())) + } + } + + //@Test + final void startStop(){ + Service service = createPing() + service.init() + // make sure it is not already installed + service.uninstall() + assertTrue(service.install()) + assertTrue(service.isInstalled(service.state())) + + assertTrue(service.start()) + assertTrue(service.isRunning(service.state())) + + assertTrue(service.stop()) + assertTrue(!service.isRunning(service.state())) + + assertTrue(service.uninstall()) + assertTrue(!service.isInstalled(service.state())) + } + + //@Test + final void multiStartStop(){ + Service service = createPing() + service.init() + // make sure it is not already installed + service.uninstall() + assertTrue(service.install()) + assertTrue(service.isInstalled(service.state())) + + (1..10).each + { + assertTrue(service.start()) + assertTrue(service.isRunning(service.state())) + + assertTrue(service.stop()) + assertTrue(!service.isRunning(service.state())) + } + + assertTrue(service.uninstall()) + assertTrue(!service.isInstalled(service.state())) + } + + +} \ No newline at end of file diff --git a/javaUtilities/yajsw/src/test/groovy/test/tools/TestGenConfig.groovy b/javaUtilities/yajsw/src/test/groovy/test/tools/TestGenConfig.groovy new file mode 100644 index 0000000000..d762807ddd --- /dev/null +++ b/javaUtilities/yajsw/src/test/groovy/test/tools/TestGenConfig.groovy @@ -0,0 +1,79 @@ +/** + * + */ + package test.tools + + +import org.testng.annotations.* +import org.testng.TestNG +import org.testng.TestListenerAdapter +import static org.testng.AssertJUnit.*; + +import org.rzo.yajsw.tools.*; + +import org.rzo.yajsw.os.*; +import org.apache.commons.configuration.*; + + +/** +* +*/ +public class TestGenConfig { + + /** + * Main entry point to run TestGenConfig as + * simple Groovy class + */ + public static void main(String[] args){ + def testng = new TestNG() + testng.setTestClasses(TestGenConfig) + testng.addListener(new TestListenerAdapter()) + testng.run() + } + + + /** + * + */ + @Test + final void testSplitArgs() + { + assertEquals(ConfigGenerator.splitArgs(''), []); + assertEquals(ConfigGenerator.splitArgs('1'), ['1']); + assertEquals(ConfigGenerator.splitArgs('1 2'), ['1', '2']); + assertEquals(ConfigGenerator.splitArgs('-Xmx562m -Xms521m'), ['-Xmx562m', '-Xms521m']); + assertEquals(ConfigGenerator.splitArgs('-Xmx562m -Xms521m'), ['-Xmx562m', '-Xms521m']); + assertEquals(ConfigGenerator.splitArgs('-Xmx562m -Xms521m'), ['-Xmx562m', '-Xms521m']); + assertEquals(ConfigGenerator.splitArgs('"1 "'), ['1']); + assertEquals(ConfigGenerator.splitArgs('"1 " "2 "'), ['1', '2']); + assertEquals(ConfigGenerator.splitArgs('" 1 " "2 "'), ['1', '2']); + assertEquals(ConfigGenerator.splitArgs('"1 2" "3 4"'), ['1 2', '3 4']); + } + + @Test + final void testGenConfig() + { + Process process = OperatingSystem.instance().processManagerInstance().createProcess(); + process.setTitle('test') + process.setCommand("java -Xmx25m -Xms25m -Dx=y \"-Dx=z z\" -cp wrapper.jar${File.pathSeparatorChar}test.jar test.HelloWorld c:/x.txt \"d d\"") + process.start() + Thread.sleep(1000) + ConfigGenerator.generate(process.getPid(), null, new File("test.conf")); + process.kill(0) + org.apache.commons.configuration.Configuration conf = new PropertiesConfiguration("test.conf") + new File("test.conf").delete() + println conf.getString("wrapper.java.command") + assertEquals(conf.getString("wrapper.java.command"), "java") + assertEquals(conf.getString("wrapper.java.classpath.1"), "wrapper.jar") + assertEquals(conf.getString("wrapper.java.classpath.2"), "test.jar") + assertEquals(conf.getString("wrapper.java.app.mainclass"), "test.HelloWorld") + assertEquals(conf.getString("wrapper.app.parameter.1"), "c:/x.txt") + assertEquals(conf.getString("wrapper.app.parameter.2"), "d d") + assertEquals(conf.getString("wrapper.working.dir"), new File(".").canonicalPath+File.separator) + assertEquals(conf.getString("wrapper.java.additional.1"), '-Xmx25m') + assertEquals(conf.getString("wrapper.java.additional.2"), '-Xms25m') + assertEquals(conf.getString("wrapper.java.additional.3"), '-Dx=y') + assertEquals(conf.getString("wrapper.java.additional.4"), '-Dx=z z') + } + +} \ No newline at end of file diff --git a/javaUtilities/yajsw/src/test/groovy/test/wrapper/TestMultiple.groovy b/javaUtilities/yajsw/src/test/groovy/test/wrapper/TestMultiple.groovy new file mode 100644 index 0000000000..111609e564 --- /dev/null +++ b/javaUtilities/yajsw/src/test/groovy/test/wrapper/TestMultiple.groovy @@ -0,0 +1,91 @@ +/** + * + */ + package test.wrapper + + +import org.testng.annotations.* +import org.testng.TestNG +import org.testng.TestListenerAdapter +import static org.testng.AssertJUnit.*; + + import java.util.concurrent.*; + import java.lang.Runnable; + + import org.rzo.yajsw.groovy.*; + import org.rzo.yajsw.wrapper.*; + + import com.sun.jna.Platform; + + +/** +* +*/ +public class TestMultiple { + + /** + * Main entry point to run TestMultiple as + * simple Groovy class + */ + public static void main(String[] args){ + def testng = new TestNG() + testng.setTestClasses(TestMultiple) + testng.addListener(new TestListenerAdapter()) + testng.run() + } + + Object getJavaProcess() + { + def builder = new WrapperBuilder(); + builder.'wrapper.java.app.mainclass'='test.HelloWorld'; + builder.'wrapper.control'='LOOSE' + return builder.process(); + } + + Object getNativeProcess() + { + WrapperBuilder builder = new WrapperBuilder(); + builder.'wrapper.image' = "ping localhost" + (Platform.isWindows() ? " -t" : ""); + builder.'wrapper.control'='LOOSE' + builder.'wrapper.jvm_exit.timeout'='0' + builder.'wrapper.shutdown.timeout'='1' + return builder.process(); + } + + + /** + * + */ + @Test + final void testMultiple(){ + List processes = [] + ExecutorService pool = Executors.newCachedThreadPool(); + (1..1).each {processes.add(getJavaProcess())} + (1..1).each {processes.add(getNativeProcess())} + (1..100).each + { + processes.each + { final process = it + pool.execute( + { + process.start() + assertTrue(process.isOSProcessRunning()) + } as Runnable) + } + Thread.sleep(5000) + processes.each + { final process = it + pool.execute( + { + process.stop() + assertTrue(!process.isOSProcessRunning()) + } as Runnable) + } + Thread.sleep(2000) + } + pool.shutdown(); + + } + + +} \ No newline at end of file diff --git a/javaUtilities/yajsw/src/test/groovy/test/wrapper/TestWrappedJavaProcess.groovy b/javaUtilities/yajsw/src/test/groovy/test/wrapper/TestWrappedJavaProcess.groovy new file mode 100644 index 0000000000..454b39a50a --- /dev/null +++ b/javaUtilities/yajsw/src/test/groovy/test/wrapper/TestWrappedJavaProcess.groovy @@ -0,0 +1,37 @@ +/** + * + */ + package test.wrapper + + +import org.testng.annotations.* +import org.testng.TestNG +import org.testng.TestListenerAdapter +import static org.testng.AssertJUnit.*; + +/** +* +*/ +public class TestWrappedJavaProcess { + + /** + * Main entry point to run TestWrappedJavaProcess as + * simple Groovy class + */ + public static void main(String[] args){ + def testng = new TestNG() + testng.setTestClasses(TestWrappedJavaProcess) + testng.addListener(new TestListenerAdapter()) + testng.run() + } + + + /** + * + */ + @Test + final void testSomething(){ + assertTrue(true) + } + +} \ No newline at end of file diff --git a/javaUtilities/yajsw/src/test/java/org/rzo/test/RunGroovyTestNg.java b/javaUtilities/yajsw/src/test/java/org/rzo/test/RunGroovyTestNg.java new file mode 100644 index 0000000000..4fe3c8f077 --- /dev/null +++ b/javaUtilities/yajsw/src/test/java/org/rzo/test/RunGroovyTestNg.java @@ -0,0 +1,19 @@ +package org.rzo.test; + +import java.util.List; + +// boot yajsw, load groovy test files and run the tests +public class RunGroovyTestNg +{ + + static List loadGroovyScripts(String path) + { + return null; + } + + public static void main(String[] args) + { + + } + +} diff --git a/javaUtilities/yajsw/src/ws/java/org/rzo/yajsw/ws/WSForm.java b/javaUtilities/yajsw/src/ws/java/org/rzo/yajsw/ws/WSForm.java new file mode 100644 index 0000000000..9ef5ecd4e6 --- /dev/null +++ b/javaUtilities/yajsw/src/ws/java/org/rzo/yajsw/ws/WSForm.java @@ -0,0 +1,284 @@ +package org.rzo.yajsw.ws; + +import com.jeta.forms.components.border.TitledBorderBottom; +import com.jeta.forms.components.border.TitledBorderSide; +import com.jeta.forms.components.separator.TitledSeparator; +import com.jeta.open.i18n.I18NUtils; +import com.jgoodies.forms.layout.CellConstraints; +import com.jgoodies.forms.layout.FormLayout; +import java.awt.BorderLayout; +import java.awt.ComponentOrientation; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import javax.swing.Box; +import javax.swing.ButtonGroup; +import javax.swing.ImageIcon; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JRadioButton; +import javax.swing.JScrollPane; +import javax.swing.JTextArea; +import javax.swing.JTextField; + + +public class WSForm extends JPanel +{ + JTextArea _LOG_AREA = new JTextArea(); + TitledBorderBottom _titledborderbottom1 = new TitledBorderBottom(); + JRadioButton _INSTALL_OPTION = new JRadioButton(); + ButtonGroup _buttongroup1 = new ButtonGroup(); + JRadioButton _CONSOLE_OPTION = new JRadioButton(); + JLabel _APPLICATION = new JLabel(); + TitledBorderSide _titledborderside1 = new TitledBorderSide(); + JCheckBox _START_OPTION = new JCheckBox(); + JTextField _INSTALL_FOLDER = new JTextField(); + JCheckBox _TRAY_ICON_OPTION = new JCheckBox(); + JButton _GO_BUTTON = new JButton(); + JButton _CANCEL_BUTTON = new JButton(); + JLabel _STATE = new JLabel(); + JLabel _SPEED = new JLabel(); + JButton _SELECT_FOLDER_BUTTON = new JButton(); + JButton _SHOW_CONF_BUTTON = new JButton(); + TitledBorderSide _titledborderside2 = new TitledBorderSide(); + + /** + * Default constructor + */ + public WSForm() + { + initializePanel(); + } + + /** + * Main method for panel + */ + public static void main(String[] args) + { + JFrame frame = new JFrame(); + frame.setSize(600, 400); + frame.setLocation(100, 100); + frame.getContentPane().add(new WSForm()); + frame.setVisible(true); + + frame.addWindowListener( new WindowAdapter() + { + public void windowClosing( WindowEvent evt ) + { + System.exit(0); + } + }); + } + + /** + * Adds fill components to empty cells in the first row and first column of the grid. + * This ensures that the grid spacing will be the same as shown in the designer. + * @param cols an array of column indices in the first row where fill components should be added. + * @param rows an array of row indices in the first column where fill components should be added. + */ + void addFillComponents( Container panel, int[] cols, int[] rows ) + { + Dimension filler = new Dimension(10,10); + + boolean filled_cell_11 = false; + CellConstraints cc = new CellConstraints(); + if ( cols.length > 0 && rows.length > 0 ) + { + if ( cols[0] == 1 && rows[0] == 1 ) + { + /** add a rigid area */ + panel.add( Box.createRigidArea( filler ), cc.xy(1,1) ); + filled_cell_11 = true; + } + } + + for( int index = 0; index < cols.length; index++ ) + { + if ( cols[index] == 1 && filled_cell_11 ) + { + continue; + } + panel.add( Box.createRigidArea( filler ), cc.xy(cols[index],1) ); + } + + for( int index = 0; index < rows.length; index++ ) + { + if ( rows[index] == 1 && filled_cell_11 ) + { + continue; + } + panel.add( Box.createRigidArea( filler ), cc.xy(1,rows[index]) ); + } + + } + + /** + * Helper method to load an image file from the CLASSPATH + * @param imageName the package and name of the file to load relative to the CLASSPATH + * @return an ImageIcon instance with the specified image file + * @throws IllegalArgumentException if the image resource cannot be loaded. + */ + public ImageIcon loadImage( String imageName ) + { + try + { + ClassLoader classloader = getClass().getClassLoader(); + java.net.URL url = classloader.getResource( imageName ); + if ( url != null ) + { + ImageIcon icon = new ImageIcon( url ); + return icon; + } + } + catch( Exception e ) + { + e.printStackTrace(); + } + throw new IllegalArgumentException( "Unable to load image: " + imageName ); + } + + /** + * Method for recalculating the component orientation for + * right-to-left Locales. + * @param orientation the component orientation to be applied + */ + public void applyComponentOrientation( ComponentOrientation orientation ) + { + // Not yet implemented... + // I18NUtils.applyComponentOrientation(this, orientation); + super.applyComponentOrientation(orientation); + } + + public JPanel createPanel() + { + JPanel jpanel1 = new JPanel(); + FormLayout formlayout1 = new FormLayout("FILL:DEFAULT:NONE,FILL:8DLU:NONE,FILL:84PX:NONE,FILL:DEFAULT:NONE,FILL:97PX:NONE,FILL:DEFAULT:NONE,FILL:109PX:NONE,FILL:DEFAULT:NONE,FILL:95PX:NONE,FILL:25PX:NONE,FILL:8DLU:NONE,FILL:8DLU:NONE,FILL:DEFAULT:NONE","CENTER:DEFAULT:NONE,CENTER:DEFAULT:NONE,CENTER:DEFAULT:NONE,CENTER:DEFAULT:NONE,CENTER:2DLU:NONE,CENTER:DEFAULT:NONE,CENTER:2DLU:NONE,CENTER:DEFAULT:NONE,CENTER:DEFAULT:NONE,CENTER:2DLU:NONE,CENTER:DEFAULT:NONE,CENTER:2DLU:NONE,CENTER:DEFAULT:NONE,FILL:174PX:NONE,CENTER:4DLU:NONE,CENTER:DEFAULT:NONE"); + CellConstraints cc = new CellConstraints(); + jpanel1.setLayout(formlayout1); + + _LOG_AREA.setName("LOG_AREA"); + JScrollPane jscrollpane1 = new JScrollPane(); + jscrollpane1.setViewportView(_LOG_AREA); + jscrollpane1.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED); + jscrollpane1.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); + jpanel1.add(jscrollpane1,cc.xywh(3,14,9,1)); + + TitledSeparator titledseparator1 = new TitledSeparator(); + titledseparator1.setText("Log"); + jpanel1.add(titledseparator1,cc.xywh(3,13,9,1)); + + jpanel1.add(_titledborderbottom1,cc.xywh(3,15,9,1)); + + JLabel jlabel1 = new JLabel(); + jlabel1.setText("Action"); + jpanel1.add(jlabel1,cc.xy(3,8)); + + _INSTALL_OPTION.setActionCommand("Install Service"); + _INSTALL_OPTION.setName("INSTALL_OPTION"); + _INSTALL_OPTION.setText("Install Service"); + _buttongroup1.add(_INSTALL_OPTION); + jpanel1.add(_INSTALL_OPTION,cc.xy(5,8)); + + _CONSOLE_OPTION.setActionCommand("Run Console"); + _CONSOLE_OPTION.setName("CONSOLE_OPTION"); + _CONSOLE_OPTION.setText("Run Console"); + _buttongroup1.add(_CONSOLE_OPTION); + jpanel1.add(_CONSOLE_OPTION,cc.xy(5,9)); + + JLabel jlabel2 = new JLabel(); + jlabel2.setText("Application"); + jpanel1.add(jlabel2,cc.xy(3,4)); + + JLabel jlabel3 = new JLabel(); + jlabel3.setText("Install Folder"); + jpanel1.add(jlabel3,cc.xy(3,6)); + + _APPLICATION.setName("APPLICATION"); + _APPLICATION.setText("???"); + jpanel1.add(_APPLICATION,cc.xywh(5,4,6,1)); + + jpanel1.add(_titledborderside1,cc.xywh(2,13,1,3)); + + _START_OPTION.setActionCommand("& Start"); + _START_OPTION.setName("START_OPTION"); + _START_OPTION.setText("Start Service"); + jpanel1.add(_START_OPTION,cc.xy(7,8)); + + _INSTALL_FOLDER.setName("INSTALL_FOLDER"); + jpanel1.add(_INSTALL_FOLDER,cc.xywh(5,6,6,1)); + + _TRAY_ICON_OPTION.setActionCommand("& Tray Icon"); + _TRAY_ICON_OPTION.setName("TRAY_ICON_OPTION"); + _TRAY_ICON_OPTION.setText("Tray Icon"); + jpanel1.add(_TRAY_ICON_OPTION,cc.xy(9,8)); + + jpanel1.add(createPanel1(),cc.xywh(2,11,11,1)); + _SELECT_FOLDER_BUTTON.setActionCommand("..."); + _SELECT_FOLDER_BUTTON.setName("SELECT_FOLDER_BUTTON"); + _SELECT_FOLDER_BUTTON.setText("..."); + jpanel1.add(_SELECT_FOLDER_BUTTON,cc.xy(12,6)); + + _SHOW_CONF_BUTTON.setActionCommand("..."); + _SHOW_CONF_BUTTON.setName("SHOW_CONF_BUTTON"); + _SHOW_CONF_BUTTON.setText("..."); + jpanel1.add(_SHOW_CONF_BUTTON,cc.xy(12,4)); + + JLabel jlabel4 = new JLabel(); + jlabel4.setFont(new Font("Tahoma",Font.BOLD,16)); + jlabel4.setText("YAJSW - Java Web Start Booter"); + jlabel4.setHorizontalAlignment(JLabel.CENTER); + jpanel1.add(jlabel4,cc.xywh(2,2,11,1)); + + _titledborderside2.setOrientation(TitledBorderSide.RIGHT); + jpanel1.add(_titledborderside2,cc.xywh(12,13,1,3)); + + addFillComponents(jpanel1,new int[]{ 1,2,3,4,5,6,7,8,9,10,11,12,13 },new int[]{ 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16 }); + return jpanel1; + } + + public JPanel createPanel1() + { + JPanel jpanel1 = new JPanel(); + FormLayout formlayout1 = new FormLayout("FILL:DEFAULT:NONE,FILL:DEFAULT:NONE,FILL:235PX:NONE,FILL:86PX:NONE,FILL:DEFAULT:NONE,FILL:DEFAULT:NONE","CENTER:DEFAULT:NONE"); + CellConstraints cc = new CellConstraints(); + jpanel1.setLayout(formlayout1); + + _GO_BUTTON.setActionCommand("Start"); + _GO_BUTTON.setName("GO_BUTTON"); + _GO_BUTTON.setText("Continue"); + jpanel1.add(_GO_BUTTON,cc.xy(2,1)); + + _CANCEL_BUTTON.setActionCommand("Cancel"); + _CANCEL_BUTTON.setName("CANCEL_BUTTON"); + _CANCEL_BUTTON.setText("Close"); + jpanel1.add(_CANCEL_BUTTON,cc.xy(5,1)); + + _STATE.setFont(new Font("Tahoma",Font.BOLD,12)); + _STATE.setName("STATE"); + _STATE.setText("Starting"); + _STATE.setHorizontalAlignment(JLabel.CENTER); + jpanel1.add(_STATE,cc.xy(3,1)); + + _SPEED.setName("SPEED"); + jpanel1.add(_SPEED,cc.xy(4,1)); + + addFillComponents(jpanel1,new int[]{ 1,6 },new int[]{ 1 }); + return jpanel1; + } + + /** + * Initializer + */ + protected void initializePanel() + { + setLayout(new BorderLayout()); + add(createPanel(), BorderLayout.CENTER); + } + + +} diff --git a/javaUtilities/yajsw/src/ws/java/org/rzo/yajsw/ws/WebStartBooter.java b/javaUtilities/yajsw/src/ws/java/org/rzo/yajsw/ws/WebStartBooter.java new file mode 100644 index 0000000000..18f4e3df1f --- /dev/null +++ b/javaUtilities/yajsw/src/ws/java/org/rzo/yajsw/ws/WebStartBooter.java @@ -0,0 +1,565 @@ +package org.rzo.yajsw.ws; + +import java.awt.TextArea; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.BufferedReader; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.FilterOutputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.PrintStream; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLConnection; +import java.util.Enumeration; +import java.util.HashSet; +import java.util.Set; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; + +import javax.swing.JFileChooser; +import javax.swing.JFrame; + +public class WebStartBooter +{ + + static WSForm wsform = new WSForm(); + static String source; + static String destination = ""; + static String action = "-c"; + static boolean start = false; + static boolean tray = false; + static String configuration = ""; + static String _wrapperJar; + + static String TITLE = "YAJSW-WS Rel 0.2"; + + static boolean _useProxies = false; + + private static void showFile(String file) + { + // Create a TextArea to display the contents of the file in + TextArea textarea = new TextArea("", 24, 80); + textarea.setEditable(false); + final JFrame frame = new JFrame(); + frame.setSize(530, 450); + frame.setLocation(100, 100); + frame.getContentPane().add(textarea); + frame.setVisible(true); + + frame.addWindowListener(new WindowAdapter() + { + public void windowClosing(WindowEvent evt) + { + frame.dispose(); + } + }); + textarea.setText(getFile(file)); + + } + + private static boolean downloadFile(String source, String destination) throws IOException + { + + System.out.println("checking " + source); + URL url = new URL(source); + URLConnection con = url.openConnection(); + + File outFile = new File(destination); + if (outFile.exists() && outFile.lastModified() < con.getLastModified()) + System.out.println("file changed -> overwrite " + destination); + else if (outFile.exists()) + { + System.out.println("file unchanged -> continue"); + return false; + } + if (!outFile.getParentFile().exists()) + outFile.getParentFile().mkdirs(); + System.out.println("loading " + source + " -> " + destination); + BufferedInputStream in = new BufferedInputStream(url.openStream()); + BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(outFile), 1024); + copyStream(in, out); + out.close(); + in.close(); + return true; + } + + private static void copyStream(BufferedInputStream in, BufferedOutputStream out) throws IOException + { + byte data[] = new byte[1024]; + int count = 0; + long total = 0; + long startTime = System.currentTimeMillis(); + long duration = startTime; + while ((count = in.read(data, 0, 1024)) != -1) + { + out.write(data, 0, count); + total += count; + duration = (System.currentTimeMillis() - startTime) / 1000; + if (duration > 0) + showSpeed(total / (duration)); + } + } + + private static void loadWrapperJar(String base, String destination) throws IOException + { + downloadFile(base + "/wrapper.jar", destination + "/wrapper.jar"); + _wrapperJar = destination + "/wrapper.jar"; + } + + public static void main(String[] args) throws Exception + { + source = args[0]; + destination = args[1]; + action = args[2]; + configuration = args[3]; + if (!args[args.length - 1].contains("nogui")) + ; + doGui(args); + File destF = new File(destination); + if (!destF.exists()) + { + System.out.println("creating installation folder"); + destF.mkdirs(); + } + try + { + downloadWrapper(); + showStep("Loading & Executing application ..."); + doActions(destination, action, configuration); + } + catch (Exception ex) + { + showStep("Error -> aborted"); + System.out.println("Error : " + ex.getMessage()); + } + + } + + private static void downloadWrapper() throws IOException + { + // System.out.println("source: "+source); + if (source.endsWith(".zip")) + { + URL sourceURL = new URL(source); + String sourceName = new File(sourceURL.getPath()).getName(); + showStep("Downloading " + sourceName + " ..."); + if (downloadFile(source, destination + "/" + sourceName)) + { + showStep("Unzip " + sourceName + " ..."); + unzipWrapper(destination, destination + "/" + sourceName); + } + } + else + { + showStep("Loading wrapper.jar ..."); + loadWrapperJar(source, destination); + showStep("Loading wrapper libs ..."); + loadManifestFiles(source, destination); + showStep("Loading wrapper resources ..."); + loadResources(source, destination); + } + findWrapperJar(new File(destination)); + } + + private static void findWrapperJar(File file) + { + if (_wrapperJar == null) + { + if (file.isDirectory()) + { + for (File f : file.listFiles()) + { + findWrapperJar(f); + } + } + else if (file.getName().equals("wrapper.jar")) + try + { + _wrapperJar = file.getCanonicalPath(); + } + catch (IOException e) + { + e.printStackTrace(); + } + } + } + + private static void unzipWrapper(String destination, String zipFileName) throws IOException, IOException + { + ZipFile zipFile = new ZipFile(zipFileName); + + Enumeration entries = zipFile.entries(); + + while (entries.hasMoreElements()) + { + ZipEntry entry = (ZipEntry) entries.nextElement(); + + if (entry.isDirectory()) + { + // Assume directories are stored parents first then children. + System.err.println("Extracting wrapper folder: " + entry.getName()); + // This is not robust, just for demonstration purposes. + (new File(destination + "/" + entry.getName())).mkdir(); + continue; + } + + if (entry.getName().endsWith("wrapper.jar")) + _wrapperJar = destination + "/" + entry.getName(); + // skip java source file + if (entry.getName().endsWith(".java")) + continue; + System.err.println("Extracting wrapper file: " + entry.getName()); + BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(destination + "/" + entry.getName())); + copyStream(new BufferedInputStream(zipFile.getInputStream(entry)), out); + out.close(); + } + zipFile.close(); + } + + private static void doActions(String destination, String action, String configuration) throws IOException + { + if (!action.startsWith("-")) + action = "-" + action; + startAppl(destination, action, configuration); + } + + private static void showStep(String txt) + { + if (wsform != null) + wsform._STATE.setText(txt); + } + + private static void showSpeed(long speed) + { + if (wsform != null) + wsform._SPEED.setText(speed / 1024 + " kB/s"); + } + + private static void doGui(String[] args) throws IOException, Exception + { + + // set data in form + wsform._APPLICATION.setText(configuration); + wsform._INSTALL_FOLDER.setText(new File(destination).getAbsolutePath()); + if (action.contains("c")) + wsform._CONSOLE_OPTION.setSelected(true); + if (action.contains("i")) + wsform._INSTALL_OPTION.setSelected(true); + if (action.contains("y")) + wsform._TRAY_ICON_OPTION.setSelected(true); + if (action.contains("t")) + wsform._START_OPTION.setSelected(true); + + // pipe output to log text area in form + PrintStream aPrintStream = new PrintStream(new FilteredStream(new ByteArrayOutputStream())); + + System.setOut(aPrintStream); // catches System.out messages + System.setErr(aPrintStream); // catches error messages + + // signal condition if continue button hit + final Lock lock = new ReentrantLock(); + final Condition cont = lock.newCondition(); + + wsform._GO_BUTTON.addActionListener(new ActionListener() + { + + public void actionPerformed(ActionEvent e) + { + lock.lock(); + cont.signal(); + lock.unlock(); + } + + }); + + wsform._SHOW_CONF_BUTTON.addActionListener(new ActionListener() + { + + public void actionPerformed(ActionEvent e) + { + showFile(configuration); + } + + }); + + wsform._SELECT_FOLDER_BUTTON.addActionListener(new ActionListener() + { + + public void actionPerformed(ActionEvent e) + { + JFileChooser fc = new JFileChooser(); + fc.setCurrentDirectory(new File(".")); + fc.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); + fc.setAcceptAllFileFilterUsed(false); + int retval = fc.showOpenDialog(wsform); + + if (retval == JFileChooser.APPROVE_OPTION) + { + // ... The user selected a file, get it, use it. + File file = fc.getSelectedFile(); + + // ... Update user interface. + try + { + wsform._INSTALL_FOLDER.setText(file.getCanonicalPath()); + } + catch (IOException e1) + { + e1.printStackTrace(); + } + } + } + + }); + + // exit if cancel button hit + wsform._CANCEL_BUTTON.addActionListener(new ActionListener() + { + + public void actionPerformed(ActionEvent e) + { + System.exit(0); + } + + }); + + JFrame frame = new JFrame(); + frame.setTitle(TITLE); + frame.setSize(530, 450); + frame.setLocation(100, 100); + frame.getContentPane().add(wsform); + frame.setVisible(true); + + frame.addWindowListener(new WindowAdapter() + { + public void windowClosing(WindowEvent evt) + { + System.exit(0); + } + }); + + showStep("Click button to continue"); + + // wait for continue button + lock.lock(); + cont.await(); + lock.unlock(); + + // get user data + destination = wsform._INSTALL_FOLDER.getText(); + if (wsform._INSTALL_OPTION.isSelected()) + { + action = "i"; + if (wsform._START_OPTION.isSelected()) + action += "t"; + } + if (wsform._CONSOLE_OPTION.isSelected()) + action = "c"; + if (wsform._TRAY_ICON_OPTION.isSelected()) + action += "y"; + } + + private static void startAppl(String destination, String func, String conf) throws IOException + { + String useProxies = _useProxies ? " -Djava.net.useSystemProxies=true" : ""; + String wrapperJar = _wrapperJar.contains(" ") ? "\"" + _wrapperJar + "\"" : _wrapperJar; + String cmd = getJava() + useProxies + " -jar " + wrapperJar + " " + func + " " + conf; + System.out.println("executing " + cmd); + final Process p = Runtime.getRuntime().exec(cmd); + new Thread(new Runnable() + { + public void run() + { + BufferedReader in = new BufferedReader(new InputStreamReader(p.getInputStream())); + try + { + String line = null; + while ((line = in.readLine()) != null) + System.out.println(line); + } + catch (Exception ex) + { + } + } + + }).start(); + new Thread(new Runnable() + { + public void run() + { + BufferedReader in = new BufferedReader(new InputStreamReader(p.getErrorStream())); + try + { + String line = null; + while ((line = in.readLine()) != null) + System.err.println(line); + } + catch (Exception ex) + { + } + } + + }).start(); + } + + // TODO + private static String getJava() + { + return "java"; + } + + private static void loadManifestFiles(String base, String destination) throws IOException + { + String manifest = getManifest(destination); + Set jars = getJars(manifest); + for (String jar : jars) + if (!jar.contains("wrapper.jar")) + try + { + downloadFile(base + "/" + jar, destination + "/" + jar); + } + catch (Exception ex) + { + System.out.println("error loading " + base + "/" + jar); + System.out.println(ex.getMessage()); + } + } + + private static void loadResources(String base, String destination) throws IOException + { + for (String r : getResources(base)) + { + r = r.trim(); + downloadFile(base + "/" + r, destination + "/" + r); + } + } + + private static Set getJars(String manifest) + { + Set result = new HashSet(); + String[] s = manifest.split(" "); + for (String x : s) + if (x.endsWith(".jar")) + result.add(x.trim()); + return result; + } + + private static String[] getResources(String base) + { + return getFile(base + "/resources.txt").split("\r\n"); + } + + public static String getFile(String file) + { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + URL url; + try + { + url = new URL(file); + } + catch (MalformedURLException e1) + { + System.out.println(e1.getMessage()); + return ""; + } + try + { + URLConnection con = url.openConnection(); + } + catch (IOException e1) + { + System.out.println(e1.getMessage()); + return ""; + } + BufferedInputStream in; + try + { + in = new BufferedInputStream(url.openStream()); + } + catch (IOException e1) + { + System.out.println(e1.getMessage()); + return ""; + } + byte data[] = new byte[1024]; + int count = 0; + try + { + while ((count = in.read(data, 0, 1024)) != -1) + { + out.write(data, 0, count); + } + } + catch (IOException e) + { + e.printStackTrace(); + } + try + { + in.close(); + } + catch (IOException e) + { + e.printStackTrace(); + } + return new String(out.toByteArray()); + + } + + private static String getManifest(String destination) throws IOException + { + ZipFile z = new ZipFile(destination + "/wrapper.jar"); + ZipEntry ze = z.getEntry("META-INF/MANIFEST.MF"); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + BufferedInputStream in = new BufferedInputStream(z.getInputStream(ze)); + byte data[] = new byte[1024]; + int count = 0; + while ((count = in.read(data, 0, 1024)) != -1) + { + out.write(data, 0, count); + } + in.close(); + z.close(); + return new String(out.toByteArray()).replaceAll("\r\n ", ""); + } + + static class FilteredStream extends FilterOutputStream + { + public FilteredStream(OutputStream aStream) + { + super(aStream); + } + + public void write(byte b[]) throws IOException + { + String aString = new String(b); + wsform._LOG_AREA.append(aString); + } + + public void write(byte b[], int off, int len) throws IOException + { + String aString = new String(b, off, len); + wsform._LOG_AREA.append(aString); + if (wsform._LOG_AREA.getText().contains("Exception")) + showStep("Error found"); + if (wsform._LOG_AREA.getText().contains("configuration file not found")) + showStep("Error: Configuration file not found"); + + wsform._LOG_AREA.setCaretPosition(wsform._LOG_AREA.getDocument().getLength()); + } + } + +} diff --git a/ncep/gov.noaa.nws.ncep.common.dataplugin.airmet/src/gov/noaa/nws/ncep/common/dataplugin/airmet/AirmetRecord.java b/ncep/gov.noaa.nws.ncep.common.dataplugin.airmet/src/gov/noaa/nws/ncep/common/dataplugin/airmet/AirmetRecord.java index 7f32e54d8a..bd3fc6f37e 100644 --- a/ncep/gov.noaa.nws.ncep.common.dataplugin.airmet/src/gov/noaa/nws/ncep/common/dataplugin/airmet/AirmetRecord.java +++ b/ncep/gov.noaa.nws.ncep.common.dataplugin.airmet/src/gov/noaa/nws/ncep/common/dataplugin/airmet/AirmetRecord.java @@ -17,6 +17,7 @@ * 07/2009 39 L. Lin Migration to TO11 * 09/2011 Chin Chen changed to improve purge performance and * removed xml serialization as well + * Apr 4, 2013 1846 bkowal Added an index on refTime and forecastTime * * * This code has been developed by the SIB for use in the AWIPS2 system. @@ -48,6 +49,16 @@ import com.raytheon.uf.common.serialization.annotations.DynamicSerializeElement; @Entity @Table(name = "airmet", uniqueConstraints = { @UniqueConstraint(columnNames = { "dataURI" }) }) +/* + * Both refTime and forecastTime are included in the refTimeIndex since + * forecastTime is unlikely to be used. + */ +@org.hibernate.annotations.Table( + appliesTo = "airmet", + indexes = { + @Index(name = "airmet_refTimeIndex", columnNames = { "refTime", "forecastTime" } ) + } +) @DynamicSerialize diff --git a/ncep/gov.noaa.nws.ncep.common.dataplugin.atcf/src/gov/noaa/nws/ncep/common/dataplugin/atcf/AtcfRecord.java b/ncep/gov.noaa.nws.ncep.common.dataplugin.atcf/src/gov/noaa/nws/ncep/common/dataplugin/atcf/AtcfRecord.java index b0ff61e5c0..3158134b72 100644 --- a/ncep/gov.noaa.nws.ncep.common.dataplugin.atcf/src/gov/noaa/nws/ncep/common/dataplugin/atcf/AtcfRecord.java +++ b/ncep/gov.noaa.nws.ncep.common.dataplugin.atcf/src/gov/noaa/nws/ncep/common/dataplugin/atcf/AtcfRecord.java @@ -30,6 +30,7 @@ import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; +import org.hibernate.annotations.Index; import com.raytheon.uf.common.dataplugin.PluginDataObject; import com.raytheon.uf.common.dataplugin.IDecoderGettable; @@ -52,6 +53,7 @@ import gov.noaa.nws.ncep.common.tools.IDecoderConstantsN; * ------------ ---------- ----------- -------------------------- * 06/23/10 208 F. J. Yen Initial Coding. * 03/10/12 606 G. Hull added reportType to URI + * Apr 4, 2013 1846 bkowal Added an index on refTime and forecastTime * * * @@ -60,6 +62,16 @@ import gov.noaa.nws.ncep.common.tools.IDecoderConstantsN; */ @Entity @Table(name = "atcf", uniqueConstraints = { @UniqueConstraint(columnNames = { "dataURI" }) }) +/* + * Both refTime and forecastTime are included in the refTimeIndex since + * forecastTime is unlikely to be used. + */ +@org.hibernate.annotations.Table( + appliesTo = "atcf", + indexes = { + @Index(name = "atcf_refTimeIndex", columnNames = { "refTime", "forecastTime" } ) + } +) @XmlRootElement @XmlAccessorType(XmlAccessType.NONE) @DynamicSerialize diff --git a/ncep/gov.noaa.nws.ncep.common.dataplugin.aww/src/gov/noaa/nws/ncep/common/dataplugin/aww/AwwRecord.java b/ncep/gov.noaa.nws.ncep.common.dataplugin.aww/src/gov/noaa/nws/ncep/common/dataplugin/aww/AwwRecord.java index 081ff93ae5..bd0145c967 100644 --- a/ncep/gov.noaa.nws.ncep.common.dataplugin.aww/src/gov/noaa/nws/ncep/common/dataplugin/aww/AwwRecord.java +++ b/ncep/gov.noaa.nws.ncep.common.dataplugin.aww/src/gov/noaa/nws/ncep/common/dataplugin/aww/AwwRecord.java @@ -23,6 +23,7 @@ * of original 5 elements is not unique in some scenarios. * 09/2011 Chin Chen changed to improve purge performance and * removed xml serialization as well + * Apr 4, 2013 1846 bkowal Added an index on refTime and forecastTime * * * This code has been developed by the SIB for use in the AWIPS2 system. @@ -61,6 +62,16 @@ import com.raytheon.uf.common.serialization.annotations.DynamicSerializeElement; @Entity @Table(name = "aww", uniqueConstraints = { @UniqueConstraint(columnNames = { "dataURI" }) }) +/* + * Both refTime and forecastTime are included in the refTimeIndex since + * forecastTime is unlikely to be used. + */ +@org.hibernate.annotations.Table( + appliesTo = "aww", + indexes = { + @Index(name = "aww_refTimeIndex", columnNames = { "refTime", "forecastTime" } ) + } +) @DynamicSerialize public class AwwRecord extends PluginDataObject{ diff --git a/ncep/gov.noaa.nws.ncep.common.dataplugin.convsigmet/src/gov/noaa/nws/ncep/common/dataplugin/convsigmet/ConvSigmetRecord.java b/ncep/gov.noaa.nws.ncep.common.dataplugin.convsigmet/src/gov/noaa/nws/ncep/common/dataplugin/convsigmet/ConvSigmetRecord.java index cc2cd3b18e..2693ff5bf6 100644 --- a/ncep/gov.noaa.nws.ncep.common.dataplugin.convsigmet/src/gov/noaa/nws/ncep/common/dataplugin/convsigmet/ConvSigmetRecord.java +++ b/ncep/gov.noaa.nws.ncep.common.dataplugin.convsigmet/src/gov/noaa/nws/ncep/common/dataplugin/convsigmet/ConvSigmetRecord.java @@ -11,6 +11,7 @@ * 07/2009 87/114 L. Lin Migration to TO11 * 09/2011 Chin Chen changed to improve purge performance and * removed xml serialization as well + * Apr 4, 2013 1846 bkowal Added an index on refTime and forecastTime * * * This code has been developed by the SIB for use in the AWIPS2 system. @@ -44,6 +45,16 @@ import com.raytheon.uf.common.serialization.annotations.DynamicSerializeElement; @Entity @Table(name = "convsigmet", uniqueConstraints = { @UniqueConstraint(columnNames = { "dataURI" }) }) +/* + * Both refTime and forecastTime are included in the refTimeIndex since + * forecastTime is unlikely to be used. + */ +@org.hibernate.annotations.Table( + appliesTo = "convsigmet", + indexes = { + @Index(name = "convsigmet_refTimeIndex", columnNames = { "refTime", "forecastTime" } ) + } +) @DynamicSerialize diff --git a/ncep/gov.noaa.nws.ncep.common.dataplugin.ffg/src/gov/noaa/nws/ncep/common/dataplugin/ffg/FfgRecord.java b/ncep/gov.noaa.nws.ncep.common.dataplugin.ffg/src/gov/noaa/nws/ncep/common/dataplugin/ffg/FfgRecord.java index 9bb2504961..5e7ee7070a 100644 --- a/ncep/gov.noaa.nws.ncep.common.dataplugin.ffg/src/gov/noaa/nws/ncep/common/dataplugin/ffg/FfgRecord.java +++ b/ncep/gov.noaa.nws.ncep.common.dataplugin.ffg/src/gov/noaa/nws/ncep/common/dataplugin/ffg/FfgRecord.java @@ -15,6 +15,7 @@ * 07/2009 14 T. Lee Migration to TO11 * 09/2011 Chin Chen changed to improve purge performance and * removed xml serialization as well + * Apr 4, 2013 1846 bkowal Added an index on refTime and forecastTime * * * @author T.Lee @@ -45,6 +46,16 @@ import gov.noaa.nws.ncep.common.dataplugin.ffg.FfgPrecip; @Entity @Table(name = "ffg", uniqueConstraints = { @UniqueConstraint(columnNames = { "dataURI" }) }) +/* + * Both refTime and forecastTime are included in the refTimeIndex since + * forecastTime is unlikely to be used. + */ +@org.hibernate.annotations.Table( + appliesTo = "ffg", + indexes = { + @Index(name = "ffg_refTimeIndex", columnNames = { "refTime", "forecastTime" } ) + } +) @DynamicSerialize public class FfgRecord extends PluginDataObject { diff --git a/ncep/gov.noaa.nws.ncep.common.dataplugin.idft/src/gov/noaa/nws/ncep/common/dataplugin/idft/IdftRecord.java b/ncep/gov.noaa.nws.ncep.common.dataplugin.idft/src/gov/noaa/nws/ncep/common/dataplugin/idft/IdftRecord.java index de56b96958..adc067b543 100644 --- a/ncep/gov.noaa.nws.ncep.common.dataplugin.idft/src/gov/noaa/nws/ncep/common/dataplugin/idft/IdftRecord.java +++ b/ncep/gov.noaa.nws.ncep.common.dataplugin.idft/src/gov/noaa/nws/ncep/common/dataplugin/idft/IdftRecord.java @@ -13,6 +13,7 @@ * 05/21/09 100 F. J. Yen Initial creation * 12/08/09 100 F. J. Yen Modified for to11d6 from to11d3 * 05/27/10 100 F. J. Yen Refactored from to11dr3 for tolldr11 + * Apr 4, 2013 1846 bkowal Added an index on refTime and forecastTime * * * * This code has been developed by the SIB for use in the AWIPS2 system. @@ -39,12 +40,23 @@ import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; +import org.hibernate.annotations.Index; import java.util.Calendar; import gov.noaa.nws.ncep.common.tools.IDecoderConstantsN; @Entity @Table(name = "idft", uniqueConstraints = { @UniqueConstraint(columnNames = { "dataURI" }) }) +/* + * Both refTime and forecastTime are included in the refTimeIndex since + * forecastTime is unlikely to be used. + */ +@org.hibernate.annotations.Table( + appliesTo = "idft", + indexes = { + @Index(name = "idft_refTimeIndex", columnNames = { "refTime", "forecastTime" } ) + } +) @XmlRootElement @XmlAccessorType(XmlAccessType.NONE) @DynamicSerialize diff --git a/ncep/gov.noaa.nws.ncep.common.dataplugin.intlsigmet/src/gov/noaa/nws/ncep/common/dataplugin/intlsigmet/IntlSigmetRecord.java b/ncep/gov.noaa.nws.ncep.common.dataplugin.intlsigmet/src/gov/noaa/nws/ncep/common/dataplugin/intlsigmet/IntlSigmetRecord.java index ef56c04545..b19ff030ed 100644 --- a/ncep/gov.noaa.nws.ncep.common.dataplugin.intlsigmet/src/gov/noaa/nws/ncep/common/dataplugin/intlsigmet/IntlSigmetRecord.java +++ b/ncep/gov.noaa.nws.ncep.common.dataplugin.intlsigmet/src/gov/noaa/nws/ncep/common/dataplugin/intlsigmet/IntlSigmetRecord.java @@ -13,6 +13,7 @@ * 05/2010 113 L. Lin Migration to TO11DR11 * 09/2011 Chin Chen changed to improve purge performance and * removed xml serialization as well + * Apr 4, 2013 1846 bkowal Added an index on refTime and forecastTime * * * This code has been developed by the SIB for use in the AWIPS2 system. @@ -44,6 +45,16 @@ import com.raytheon.uf.common.serialization.annotations.DynamicSerializeElement; @Entity @Table(name = "intlsigmet", uniqueConstraints = { @UniqueConstraint(columnNames = { "dataURI" }) }) +/* + * Both refTime and forecastTime are included in the refTimeIndex since + * forecastTime is unlikely to be used. + */ +@org.hibernate.annotations.Table( + appliesTo = "intlsigmet", + indexes = { + @Index(name = "intlsigmet_refTimeIndex", columnNames = { "refTime", "forecastTime" } ) + } +) @DynamicSerialize diff --git a/ncep/gov.noaa.nws.ncep.common.dataplugin.mcidas/src/gov/noaa/nws/ncep/common/dataplugin/mcidas/McidasRecord.java b/ncep/gov.noaa.nws.ncep.common.dataplugin.mcidas/src/gov/noaa/nws/ncep/common/dataplugin/mcidas/McidasRecord.java index d6845e7bb1..c25dccde9d 100644 --- a/ncep/gov.noaa.nws.ncep.common.dataplugin.mcidas/src/gov/noaa/nws/ncep/common/dataplugin/mcidas/McidasRecord.java +++ b/ncep/gov.noaa.nws.ncep.common.dataplugin.mcidas/src/gov/noaa/nws/ncep/common/dataplugin/mcidas/McidasRecord.java @@ -14,6 +14,7 @@ * and imageTypeNumber * 05/2010 144 L. Lin Migration to TO11DR11. * 09/2012 B. Hebbard Merge out RTS changes from OB12.9.1 + * Apr 4, 2013 1846 bkowal Added an index on refTime and forecastTime * * * @author tlee @@ -39,6 +40,7 @@ import javax.xml.bind.annotation.XmlRootElement; import org.hibernate.annotations.Cache; import org.hibernate.annotations.CacheConcurrencyStrategy; +import org.hibernate.annotations.Index; import com.raytheon.uf.common.dataplugin.IDecoderGettable; import com.raytheon.uf.common.dataplugin.annotations.DataURI; @@ -50,6 +52,16 @@ import com.raytheon.uf.common.serialization.annotations.DynamicSerializeElement; @Entity @Table(name = "mcidas", uniqueConstraints = { @UniqueConstraint(columnNames = { "dataURI" }) }) +/* + * Both refTime and forecastTime are included in the refTimeIndex since + * forecastTime is unlikely to be used. + */ +@org.hibernate.annotations.Table( + appliesTo = "mcidas", + indexes = { + @Index(name = "mcidas_refTimeIndex", columnNames = { "refTime", "forecastTime" } ) + } +) @Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL) @XmlRootElement @XmlAccessorType(XmlAccessType.NONE) diff --git a/ncep/gov.noaa.nws.ncep.common.dataplugin.ncairep/src/gov/noaa/nws/ncep/common/dataplugin/ncairep/NcAirepRecord.java b/ncep/gov.noaa.nws.ncep.common.dataplugin.ncairep/src/gov/noaa/nws/ncep/common/dataplugin/ncairep/NcAirepRecord.java index d8297739f5..14a1427c44 100644 --- a/ncep/gov.noaa.nws.ncep.common.dataplugin.ncairep/src/gov/noaa/nws/ncep/common/dataplugin/ncairep/NcAirepRecord.java +++ b/ncep/gov.noaa.nws.ncep.common.dataplugin.ncairep/src/gov/noaa/nws/ncep/common/dataplugin/ncairep/NcAirepRecord.java @@ -33,6 +33,7 @@ import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; +import org.hibernate.annotations.Index; import com.raytheon.uf.common.dataplugin.IDecoderGettable; import com.raytheon.uf.common.dataplugin.PluginDataObject; @@ -63,6 +64,7 @@ import com.vividsolutions.jts.geom.Geometry; * 9/20/2011 286 qzhou Change reportType to String * 04/05/2012 420 dgilling Prevent NullPointerExceptions in * buildMessageData(). + * Apr 4, 2013 1846 bkowal Added an index on refTime and forecastTime * * * @author jkorman @@ -71,6 +73,16 @@ import com.vividsolutions.jts.geom.Geometry; @Entity @Table(name = "ncairep", uniqueConstraints = { @UniqueConstraint(columnNames = { "dataURI" }) }) +/* + * Both refTime and forecastTime are included in the refTimeIndex since + * forecastTime is unlikely to be used. + */ +@org.hibernate.annotations.Table( + appliesTo = "ncairep", + indexes = { + @Index(name = "ncairep_refTimeIndex", columnNames = { "refTime", "forecastTime" } ) + } +) @XmlRootElement @XmlAccessorType(XmlAccessType.NONE) @DynamicSerialize diff --git a/ncep/gov.noaa.nws.ncep.common.dataplugin.ncccfp/src/gov/noaa/nws/ncep/common/dataplugin/ncccfp/NcccfpRecord.java b/ncep/gov.noaa.nws.ncep.common.dataplugin.ncccfp/src/gov/noaa/nws/ncep/common/dataplugin/ncccfp/NcccfpRecord.java index d52e99cba8..8ad3bfdd63 100644 --- a/ncep/gov.noaa.nws.ncep.common.dataplugin.ncccfp/src/gov/noaa/nws/ncep/common/dataplugin/ncccfp/NcccfpRecord.java +++ b/ncep/gov.noaa.nws.ncep.common.dataplugin.ncccfp/src/gov/noaa/nws/ncep/common/dataplugin/ncccfp/NcccfpRecord.java @@ -12,6 +12,7 @@ import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; +import org.hibernate.annotations.Index; import com.raytheon.uf.common.dataplugin.IDecoderGettable; import com.raytheon.uf.common.dataplugin.PluginDataObject; @@ -30,6 +31,7 @@ import com.raytheon.uf.common.serialization.annotations.DynamicSerializeElement; * --------- ---------- ----------- -------------------------- * 10/05/2009 155 F. J. Yen From Raytheon's CCFP; mod for NC_CCFP * 26/05/2010 155 F. J. Yen Refactored to dataplugin for migration to to11dr11 + * Apr 4, 2013 1846 bkowal Added an index on refTime and forecastTime * * * @@ -38,6 +40,16 @@ import com.raytheon.uf.common.serialization.annotations.DynamicSerializeElement; */ @Entity @Table(name = "ncccfp", uniqueConstraints = { @UniqueConstraint(columnNames = { "dataURI" }) }) +/* + * Both refTime and forecastTime are included in the refTimeIndex since + * forecastTime is unlikely to be used. + */ +@org.hibernate.annotations.Table( + appliesTo = "ncccfp", + indexes = { + @Index(name = "ncccfp_refTimeIndex", columnNames = { "refTime", "forecastTime" } ) + } +) @XmlRootElement @XmlAccessorType(XmlAccessType.NONE) @DynamicSerialize diff --git a/ncep/gov.noaa.nws.ncep.common.dataplugin.ncgrib/src/gov/noaa/nws/ncep/common/dataplugin/ncgrib/NcgribRecord.java b/ncep/gov.noaa.nws.ncep.common.dataplugin.ncgrib/src/gov/noaa/nws/ncep/common/dataplugin/ncgrib/NcgribRecord.java index 0aa4effef9..f8ea7eb543 100644 --- a/ncep/gov.noaa.nws.ncep.common.dataplugin.ncgrib/src/gov/noaa/nws/ncep/common/dataplugin/ncgrib/NcgribRecord.java +++ b/ncep/gov.noaa.nws.ncep.common.dataplugin.ncgrib/src/gov/noaa/nws/ncep/common/dataplugin/ncgrib/NcgribRecord.java @@ -40,6 +40,7 @@ import javax.xml.bind.annotation.XmlRootElement; import org.hibernate.annotations.Fetch; import org.hibernate.annotations.FetchMode; +import org.hibernate.annotations.Index; import com.raytheon.uf.common.dataplugin.IDecoderGettable; import com.raytheon.uf.common.dataplugin.annotations.DataURI; @@ -62,6 +63,7 @@ import com.raytheon.uf.common.serialization.annotations.DynamicSerializeElement; * 4/7/09 1994 bphillip Initial Creation * 10/13/10 276 llin Modified for NC GRIB. * 03/07/12 606 ghull Added eventName to URI for NcInventory updating. + * Apr 4, 2013 1846 bkowal Added an index on refTime and forecastTime * * * @@ -70,6 +72,16 @@ import com.raytheon.uf.common.serialization.annotations.DynamicSerializeElement; */ @Entity @Table(name = "ncgrib", uniqueConstraints = { @UniqueConstraint(columnNames = { "dataURI" }) }) +/* + * Both refTime and forecastTime are included in the refTimeIndex since + * forecastTime is unlikely to be used. + */ +@org.hibernate.annotations.Table( + appliesTo = "ncgrib", + indexes = { + @Index(name = "ncgrib_refTimeIndex", columnNames = { "refTime", "forecastTime" } ) + } +) @XmlRootElement @XmlAccessorType(XmlAccessType.NONE) @DynamicSerialize diff --git a/ncep/gov.noaa.nws.ncep.common.dataplugin.ncpafm/src/gov/noaa/nws/ncep/common/dataplugin/ncpafm/NcPafmRecord.java b/ncep/gov.noaa.nws.ncep.common.dataplugin.ncpafm/src/gov/noaa/nws/ncep/common/dataplugin/ncpafm/NcPafmRecord.java index 6132ec7ebf..59079e1bda 100644 --- a/ncep/gov.noaa.nws.ncep.common.dataplugin.ncpafm/src/gov/noaa/nws/ncep/common/dataplugin/ncpafm/NcPafmRecord.java +++ b/ncep/gov.noaa.nws.ncep.common.dataplugin.ncpafm/src/gov/noaa/nws/ncep/common/dataplugin/ncpafm/NcPafmRecord.java @@ -34,6 +34,7 @@ import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; +import org.hibernate.annotations.Index; import com.raytheon.uf.common.dataplugin.IDecoderGettable; import com.raytheon.uf.common.dataplugin.annotations.DataURI; @@ -65,6 +66,7 @@ import com.raytheon.uf.common.serialization.annotations.DynamicSerializeElement; * object suitable for HDF5 persistence. * 10 Oct 2011 126 G. Hull replace stnid,lat&lon with the SurfaceObsLocation. * 03 Feb 2012 606 G. Hull added reportType to the URI for inventory updating + * Apr 4, 2013 1846 bkowal Added an index on refTime and forecastTime * * * @@ -73,6 +75,16 @@ import com.raytheon.uf.common.serialization.annotations.DynamicSerializeElement; */ @Entity @Table(name = "ncpafm", uniqueConstraints = { @UniqueConstraint(columnNames = { "dataURI" }) }) +/* + * Both refTime and forecastTime are included in the refTimeIndex since + * forecastTime is unlikely to be used. + */ +@org.hibernate.annotations.Table( + appliesTo = "ncpafm", + indexes = { + @Index(name = "ncpafm_refTimeIndex", columnNames = { "refTime", "forecastTime" } ) + } +) @XmlRootElement @XmlAccessorType(XmlAccessType.NONE) @DynamicSerialize diff --git a/ncep/gov.noaa.nws.ncep.common.dataplugin.ncpirep/src/gov/noaa/nws/ncep/common/dataplugin/ncpirep/NcPirepRecord.java b/ncep/gov.noaa.nws.ncep.common.dataplugin.ncpirep/src/gov/noaa/nws/ncep/common/dataplugin/ncpirep/NcPirepRecord.java index b5e62cf8a0..25f1c598ef 100644 --- a/ncep/gov.noaa.nws.ncep.common.dataplugin.ncpirep/src/gov/noaa/nws/ncep/common/dataplugin/ncpirep/NcPirepRecord.java +++ b/ncep/gov.noaa.nws.ncep.common.dataplugin.ncpirep/src/gov/noaa/nws/ncep/common/dataplugin/ncpirep/NcPirepRecord.java @@ -33,6 +33,7 @@ import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; +import org.hibernate.annotations.Index; import com.raytheon.uf.common.dataplugin.IDecoderGettable; import com.raytheon.uf.common.dataplugin.PluginDataObject; @@ -61,6 +62,7 @@ import com.vividsolutions.jts.geom.Geometry; * 08/30/2011 286 qzhou Use IDecoderConstantsN.INTEGER_MISSING instead -9999 in visibility. * 08/31/2011 286 qzhou Created project and moved this from ~edex.plugin.pirep * 09/19/2011 286 Q.Zhou Changed reportType to string, + * Apr 4, 2013 1846 bkowal Added an index on refTime and forecastTime * * * @author jkorman @@ -68,6 +70,16 @@ import com.vividsolutions.jts.geom.Geometry; */ @Entity @Table(name = "ncpirep", uniqueConstraints = { @UniqueConstraint(columnNames = { "dataURI" }) }) +/* + * Both refTime and forecastTime are included in the refTimeIndex since + * forecastTime is unlikely to be used. + */ +@org.hibernate.annotations.Table( + appliesTo = "ncpirep", + indexes = { + @Index(name = "ncpirep_refTimeIndex", columnNames = { "refTime", "forecastTime" } ) + } +) @XmlRootElement @XmlAccessorType(XmlAccessType.NONE) @DynamicSerialize diff --git a/ncep/gov.noaa.nws.ncep.common.dataplugin.ncscat/src/gov/noaa/nws/ncep/common/dataplugin/ncscat/NcscatRecord.java b/ncep/gov.noaa.nws.ncep.common.dataplugin.ncscat/src/gov/noaa/nws/ncep/common/dataplugin/ncscat/NcscatRecord.java index d6d3636e89..66e048ce9e 100644 --- a/ncep/gov.noaa.nws.ncep.common.dataplugin.ncscat/src/gov/noaa/nws/ncep/common/dataplugin/ncscat/NcscatRecord.java +++ b/ncep/gov.noaa.nws.ncep.common.dataplugin.ncscat/src/gov/noaa/nws/ncep/common/dataplugin/ncscat/NcscatRecord.java @@ -8,6 +8,7 @@ * Date Author Description * ------------ ---------- ----------- -------------------------- * 11/2009 Uma Josyula Initial creation + * Apr 4, 2013 1846 bkowal Added an index on refTime and forecastTime * * This code has been developed by the SIB for use in the AWIPS2 system. */ @@ -28,6 +29,7 @@ import javax.xml.bind.annotation.XmlRootElement; import org.hibernate.annotations.Cache; import org.hibernate.annotations.CacheConcurrencyStrategy; +import org.hibernate.annotations.Index; import com.raytheon.uf.common.dataplugin.IDecoderGettable; import com.raytheon.uf.common.dataplugin.annotations.DataURI; @@ -37,6 +39,16 @@ import com.raytheon.uf.common.serialization.annotations.DynamicSerializeElement; @Entity @Table(name = "ncscat", uniqueConstraints = { @UniqueConstraint(columnNames = { "dataURI" }) }) +/* + * Both refTime and forecastTime are included in the refTimeIndex since + * forecastTime is unlikely to be used. + */ +@org.hibernate.annotations.Table( + appliesTo = "ncscat", + indexes = { + @Index(name = "ncscat_refTimeIndex", columnNames = { "refTime", "forecastTime" } ) + } +) @Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL) @XmlRootElement @XmlAccessorType(XmlAccessType.NONE) diff --git a/ncep/gov.noaa.nws.ncep.common.dataplugin.ncscd/src/gov/noaa/nws/ncep/common/dataplugin/ncscd/NcScdRecord.java b/ncep/gov.noaa.nws.ncep.common.dataplugin.ncscd/src/gov/noaa/nws/ncep/common/dataplugin/ncscd/NcScdRecord.java index 512dde2b85..f9ad75d7f9 100644 --- a/ncep/gov.noaa.nws.ncep.common.dataplugin.ncscd/src/gov/noaa/nws/ncep/common/dataplugin/ncscd/NcScdRecord.java +++ b/ncep/gov.noaa.nws.ncep.common.dataplugin.ncscd/src/gov/noaa/nws/ncep/common/dataplugin/ncscd/NcScdRecord.java @@ -17,6 +17,7 @@ * of suspectTimeFlag from Boolean to String * since undefined in PointDataDescription. * 09/2011 457 S. Gurung Renamed H5 to Nc and h5 to nc + * Apr 4, 2013 1846 bkowal Added an index on refTime and forecastTime * * * @author T.Lee @@ -48,6 +49,7 @@ import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; +import org.hibernate.annotations.Index; import com.raytheon.uf.common.dataplugin.IDecoderGettable; import com.raytheon.uf.common.dataplugin.PluginDataObject; @@ -62,6 +64,16 @@ import com.raytheon.uf.common.serialization.annotations.DynamicSerializeElement; @Entity @Table(name = "ncscd", uniqueConstraints = { @UniqueConstraint(columnNames = { "dataURI" }) }) +/* + * Both refTime and forecastTime are included in the refTimeIndex since + * forecastTime is unlikely to be used. + */ +@org.hibernate.annotations.Table( + appliesTo = "ncscd", + indexes = { + @Index(name = "ncscd_refTimeIndex", columnNames = { "refTime", "forecastTime" } ) + } +) @XmlRootElement @XmlAccessorType(XmlAccessType.NONE) @DynamicSerialize diff --git a/ncep/gov.noaa.nws.ncep.common.dataplugin.nctaf/src/gov/noaa/nws/ncep/common/dataplugin/nctaf/NcTafRecord.java b/ncep/gov.noaa.nws.ncep.common.dataplugin.nctaf/src/gov/noaa/nws/ncep/common/dataplugin/nctaf/NcTafRecord.java index b2716e7418..5b75719a0c 100644 --- a/ncep/gov.noaa.nws.ncep.common.dataplugin.nctaf/src/gov/noaa/nws/ncep/common/dataplugin/nctaf/NcTafRecord.java +++ b/ncep/gov.noaa.nws.ncep.common.dataplugin.nctaf/src/gov/noaa/nws/ncep/common/dataplugin/nctaf/NcTafRecord.java @@ -31,6 +31,7 @@ import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; import org.apache.commons.lang.time.DateUtils; +import org.hibernate.annotations.Index; import com.raytheon.uf.common.dataplugin.IDecoderGettable; import com.raytheon.uf.common.dataplugin.PluginDataObject; @@ -64,6 +65,7 @@ import com.raytheon.uf.edex.decodertools.time.TimeTools; * 11/03/2011 sgurung Added probable weather and method to calculate ceiling. * 11/04/2011 sgurung Sort sky_cover before calculating ceiling. * Change startRefTime to nearest hour to get hourly refTimes + * Apr 4, 2013 1846 bkowal Added an index on refTime and forecastTime * * * @@ -35,6 +36,7 @@ import javax.persistence.Table; import javax.persistence.Transient; import javax.persistence.UniqueConstraint; import javax.xml.bind.annotation.XmlElement; +import org.hibernate.annotations.Index; import com.raytheon.uf.common.dataplugin.IDecoderGettable; import com.raytheon.uf.common.dataplugin.annotations.DataURI; @@ -50,6 +52,16 @@ import com.vividsolutions.jts.geom.Geometry; @Entity @Table(name = "ncuair", uniqueConstraints = { @UniqueConstraint(columnNames = { "dataURI" }) }) +/* + * Both refTime and forecastTime are included in the refTimeIndex since + * forecastTime is unlikely to be used. + */ +@org.hibernate.annotations.Table( + appliesTo = "ncuair", + indexes = { + @Index(name = "ncuair_refTimeIndex", columnNames = { "refTime", "forecastTime" } ) + } +) @DynamicSerialize public class NcUairRecord extends PersistablePluginDataObject implements ISpatialEnabled, IDecoderGettable, IPointData, IPersistable { diff --git a/ncep/gov.noaa.nws.ncep.common.dataplugin.nonconvsigmet/src/gov/noaa/nws/ncep/common/dataplugin/nonconvsigmet/NonConvSigmetRecord.java b/ncep/gov.noaa.nws.ncep.common.dataplugin.nonconvsigmet/src/gov/noaa/nws/ncep/common/dataplugin/nonconvsigmet/NonConvSigmetRecord.java index 13c9209b5d..721cbe94be 100644 --- a/ncep/gov.noaa.nws.ncep.common.dataplugin.nonconvsigmet/src/gov/noaa/nws/ncep/common/dataplugin/nonconvsigmet/NonConvSigmetRecord.java +++ b/ncep/gov.noaa.nws.ncep.common.dataplugin.nonconvsigmet/src/gov/noaa/nws/ncep/common/dataplugin/nonconvsigmet/NonConvSigmetRecord.java @@ -10,6 +10,7 @@ * 06/2009 Uma Josyula Initial creation * 09/2011 Chin Chen changed to improve purge performance and * removed xml serialization as well + * Apr 4, 2013 1846 bkowal Added an index on refTime and forecastTime * * This code has been developed by the SIB for use in the AWIPS2 system. */ @@ -42,6 +43,16 @@ import com.raytheon.uf.common.serialization.annotations.DynamicSerializeElement; @Entity @Table(name = "nonconvsigmet", uniqueConstraints = { @UniqueConstraint(columnNames = { "dataURI" }) }) +/* + * Both refTime and forecastTime are included in the refTimeIndex since + * forecastTime is unlikely to be used. + */ +@org.hibernate.annotations.Table( + appliesTo = "nonconvsigmet", + indexes = { + @Index(name = "nonconvsigmet_refTimeIndex", columnNames = { "refTime", "forecastTime" } ) + } +) @DynamicSerialize diff --git a/ncep/gov.noaa.nws.ncep.common.dataplugin.ntrans/src/gov/noaa/nws/ncep/common/dataplugin/ntrans/NtransRecord.java b/ncep/gov.noaa.nws.ncep.common.dataplugin.ntrans/src/gov/noaa/nws/ncep/common/dataplugin/ntrans/NtransRecord.java index 50c96bb228..710d16b6a0 100644 --- a/ncep/gov.noaa.nws.ncep.common.dataplugin.ntrans/src/gov/noaa/nws/ncep/common/dataplugin/ntrans/NtransRecord.java +++ b/ncep/gov.noaa.nws.ncep.common.dataplugin.ntrans/src/gov/noaa/nws/ncep/common/dataplugin/ntrans/NtransRecord.java @@ -7,7 +7,8 @@ * * Date Author Description * ------------ ---------- ----------- -------------------------- - * 02/2013 B. Hebbard Initial creation + * 02/2013 B. Hebbard Initial creation + * Apr 4, 2013 1846 bkowal Added an index on refTime and forecastTime * * This code has been developed by the SIB for use in the AWIPS2 system. */ @@ -28,6 +29,7 @@ import javax.xml.bind.annotation.XmlRootElement; import org.hibernate.annotations.Cache; import org.hibernate.annotations.CacheConcurrencyStrategy; +import org.hibernate.annotations.Index; import com.raytheon.uf.common.dataplugin.IDecoderGettable; import com.raytheon.uf.common.dataplugin.annotations.DataURI; @@ -37,6 +39,16 @@ import com.raytheon.uf.common.serialization.annotations.DynamicSerializeElement; @Entity @Table(name = "ntrans", uniqueConstraints = { @UniqueConstraint(columnNames = { "dataURI" }) }) +/* + * Both refTime and forecastTime are included in the refTimeIndex since + * forecastTime is unlikely to be used. + */ +@org.hibernate.annotations.Table( + appliesTo = "ntrans", + indexes = { + @Index(name = "ntrans_refTimeIndex", columnNames = { "refTime", "forecastTime" } ) + } +) @Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL) @XmlRootElement @XmlAccessorType(XmlAccessType.NONE) diff --git a/ncep/gov.noaa.nws.ncep.common.dataplugin.sgwh/src/gov/noaa/nws/ncep/common/dataplugin/sgwh/SgwhRecord.java b/ncep/gov.noaa.nws.ncep.common.dataplugin.sgwh/src/gov/noaa/nws/ncep/common/dataplugin/sgwh/SgwhRecord.java index 3cf8ca7862..26ab126a9e 100644 --- a/ncep/gov.noaa.nws.ncep.common.dataplugin.sgwh/src/gov/noaa/nws/ncep/common/dataplugin/sgwh/SgwhRecord.java +++ b/ncep/gov.noaa.nws.ncep.common.dataplugin.sgwh/src/gov/noaa/nws/ncep/common/dataplugin/sgwh/SgwhRecord.java @@ -10,6 +10,7 @@ * ------------ ----------- ----------- -------------------------- * Aug17 2011 Chin Chen Initial Coding (Following BufrsgwhRecord to refactor for * saving data to HDF5) + * Apr 4, 2013 1846 bkowal Added an index on refTime and forecastTime * * * @author chin chen @@ -29,6 +30,7 @@ import javax.persistence.Entity; import javax.persistence.Table; import javax.persistence.Transient; import javax.persistence.UniqueConstraint; +import org.hibernate.annotations.Index; import com.raytheon.uf.common.dataplugin.IDecoderGettable; import com.raytheon.uf.common.dataplugin.PluginDataObject; @@ -41,6 +43,16 @@ import com.raytheon.uf.common.serialization.annotations.DynamicSerializeElement; @Entity @Table(name = "sgwh", uniqueConstraints = { @UniqueConstraint(columnNames = { "dataURI" }) }) +/* + * Both refTime and forecastTime are included in the refTimeIndex since + * forecastTime is unlikely to be used. + */ +@org.hibernate.annotations.Table( + appliesTo = "sgwh", + indexes = { + @Index(name = "sgwh_refTimeIndex", columnNames = { "refTime", "forecastTime" } ) + } +) @DynamicSerialize public class SgwhRecord extends PluginDataObject implements IDecoderGettable, IPointData, IPersistable { diff --git a/ncep/gov.noaa.nws.ncep.common.dataplugin.sgwhv/src/gov/noaa/nws/ncep/common/dataplugin/sgwhv/SgwhvRecord.java b/ncep/gov.noaa.nws.ncep.common.dataplugin.sgwhv/src/gov/noaa/nws/ncep/common/dataplugin/sgwhv/SgwhvRecord.java index e786be73dc..5388ac2cf4 100644 --- a/ncep/gov.noaa.nws.ncep.common.dataplugin.sgwhv/src/gov/noaa/nws/ncep/common/dataplugin/sgwhv/SgwhvRecord.java +++ b/ncep/gov.noaa.nws.ncep.common.dataplugin.sgwhv/src/gov/noaa/nws/ncep/common/dataplugin/sgwhv/SgwhvRecord.java @@ -10,6 +10,7 @@ * ------------ ----------- ----------- -------------------------- * Aug23 2011 Chin Chen Initial Coding (Following BufrsgwhvRecord to refactor for * saving data to HDF5) + * Apr 4, 2013 1846 bkowal Added an index on refTime and forecastTime * * * @author chin chen @@ -29,6 +30,7 @@ import javax.persistence.Entity; import javax.persistence.Table; import javax.persistence.Transient; import javax.persistence.UniqueConstraint; +import org.hibernate.annotations.Index; import com.raytheon.uf.common.dataplugin.IDecoderGettable; import com.raytheon.uf.common.dataplugin.PluginDataObject; @@ -41,6 +43,16 @@ import com.raytheon.uf.common.serialization.annotations.DynamicSerializeElement; @Entity @Table(name = "sgwhv", uniqueConstraints = { @UniqueConstraint(columnNames = { "dataURI" }) }) +/* + * Both refTime and forecastTime are included in the refTimeIndex since + * forecastTime is unlikely to be used. + */ +@org.hibernate.annotations.Table( + appliesTo = "sgwhv", + indexes = { + @Index(name = "sgwhv_refTimeIndex", columnNames = { "refTime", "forecastTime" } ) + } +) @DynamicSerialize public class SgwhvRecord extends PluginDataObject implements IDecoderGettable, IPointData, IPersistable { diff --git a/ncep/gov.noaa.nws.ncep.common.dataplugin.solarimage/src/gov/noaa/nws/ncep/common/dataplugin/solarimage/SolarImageRecord.java b/ncep/gov.noaa.nws.ncep.common.dataplugin.solarimage/src/gov/noaa/nws/ncep/common/dataplugin/solarimage/SolarImageRecord.java index 0d8f76a2b6..5e5c5b1829 100644 --- a/ncep/gov.noaa.nws.ncep.common.dataplugin.solarimage/src/gov/noaa/nws/ncep/common/dataplugin/solarimage/SolarImageRecord.java +++ b/ncep/gov.noaa.nws.ncep.common.dataplugin.solarimage/src/gov/noaa/nws/ncep/common/dataplugin/solarimage/SolarImageRecord.java @@ -9,6 +9,7 @@ import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlRootElement; +import org.hibernate.annotations.Index; import com.raytheon.uf.common.dataplugin.IDecoderGettable; import com.raytheon.uf.common.dataplugin.annotations.DataURI; @@ -28,6 +29,7 @@ import com.raytheon.uf.common.serialization.annotations.DynamicSerializeElement; * 12/05/2012 865 sgurung, qzhou Initial creation. * 01/07/2013 865 qzhou Added "Site" for Halpha. * 01/28/2013 865 qzhou Changed float to double for intTime. + * Apr 4, 2013 1846 bkowal Added an index on refTime and forecastTime * * * @author sgurung, qzhou @@ -36,6 +38,16 @@ import com.raytheon.uf.common.serialization.annotations.DynamicSerializeElement; @Entity @Table(name = "solarimage", uniqueConstraints = { @UniqueConstraint(columnNames = { "dataURI" }) }) +/* + * Both refTime and forecastTime are included in the refTimeIndex since + * forecastTime is unlikely to be used. + */ +@org.hibernate.annotations.Table( + appliesTo = "solarimage", + indexes = { + @Index(name = "solarimage_refTimeIndex", columnNames = { "refTime", "forecastTime" } ) + } +) @XmlRootElement @XmlAccessorType(XmlAccessType.NONE) @DynamicSerialize diff --git a/ncep/gov.noaa.nws.ncep.common.dataplugin.ssha/src/gov/noaa/nws/ncep/common/dataplugin/ssha/SshaRecord.java b/ncep/gov.noaa.nws.ncep.common.dataplugin.ssha/src/gov/noaa/nws/ncep/common/dataplugin/ssha/SshaRecord.java index e544bbb878..8e8afd631b 100644 --- a/ncep/gov.noaa.nws.ncep.common.dataplugin.ssha/src/gov/noaa/nws/ncep/common/dataplugin/ssha/SshaRecord.java +++ b/ncep/gov.noaa.nws.ncep.common.dataplugin.ssha/src/gov/noaa/nws/ncep/common/dataplugin/ssha/SshaRecord.java @@ -11,6 +11,7 @@ * ------- ------- -------- ----------- * Sep 2011 Chin Chen Initial Coding (Following BufrsshaRecord to refactor for * saving data to HDF5) + * Apr 4, 2013 1846 bkowal Added an index on refTime and forecastTime * * * @author Chin Chen @@ -31,6 +32,7 @@ import javax.persistence.Entity; import javax.persistence.Table; import javax.persistence.Transient; import javax.persistence.UniqueConstraint; +import org.hibernate.annotations.Index; import com.raytheon.uf.common.dataplugin.IDecoderGettable; import com.raytheon.uf.common.dataplugin.annotations.DataURI; @@ -43,6 +45,16 @@ import com.raytheon.uf.common.serialization.annotations.DynamicSerializeElement; @Entity @Table(name = "ssha", uniqueConstraints = { @UniqueConstraint(columnNames = { "dataURI" }) }) +/* + * Both refTime and forecastTime are included in the refTimeIndex since + * forecastTime is unlikely to be used. + */ +@org.hibernate.annotations.Table( + appliesTo = "ssha", + indexes = { + @Index(name = "ssha_refTimeIndex", columnNames = { "refTime", "forecastTime" } ) + } +) @DynamicSerialize public class SshaRecord extends PersistablePluginDataObject implements IDecoderGettable, IPointData, IPersistable { diff --git a/ncep/gov.noaa.nws.ncep.common.dataplugin.stormtrack/src/gov/noaa/nws/ncep/common/dataplugin/stormtrack/StormTrackRecord.java b/ncep/gov.noaa.nws.ncep.common.dataplugin.stormtrack/src/gov/noaa/nws/ncep/common/dataplugin/stormtrack/StormTrackRecord.java index c42f034a6a..8e9fdc59f4 100644 --- a/ncep/gov.noaa.nws.ncep.common.dataplugin.stormtrack/src/gov/noaa/nws/ncep/common/dataplugin/stormtrack/StormTrackRecord.java +++ b/ncep/gov.noaa.nws.ncep.common.dataplugin.stormtrack/src/gov/noaa/nws/ncep/common/dataplugin/stormtrack/StormTrackRecord.java @@ -30,6 +30,7 @@ import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; +import org.hibernate.annotations.Index; import com.raytheon.uf.common.dataplugin.PluginDataObject; import com.raytheon.uf.common.dataplugin.IDecoderGettable; @@ -54,6 +55,7 @@ import gov.noaa.nws.ncep.common.tools.IDecoderConstantsN; * ------------ ---------- ----------- -------------------------- * 07/2011 T. Lee ATCF and Ensemble storm tracks * 10/19/2011 858 Greg Hull remove forecastHr + * Apr 4, 2013 1846 bkowal Added an index on refTime and forecastTime * * * @@ -62,6 +64,16 @@ import gov.noaa.nws.ncep.common.tools.IDecoderConstantsN; */ @Entity @Table(name = "stormtrack", uniqueConstraints = { @UniqueConstraint(columnNames = { "dataURI" }) }) +/* + * Both refTime and forecastTime are included in the refTimeIndex since + * forecastTime is unlikely to be used. + */ +@org.hibernate.annotations.Table( + appliesTo = "stormtrack", + indexes = { + @Index(name = "stormtrack_refTimeIndex", columnNames = { "refTime", "forecastTime" } ) + } +) @XmlRootElement @XmlAccessorType(XmlAccessType.NONE) @DynamicSerialize diff --git a/ncep/gov.noaa.nws.ncep.common.dataplugin.tcm/src/gov/noaa/nws/ncep/common/dataplugin/tcm/TcmRecord.java b/ncep/gov.noaa.nws.ncep.common.dataplugin.tcm/src/gov/noaa/nws/ncep/common/dataplugin/tcm/TcmRecord.java index 25fd1ccae1..917ad379e8 100644 --- a/ncep/gov.noaa.nws.ncep.common.dataplugin.tcm/src/gov/noaa/nws/ncep/common/dataplugin/tcm/TcmRecord.java +++ b/ncep/gov.noaa.nws.ncep.common.dataplugin.tcm/src/gov/noaa/nws/ncep/common/dataplugin/tcm/TcmRecord.java @@ -15,6 +15,7 @@ * 09/2011 Chin Chen changed to improve purge performance and * removed xml serialization as well * 07/2012 #606 Greg Huoll added reportType to the dataURI + * Apr 4, 2013 1846 bkowal Added an index on refTime and forecastTime * * * @@ -48,6 +49,16 @@ import gov.noaa.nws.ncep.common.tools.IDecoderConstantsN; @Entity @Table(name = "tcm", uniqueConstraints = { @UniqueConstraint(columnNames = { "dataURI" }) }) +/* + * Both refTime and forecastTime are included in the refTimeIndex since + * forecastTime is unlikely to be used. + */ +@org.hibernate.annotations.Table( + appliesTo = "tcm", + indexes = { + @Index(name = "tcm_refTimeIndex", columnNames = { "refTime", "forecastTime" } ) + } +) @DynamicSerialize public class TcmRecord extends PluginDataObject { diff --git a/ncep/gov.noaa.nws.ncep.common.dataplugin.wcp/src/gov/noaa/nws/ncep/common/dataplugin/wcp/WcpRecord.java b/ncep/gov.noaa.nws.ncep.common.dataplugin.wcp/src/gov/noaa/nws/ncep/common/dataplugin/wcp/WcpRecord.java index 255ba83e2b..d9a8740794 100644 --- a/ncep/gov.noaa.nws.ncep.common.dataplugin.wcp/src/gov/noaa/nws/ncep/common/dataplugin/wcp/WcpRecord.java +++ b/ncep/gov.noaa.nws.ncep.common.dataplugin.wcp/src/gov/noaa/nws/ncep/common/dataplugin/wcp/WcpRecord.java @@ -16,6 +16,7 @@ * 17May2010 37 F. J. Yen Refactored to dataplugin for migration to to11dr11 * 09/2011 Chin Chen changed to improve purge performance and * removed xml serialization as well + * * Apr 4, 2013 1846 bkowal Added an index on refTime and forecastTime * * * @author F. J. Yen, SIB @@ -47,6 +48,17 @@ import com.raytheon.uf.common.serialization.annotations.DynamicSerialize; import com.raytheon.uf.common.serialization.annotations.DynamicSerializeElement; @Entity @Table(name = "wcp", uniqueConstraints = { @UniqueConstraint(columnNames = { "dataURI" }) }) +/* + * Both refTime and forecastTime are included in the refTimeIndex since + * forecastTime is unlikely to be used. + */ +@org.hibernate.annotations.Table( + appliesTo = "wcp", + indexes = { + @Index(name = "wcp_refTimeIndex", columnNames = { "refTime", "forecastTime" } ) + } +) + @DynamicSerialize public class WcpRecord extends PluginDataObject{ diff --git a/ncep/gov.noaa.nws.ncep.edex.plugin.mosaic/src/gov/noaa/nws/ncep/edex/plugin/mosaic/common/MosaicRecord.java b/ncep/gov.noaa.nws.ncep.edex.plugin.mosaic/src/gov/noaa/nws/ncep/edex/plugin/mosaic/common/MosaicRecord.java index acf0f55e19..9c9bd91e7b 100644 --- a/ncep/gov.noaa.nws.ncep.edex.plugin.mosaic/src/gov/noaa/nws/ncep/edex/plugin/mosaic/common/MosaicRecord.java +++ b/ncep/gov.noaa.nws.ncep.edex.plugin.mosaic/src/gov/noaa/nws/ncep/edex/plugin/mosaic/common/MosaicRecord.java @@ -28,6 +28,7 @@ import javax.xml.bind.annotation.XmlRootElement; import org.hibernate.annotations.Cache; import org.hibernate.annotations.CacheConcurrencyStrategy; +import org.hibernate.annotations.Index; import org.opengis.referencing.crs.ProjectedCRS; import org.opengis.referencing.operation.MathTransform; @@ -61,6 +62,7 @@ import com.vividsolutions.jts.geom.Coordinate; * Extracted prod name from mosaicInfo.txt * 6/2012 825 G. Hull rm prodName from URI. Use prodCode where needed. * 09/2012 B. Hebbard Merge out RTS changes from OB12.9.1 + * Apr 4, 2013 1846 bkowal Added an index on refTime and forecastTime * * * @@ -73,6 +75,16 @@ import com.vividsolutions.jts.geom.Coordinate; @Entity @Table(name = "mosaic", uniqueConstraints = { @UniqueConstraint(columnNames = { "dataURI" }) }) +/* + * Both refTime and forecastTime are included in the refTimeIndex since + * forecastTime is unlikely to be used. + */ +@org.hibernate.annotations.Table( + appliesTo = "mosaic", + indexes = { + @Index(name = "mosaic_refTimeIndex", columnNames = { "refTime", "forecastTime" } ) + } +) @Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL) @XmlRootElement @XmlAccessorType(XmlAccessType.NONE) diff --git a/ncep/gov.noaa.nws.ncep.edex.plugin.nctext/src/gov/noaa/nws/ncep/edex/plugin/nctext/common/NctextRecord.java b/ncep/gov.noaa.nws.ncep.edex.plugin.nctext/src/gov/noaa/nws/ncep/edex/plugin/nctext/common/NctextRecord.java index e4af79c789..938a908814 100644 --- a/ncep/gov.noaa.nws.ncep.edex.plugin.nctext/src/gov/noaa/nws/ncep/edex/plugin/nctext/common/NctextRecord.java +++ b/ncep/gov.noaa.nws.ncep.edex.plugin.nctext/src/gov/noaa/nws/ncep/edex/plugin/nctext/common/NctextRecord.java @@ -18,7 +18,8 @@ * Date Ticket# Engineer Description * ------- ------- -------- ----------- * 10/22/2009 191 Chin Chen Initial coding - * 07/23/2010 191 Archana Added DataUri annotation to productType + * 07/23/2010 191 Archana Added DataUri annotation to productType + * Apr 4, 2013 1846 bkowal Added an index on refTime and forecastTime * * * @author Chin Chen @@ -36,7 +37,7 @@ import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; - +import org.hibernate.annotations.Index; import com.raytheon.uf.common.dataplugin.IDecoderGettable; import com.raytheon.uf.common.dataplugin.PluginDataObject; @@ -47,6 +48,16 @@ import com.raytheon.uf.common.serialization.annotations.DynamicSerializeElement; @Entity @Table(name = "nctext", uniqueConstraints = { @UniqueConstraint(columnNames = { "dataURI" }) }) +/* + * Both refTime and forecastTime are included in the refTimeIndex since + * forecastTime is unlikely to be used. + */ +@org.hibernate.annotations.Table( + appliesTo = "nctext", + indexes = { + @Index(name = "nctext_refTimeIndex", columnNames = { "refTime", "forecastTime" } ) + } +) @XmlRootElement @XmlAccessorType(XmlAccessType.NONE) @DynamicSerialize diff --git a/rpms/awips2.core/Installer.python/component.spec b/rpms/awips2.core/Installer.python/component.spec index 31e24df4c7..2113a3d3b3 100644 --- a/rpms/awips2.core/Installer.python/component.spec +++ b/rpms/awips2.core/Installer.python/component.spec @@ -7,7 +7,7 @@ Name: awips2-python Summary: AWIPS II Python Distribution Version: 2.7.1 -Release: 7 +Release: 8 Group: AWIPSII BuildRoot: %{_build_root} BuildArch: %{_build_arch} diff --git a/rpms/build/i386/build.sh b/rpms/build/i386/build.sh index d5fbf259f3..fc36418480 100644 --- a/rpms/build/i386/build.sh +++ b/rpms/build/i386/build.sh @@ -333,8 +333,8 @@ fi if [ "${1}" = "-viz" ]; then buildRPM "awips2" - buildCAVE buildRPM "awips2-rcm" + buildCAVE if [ $? -ne 0 ]; then exit 1 fi