[241668] Singleton session bean
diff --git a/plugins/org.eclipse.jst.j2ee.ejb/ejb/org/eclipse/jst/j2ee/ejb/internal/operations/CreateSessionBeanTemplateModel.java b/plugins/org.eclipse.jst.j2ee.ejb/ejb/org/eclipse/jst/j2ee/ejb/internal/operations/CreateSessionBeanTemplateModel.java
index 868c3ac..e5973bb 100644
--- a/plugins/org.eclipse.jst.j2ee.ejb/ejb/org/eclipse/jst/j2ee/ejb/internal/operations/CreateSessionBeanTemplateModel.java
+++ b/plugins/org.eclipse.jst.j2ee.ejb/ejb/org/eclipse/jst/j2ee/ejb/internal/operations/CreateSessionBeanTemplateModel.java
@@ -31,6 +31,7 @@
 
 	public static final String QUALIFIED_STATELESS = "javax.ejb.Stateless"; //$NON-NLS-1$
 	public static final String QUALIFIED_STATEFUL = "javax.ejb.Stateful"; //$NON-NLS-1$
+	public static final String QUALIFIED_SINGLETON = "javax.ejb.Singleton"; //$NON-NLS-1$
 	public static final String QUALIFIED_LOCAL = "javax.ejb.Local"; //$NON-NLS-1$
 	public static final String QUALIFIED_REMOTE = "javax.ejb.Remote"; //$NON-NLS-1$
 	public static final String QUALIFIED_REMOTE_HOME = "javax.ejb.RemoteHome"; //$NON-NLS-1$
@@ -40,6 +41,7 @@
 	
 	public static final String STATELESS_ANNOTATION = "@Stateless"; //$NON-NLS-1$
 	public static final String STATEFUL_ANNOTATION = "@Stateful"; //$NON-NLS-1$
+	public static final String SINGLETON_ANNOTATION = "@Singleton"; //$NON-NLS-1$
 
 	protected BusinessInterface currentBusinessInterface = null;
 	protected String localHomeClassName = null;
@@ -56,12 +58,15 @@
 		Collection<String> collection = super.getImports();
 		
 		String stateType = dataModel.getStringProperty(STATE_TYPE);
-		if (stateType.equals(StateType.STATELESS.toString()))
+		if (stateType.equals(StateType.STATELESS.toString())) {
 			collection.add(QUALIFIED_STATELESS);
-		else if (stateType.equals(StateType.STATEFUL.toString()))
+		} else if (stateType.equals(StateType.STATEFUL.toString())) {
 			collection.add(QUALIFIED_STATEFUL);
-		else
+		} else if (stateType.equals(StateType.SINGLETON.toString())) {
+			collection.add(QUALIFIED_SINGLETON);
+		} else {
 			throw new IllegalStateException("illegal state type: " + stateType); //$NON-NLS-1$
+		}
 		
 		if (!isContainerType()) {
 			collection.add(QUALIFIED_TRANSACTION_MANAGEMENT);
@@ -106,12 +111,15 @@
 		String stateType = dataModel.getStringProperty(STATE_TYPE);
 		
 		String beanType;
-		if (stateType.equals(StateType.STATELESS.toString()))
+		if (stateType.equals(StateType.STATELESS.toString())) {
 			beanType = STATELESS_ANNOTATION;
-		else if (stateType.equals(StateType.STATEFUL.toString()))
+		} else if (stateType.equals(StateType.STATEFUL.toString())) {
 			beanType = STATEFUL_ANNOTATION;
-		else 
+		} else if (stateType.equals(StateType.SINGLETON.toString())) {
+			beanType = SINGLETON_ANNOTATION;
+		} else { 
 			throw new IllegalStateException("illegal state type: " + stateType); //$NON-NLS-1$
+		}
 		
 		return beanType;
 	}
diff --git a/plugins/org.eclipse.jst.j2ee.ejb/ejb/org/eclipse/jst/j2ee/ejb/internal/operations/INewEnterpriseBeanClassDataModelProperties.java b/plugins/org.eclipse.jst.j2ee.ejb/ejb/org/eclipse/jst/j2ee/ejb/internal/operations/INewEnterpriseBeanClassDataModelProperties.java
index d79cde8..731c5a8 100644
--- a/plugins/org.eclipse.jst.j2ee.ejb/ejb/org/eclipse/jst/j2ee/ejb/internal/operations/INewEnterpriseBeanClassDataModelProperties.java
+++ b/plugins/org.eclipse.jst.j2ee.ejb/ejb/org/eclipse/jst/j2ee/ejb/internal/operations/INewEnterpriseBeanClassDataModelProperties.java
@@ -25,13 +25,13 @@
 	 * Optional, String property of the EJB mapped name for the enterprise bean. 
 	 */
 	public static final String MAPPED_NAME = "INewEnterpriseBeanClassDataModelProperties.MAPPED_NAME"; //$NON-NLS-1$
-	
+
 	/**
-	 * Required, Integer property that determines the transaction type of the enterprise
-	 * bean.
+	 * Required, String property that determines the transaction type of the
+	 * enterprise bean. Valid values are the string representation of the
+	 * <code>TransactionType<code> enumeration.
 	 * 
-	 * @see NewSessionBeanClassDataModelProvider#TRANSACTION_TYPE_CONTAINER_INDEX
-	 * @see NewSessionBeanClassDataModelProvider#TRANSACTION_TYPE_BEAN_INDEX
+	 * @see TransactionType
 	 */
 	public static final String TRANSACTION_TYPE = "INewEnterpriseBeanClassDataModelProperties.TRANSACTION_TYPE"; //$NON-NLS-1$
 	
diff --git a/plugins/org.eclipse.jst.j2ee.ejb/ejb/org/eclipse/jst/j2ee/ejb/internal/operations/INewSessionBeanClassDataModelProperties.java b/plugins/org.eclipse.jst.j2ee.ejb/ejb/org/eclipse/jst/j2ee/ejb/internal/operations/INewSessionBeanClassDataModelProperties.java
index 0beab60..3717377 100644
--- a/plugins/org.eclipse.jst.j2ee.ejb/ejb/org/eclipse/jst/j2ee/ejb/internal/operations/INewSessionBeanClassDataModelProperties.java
+++ b/plugins/org.eclipse.jst.j2ee.ejb/ejb/org/eclipse/jst/j2ee/ejb/internal/operations/INewSessionBeanClassDataModelProperties.java
@@ -62,13 +62,13 @@
 	 * compatible remote home and components interfaces. The default is false.
 	 */
 	public static final String REMOTE_HOME = "INewSessionBeanClassDataModelProperties.REMOTE_HOME"; //$NON-NLS-1$
-	
+
 	/**
-	 * Required, Integer property that determines the state type of the session
-	 * bean.
+	 * Required, String property that determines the state type of the session
+	 * bean. Valid values are the string representation of the
+	 * <code>StateType<code> enumeration.
 	 * 
-	 * @see NewSessionBeanClassDataModelProvider#STATE_TYPE_STATELESS_INDEX
-	 * @see NewSessionBeanClassDataModelProvider#STATE_TYPE_STATEFUL_INDEX
+	 * @see StateType
 	 */
 	public static final String STATE_TYPE = "INewSessionBeanClassDataModelProperties.STATE_TYPE"; //$NON-NLS-1$
 	
diff --git a/plugins/org.eclipse.jst.j2ee.ejb/ejb/org/eclipse/jst/j2ee/ejb/internal/operations/NewSessionBeanClassDataModelProvider.java b/plugins/org.eclipse.jst.j2ee.ejb/ejb/org/eclipse/jst/j2ee/ejb/internal/operations/NewSessionBeanClassDataModelProvider.java
index c8297b4..810de09 100644
--- a/plugins/org.eclipse.jst.j2ee.ejb/ejb/org/eclipse/jst/j2ee/ejb/internal/operations/NewSessionBeanClassDataModelProvider.java
+++ b/plugins/org.eclipse.jst.j2ee.ejb/ejb/org/eclipse/jst/j2ee/ejb/internal/operations/NewSessionBeanClassDataModelProvider.java
@@ -46,8 +46,10 @@
 import org.eclipse.jst.j2ee.ejb.internal.operations.BusinessInterface.BusinessInterfaceType;
 import org.eclipse.jst.j2ee.ejb.internal.plugin.EjbPlugin;
 import org.eclipse.jst.j2ee.internal.common.J2EECommonMessages;
+import org.eclipse.jst.j2ee.internal.common.J2EEVersionUtil;
 import org.eclipse.jst.j2ee.internal.common.operations.NewJavaClassDataModelProvider;
 import org.eclipse.jst.j2ee.internal.ejb.project.operations.EJBCreationResourceHandler;
+import org.eclipse.jst.j2ee.project.JavaEEProjectUtilities;
 import org.eclipse.jst.j2ee.project.facet.IJ2EEFacetConstants;
 import org.eclipse.osgi.util.NLS;
 import org.eclipse.wst.common.frameworks.datamodel.DataModelPropertyDescriptor;
@@ -55,14 +57,11 @@
 import org.eclipse.wst.common.frameworks.datamodel.IDataModelOperation;
 import org.eclipse.wst.common.frameworks.datamodel.IDataModelProvider;
 import org.eclipse.wst.common.frameworks.internal.plugin.WTPCommonPlugin;
+import org.eclipse.wst.common.project.facet.core.IProjectFacetVersion;
 import org.eclipse.wst.common.project.facet.core.ProjectFacetsManager;
 
 public class NewSessionBeanClassDataModelProvider extends NewEnterpriseBeanClassDataModelProvider {
 
-
-	public static final int STATE_TYPE_STATELESS_INDEX = 0;
-	public static final int STATE_TYPE_STATEFUL_INDEX = 1;
-
 	private static final String LOCAL_SUFFIX = "Local"; //$NON-NLS-1$
 	private static final String REMOTE_SUFFIX = "Remote"; //$NON-NLS-1$
 	private static final String LOCAL_HOME_SUFFIX = "LocalHome"; //$NON-NLS-1$
@@ -249,19 +248,30 @@
 	public DataModelPropertyDescriptor[] getValidPropertyDescriptors(String propertyName) {
 		if (propertyName.equals(STATE_TYPE)) {
 			return DataModelPropertyDescriptor.createDescriptors(
-					new String[] { 
+					new String[] {
 							StateType.STATELESS.toString(), 
-							StateType.STATEFUL.toString()
+							StateType.STATEFUL.toString(), 
+							StateType.SINGLETON.toString()
 					}, 
 					new String[] {
 							EJBCreationResourceHandler.STATE_TYPE_STATELESS, 
-							EJBCreationResourceHandler.STATE_TYPE_STATEFUL
-					});
+							EJBCreationResourceHandler.STATE_TYPE_STATEFUL, 
+							EJBCreationResourceHandler.STATE_TYPE_SINGLETON
+					}
+			);
 		} 
 		
 		return super.getValidPropertyDescriptors(propertyName);
 	}
 
+	private boolean ejb31OrLater() {
+		IProject project = getTargetProject();
+		IProjectFacetVersion facetVersion = JavaEEProjectUtilities.getProjectFacetVersion(project, IJ2EEFacetConstants.EJB);
+		int version = J2EEVersionUtil.convertVersionStringToInt(facetVersion.getVersionString());
+		int ejb31version = J2EEVersionUtil.convertVersionStringToInt(IJ2EEFacetConstants.EJB_31.getVersionString());
+		return version >= ejb31version;
+	}
+
 	private void updateBusinessInterfaces(String propertyName) {
 		List<BusinessInterface> list = (List<BusinessInterface>) getProperty(INTERFACES);
 		if (propertyName.equals(REMOTE)) {
@@ -304,7 +314,9 @@
 
 	@Override
 	public IStatus validate(String propertyName) {
-		if (LOCAL_BUSINESS_INTERFACE.equals(propertyName)) {
+		if (STATE_TYPE.equals(propertyName)) {
+			return validateStateType();			
+		} else if (LOCAL_BUSINESS_INTERFACE.equals(propertyName)) {
 			if (getBooleanProperty(LOCAL)) {
 				return validateEjbInterface(getStringProperty(propertyName));
 			}
@@ -324,6 +336,14 @@
 		return super.validate(propertyName);
 	}
 
+	protected IStatus validateStateType() {
+		String value = getStringProperty(STATE_TYPE);
+		if (StateType.SINGLETON.toString().equals(value) && !ejb31OrLater()) {
+			return WTPCommonPlugin.createErrorStatus(EJBCreationResourceHandler.ERR_SINGLETON_ALLOWED_ONLY_FOR_31_AND_LATER);
+		}
+		return Status.OK_STATUS;
+	}
+
 	protected IStatus validateEjbInterface(String fullyQualifiedName) {
 		IStatus status = validateJavaTypeName(fullyQualifiedName);
 		if (status.getSeverity() != IStatus.ERROR) {
diff --git a/plugins/org.eclipse.jst.j2ee.ejb/ejb/org/eclipse/jst/j2ee/ejb/internal/operations/StateType.java b/plugins/org.eclipse.jst.j2ee.ejb/ejb/org/eclipse/jst/j2ee/ejb/internal/operations/StateType.java
index 8b7b6d2..a379220 100644
--- a/plugins/org.eclipse.jst.j2ee.ejb/ejb/org/eclipse/jst/j2ee/ejb/internal/operations/StateType.java
+++ b/plugins/org.eclipse.jst.j2ee.ejb/ejb/org/eclipse/jst/j2ee/ejb/internal/operations/StateType.java
@@ -12,17 +12,30 @@
 
 /**
  * Enumerates the session state type values of a session bean. 
+ * 
+ * @since 3.0
  */
 public enum StateType {
 
 	/**
-	 * Represents the <code>Stateless</code> session state type. 
+	 * Represents the <code>Stateless</code> session state type.
+	 * 
+	 * @since 3.0
 	 */
 	STATELESS,
 	
 	/**
 	 * Represents the <code>Stateful</code> session state type.
+	 * 
+	 * @since 3.0
 	 */
-	STATEFUL;
+	STATEFUL, 
+	
+	/**
+	 * Represents the <code>Singleton</code> session state type.
+	 * 
+	 * @since 3.2
+	 */
+	SINGLETON;
 
 }
diff --git a/plugins/org.eclipse.jst.j2ee.ejb/ejbcreation/org/eclipse/jst/j2ee/internal/ejb/project/operations/EJBCreationResourceHandler.java b/plugins/org.eclipse.jst.j2ee.ejb/ejbcreation/org/eclipse/jst/j2ee/internal/ejb/project/operations/EJBCreationResourceHandler.java
index a34484e..5d1dcff 100644
--- a/plugins/org.eclipse.jst.j2ee.ejb/ejbcreation/org/eclipse/jst/j2ee/internal/ejb/project/operations/EJBCreationResourceHandler.java
+++ b/plugins/org.eclipse.jst.j2ee.ejb/ejbcreation/org/eclipse/jst/j2ee/internal/ejb/project/operations/EJBCreationResourceHandler.java
@@ -147,6 +147,7 @@
 	public static String TRANSACTION_TYPE_BEAN;
 	public static String STATE_TYPE_STATELESS;
 	public static String STATE_TYPE_STATEFUL;
+	public static String STATE_TYPE_SINGLETON;
 	public static String DESTINATION_TYPE_QUEUE;
 	public static String DESTINATION_TYPE_TOPIC;
 	
@@ -163,6 +164,7 @@
 	public static String ERR_NO_MESSAGE_LISTENER_INTERFACE;
 	public static String WRN_BEAN_NAME_IS_EMPTY;
 	public static String WRN_NO_BUSINESS_INTERFACE;
+	public static String ERR_SINGLETON_ALLOWED_ONLY_FOR_31_AND_LATER;
 
 
 	static {
diff --git a/plugins/org.eclipse.jst.j2ee.ejb/property_files/ejbcreation.properties b/plugins/org.eclipse.jst.j2ee.ejb/property_files/ejbcreation.properties
index e0cd2ab..00c3dc8 100644
--- a/plugins/org.eclipse.jst.j2ee.ejb/property_files/ejbcreation.properties
+++ b/plugins/org.eclipse.jst.j2ee.ejb/property_files/ejbcreation.properties
@@ -127,11 +127,12 @@
 remove_client_jar_client_binary=
 EJB_Client_JAR_Creation_Error_=EJB client JAR Creation Error
 Cannot_Be_Binary_Project_For_Client_=Cannot create a new EJB client JAR for a binary project.
-Cannot_Be_StandAlone_Project_For_Client_=Cannont create a new EJB client JAR for a stand-alone project.
+Cannot_Be_StandAlone_Project_For_Client_=Cannot create a new EJB client JAR for a stand-alone project.
 TRANSACTION_TYPE_CONTAINER=Container
 TRANSACTION_TYPE_BEAN=Bean
 STATE_TYPE_STATELESS=Stateless
 STATE_TYPE_STATEFUL=Stateful
+STATE_TYPE_SINGLETON=Singleton
 DESTINATION_TYPE_QUEUE=Queue
 DESTINATION_TYPE_TOPIC=Topic
 
@@ -147,4 +148,5 @@
 ERR_REMOTE_HOME_NOT_INTERFACE=The specified Remote Home interface is not valid.
 ERR_BEAN_ALREADY_EXISTS=Enterprise bean with the same Ejb Name already exists.
 WRN_BEAN_NAME_IS_EMPTY=Bean name is empty, the containter will use the name of the bean class.
-WRN_NO_BUSINESS_INTERFACE=No business interface configured. Clients will not be able to access this bean. 
+WRN_NO_BUSINESS_INTERFACE=No business interface configured. Clients will not be able to access this bean.
+ERR_SINGLETON_ALLOWED_ONLY_FOR_31_AND_LATER=The 'Singleton' state type is allowed only for EJB projects with version 3.1 and later.