diff options
author | Henrik Paul <henrik@vaadin.com> | 2014-03-03 11:46:35 +0200 |
---|---|---|
committer | John Ahlroos <john@vaadin.com> | 2014-03-03 09:56:47 +0000 |
commit | 3798a8ab782d5e68b269692bbb784069b9312122 (patch) | |
tree | 248e2d6317f11a6906c2c13161609c118ffff8c4 /server | |
parent | 54b448d018922f6315bb756c24c6ce7feff6e14d (diff) | |
parent | 6b7ad587042d2c98c2133c93382fb5ea8cdded8c (diff) | |
download | vaadin-framework-3798a8ab782d5e68b269692bbb784069b9312122.tar.gz vaadin-framework-3798a8ab782d5e68b269692bbb784069b9312122.zip |
Merge branch 'master' into grid
Change-Id: I2f1134ce1bd5e8dbb183881fc69120e964271245
Diffstat (limited to 'server')
-rw-r--r-- | server/build.xml | 2 | ||||
-rw-r--r-- | server/src/com/vaadin/data/Container.java | 85 | ||||
-rw-r--r-- | server/src/com/vaadin/data/util/sqlcontainer/SQLContainer.java | 180 | ||||
-rw-r--r-- | server/src/com/vaadin/server/FontAwesome.java | 447 | ||||
-rw-r--r-- | server/src/com/vaadin/server/FontIcon.java | 67 | ||||
-rw-r--r-- | server/src/com/vaadin/server/JsonCodec.java | 4 | ||||
-rw-r--r-- | server/src/com/vaadin/server/ResourceReference.java | 7 | ||||
-rw-r--r-- | server/src/com/vaadin/server/VaadinServletService.java | 21 | ||||
-rw-r--r-- | server/src/com/vaadin/server/communication/PushRequestHandler.java | 2 | ||||
-rw-r--r-- | server/src/com/vaadin/ui/Upload.java | 92 |
10 files changed, 776 insertions, 131 deletions
diff --git a/server/build.xml b/server/build.xml index 98ee2342cf..ad377584b9 100644 --- a/server/build.xml +++ b/server/build.xml @@ -24,7 +24,7 @@ <target name="jar"> <property name="server.osgi.import" value="javax.servlet;version="2.4.0",javax.servlet.http;version="2.4.0",javax.validation;version="1.0.0.GA";resolution:=optional,org.jsoup;version="1.6.3",org.jsoup.parser;version="1.6.3",org.jsoup.nodes;version="1.6.3",org.jsoup.helper;version="1.6.3",org.jsoup.safety;version="1.6.3",org.json;version="0.0.20080701"" /> - <property name="server.osgi.require" value="com.vaadin.shared;bundle-version="${vaadin.version}",com.vaadin.push;bundle-version="${vaadin.version}";resolution:=optional" /> + <property name="server.osgi.require" value="com.vaadin.shared;bundle-version="${vaadin.version}",com.vaadin.push;bundle-version="${vaadin.version}";resolution:=optional,com.vaadin.theme-compiler;bundle-version="${vaadin.version}";resolution:=optional" /> <antcall target="common.jar"> <param name="require-bundle" value="${server.osgi.require}" /> <param name="import-package" value="${server.osgi.import}" /> diff --git a/server/src/com/vaadin/data/Container.java b/server/src/com/vaadin/data/Container.java index bf553f31d2..1e053d1091 100644 --- a/server/src/com/vaadin/data/Container.java +++ b/server/src/com/vaadin/data/Container.java @@ -86,7 +86,7 @@ public interface Container extends Serializable { * Gets the {@link Item} with the given Item ID from the Container. If the * Container does not contain the requested Item, <code>null</code> is * returned. - * + * <p> * Containers should not return Items that are filtered out. * * @param itemId @@ -108,11 +108,11 @@ public interface Container extends Serializable { * Gets the ID's of all visible (after filtering and sorting) Items stored * in the Container. The ID's cannot be modified through the returned * collection. - * + * <p> * If the container is {@link Ordered}, the collection returned by this * method should follow that order. If the container is {@link Sortable}, * the items should be in the sorted order. - * + * <p> * Calling this method for large lazy containers can be an expensive * operation and should be avoided when practical. * @@ -145,7 +145,7 @@ public interface Container extends Serializable { /** * Gets the number of visible Items in the Container. - * + * <p> * Filtering can hide items so that they will not be visible through the * container API. * @@ -155,7 +155,7 @@ public interface Container extends Serializable { /** * Tests if the Container contains the specified Item. - * + * <p> * Filtering can hide items so that they will not be visible through the * container API, and this method should respect visibility of items (i.e. * only indicate visible items as being in the container) if feasible for @@ -235,7 +235,7 @@ public interface Container extends Serializable { /** * Adds a new Property to all Items in the Container. The Property ID, data * type and default value of the new Property are given as parameters. - * + * <p> * This functionality is optional. * * @param propertyId @@ -256,7 +256,7 @@ public interface Container extends Serializable { /** * Removes a Property specified by the given Property ID from the Container. * Note that the Property will be removed from all Items in the Container. - * + * <p> * This functionality is optional. * * @param propertyId @@ -427,10 +427,8 @@ public interface Container extends Serializable { public interface Sortable extends Ordered { /** - * Sort method. - * * Sorts the container items. - * + * <p> * Sorting a container can irreversibly change the order of its items or * only change the order temporarily, depending on the container. * @@ -486,40 +484,34 @@ public interface Container extends Serializable { /** * Get the item id for the item at the position given by - * <code>index</code>. <br> - * <br> - * <b>Throws:</b> {@link IndexOutOfBoundsException} if - * <code>index</code> is outside the range of the container. (i.e. - * <code>index < 0 || container.size()-1 < index</code>) + * <code>index</code>. + * <p> * * @param index * the index of the requested item id * @return the item id of the item at the given index + * @throws IndexOutOfBoundsException + * if <code>index</code> is outside the range of the + * container. (i.e. + * <code>index < 0 || container.size()-1 < index</code> + * ) */ public Object getIdByIndex(int index); /** * Get <code>numberOfItems</code> consecutive item ids from the - * container, starting with the item id at <code>startIndex</code>. <br> - * <br> - * + * container, starting with the item id at <code>startIndex</code>. + * <p> * Implementations should return at most <code>numberOfItems</code> item * ids, but can contain less if the container has less items than * required to fulfill the request. The returned list must hence contain - * all of the item ids from the range: <br> - * <br> + * all of the item ids from the range: + * <p> * <code>startIndex</code> to - * <code>max(startIndex + (numberOfItems-1), container.size()-1)</code>. <br> - * <br> + * <code>max(startIndex + (numberOfItems-1), container.size()-1)</code>. + * <p> * For quick migration to new API see: * {@link ContainerHelpers#getItemIdsUsingGetIdByIndex(int, int, Indexed)} - * . <br> - * <br> - * <b>Throws:</b> {@link IllegalArgumentException} if - * <code>numberOfItems</code> is < 0 <br> - * <b>Throws:</b> {@link IndexOutOfBoundsException} if - * <code>startIndex</code> is outside the range of the container. (i.e. - * <code>startIndex < 0 || container.size()-1 < startIndex</code>) * * @param startIndex * the index for the first item which id to include @@ -529,6 +521,14 @@ public interface Container extends Serializable { * @return List containing the requested item ids or empty list if * <code>numberOfItems</code> == 0; not null * + * @throws IllegalArgumentException + * if <code>numberOfItems</code> is < 0 + * @throws IndexOutOfBoundsException + * if <code>startIndex</code> is outside the range of the + * container. (i.e. + * <code>startIndex < 0 || container.size()-1 < startIndex</code> + * ) + * * @since 7.0 */ public List<?> getItemIds(int startIndex, int numberOfItems); @@ -777,7 +777,6 @@ public interface Container extends Serializable { * Note that being a leaf does not imply whether or not an Item is * allowed to have children. * </p> - * . * * @param itemId * ID of the Item to be tested @@ -849,15 +848,15 @@ public interface Container extends Serializable { /** * Add a filter for given property. - * + * <p> * The API {@link Filterable#addContainerFilter(Filter)} is recommended * instead of this method. A {@link SimpleStringFilter} can be used with * the new API to implement the old string filtering functionality. - * + * <p> * The filter accepts items for which toString() of the value of the * given property contains or starts with given filterString. Other * items are not visible in the container when filtered. - * + * <p> * If a container has multiple filters, only items accepted by all * filters are visible. * @@ -890,17 +889,17 @@ public interface Container extends Serializable { /** * Filter interface for container filtering. - * + * <p> * If a filter does not support in-memory filtering, * {@link #passesFilter(Item)} should throw * {@link UnsupportedOperationException}. - * + * <p> * Lazy containers must be able to map filters to their internal * representation (e.g. SQL or JPA 2.0 Criteria). - * + * <p> * An {@link UnsupportedFilterException} can be thrown by the container if a * particular filter is not supported by the container. - * + * <p> * An {@link Filter} should implement {@link #equals(Object)} and * {@link #hashCode()} correctly to avoid duplicate filter registrations * etc. @@ -984,7 +983,7 @@ public interface Container extends Serializable { public interface Filterable extends Container, Serializable { /** * Adds a filter for the container. - * + * <p> * If a container has multiple filters, only items accepted by all * filters are visible. * @@ -996,7 +995,7 @@ public interface Container extends Serializable { /** * Removes a filter from the container. - * + * <p> * This requires that the equals() method considers the filters as * equivalent (same instance or properly implemented equals() method). */ @@ -1077,7 +1076,7 @@ public interface Container extends Serializable { /** * Container Item set change listener interface. - * + * <p> * An item set change refers to addition, removal or reordering of items in * the container. A simple property value change is not an item set change. */ @@ -1098,7 +1097,7 @@ public interface Container extends Serializable { * listeners. By implementing this interface a class explicitly announces * that it will generate a <code>ItemSetChangeEvent</code> when its contents * are modified. - * + * <p> * An item set change refers to addition, removal or reordering of items in * the container. A simple property value change is not an item set change. * @@ -1151,7 +1150,7 @@ public interface Container extends Serializable { /** * An <code>Event</code> object specifying the Container whose Property set * has changed. - * + * <p> * A property set change means the addition, removal or other structural * changes to the properties of a container. Changes concerning the set of * items in the container and their property values are not property set @@ -1170,7 +1169,7 @@ public interface Container extends Serializable { /** * The listener interface for receiving <code>PropertySetChangeEvent</code> * objects. - * + * <p> * A property set change means the addition, removal or other structural * change of the properties (supported property IDs) of a container. Changes * concerning the set of items in the container and their property values diff --git a/server/src/com/vaadin/data/util/sqlcontainer/SQLContainer.java b/server/src/com/vaadin/data/util/sqlcontainer/SQLContainer.java index e9a1a2d98f..32b46df166 100644 --- a/server/src/com/vaadin/data/util/sqlcontainer/SQLContainer.java +++ b/server/src/com/vaadin/data/util/sqlcontainer/SQLContainer.java @@ -50,7 +50,7 @@ public class SQLContainer implements Container, Container.Filterable, Container.Indexed, Container.Sortable, Container.ItemSetChangeNotifier { /** Query delegate */ - private QueryDelegate delegate; + private QueryDelegate queryDelegate; /** Auto commit mode, default = false */ private boolean autoCommit = false; @@ -61,6 +61,9 @@ public class SQLContainer implements Container, Container.Filterable, /** Number of items to cache = CACHE_RATIO x pageLength */ public static final int CACHE_RATIO = 2; + /** Amount of cache to overlap with previous page */ + private int cacheOverlap = pageLength; + /** Item and index caches */ private final Map<Integer, RowId> itemIndexes = new HashMap<Integer, RowId>(); private final CacheMap<RowId, RowItem> cachedItems = new CacheMap<RowId, RowItem>(); @@ -127,9 +130,9 @@ public class SQLContainer implements Container, Container.Filterable, throw new IllegalArgumentException( "QueryDelegate must not be null."); } - this.delegate = delegate; + queryDelegate = delegate; getPropertyIds(); - cachedItems.setCacheLimit(CACHE_RATIO * getPageLength()); + cachedItems.setCacheLimit(CACHE_RATIO * getPageLength() + cacheOverlap); } /**************************************/ @@ -147,7 +150,8 @@ public class SQLContainer implements Container, Container.Filterable, @Override public Object addItem() throws UnsupportedOperationException { - Object emptyKey[] = new Object[delegate.getPrimaryKeyColumns().size()]; + Object emptyKey[] = new Object[queryDelegate.getPrimaryKeyColumns() + .size()]; RowId itemId = new TemporaryRowId(emptyKey); // Create new empty column properties for the row item. List<ColumnProperty> itemProperties = new ArrayList<ColumnProperty>(); @@ -167,13 +171,13 @@ public class SQLContainer implements Container, Container.Filterable, if (autoCommit) { /* Add and commit instantly */ try { - if (delegate instanceof TableQuery) { - itemId = ((TableQuery) delegate) + if (queryDelegate instanceof TableQuery) { + itemId = ((TableQuery) queryDelegate) .storeRowImmediately(newRowItem); } else { - delegate.beginTransaction(); - delegate.storeRow(newRowItem); - delegate.commit(); + queryDelegate.beginTransaction(); + queryDelegate.storeRow(newRowItem); + queryDelegate.commit(); } refresh(); if (notificationsEnabled) { @@ -185,7 +189,7 @@ public class SQLContainer implements Container, Container.Filterable, getLogger().log(Level.WARNING, "Failed to add row to DB. Rolling back.", e); try { - delegate.rollback(); + queryDelegate.rollback(); } catch (SQLException ee) { getLogger().log(Level.SEVERE, "Failed to roll back row addition", e); @@ -231,7 +235,8 @@ public class SQLContainer implements Container, Container.Filterable, if (itemId instanceof RowId && !(itemId instanceof TemporaryRowId)) { try { - return delegate.containsRowWithKey(((RowId) itemId).getId()); + return queryDelegate.containsRowWithKey(((RowId) itemId) + .getId()); } catch (Exception e) { /* Query failed, just return false. */ getLogger().log(Level.WARNING, "containsId query failed", e); @@ -328,9 +333,9 @@ public class SQLContainer implements Container, Container.Filterable, ResultSet rs = null; try { // Load ALL rows :( - delegate.beginTransaction(); - rs = delegate.getResults(0, 0); - List<String> pKeys = delegate.getPrimaryKeyColumns(); + queryDelegate.beginTransaction(); + rs = queryDelegate.getResults(0, 0); + List<String> pKeys = queryDelegate.getPrimaryKeyColumns(); while (rs.next()) { RowId id = null; if (pKeys.isEmpty()) { @@ -350,12 +355,12 @@ public class SQLContainer implements Container, Container.Filterable, } rs.getStatement().close(); rs.close(); - delegate.commit(); + queryDelegate.commit(); } catch (SQLException e) { getLogger().log(Level.WARNING, "getItemIds() failed, rolling back.", e); try { - delegate.rollback(); + queryDelegate.rollback(); } catch (SQLException e1) { getLogger().log(Level.SEVERE, "Failed to roll back state", e1); } @@ -426,9 +431,9 @@ public class SQLContainer implements Container, Container.Filterable, return false; } try { - delegate.beginTransaction(); - boolean success = delegate.removeRow((RowItem) i); - delegate.commit(); + queryDelegate.beginTransaction(); + boolean success = queryDelegate.removeRow((RowItem) i); + queryDelegate.commit(); refresh(); if (notificationsEnabled) { CacheFlushNotifier.notifyOfCacheFlush(this); @@ -441,7 +446,7 @@ public class SQLContainer implements Container, Container.Filterable, getLogger().log(Level.WARNING, "Failed to remove row, rolling back", e); try { - delegate.rollback(); + queryDelegate.rollback(); } catch (SQLException ee) { /* Nothing can be done here */ getLogger().log(Level.SEVERE, @@ -452,7 +457,7 @@ public class SQLContainer implements Container, Container.Filterable, getLogger().log(Level.WARNING, "Failed to remove row, rolling back", e); try { - delegate.rollback(); + queryDelegate.rollback(); } catch (SQLException ee) { /* Nothing can be done here */ getLogger().log(Level.SEVERE, @@ -479,29 +484,29 @@ public class SQLContainer implements Container, Container.Filterable, if (autoCommit) { /* Remove and commit instantly. */ try { - delegate.beginTransaction(); + queryDelegate.beginTransaction(); boolean success = true; for (Object id : getItemIds()) { - if (!delegate.removeRow((RowItem) getItem(id))) { + if (!queryDelegate.removeRow((RowItem) getItem(id))) { success = false; } } if (success) { - delegate.commit(); + queryDelegate.commit(); getLogger().log(Level.FINER, "All rows removed from DB..."); refresh(); if (notificationsEnabled) { CacheFlushNotifier.notifyOfCacheFlush(this); } } else { - delegate.rollback(); + queryDelegate.rollback(); } return success; } catch (SQLException e) { getLogger().log(Level.WARNING, "removeAllItems() failed, rolling back", e); try { - delegate.rollback(); + queryDelegate.rollback(); } catch (SQLException ee) { /* Nothing can be done here */ getLogger().log(Level.SEVERE, "Failed to roll back", ee); @@ -511,7 +516,7 @@ public class SQLContainer implements Container, Container.Filterable, getLogger().log(Level.WARNING, "removeAllItems() failed, rolling back", e); try { - delegate.rollback(); + queryDelegate.rollback(); } catch (SQLException ee) { /* Nothing can be done here */ getLogger().log(Level.SEVERE, "Failed to roll back", ee); @@ -650,21 +655,31 @@ public class SQLContainer implements Container, Container.Filterable, int size = size(); // this protects against infinite looping int counter = 0; + int oldIndex; while (counter < size) { - for (Integer i : itemIndexes.keySet()) { - if (itemIndexes.get(i).equals(itemId)) { - return i; + if (itemIndexes.containsValue(itemId)) { + for (Integer idx : itemIndexes.keySet()) { + if (itemIndexes.get(idx).equals(itemId)) { + return idx; + } } - counter++; } + oldIndex = currentOffset; // load in the next page. - int nextIndex = (currentOffset / (pageLength * CACHE_RATIO) + 1) - * (pageLength * CACHE_RATIO); + int nextIndex = currentOffset + pageLength * CACHE_RATIO + + cacheOverlap; if (nextIndex >= size) { // Container wrapped around, start from index 0. nextIndex = 0; } updateOffsetAndCache(nextIndex); + + // Update counter + if (currentOffset > oldIndex) { + counter += currentOffset - oldIndex; + } else { + counter += size - oldIndex; + } } // safeguard in case item not found return -1; @@ -958,7 +973,8 @@ public class SQLContainer implements Container, Container.Filterable, */ private void setPageLengthInternal(int pageLength) { this.pageLength = pageLength > 0 ? pageLength : DEFAULT_PAGE_LENGTH; - cachedItems.setCacheLimit(CACHE_RATIO * getPageLength()); + cacheOverlap = getPageLength(); + cachedItems.setCacheLimit(CACHE_RATIO * getPageLength() + cacheOverlap); } /** @@ -994,24 +1010,24 @@ public class SQLContainer implements Container, Container.Filterable, try { getLogger().log(Level.FINER, "Commiting changes through delegate..."); - delegate.beginTransaction(); + queryDelegate.beginTransaction(); /* Perform buffered deletions */ for (RowItem item : removedItems.values()) { - if (!delegate.removeRow(item)) { + if (!queryDelegate.removeRow(item)) { throw new SQLException("Removal failed for row with ID: " + item.getId()); } } /* Perform buffered modifications */ for (RowItem item : modifiedItems) { - if (delegate.storeRow(item) > 0) { + if (queryDelegate.storeRow(item) > 0) { /* * Also reset the modified state in the item in case it is * reused e.g. in a form. */ item.commit(); } else { - delegate.rollback(); + queryDelegate.rollback(); refresh(); throw new ConcurrentModificationException( "Item with the ID '" + item.getId() @@ -1020,9 +1036,9 @@ public class SQLContainer implements Container, Container.Filterable, } /* Perform buffered additions */ for (RowItem item : addedItems) { - delegate.storeRow(item); + queryDelegate.storeRow(item); } - delegate.commit(); + queryDelegate.commit(); removedItems.clear(); addedItems.clear(); modifiedItems.clear(); @@ -1031,10 +1047,10 @@ public class SQLContainer implements Container, Container.Filterable, CacheFlushNotifier.notifyOfCacheFlush(this); } } catch (SQLException e) { - delegate.rollback(); + queryDelegate.rollback(); throw e; } catch (OptimisticLockException e) { - delegate.rollback(); + queryDelegate.rollback(); throw e; } } @@ -1065,15 +1081,15 @@ public class SQLContainer implements Container, Container.Filterable, void itemChangeNotification(RowItem changedItem) { if (autoCommit) { try { - delegate.beginTransaction(); - if (delegate.storeRow(changedItem) == 0) { - delegate.rollback(); + queryDelegate.beginTransaction(); + if (queryDelegate.storeRow(changedItem) == 0) { + queryDelegate.rollback(); refresh(); throw new ConcurrentModificationException( "Item with the ID '" + changedItem.getId() + "' has been externally modified."); } - delegate.commit(); + queryDelegate.commit(); if (notificationsEnabled) { CacheFlushNotifier.notifyOfCacheFlush(this); } @@ -1082,7 +1098,7 @@ public class SQLContainer implements Container, Container.Filterable, getLogger().log(Level.WARNING, "itemChangeNotification failed, rolling back...", e); try { - delegate.rollback(); + queryDelegate.rollback(); } catch (SQLException ee) { /* Nothing can be done here */ getLogger().log(Level.SEVERE, "Rollback failed", e); @@ -1106,14 +1122,19 @@ public class SQLContainer implements Container, Container.Filterable, * Index of the item that was requested, but not found in cache */ private void updateOffsetAndCache(int index) { - if (itemIndexes.containsKey(index)) { - return; - } - currentOffset = (index / (pageLength * CACHE_RATIO)) - * (pageLength * CACHE_RATIO); + + int oldOffset = currentOffset; + + currentOffset = (index / pageLength) * pageLength - cacheOverlap; + if (currentOffset < 0) { currentOffset = 0; } + + if (oldOffset == currentOffset && !cachedItems.isEmpty()) { + return; + } + getPage(); } @@ -1128,18 +1149,18 @@ public class SQLContainer implements Container, Container.Filterable, } try { try { - delegate.setFilters(filters); + queryDelegate.setFilters(filters); } catch (UnsupportedOperationException e) { getLogger().log(Level.FINE, "The query delegate doesn't support filtering", e); } try { - delegate.setOrderBy(sorters); + queryDelegate.setOrderBy(sorters); } catch (UnsupportedOperationException e) { getLogger().log(Level.FINE, "The query delegate doesn't support sorting", e); } - int newSize = delegate.getCount(); + int newSize = queryDelegate.getCount(); sizeUpdated = new Date(); sizeDirty = false; if (newSize != size) { @@ -1163,13 +1184,13 @@ public class SQLContainer implements Container, Container.Filterable, private void getPropertyIds() throws SQLException { propertyIds.clear(); propertyTypes.clear(); - delegate.setFilters(null); - delegate.setOrderBy(null); + queryDelegate.setFilters(null); + queryDelegate.setOrderBy(null); ResultSet rs = null; ResultSetMetaData rsmd = null; try { - delegate.beginTransaction(); - rs = delegate.getResults(0, 1); + queryDelegate.beginTransaction(); + rs = queryDelegate.getResults(0, 1); rsmd = rs.getMetaData(); boolean resultExists = rs.next(); Class<?> type = null; @@ -1208,9 +1229,9 @@ public class SQLContainer implements Container, Container.Filterable, boolean persistable = !rsmd.isReadOnly(i); - if (delegate instanceof TableQuery) { + if (queryDelegate instanceof TableQuery) { if (rsmd.getColumnLabel(i).equals( - ((TableQuery) delegate).getVersionColumn())) { + ((TableQuery) queryDelegate).getVersionColumn())) { readOnly = true; } } @@ -1219,19 +1240,20 @@ public class SQLContainer implements Container, Container.Filterable, propertyPersistable.put(colName, persistable); propertyNullable.put(colName, rsmd.isNullable(i) == ResultSetMetaData.columnNullable); - propertyPrimaryKey.put(colName, delegate.getPrimaryKeyColumns() + propertyPrimaryKey.put(colName, queryDelegate + .getPrimaryKeyColumns() .contains(rsmd.getColumnLabel(i))); propertyTypes.put(colName, type); } rs.getStatement().close(); rs.close(); - delegate.commit(); + queryDelegate.commit(); getLogger().log(Level.FINER, "Property IDs fetched."); } catch (SQLException e) { getLogger().log(Level.WARNING, "Failed to fetch property ids, rolling back", e); try { - delegate.rollback(); + queryDelegate.rollback(); } catch (SQLException e1) { getLogger().log(Level.SEVERE, "Failed to roll back", e1); } @@ -1262,23 +1284,23 @@ public class SQLContainer implements Container, Container.Filterable, itemIndexes.clear(); try { try { - delegate.setOrderBy(sorters); + queryDelegate.setOrderBy(sorters); } catch (UnsupportedOperationException e) { /* The query delegate doesn't support sorting. */ /* No need to do anything. */ getLogger().log(Level.FINE, "The query delegate doesn't support sorting", e); } - delegate.beginTransaction(); - int fetchedRows = pageLength * CACHE_RATIO; - rs = delegate.getResults(currentOffset, fetchedRows); + queryDelegate.beginTransaction(); + int fetchedRows = pageLength * CACHE_RATIO + cacheOverlap; + rs = queryDelegate.getResults(currentOffset, fetchedRows); rsmd = rs.getMetaData(); - List<String> pKeys = delegate.getPrimaryKeyColumns(); + List<String> pKeys = queryDelegate.getPrimaryKeyColumns(); // } /* Create new items and column properties */ ColumnProperty cp = null; int rowCount = currentOffset; - if (!delegate.implementationRespectsPagingLimits()) { + if (!queryDelegate.implementationRespectsPagingLimits()) { rowCount = currentOffset = 0; setPageLengthInternal(size); } @@ -1351,14 +1373,14 @@ public class SQLContainer implements Container, Container.Filterable, } rs.getStatement().close(); rs.close(); - delegate.commit(); + queryDelegate.commit(); getLogger().log(Level.FINER, "Fetched {0} rows starting from {1}", new Object[] { fetchedRows, currentOffset }); } catch (SQLException e) { getLogger().log(Level.WARNING, "Failed to fetch rows, rolling back", e); try { - delegate.rollback(); + queryDelegate.rollback(); } catch (SQLException e1) { getLogger().log(Level.SEVERE, "Failed to roll back", e1); } @@ -1431,8 +1453,8 @@ public class SQLContainer implements Container, Container.Filterable, */ private boolean isColumnIdentifierValid(String identifier) { if (identifier.equalsIgnoreCase("rownum") - && delegate instanceof TableQuery) { - TableQuery tq = (TableQuery) delegate; + && queryDelegate instanceof TableQuery) { + TableQuery tq = (TableQuery) queryDelegate; if (tq.getSqlGenerator() instanceof MSSQLGenerator || tq.getSqlGenerator() instanceof OracleGenerator) { return false; @@ -1447,7 +1469,7 @@ public class SQLContainer implements Container, Container.Filterable, * @return current querydelegate */ protected QueryDelegate getQueryDelegate() { - return delegate; + return queryDelegate; } /************************************/ @@ -1634,8 +1656,8 @@ public class SQLContainer implements Container, Container.Filterable, * @param listener */ public void addRowIdChangeListener(RowIdChangeListener listener) { - if (delegate instanceof QueryDelegate.RowIdChangeNotifier) { - ((QueryDelegate.RowIdChangeNotifier) delegate) + if (queryDelegate instanceof QueryDelegate.RowIdChangeNotifier) { + ((QueryDelegate.RowIdChangeNotifier) queryDelegate) .addListener(listener); } } @@ -1655,8 +1677,8 @@ public class SQLContainer implements Container, Container.Filterable, * @param listener */ public void removeRowIdChangeListener(RowIdChangeListener listener) { - if (delegate instanceof QueryDelegate.RowIdChangeNotifier) { - ((QueryDelegate.RowIdChangeNotifier) delegate) + if (queryDelegate instanceof QueryDelegate.RowIdChangeNotifier) { + ((QueryDelegate.RowIdChangeNotifier) queryDelegate) .removeListener(listener); } } diff --git a/server/src/com/vaadin/server/FontAwesome.java b/server/src/com/vaadin/server/FontAwesome.java new file mode 100644 index 0000000000..a7f4c7b342 --- /dev/null +++ b/server/src/com/vaadin/server/FontAwesome.java @@ -0,0 +1,447 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * 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 com.vaadin.server; + +/** + * FontAwesome set of font icons. + * <p> + * Each {@link FontIcon} comes from the FontAwesome font family, which is + * included in the theme.<br/> + * Consider this a starting point: it is unlikely an application needs exactly + * these icons, and all of them, so you might want to consider making a custom + * icon font - either to get other icons, or to minimize the size of the font. + * </p> + * + * @since 7.2 + * @author Vaadin Ltd + * @see http://fortawesome.github.io/Font-Awesome/ + */ +public enum FontAwesome implements FontIcon { + GLASS(0XF000), // + MUSIC(0XF001), // + SEARCH(0XF002), // + ENVELOPE_O(0XF003), // + HEART(0XF004), // + STAR(0XF005), // + STAR_O(0XF006), // + USER(0XF007), // + FILM(0XF008), // + TH_LARGE(0XF009), // + TH(0XF00A), // + TH_LIST(0XF00B), // + CHECK(0XF00C), // + TIMES(0XF00D), // + SEARCH_PLUS(0XF00E), // + SEARCH_MINUS(0XF010), // + POWER_OFF(0XF011), // + SIGNAL(0XF012), // + COG(0XF013), // + TRASH_O(0XF014), // + HOME(0XF015), // + FILE_O(0XF016), // + CLOCK_O(0XF017), // + ROAD(0XF018), // + DOWNLOAD(0XF019), // + ARROW_CIRCLE_O_DOWN(0XF01A), // + ARROW_CIRCLE_O_UP(0XF01B), // + INBOX(0XF01C), // + PLAY_CIRCLE_O(0XF01D), // + REPEAT(0XF01E), // + REFRESH(0XF021), // + LIST_ALT(0XF022), // + LOCK(0XF023), // + FLAG(0XF024), // + HEADPHONES(0XF025), // + VOLUME_OFF(0XF026), // + VOLUME_DOWN(0XF027), // + VOLUME_UP(0XF028), // + QRCODE(0XF029), // + BARCODE(0XF02A), // + TAG(0XF02B), // + TAGS(0XF02C), // + BOOK(0XF02D), // + BOOKMARK(0XF02E), // + PRINT(0XF02F), // + CAMERA(0XF030), // + FONT(0XF031), // + BOLD(0XF032), // + ITALIC(0XF033), // + TEXT_HEIGHT(0XF034), // + TEXT_WIDTH(0XF035), // + ALIGN_LEFT(0XF036), // + ALIGN_CENTER(0XF037), // + ALIGN_RIGHT(0XF038), // + ALIGN_JUSTIFY(0XF039), // + LIST(0XF03A), // + OUTDENT(0XF03B), // + INDENT(0XF03C), // + VIDEO_CAMERA(0XF03D), // + PICTURE_O(0XF03E), // + PENCIL(0XF040), // + MAP_MARKER(0XF041), // + ADJUST(0XF042), // + TINT(0XF043), // + PENCIL_SQUARE_O(0XF044), // + SHARE_SQUARE_O(0XF045), // + CHECK_SQUARE_O(0XF046), // + ARROWS(0XF047), // + STEP_BACKWARD(0XF048), // + FAST_BACKWARD(0XF049), // + BACKWARD(0XF04A), // + PLAY(0XF04B), // + PAUSE(0XF04C), // + STOP(0XF04D), // + FORWARD(0XF04E), // + FAST_FORWARD(0XF050), // + STEP_FORWARD(0XF051), // + EJECT(0XF052), // + CHEVRON_LEFT(0XF053), // + CHEVRON_RIGHT(0XF054), // + PLUS_CIRCLE(0XF055), // + MINUS_CIRCLE(0XF056), // + TIMES_CIRCLE(0XF057), // + CHECK_CIRCLE(0XF058), // + QUESTION_CIRCLE(0XF059), // + INFO_CIRCLE(0XF05A), // + CROSSHAIRS(0XF05B), // + TIMES_CIRCLE_O(0XF05C), // + CHECK_CIRCLE_O(0XF05D), // + BAN(0XF05E), // + ARROW_LEFT(0XF060), // + ARROW_RIGHT(0XF061), // + ARROW_UP(0XF062), // + ARROW_DOWN(0XF063), // + SHARE(0XF064), // + EXPAND(0XF065), // + COMPRESS(0XF066), // + PLUS(0XF067), // + MINUS(0XF068), // + ASTERISK(0XF069), // + EXCLAMATION_CIRCLE(0XF06A), // + GIFT(0XF06B), // + LEAF(0XF06C), // + FIRE(0XF06D), // + EYE(0XF06E), // + EYE_SLASH(0XF070), // + EXCLAMATION_TRIANGLE(0XF071), // + PLANE(0XF072), // + CALENDAR(0XF073), // + RANDOM(0XF074), // + COMMENT(0XF075), // + MAGNET(0XF076), // + CHEVRON_UP(0XF077), // + CHEVRON_DOWN(0XF078), // + RETWEET(0XF079), // + SHOPPING_CART(0XF07A), // + FOLDER(0XF07B), // + FOLDER_OPEN(0XF07C), // + ARROWS_V(0XF07D), // + ARROWS_H(0XF07E), // + BAR_CHART_O(0XF080), // + TWITTER_SQUARE(0XF081), // + FACEBOOK_SQUARE(0XF082), // + CAMERA_RETRO(0XF083), // + KEY(0XF084), // + COGS(0XF085), // + COMMENTS(0XF086), // + THUMBS_O_UP(0XF087), // + THUMBS_O_DOWN(0XF088), // + STAR_HALF(0XF089), // + HEART_O(0XF08A), // + SIGN_OUT(0XF08B), // + LINKEDIN_SQUARE(0XF08C), // + THUMB_TACK(0XF08D), // + EXTERNAL_LINK(0XF08E), // + SIGN_IN(0XF090), // + TROPHY(0XF091), // + GITHUB_SQUARE(0XF092), // + UPLOAD(0XF093), // + LEMON_O(0XF094), // + PHONE(0XF095), // + SQUARE_O(0XF096), // + BOOKMARK_O(0XF097), // + PHONE_SQUARE(0XF098), // + TWITTER(0XF099), // + FACEBOOK(0XF09A), // + GITHUB(0XF09B), // + UNLOCK(0XF09C), // + CREDIT_CARD(0XF09D), // + RSS(0XF09E), // + HDD_O(0XF0A0), // + BULLHORN(0XF0A1), // + BELL(0XF0F3), // + CERTIFICATE(0XF0A3), // + HAND_O_RIGHT(0XF0A4), // + HAND_O_LEFT(0XF0A5), // + HAND_O_UP(0XF0A6), // + HAND_O_DOWN(0XF0A7), // + ARROW_CIRCLE_LEFT(0XF0A8), // + ARROW_CIRCLE_RIGHT(0XF0A9), // + ARROW_CIRCLE_UP(0XF0AA), // + ARROW_CIRCLE_DOWN(0XF0AB), // + GLOBE(0XF0AC), // + WRENCH(0XF0AD), // + TASKS(0XF0AE), // + FILTER(0XF0B0), // + BRIEFCASE(0XF0B1), // + ARROWS_ALT(0XF0B2), // + USERS(0XF0C0), // + LINK(0XF0C1), // + CLOUD(0XF0C2), // + FLASK(0XF0C3), // + SCISSORS(0XF0C4), // + FILES_O(0XF0C5), // + PAPERCLIP(0XF0C6), // + FLOPPY_O(0XF0C7), // + SQUARE(0XF0C8), // + BARS(0XF0C9), // + LIST_UL(0XF0CA), // + LIST_OL(0XF0CB), // + STRIKETHROUGH(0XF0CC), // + UNDERLINE(0XF0CD), // + TABLE(0XF0CE), // + MAGIC(0XF0D0), // + TRUCK(0XF0D1), // + PINTEREST(0XF0D2), // + PINTEREST_SQUARE(0XF0D3), // + GOOGLE_PLUS_SQUARE(0XF0D4), // + GOOGLE_PLUS(0XF0D5), // + MONEY(0XF0D6), // + CARET_DOWN(0XF0D7), // + CARET_UP(0XF0D8), // + CARET_LEFT(0XF0D9), // + CARET_RIGHT(0XF0DA), // + COLUMNS(0XF0DB), // + SORT(0XF0DC), // + SORT_ASC(0XF0DD), // + SORT_DESC(0XF0DE), // + ENVELOPE(0XF0E0), // + LINKEDIN(0XF0E1), // + UNDO(0XF0E2), // + GAVEL(0XF0E3), // + TACHOMETER(0XF0E4), // + COMMENT_O(0XF0E5), // + COMMENTS_O(0XF0E6), // + BOLT(0XF0E7), // + SITEMAP(0XF0E8), // + UMBRELLA(0XF0E9), // + CLIPBOARD(0XF0EA), // + LIGHTBULB_O(0XF0EB), // + EXCHANGE(0XF0EC), // + CLOUD_DOWNLOAD(0XF0ED), // + CLOUD_UPLOAD(0XF0EE), // + USER_MD(0XF0F0), // + STETHOSCOPE(0XF0F1), // + SUITCASE(0XF0F2), // + BELL_O(0XF0A2), // + COFFEE(0XF0F4), // + CUTLERY(0XF0F5), // + FILE_TEXT_O(0XF0F6), // + BUILDING_O(0XF0F7), // + HOSPITAL_O(0XF0F8), // + AMBULANCE(0XF0F9), // + MEDKIT(0XF0FA), // + FIGHTER_JET(0XF0FB), // + BEER(0XF0FC), // + H_SQUARE(0XF0FD), // + PLUS_SQUARE(0XF0FE), // + ANGLE_DOUBLE_LEFT(0XF100), // + ANGLE_DOUBLE_RIGHT(0XF101), // + ANGLE_DOUBLE_UP(0XF102), // + ANGLE_DOUBLE_DOWN(0XF103), // + ANGLE_LEFT(0XF104), // + ANGLE_RIGHT(0XF105), // + ANGLE_UP(0XF106), // + ANGLE_DOWN(0XF107), // + DESKTOP(0XF108), // + LAPTOP(0XF109), // + TABLET(0XF10A), // + MOBILE(0XF10B), // + CIRCLE_O(0XF10C), // + QUOTE_LEFT(0XF10D), // + QUOTE_RIGHT(0XF10E), // + SPINNER(0XF110), // + CIRCLE(0XF111), // + REPLY(0XF112), // + GITHUB_ALT(0XF113), // + FOLDER_O(0XF114), // + FOLDER_OPEN_O(0XF115), // + SMILE_O(0XF118), // + FROWN_O(0XF119), // + MEH_O(0XF11A), // + GAMEPAD(0XF11B), // + KEYBOARD_O(0XF11C), // + FLAG_O(0XF11D), // + FLAG_CHECKERED(0XF11E), // + TERMINAL(0XF120), // + CODE(0XF121), // + REPLY_ALL(0XF122), // + MAIL_REPLY_ALL(0XF122), // + STAR_HALF_O(0XF123), // + LOCATION_ARROW(0XF124), // + CROP(0XF125), // + CODE_FORK(0XF126), // + CHAIN_BROKEN(0XF127), // + QUESTION(0XF128), // + INFO(0XF129), // + EXCLAMATION(0XF12A), // + SUPERSCRIPT(0XF12B), // + SUBSCRIPT(0XF12C), // + ERASER(0XF12D), // + PUZZLE_PIECE(0XF12E), // + MICROPHONE(0XF130), // + MICROPHONE_SLASH(0XF131), // + SHIELD(0XF132), // + CALENDAR_O(0XF133), // + FIRE_EXTINGUISHER(0XF134), // + ROCKET(0XF135), // + MAXCDN(0XF136), // + CHEVRON_CIRCLE_LEFT(0XF137), // + CHEVRON_CIRCLE_RIGHT(0XF138), // + CHEVRON_CIRCLE_UP(0XF139), // + CHEVRON_CIRCLE_DOWN(0XF13A), // + HTML5(0XF13B), // + CSS3(0XF13C), // + ANCHOR(0XF13D), // + UNLOCK_ALT(0XF13E), // + BULLSEYE(0XF140), // + ELLIPSIS_H(0XF141), // + ELLIPSIS_V(0XF142), // + RSS_SQUARE(0XF143), // + PLAY_CIRCLE(0XF144), // + TICKET(0XF145), // + MINUS_SQUARE(0XF146), // + MINUS_SQUARE_O(0XF147), // + LEVEL_UP(0XF148), // + LEVEL_DOWN(0XF149), // + CHECK_SQUARE(0XF14A), // + PENCIL_SQUARE(0XF14B), // + EXTERNAL_LINK_SQUARE(0XF14C), // + SHARE_SQUARE(0XF14D), // + COMPASS(0XF14E), // + CARET_SQUARE_O_DOWN(0XF150), // + CARET_SQUARE_O_UP(0XF151), // + CARET_SQUARE_O_RIGHT(0XF152), // + EUR(0XF153), // + GBP(0XF154), // + USD(0XF155), // + INR(0XF156), // + JPY(0XF157), // + RUB(0XF158), // + KRW(0XF159), // + BTC(0XF15A), // + FILE(0XF15B), // + FILE_TEXT(0XF15C), // + SORT_ALPHA_ASC(0XF15D), // + SORT_ALPHA_DESC(0XF15E), // + SORT_AMOUNT_ASC(0XF160), // + SORT_AMOUNT_DESC(0XF161), // + SORT_NUMERIC_ASC(0XF162), // + SORT_NUMERIC_DESC(0XF163), // + THUMBS_UP(0XF164), // + THUMBS_DOWN(0XF165), // + YOUTUBE_SQUARE(0XF166), // + YOUTUBE(0XF167), // + XING(0XF168), // + XING_SQUARE(0XF169), // + YOUTUBE_PLAY(0XF16A), // + DROPBOX(0XF16B), // + STACK_OVERFLOW(0XF16C), // + INSTAGRAM(0XF16D), // + FLICKR(0XF16E), // + ADN(0XF170), // + BITBUCKET(0XF171), // + BITBUCKET_SQUARE(0XF172), // + TUMBLR(0XF173), // + TUMBLR_SQUARE(0XF174), // + LONG_ARROW_DOWN(0XF175), // + LONG_ARROW_UP(0XF176), // + LONG_ARROW_LEFT(0XF177), // + LONG_ARROW_RIGHT(0XF178), // + APPLE(0XF179), // + WINDOWS(0XF17A), // + ANDROID(0XF17B), // + LINUX(0XF17C), // + DRIBBBLE(0XF17D), // + SKYPE(0XF17E), // + FOURSQUARE(0XF180), // + TRELLO(0XF181), // + FEMALE(0XF182), // + MALE(0XF183), // + GITTIP(0XF184), // + SUN_O(0XF185), // + MOON_O(0XF186), // + ARCHIVE(0XF187), // + BUG(0XF188), // + VK(0XF189), // + WEIBO(0XF18A), // + RENREN(0XF18B), // + PAGELINES(0XF18C), // + STACK_EXCHANGE(0XF18D), // + ARROW_CIRCLE_O_RIGHT(0XF18E), // + ARROW_CIRCLE_O_LEFT(0XF190), // + CARET_SQUARE_O_LEFT(0XF191), // + DOT_CIRCLE_O(0XF192), // + WHEELCHAIR(0XF193), // + VIMEO_SQUARE(0XF194), // + TRY(0XF195), // + PLUS_SQUARE_O(0XF196); + + private static final String fontFamily = "FontAwesome"; + private int codepoint; + + FontAwesome(int codepoint) { + this.codepoint = codepoint; + } + + /** + * Unsupported: {@link FontIcon} does not have a MIME type and is not a + * {@link Resource} that can be used in a context where a MIME type would be + * needed. + */ + @Override + public String getMIMEType() { + throw new UnsupportedOperationException(FontIcon.class.getSimpleName() + + " should not be used where a MIME type is needed."); + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.ui.FontIcon#getFontFamily() + */ + @Override + public String getFontFamily() { + return fontFamily; + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.ui.FontIcon#getCodepoint() + */ + @Override + public int getCodepoint() { + return codepoint; + } + + @Override + public String getHtml() { + return "<span class=\"v-icon\" style=\"font-family: " + fontFamily + + ";\">&#x" + Integer.toHexString(codepoint) + ";</span>"; + } + +} diff --git a/server/src/com/vaadin/server/FontIcon.java b/server/src/com/vaadin/server/FontIcon.java new file mode 100644 index 0000000000..45279f2c44 --- /dev/null +++ b/server/src/com/vaadin/server/FontIcon.java @@ -0,0 +1,67 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * 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 com.vaadin.server; + +import com.vaadin.shared.ui.label.ContentMode; +import com.vaadin.ui.Label; + +/** + * A font icon is a type of icon that is made by displaying one character from a + * specially constructed font containing icons ("icon font"). + * <p> + * {@link FontIcon} is a custom resource type which uses the URI scheme + * <code>fonticon://<fontfamily>/<codepoint></code> to reference a + * specific icon from a specific icon font. <br/> + * </p> + * + * @since 7.2 + * @author Vaadin Ltd + */ +public interface FontIcon extends Resource { + /** + * Returns the name (font family) of the font from which this icon comes. + * The name is used to apply the correct font where the icon is used. + * + * @since 7.2 + * @return + */ + public String getFontFamily(); + + /** + * Returns the unicode codepoint (character location) for this icon within + * the font given in {@link #getFontFamily()}. + * <p> + * For example, 0x0021 would in a regular font be the codepoint for the + * exclamation-point character.<br/> + * When constructing icon fonts, it might be a good idea to use the + * codepoints in the "Private use area", from 0xE000 0xF8FF. + * </p> + * + * @since 7.2 + * @return + */ + public int getCodepoint(); + + /** + * Returns HTML that can be used to display the icon in places where HTML + * can be used, such as a {@link Label} with {@link ContentMode#HTML}. + * + * + * @since 7.2 + * @return HTML needed to display icon + */ + public String getHtml(); +} diff --git a/server/src/com/vaadin/server/JsonCodec.java b/server/src/com/vaadin/server/JsonCodec.java index 129307e5c1..d05922b40d 100644 --- a/server/src/com/vaadin/server/JsonCodec.java +++ b/server/src/com/vaadin/server/JsonCodec.java @@ -617,7 +617,7 @@ public class JsonCodec implements Serializable { return decodedObject; } catch (Exception e) { - throw new JSONException(e); + throw new JSONException(e.getMessage()); } } @@ -765,7 +765,7 @@ public class JsonCodec implements Serializable { } } catch (Exception e) { // TODO: Should exceptions be handled in a different way? - throw new JSONException(e); + throw new JSONException(e.getMessage()); } return new EncodeResult(encoded, diff); } diff --git a/server/src/com/vaadin/server/ResourceReference.java b/server/src/com/vaadin/server/ResourceReference.java index 6747dd2b74..4bc8febd72 100644 --- a/server/src/com/vaadin/server/ResourceReference.java +++ b/server/src/com/vaadin/server/ResourceReference.java @@ -67,6 +67,13 @@ public class ResourceReference extends URLReference { final String uri = "theme://" + ((ThemeResource) resource).getResourceId(); return uri; + } else if (resource instanceof FontIcon) { + // fonticon://[font-family]/[codepoint] + final FontIcon icon = (FontIcon) resource; + final String uri = ApplicationConstants.FONTICON_PROTOCOL_PREFIX + + urlEncode(icon.getFontFamily()) + "/" + + Integer.toHexString(icon.getCodepoint()); + return uri; } else { throw new RuntimeException(getClass().getSimpleName() + " does not support resources of type: " diff --git a/server/src/com/vaadin/server/VaadinServletService.java b/server/src/com/vaadin/server/VaadinServletService.java index 818f2e87c6..daefad0644 100644 --- a/server/src/com/vaadin/server/VaadinServletService.java +++ b/server/src/com/vaadin/server/VaadinServletService.java @@ -37,7 +37,7 @@ import com.vaadin.ui.UI; public class VaadinServletService extends VaadinService { private final VaadinServlet servlet; - private final static boolean atmosphereAvailable = checkAtmosphereSupport(); + private boolean atmosphereAvailable = checkAtmosphereSupport(); /** * Keeps track of whether a warning about missing push support has already @@ -66,11 +66,13 @@ public class VaadinServletService extends VaadinService { private static boolean checkAtmosphereSupport() { try { String rawVersion = Version.getRawVersion(); - if (!Constants.REQUIRED_ATMOSPHERE_RUNTIME_VERSION.equals(rawVersion)) { + if (!Constants.REQUIRED_ATMOSPHERE_RUNTIME_VERSION + .equals(rawVersion)) { getLogger().log( Level.WARNING, Constants.INVALID_ATMOSPHERE_VERSION_WARNING, - new Object[] { Constants.REQUIRED_ATMOSPHERE_RUNTIME_VERSION, + new Object[] { + Constants.REQUIRED_ATMOSPHERE_RUNTIME_VERSION, rawVersion }); } return true; @@ -86,7 +88,18 @@ public class VaadinServletService extends VaadinService { handlers.add(0, new ServletBootstrapHandler()); handlers.add(new ServletUIInitHandler()); if (atmosphereAvailable) { - handlers.add(new PushRequestHandler(this)); + try { + handlers.add(new PushRequestHandler(this)); + } catch (ServiceException e) { + // Atmosphere init failed. Push won't work but we don't throw a + // service exception as we don't want to prevent non-push + // applications from working + getLogger() + .log(Level.WARNING, + "Error initializing Atmosphere. Push will not work.", + e); + atmosphereAvailable = false; + } } return handlers; } diff --git a/server/src/com/vaadin/server/communication/PushRequestHandler.java b/server/src/com/vaadin/server/communication/PushRequestHandler.java index f3869a76f8..aec3aa54c0 100644 --- a/server/src/com/vaadin/server/communication/PushRequestHandler.java +++ b/server/src/com/vaadin/server/communication/PushRequestHandler.java @@ -113,7 +113,7 @@ public class PushRequestHandler implements RequestHandler, trackMessageSize.configure(atmosphere.getAtmosphereConfig()); atmosphere.interceptor(trackMessageSize); } catch (ServletException e) { - throw new ServiceException("Could not read atmosphere settings", e); + throw new ServiceException("Atmosphere init failed", e); } } diff --git a/server/src/com/vaadin/ui/Upload.java b/server/src/com/vaadin/ui/Upload.java index 98f5d2ded9..c8d9f3ff09 100644 --- a/server/src/com/vaadin/ui/Upload.java +++ b/server/src/com/vaadin/ui/Upload.java @@ -28,7 +28,10 @@ import com.vaadin.server.NoOutputStreamException; import com.vaadin.server.PaintException; import com.vaadin.server.PaintTarget; import com.vaadin.server.StreamVariable.StreamingProgressEvent; +import com.vaadin.shared.EventId; import com.vaadin.shared.ui.upload.UploadClientRpc; +import com.vaadin.shared.ui.upload.UploadServerRpc; +import com.vaadin.util.ReflectTools; /** * Component for uploading files from client to server. @@ -113,9 +116,16 @@ public class Upload extends AbstractComponent implements Component.Focusable, * The receiver must be set before performing an upload. */ public Upload() { + registerRpc(new UploadServerRpc() { + @Override + public void change(String filename) { + fireEvent(new ChangeEvent(Upload.this, filename)); + } + }); } public Upload(String caption, Receiver uploadReceiver) { + this(); setCaption(caption); receiver = uploadReceiver; } @@ -486,6 +496,42 @@ public class Upload extends AbstractComponent implements Component.Focusable, } /** + * Upload.ChangeEvent event is sent when the value (filename) of the upload + * changes. + * + * @since 7.2 + */ + public static class ChangeEvent extends Component.Event { + + private final String filename; + + public ChangeEvent(Upload source, String filename) { + super(source); + this.filename = filename; + } + + /** + * Uploads where the event occurred. + * + * @return the Source of the event. + */ + @Override + public Upload getSource() { + return (Upload) super.getSource(); + } + + /** + * Gets the file name. + * + * @return the filename. + */ + public String getFilename() { + return filename; + } + + } + + /** * Receives the events when the upload starts. * * @author Vaadin Ltd. @@ -554,6 +600,25 @@ public class Upload extends AbstractComponent implements Component.Focusable, } /** + * Listener for {@link ChangeEvent} + * + * @since 7.2 + */ + public interface ChangeListener extends Serializable { + + Method FILENAME_CHANGED = ReflectTools.findMethod(ChangeListener.class, + "filenameChanged", ChangeEvent.class); + + /** + * A file has been selected but upload has not yet started. + * + * @param event + * the change event + */ + public void filenameChanged(ChangeEvent event); + } + + /** * Adds the upload started event listener. * * @param listener @@ -740,6 +805,27 @@ public class Upload extends AbstractComponent implements Component.Focusable, } /** + * Adds a filename change event listener + * + * @param listener + * the Listener to add + */ + public void addChangeListener(ChangeListener listener) { + super.addListener(EventId.CHANGE, ChangeEvent.class, listener, + ChangeListener.FILENAME_CHANGED); + } + + /** + * Removes a filename change event listener + * + * @param listener + * the listener to be removed + */ + public void removeChangeListener(ChangeListener listener) { + super.removeListener(EventId.CHANGE, ChangeEvent.class, listener); + } + + /** * @deprecated As of 7.0, replaced by * {@link #removeProgressListener(ProgressListener)} **/ @@ -1040,7 +1126,11 @@ public class Upload extends AbstractComponent implements Component.Focusable, @Override public OutputStream getOutputStream() { - OutputStream receiveUpload = receiver.receiveUpload( + if (getReceiver() == null) { + throw new IllegalStateException( + "Upload cannot be performed without a receiver set"); + } + OutputStream receiveUpload = getReceiver().receiveUpload( lastStartedEvent.getFileName(), lastStartedEvent.getMimeType()); lastStartedEvent = null; |