Scarab commit: svn commit: r10748 - trunk/src: java/org/tigris/scarab/notification java/org/tigris/scarab/tools java/org/tigris/scarab/tools/localization webapp/WEB-INF/templates/email/notification
Hussayn Dabbous <[email protected]>
| Newsgroups | gmane.comp.java.scarab.cvs |
|---|---|
| Message-ID | <[email protected]> |
Author: dabbous
Date: 2009-06-03 16:13:23-0700
New Revision: 10748
Modified:
trunk/src/java/org/tigris/scarab/notification/ActivityType.java
trunk/src/java/org/tigris/scarab/notification/ScarabNewNotificationManager.java
trunk/src/java/org/tigris/scarab/tools/ScarabLocalizationTool.java
trunk/src/java/org/tigris/scarab/tools/localization/L10NKeySet.java
trunk/src/webapp/WEB-INF/templates/email/notification/IssueActivitySubject.vm
Log:
SCB2893 started refactoring. Fixed a bug where sometimes the most significant notification was wrongly calulated. Added L10NKeys for the email subject. Significantly cleaned up code in NotificationManager. Still work in progress)
Modified: trunk/src/java/org/tigris/scarab/notification/ActivityType.java
Url: http://scarab.tigris.org/source/browse/scarab/trunk/src/java/org/tigris/scarab/notification/ActivityType.java?view=diff&pathrev=10748&r1=10747&r2=10748
==============================================================================
--- trunk/src/java/org/tigris/scarab/notification/ActivityType.java (original)
+++ trunk/src/java/org/tigris/scarab/notification/ActivityType.java 2009-06-03 16:13:23-0700
@@ -4,31 +4,34 @@
import java.util.Map;
import java.util.Set;
+import org.tigris.scarab.tools.localization.L10NKeySet;
+import org.tigris.scarab.tools.localization.LocalizationKey;
+
public class ActivityType
{
- String code = null;
- String resourceId = null;
- String notificationId = null;
- Integer notificationPriority = null;
-
- public static final ActivityType ISSUE_CREATED = new ActivityType("issue_created","ActivityTypeIssueCreated", "NotificationIssueCreated", 17);
- public static final ActivityType ISSUE_MOVED = new ActivityType("issue_moved","ActivityTypeIssueMoved", "NotificationIssueMoved", 16);
- public static final ActivityType ISSUE_COPIED = new ActivityType("issue_copied","ActivityTypeIssueCopied", "NotificationIssueCopied", 15);
- public static final ActivityType ISSUE_DELETED = new ActivityType("issue_deleted","ActivityTypeIssueDeleted", "NotificationIssueDeleted", 14);
- public static final ActivityType COMMENT_ADDED = new ActivityType("comment_added","ActivityTypeCommentAdded", "NotificationCommentAdded", 13);
- public static final ActivityType COMMENT_CHANGED = new ActivityType("comment_changed","ActivityTypeCommentChanged", "NotificationCommentChanged", 12);
- public static final ActivityType ATTRIBUTE_CHANGED = new ActivityType("attribute_changed","ActivityTypeAttributeChanged", "NotificationAttributeChanged", 11);
- public static final ActivityType USER_ATTRIBUTE_CHANGED = new ActivityType("user_attribute_changed","ActivityTypeUserAttributeChanged", "NotificationUserAttributeChanged", 10);
- public static final ActivityType URL_ADDED = new ActivityType("url_added","ActivityTypeURLAdded", "NotificationURLAdded", 9);
- public static final ActivityType URL_CHANGED = new ActivityType("url_changed","ActivityTypeURLChanged", "NotificationURLCHanged", 8);
- public static final ActivityType URL_DESC_CHANGED = new ActivityType("url_desc_changed","ActivityTypeURLDescChanged", "NotificationURLDESCCHanged", 7);
- public static final ActivityType URL_DELETED = new ActivityType("url_deleted","ActivityTypeURLDeleted", "NotificationURLDeleted", 6);
- public static final ActivityType ATTACHMENT_CREATED = new ActivityType("attachment_created","ActivityTypeAttachmentCreated", "NotificationAttachmentCreated", 5);
- public static final ActivityType ATTACHMENT_REMOVED = new ActivityType("attachment_removed","ActivityTypeAttachmentRemoved", "NotificationAttachmentRemoved", 4);
- public static final ActivityType DEPENDENCY_CREATED = new ActivityType("dependency_created","ActivityTypeDependencyCreated", "NotificationDependencyCreated", 3);
- public static final ActivityType DEPENDENCY_CHANGED = new ActivityType("dependency_changed","ActivityTypeDependencyChanged", "NotificationDependencyChanged", 2);
- public static final ActivityType DEPENDENCY_DELETED = new ActivityType("dependency_deleted","ActivityTypeDependencyDeleted", "NotificationDependencyDeleted", 1);
- public static final ActivityType OTHER = new ActivityType("other","ActivityTypeOther", "NotificationIssueOther", 0);
+ String code = null;
+ String resourceId = null;
+ LocalizationKey notificationKey = null;
+ Integer notificationPriority = null;
+
+ public static final ActivityType ISSUE_CREATED = new ActivityType("issue_created","ActivityTypeIssueCreated", L10NKeySet.NotificationIssueCreated, 17);
+ public static final ActivityType ISSUE_MOVED = new ActivityType("issue_moved","ActivityTypeIssueMoved", L10NKeySet.NotificationIssueMoved, 16);
+ public static final ActivityType ISSUE_COPIED = new ActivityType("issue_copied","ActivityTypeIssueCopied", L10NKeySet.NotificationIssueCopied, 15);
+ public static final ActivityType ISSUE_DELETED = new ActivityType("issue_deleted","ActivityTypeIssueDeleted", L10NKeySet.NotificationIssueDeleted, 14);
+ public static final ActivityType ATTRIBUTE_CHANGED = new ActivityType("attribute_changed","ActivityTypeAttributeChanged", L10NKeySet.NotificationAttributeChanged, 13);
+ public static final ActivityType USER_ATTRIBUTE_CHANGED = new ActivityType("user_attribute_changed","ActivityTypeUserAttributeChanged", L10NKeySet.NotificationUserAttributeChanged, 12);
+ public static final ActivityType URL_ADDED = new ActivityType("url_added","ActivityTypeURLAdded", L10NKeySet.NotificationURLAdded, 11);
+ public static final ActivityType URL_CHANGED = new ActivityType("url_changed","ActivityTypeURLChanged", L10NKeySet.NotificationURLCHanged, 10);
+ public static final ActivityType URL_DESC_CHANGED = new ActivityType("url_desc_changed","ActivityTypeURLDescChanged", L10NKeySet.NotificationURLDESCCHanged, 9);
+ public static final ActivityType URL_DELETED = new ActivityType("url_deleted","ActivityTypeURLDeleted", L10NKeySet.NotificationURLDeleted, 8);
+ public static final ActivityType ATTACHMENT_CREATED = new ActivityType("attachment_created","ActivityTypeAttachmentCreated", L10NKeySet.NotificationAttachmentCreated, 7);
+ public static final ActivityType ATTACHMENT_REMOVED = new ActivityType("attachment_removed","ActivityTypeAttachmentRemoved", L10NKeySet.NotificationAttachmentRemoved, 6);
+ public static final ActivityType DEPENDENCY_CREATED = new ActivityType("dependency_created","ActivityTypeDependencyCreated", L10NKeySet.NotificationDependencyCreated, 5);
+ public static final ActivityType DEPENDENCY_CHANGED = new ActivityType("dependency_changed","ActivityTypeDependencyChanged", L10NKeySet.NotificationDependencyChanged, 4);
+ public static final ActivityType DEPENDENCY_DELETED = new ActivityType("dependency_deleted","ActivityTypeDependencyDeleted", L10NKeySet.NotificationDependencyDeleted, 3);
+ public static final ActivityType COMMENT_ADDED = new ActivityType("comment_added","ActivityTypeCommentAdded", L10NKeySet.NotificationCommentAdded, 2);
+ public static final ActivityType COMMENT_CHANGED = new ActivityType("comment_changed","ActivityTypeCommentChanged", L10NKeySet.NotificationCommentChanged, 1);
+ public static final ActivityType OTHER = new ActivityType("other","ActivityTypeOther", L10NKeySet.NotificationIssueOther, 0);
private static Map types = new HashMap();
private static HashMap activityPriority;
@@ -59,8 +62,8 @@
* Return the L10N Id for the Notification hint
* @return
*/
- public String getHint() {
- return notificationId;
+ public LocalizationKey getNotificationKey() {
+ return notificationKey;
}
/**
@@ -100,11 +103,11 @@
return this.code;
}
- private ActivityType(String desc, String resourceId, String notificationId, Integer notificationPriority)
+ private ActivityType(String desc, String resourceId, LocalizationKey notificationKey, Integer notificationPriority)
{
this.code = desc;
this.resourceId = resourceId;
- this.notificationId = notificationId;
+ this.notificationKey = notificationKey;
this.notificationPriority = notificationPriority;
}
Modified: trunk/src/java/org/tigris/scarab/notification/ScarabNewNotificationManager.java
Url: http://scarab.tigris.org/source/browse/scarab/trunk/src/java/org/tigris/scarab/notification/ScarabNewNotificationManager.java?view=diff&pathrev=10748&r1=10747&r2=10748
==============================================================================
--- trunk/src/java/org/tigris/scarab/notification/ScarabNewNotificationManager.java (original)
+++ trunk/src/java/org/tigris/scarab/notification/ScarabNewNotificationManager.java 2009-06-03 16:13:23-0700
@@ -202,6 +202,7 @@
{
ScarabCache.clear();
log.debug("sendPendingNotifications(): Collect pending notifications ...");
+ // It is now guaranteed, that the notifications arrive in order of CreationDate!
List pending = NotificationStatusPeer.getPendingNotifications();
if(pending == null)
@@ -215,22 +216,35 @@
Map issueActivities = new HashMap();
Map archiverActivities = new HashMap();
Set creators = new HashSet();
- NotificationStatus firstNotification;
- NotificationStatus lastNotification;
//Process each Issue ...
Iterator pendingIssuesIterator = pendingIssueMap.keySet().iterator();
+ int pendingIssueCount = pendingIssueMap.size();
+ int processedIssueCount = 0;
while( pendingIssuesIterator.hasNext())
{
Issue issue = (Issue)pendingIssuesIterator.next();
- String issueId = "???";
+
+ String issueId;
+ try
+ {
+ issueId = issue.getUniqueId();
+ }
+ catch (TorqueException te)
+ {
+ log.error("sendPendingNotifications(): No access to Issue [" + te + "]");
+ // Can not proceed with this issue !
+ continue;
+ }
+
+
// clear volatile data structures ...
issueActivities.clear();
archiverActivities.clear();
creators.clear();
- firstNotification = null;
- lastNotification = null;
- Long issueTime = null;
+
+ NotificationStatus firstNotification = null;
+ NotificationStatus lastNotification = null;
NotificationStatus mostRelevantNotification = null;
List notificationList = (List)pendingIssueMap.get(issue);
@@ -243,13 +257,14 @@
{
firstNotification = currentNotification;
}
- lastNotification = currentNotification;
+ if(!it.hasNext())
+ {
+ lastNotification = currentNotification;
+ }
mostRelevantNotification = getMostRelevantNotification(currentNotification, mostRelevantNotification, issue);
-
try
{
- issueId = issue.getUniqueId();
Integer receiverId = currentNotification.getReceiverId();
ScarabUser receiver = null;
if(receiverId.equals(Email.getArchiveUser().getUserId()))
@@ -265,11 +280,12 @@
Map userActivities = getActivitiesForUser(issueActivities, receiver);
addActivity(currentNotification, userActivities);
addActivity(currentNotification, archiverActivities);
- issueTime = adjustTimeToNewer(issueTime, currentNotification);
}
catch (TorqueException te)
{
- log.error("sendPendingNotifications(): " + te);
+ log.error("sendPendingNotifications(): No access to current Scarab User" + te);
+ // We can continue processing here. We just don't know how to process Emails
+ // for the current user.
}
}
@@ -279,8 +295,27 @@
* issueActivities and send one E-Mail per receiver for this issue:
*/
+ Long issueTime = lastNotification.getCreationDate().getTime();
if (isOldEnough(issueTime))
{
+ processedIssueCount += 1;
+
+ // ===========================================================
+ // Determine the changeKey (l10n) for the most relevant notification
+ // The resolved l10n key will appear in the subject line of the email!
+ // Note: If the key can not be resolved, the key itself will be
+ // used as replacement without further notification! (to be changed in the future)
+ // ===========================================================
+ LocalizationKey changeKey;
+ if (isStatusNotification(mostRelevantNotification, issue))
+ {
+ changeKey = getStatusKey(mostRelevantNotification, issue);
+ }
+ else
+ {
+ changeKey = getNotificationKey(mostRelevantNotification, issue);
+ }
+
log.debug("processing notifications for issue : ["+issueId+"]");
Iterator userIterator = getUsersToNotifyIterator(issueActivities);
while( userIterator.hasNext())
@@ -292,8 +327,9 @@
ectx.setLinkTool(new ScarabLink());
ectx.put("creators", creators);
ectx.put("firstNotification", firstNotification);
- ectx.put("lastNotification", lastNotification);
- ectx.put("changeHint",getChangeHint(mostRelevantNotification, issue));
+ ectx.put("lastNotification", lastNotification);
+ ectx.put("changeKey",changeKey);
+
ectx.put("cr", "\n"); // for email template to get a reliable Carriage return
Map groupedActivities = (Map) issueActivities.get(user);
@@ -322,45 +358,81 @@
{
log.debug("Issue " + issueId + ": Is not old enough.");
}
- }
- log.debug("sendPendingNotifications(): ...finished!");
+ }
+ if(pendingIssueCount > 0)
+ {
+ log.info("sendPendingNotifications(): processed " + processedIssueCount + " of " + pendingIssueCount + " pending issues.");
+ }
+ else
+ {
+ log.debug("sendPendingNotifications(): nothing todo.");
+ }
}
- private Object getChangeHint(NotificationStatus notification, Issue issue)
+ /**
+ * Return the L10NKey associated to the Attribute change.
+ * @param notification
+ * @param issue
+ * @return
+ */
+ private LocalizationKey getNotificationKey(NotificationStatus notification, Issue issue)
{
- String result = null;
+ LocalizationKey result = null;
ActivityType activityType = notification.getActivityType();
- if(activityType.equals(ActivityType.ATTRIBUTE_CHANGED))
+ result = activityType.getNotificationKey();
+ return result;
+ }
+
+ /**
+ * Return the L10NKey representation of the status value.
+ * Note: This method strongly assumes, that the given notification
+ * contains an Activity of type ATTRIBUTE_CHANGED and the contained
+ * attribute is expected to be a status-attribute. This method throws
+ * an exception, if these constraints are not fulfilled!
+ * In case of success, the method returns an L10N key. This key is currently
+ * not expected to be backed by an l10n resource. Please consider this
+ * as a preparation for a future enhancement, where it will become possible
+ * to define localized attribute values. For now the key is a verbatim copy of the
+ * attribute value.
+ * @param notification
+ * @param issue
+ * @return
+ */
+ private LocalizationKey getStatusKey(NotificationStatus notification, Issue issue)
+ {
+ LocalizationKey result = null;
+ try
{
- try
+ Attribute attribute = notification.getActivity().getAttribute();
+ if (attribute == null || !getIsStatusAttribute(attribute, issue))
{
- Attribute attribute = notification.getActivity().getAttribute();
- if (getIsStatusAttribute(attribute, issue))
- {
- String name = attribute.getName();
- AttributeValue av = issue.getAttributeValue(name);
- if(av != null)
- {
- result = av.getValue();
- }
- }
+ throw new IllegalArgumentException("Expected a notification containing a status attribute.");
}
- catch (TorqueException e)
+
+ String name = attribute.getName();
+ AttributeValue av = issue.getAttributeValue(name);
+ if(av != null)
{
- Log.get().warn("Database acess error while retrieving status attribute value.(ignored)");
- Log.get().warn("db layer reported: ["+e.getMessage()+"]");
+ result = new L10NKey(av.getValue()); // interpret the value as a L10NKey (for future use!)
+ }
+ else
+ {
+ throw new IllegalArgumentException("Received a notification containing a status attribute without a given value.");
}
}
-
- if(result == null)
+ catch (TorqueException e)
{
- result = activityType.getHint();
+ Log.get().warn("Database acess error while retrieving status attribute value.(ignored)");
+ Log.get().warn("db layer reported: ["+e.getMessage()+"]");
}
return result;
}
+
+
+
private NotificationStatus getMostRelevantNotification(
NotificationStatus currentNotification,
@@ -400,15 +472,55 @@
}
else
{
- ActivityType mostRelevantActivityType = mostRelevantNotification.getActivityType();
- if ( currentActivityType.getPriority() > mostRelevantActivityType.getPriority() )
+ if( !isStatusNotification(mostRelevantNotification, issue))
{
- mostRelevantNotification = currentNotification;
+ ActivityType mostRelevantActivityType = mostRelevantNotification.getActivityType();
+ if ( currentActivityType.getPriority() >= mostRelevantActivityType.getPriority() )
+ {
+ mostRelevantNotification = currentNotification;
+ }
}
}
return mostRelevantNotification;
}
+ /**
+ * Tell if the notification contains a status-attribute.
+ * @param notification
+ * @param issue
+ * @return
+ */
+ private boolean isStatusNotification(NotificationStatus notification, Issue issue)
+ {
+ boolean result = false;
+ {
+ ActivityType activityType = notification.getActivityType();
+ if(activityType.equals(ActivityType.ATTRIBUTE_CHANGED))
+ {
+ try
+ {
+ Attribute attribute = notification.getActivity().getAttribute();
+ if (getIsStatusAttribute(attribute, issue))
+ {
+ String name = attribute.getName();
+ AttributeValue av = issue.getAttributeValue(name);
+ if(av != null)
+ {
+ result = true;
+ }
+ }
+ }
+ catch (TorqueException e)
+ {
+ Log.get().warn("Database acess error while retrieving status attribute value.(ignored)");
+ Log.get().warn("db layer reported: ["+e.getMessage()+"]");
+ }
+ }
+ }
+ return result;
+ }
+
+
/**
@@ -566,35 +678,6 @@
/**
- * @param notificationTime
- * @param notification
- */
- private Long adjustTimeToNewer(Long notificationTime, NotificationStatus notification)
- {
- /**
- * Keep the time of the younger notification for every issue
- */
- long newTime = notification.getCreationDate().getTime();
- if (notificationTime == null)
- {
- notificationTime = new Long(newTime);
- }
- else
- {
- if (notificationTime.longValue() < newTime)
- {
- notificationTime = new Long(newTime);
- }
- else
- {
- // keep current notificationTime;
- }
- }
- return notificationTime;
- }
-
-
- /**
* @param issueActivities
* @param user
* @return
Modified: trunk/src/java/org/tigris/scarab/tools/ScarabLocalizationTool.java
Url: http://scarab.tigris.org/source/browse/scarab/trunk/src/java/org/tigris/scarab/tools/ScarabLocalizationTool.java?view=diff&pathrev=10748&r1=10747&r2=10748
==============================================================================
--- trunk/src/java/org/tigris/scarab/tools/ScarabLocalizationTool.java (original)
+++ trunk/src/java/org/tigris/scarab/tools/ScarabLocalizationTool.java 2009-06-03 16:13:23-0700
@@ -143,8 +143,31 @@
*/
public String get(Localizable key)
{
+ return this.get(key, false);
+ }
+
+
+ /**
+ * Return the localized property value.
+ * Take into account the Browser settings (in order of preference),
+ * the Turbine default settings and the System Locale,
+ * if the Turbine Default Locale is not defined.
+ * If ignoreMissingResource is set to true, return the resource key
+ * instead of throwing an exception. (Typically usefull from velocity)
+ */
+ public String get(Localizable key, boolean ignoreMissingResource)
+ {
String theKey = key.toString();
- return this.get(theKey);
+ String result;
+ if ( ignoreMissingResource )
+ {
+ result = this.getIgnoreMissingResource(theKey);
+ }
+ else
+ {
+ result = this.get(theKey);
+ }
+ return result;
}
@@ -209,6 +232,9 @@
*/
public String getIgnoreMissingResource(String key)
{
+ // [HD]: I plan to make this method private.
+ // Currently it is only used from the OldNotificationManager,
+ // which IMHO can be silently removed from the project.
String value;
try
{
Modified: trunk/src/java/org/tigris/scarab/tools/localization/L10NKeySet.java
Url: http://scarab.tigris.org/source/browse/scarab/trunk/src/java/org/tigris/scarab/tools/localization/L10NKeySet.java?view=diff&pathrev=10748&r1=10747&r2=10748
==============================================================================
--- trunk/src/java/org/tigris/scarab/tools/localization/L10NKeySet.java (original)
+++ trunk/src/java/org/tigris/scarab/tools/localization/L10NKeySet.java 2009-06-03 16:13:23-0700
@@ -1,5 +1,7 @@
package org.tigris.scarab.tools.localization;
+import org.tigris.scarab.notification.ActivityType;
+
/* ================================================================
* Copyright (c) 2000 CollabNet. All rights reserved.
*
@@ -465,6 +467,26 @@
public static final LocalizationKey ActivityComments = new L10NKey("ActivityComments");
public static final LocalizationKey ActivityAssociatedInfo = new L10NKey("ActivityAssociatedInfo");
public static final LocalizationKey ActivityDependencies = new L10NKey("ActivityDependencies");
+
+ public static final LocalizationKey NotificationIssueCreated = new L10NKey("NotificationIssueCreated");
+ public static final LocalizationKey NotificationIssueMoved = new L10NKey("NotificationIssueMoved");
+ public static final LocalizationKey NotificationIssueCopied = new L10NKey("NotificationIssueCopied");
+ public static final LocalizationKey NotificationIssueDeleted = new L10NKey("NotificationIssueDeleted");
+ public static final LocalizationKey NotificationAttributeChanged = new L10NKey("NotificationAttributeChanged");
+ public static final LocalizationKey NotificationUserAttributeChanged = new L10NKey("NotificationUserAttributeChanged");
+ public static final LocalizationKey NotificationURLAdded = new L10NKey("NotificationURLAdded");
+ public static final LocalizationKey NotificationURLCHanged = new L10NKey("NotificationURLCHanged");
+ public static final LocalizationKey NotificationURLDESCCHanged = new L10NKey("NotificationURLDESCCHanged");
+ public static final LocalizationKey NotificationURLDeleted = new L10NKey("NotificationURLDeleted");
+ public static final LocalizationKey NotificationAttachmentCreated = new L10NKey("NotificationAttachmentCreated");
+ public static final LocalizationKey NotificationAttachmentRemoved = new L10NKey("NotificationAttachmentRemoved");
+ public static final LocalizationKey NotificationDependencyCreated = new L10NKey("NotificationDependencyCreated");
+ public static final LocalizationKey NotificationDependencyChanged = new L10NKey("NotificationDependencyChanged");
+ public static final LocalizationKey NotificationDependencyDeleted = new L10NKey("NotificationDependencyDeleted");
+ public static final LocalizationKey NotificationCommentAdded = new L10NKey("NotificationCommentAdded");
+ public static final LocalizationKey NotificationCommentChanged = new L10NKey("NotificationCommentChanged");
+ public static final LocalizationKey NotificationIssueOther = new L10NKey("NotificationIssueOther");
+
public static final LocalizationKey IssueIsNowLocatedIn = new L10NKey("IssueIsNowLocatedIn");
public static final LocalizationKey IssueIdNotValid = new L10NKey("IssueIdNotValid");
public static final LocalizationKey NoPermissionToViewIssue = new L10NKey("NoPermissionToViewIssue");
Modified: trunk/src/webapp/WEB-INF/templates/email/notification/IssueActivitySubject.vm
Url: http://scarab.tigris.org/source/browse/scarab/trunk/src/webapp/WEB-INF/templates/email/notification/IssueActivitySubject.vm?view=diff&pathrev=10748&r1=10747&r2=10748
==============================================================================
--- trunk/src/webapp/WEB-INF/templates/email/notification/IssueActivitySubject.vm (original)
+++ trunk/src/webapp/WEB-INF/templates/email/notification/IssueActivitySubject.vm 2009-06-03 16:13:23-0700
@@ -1 +1 @@
-#set ($args = [$issue.Module.RealName.toUpperCase(), $issue.UniqueId, $l10n.getIgnoreMissingResource($changeHint), $issue.defaultText]) $l10n.format("DefaultModifyIssueEmailSubject", $args)
\ No newline at end of file
+#set ($args = [$issue.Module.RealName.toUpperCase(), $issue.UniqueId, $l10n.get($changeKey, true), $issue.defaultText]) $l10n.format("DefaultModifyIssueEmailSubject", $args)
------------------------------------------------------
http://scarab.tigris.org/ds/viewMessage.do?dsForumId=3577&dsMessageId=2359255