Blog Home  Home |  Breeze Home RSS 2.0 Atom 1.0 CDF  
Scott's Breeze Blog - RFID, BizTalk
...and everything in between
 
# Friday, July 30, 2010

image

Hold the phone!. Didn't I just pull that folder up in Explorer smile_sniff

image 

Ah…good old File System Redirector. Turns out that on x64 machine’s this folder is located here

C:\Windows\SysNative\AppFabric

We can browse to this location when adding references to the Windows Server AppFabric assemblies to our VS 2010 projects.

image

All is well again in the universe

Friday, July 30, 2010 4:41:06 PM (AUS Eastern Standard Time, UTC+10:00)  #    Comments [0]   VS 2010 | Windows Server AppFabric  |  Trackback
# Friday, July 09, 2010

image

Of course the official site can be found here > http://australia.msteched.com/

Friday, July 09, 2010 3:42:52 PM (AUS Eastern Standard Time, UTC+10:00)  #    Comments [0]   Humour  |  Trackback

I have been avoiding this for sometime now. That is, adding new activity items to the current BAM deployment in production. Production has been running for months now and in this high volume system we partition the BAM activities every week and archive each month (giving the client a rolling month worth of activity data). I was concerned that during the update of the BAM definition this data was going to be blown away (an experience that has caused much embarrassment in the past).

So the procedure I used this time did the trick…well almost

  • Took a “backup” of the current BAM definition using BM.exe

    bm.exe get-config -FileName:MyConfig.xml

  • Added the new activity items using Excel and edited the views
  • Exported the new BAM definition to xml
  • Removed the existing views using BM.exe

    bm.exe remove-view -Name:MyView

  • Deployed the new definition using BM.exe and the update-all command – FAILED smile_cry

    bm.exe update-all -DefinitionFile:MyNewDef.xml
     
    The error message in the command window was:
    All queries combined using a UNION, INTERSECT or EXCEPT operator must have an 
    equal number of expressions in their target lists.
     
    Upon investigation, I found that the partition tables did not get updated with the new activity items. As the view spans both the current activity tables and all the partition tables the view creation failed. Interestingly, the BAM Archive tables did get updated.

  • “Upgraded” the partition tables using the script from this blog post

    I did need to make a slight change to avoid some errors that cropped up with partition tables already archived and as such no longer present in the BAMPrimaryImport database (although the original script works).

    I changed the CURSOR definition to filter out those tables already archived:

    DECLARE partition_cursor CURSOR LOCAL FOR
    SELECT InstancesTable
    FROM [dbo].[bam_Metadata_Partitions]
    WHERE ActivityName = @activityName
    AND ArchivedTime Is Null -- Added additional filter
    ORDER BY CreationTime ASC
  • Deployed the new definition again using BM.exe and the update-all command – SUCCEEDED
  • Re-applied security to the Views using BM.exe

    bm.exe add-account -AccountName:TheStig -View:MyView

Unfortunately all my BAM Alerts got blown away smile_baringteeth . Makes sense as the alerts reference the view that was removed. Luckily taking the backup in step one allowed me to pull out the original alert definition and paste them into my new definition file. I re-deployed that using the update-all command and alerts are back to normal.

I did come across this KB 969558 article for BTS 2006 R2 that appeared to address the partition tables issue. It looks as though this did not make it into BTS 2009.

Friday, July 09, 2010 3:12:01 PM (AUS Eastern Standard Time, UTC+10:00)  #    Comments [0]   BizTalk General  |  Trackback
# Friday, July 02, 2010

Just lost a couple of hours I will never get back…

I am in the process of upgrading one of our RFID applications to 2010 beta. The upgrade went through without a hitch and I found myself the proud owner (or at least caretaker) of a brand-spanking new BizTalk RFID Server 2010.

rfid manager 2010

(No visible differences to BizTalk RFID Server 2009 – except for the icon)

I then proceeded to upgrade my Visual Studio 2008 projects to VS 2010. WOW!!! Build succeeded first time…I am on a roll baby!

So I imported my Process into RFID Manager and fired her up…

rfid 2010 error starting process

The WCF service was not responding. So I open up IIS Manager (my VM is Windows Server 2008 so I’m running IIS 7) and try to browse to the hosting.svc under my RFID process VD and I get a http 401.3 error. Where do I start to hunt this down. I have installed .NET Framework 4.0, VS 2010, BizTalk RFID Server 2010 (plus Silverlight 4, Windows Azure platform AppFabric 1.0, … ).

So I check:

  • Application pool identities are correct – Tick
  • Application pool framework version – v4.0 Classic mode
  • Authentication set to Anonymous – Tick
  • Anonymous user set to application pool identity - Tick
  • NTFS permissions on the VD - Tick
  • Test Pass-through authentication – Green Lights
  • IISRESET (last desperate attempts of a beaten Sco)

Still no go. I get on the “bat-phone” to Ninja Mick and explain what's happening. He just laughs…”What! Another 401 error. That’s about the sixth this week”. He points me off to a KB article about disabling the loopback check (I am using host headers on my RFID Services site). Still no go but it hinted at the problem…host headers.

Back into RFID Manager and look at the Advanced Server Properties

rfid manager advanced props

Even though these settings are technically correct and BizTalk RFID Server creates the provider and process services without error, I needed to set the Host IP address to the host header name I am using. This appears to have changed between either BizTalk RFID Server 2009 > 2010 or a change to IIS, Windows Update, .NET 4.0 … your guess is as good as mine?

After setting this to the host header name all worked fine and I’m back to sending my tag read events into BizTalk RFID Server 2010

Friday, July 02, 2010 11:21:47 PM (AUS Eastern Standard Time, UTC+10:00)  #    Comments [0]   BizTalk RFID  |  Trackback

During the CTP(s) one way we could provide client credentials to authenticate with the Service Bus was to use username and password. We were writing code that looked something like this:

// create the credentials object for the endpoint
TransportClientEndpointBehavior userNamePasswordServiceBusCredential = new TransportClientEndpointBehavior();
userNamePasswordServiceBusCredential.CredentialType = TransportClientCredentialType.UserNamePassword;
userNamePasswordServiceBusCredential.Credentials.UserName.UserName = this.solution;
userNamePasswordServiceBusCredential.Credentials.UserName.Password = this.password;

The UserNamePassword credential type is no longer supported in the v1.0 release. The types of supported client credential types are now:

  • Saml: this option specifies that the client credential is provided in the Security Assertion Markup Language (SAML) format, over the Secure Sockets Layer protocol. This option requires that you write your own SSL credential server.
  • SharedSecret: This option specifies that the client credential is provided as a self-issued shared secret that is registered with AppFabric Access Control through the AppFabric portal. This option requires no additional settings on the Credentials property.
  • SimpleWebToken: This option specifies that the client credential is provided as a self-issued shared secret that is registered with AppFabric Access Control through the AppFabric portal, and presented in the emerging industry-standard format called simple Web token (SWT). Similar to the shared secret option, this option requires no additional settings on the Credentials property.
  • Unauthenticated: This option specifies that there is no client credential provided. This option avoids acquiring and sending a token. It is used by clients that are not required to authenticate, based on the policy of their service binding. Note that this setting might leave data nonsecure if not used together with another security measure.

[See Choosing Authentication for an AppFabric Service Bus Application for more details]

So where we used to use UserNamePassword, the corresponding credential type is now SharedSecret (although you should check out the other authentication options as well). Here is what your code now looks like:

// create the credentials object for the endpoint
TransportClientEndpointBehavior sharedSecretServiceBusCredential = new TransportClientEndpointBehavior();
sharedSecretServiceBusCredential.CredentialType = TransportClientCredentialType.SharedSecret;
sharedSecretServiceBusCredential.Credentials.SharedSecret.IssuerName = this.issuer;
sharedSecretServiceBusCredential.Credentials.SharedSecret.IssuerSecret = this.secret;

Default issuer name and secret are generated for you when you activate your AppFabric account. Create additional ones using the Windows Azure platform AppFabric Access Control Management Tool (Acm.exe) that is installed as part of the SDK.

Friday, July 02, 2010 2:39:57 PM (AUS Eastern Standard Time, UTC+10:00)  #    Comments [0]    |  Trackback
# Thursday, July 01, 2010

I am getting a few reports that after a recent windows update (or installing .NET Framework 4.0) the ESSO service fails to restart. Microsoft have released a hotfix to address this (http://support.microsoft.com/kb/2252691)

Microsoft Reports:

This issue occurs after installing .NET Framework 4.0. The registration of the assembly used by ENTSSO to access SQL Server does not specify the correct version of the .NET Framework. When .NET Framework 4.0 is installed, the assembly will try to use the newer framework and then fail to load

To resolve this manually:

32-bit Server

1.       Open a command window
2.       Go to C:\Windows\Microsoft.NET\Framework\v2.0.50727
3.       Type: regasm “C:\Program Files\Common Files\Enterprise Single Sign-On\ssosql.dll”

64-bit Server

1.       Open a command window
2.       Go to C:\Windows\Microsoft.NET\Framework64\v2.0.50727
3.       Type each of the following and hit ENTER:

32bit:  regasm “C:\Program Files\Common Files\Enterprise Single Sign-On\win32\ssosql.dll”
64bit:  regasm “C:\Program Files\Common Files\Enterprise Single Sign-On\ssosql.dll”

Note
On a 64-bit server, regasm will need to be run for both the 32-bit and 64-bit versions of ssosql.dll.

Hope this helps smile_wink

Thursday, July 01, 2010 3:05:59 PM (AUS Eastern Standard Time, UTC+10:00)  #    Comments [0]   .NET Framework | BizTalk General  |  Trackback
# Tuesday, June 29, 2010

Setting up a new VM and hit this hurdle. Lucky for me Microsoft just released 1.2 of the SDK that supports VS 2010 and .NET 4.0 thumbs_up

Windows Azure SDK 1.2

Also, if you are using Windows Azure AppFabric and .NET 4.0 make sure the relay bindings are installed into the .NET 4.0 machine.config (not done by installer). Check out Wade’s post on how to go about it easily.

Keep those heads in the cloud

Tuesday, June 29, 2010 10:33:41 PM (AUS Eastern Standard Time, UTC+10:00)  #    Comments [0]   Windows Azure  |  Trackback

In my last post, I was exploring a different approach to implementing a simple windows service. In that solution, I used Windows Workflow to implement a folder monitor service. Part of the solution required me to gather target folder details from configuration. I needed more than the out-of-the-box appSettings collection of key-value pairs. Normally I would use a second xml configuration file and basically deserialise that into my custom collection class. However, I was in exploration mode so I decided to give the System.Configuration classes another go.

I wanted the client to easily manage the target folders to monitor and customise the alert that gets logged in BAM (see the last post). I wanted something like this:

<folderMonitors>
<folderMonitor targetFolder="E:\Data\BizTalk\Monitor1" fileMask="*.xml" timerInterval="1" processingInterval="2">
<alert alertInterval="5" sender="BIZTALK" destination="LOB1" messageType="Order" errorType="Folder Monitor" errorCode="OFFLINE"/>
</folderMonitor>
<folderMonitor targetFolder="E:\Data\BizTalk\Monitor2" fileMask="*.xml" timerInterval="1" processingInterval="2">
<alert alertInterval="5" sender="BIZTALK" destination="LOB2" messageType="Invoice" errorType="Folder Monitor" errorCode="OFFLINE"/>
</folderMonitor>
</folderMonitors>

I was surprised by the amount of code I needed to write. Basically you have to:

  1. Define a ConfigurationSection
  2. Define the ConfigurationElementCollection and implement the indexers and ARC methods
  3. Define the actual ConfigurationElement (the folder monitor structure you see above)
  4. Declare the configuration section in the app.config

Here is the source code   

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Configuration;
 
namespace Breeze.BizTalk.WorkflowLibrary
{
    public class FolderMonitorSection : ConfigurationSection
    {
        #region Static Accessors
        /// <summary>
        /// Gets the configuration section using the default element name.
        /// </summary>
        public static FolderMonitorSection GetSection()
        {
            return GetSection("folderMonitorConfig");
        }
 
        /// <summary>
        /// Gets the configuration section using the specified element name.
        /// </summary>
        public static FolderMonitorSection GetSection(string sectionName)
        {
            FolderMonitorSection section = ConfigurationManager.GetSection(sectionName) as FolderMonitorSection;
            if (section == null)
            {
                string message = string.Format("The specified configuration section (<{0}>) was not found.", sectionName);
                throw new ConfigurationErrorsException(message);
            }
            return section;
        }
        #endregion
 
        #region Configuration Properties
 
        [ConfigurationProperty("folderMonitors", IsDefaultCollection = true)]
        public FolderMonitorConfigElementCollection FolderMonitors
        {
            get { return (FolderMonitorConfigElementCollection)this["folderMonitors"]; }
            set { this["folderMonitors"] = value; }
        }
 
        public override bool IsReadOnly()
        {
            return false;
        }
        #endregion
    }
 
    [ConfigurationCollection(typeof(FolderMonitorConfigElement), CollectionType = ConfigurationElementCollectionType.BasicMap)]
    public class FolderMonitorConfigElementCollection : ConfigurationElementCollection
    {
        protected override ConfigurationElement CreateNewElement()
        {
            return new FolderMonitorConfigElement();
        }
 
        protected override string ElementName
        {
            get { return "folderMonitor"; }
        }
 
        public override ConfigurationElementCollectionType CollectionType
        {
            get { return ConfigurationElementCollectionType.BasicMap; }
        }
 
        public override bool IsReadOnly()
        {
            return false;
        }
 
        #region Indexers
 
        public FolderMonitorConfigElement this[int index]
        {
            get { return BaseGet(index) as FolderMonitorConfigElement; }
            set
            {
                if (BaseGet(index) != null)
                {
                    BaseRemoveAt(index);
                }
                BaseAdd(index, value);
            }
        }
 
        public new FolderMonitorConfigElement this[string name]
        {
            get { return BaseGet(name) as FolderMonitorConfigElement; }
        }
 
        #endregion
 
        #region Lookup Methods
 
        protected override object GetElementKey(ConfigurationElement element)
        {
            FolderMonitorConfigElement cfg = element as FolderMonitorConfigElement;
            return cfg.TargetFolder;
        }
 
        public string GetKey(int index)
        {
            return (string)BaseGetKey(index);
        }
 
        #endregion
 
        #region Add/Remove/Clear Methods
 
        public void Add(FolderMonitorConfigElement item)
        {
            BaseAdd(item);
        }
 
        public void Remove(string name)
        {
            BaseRemove(name);
        }
 
        public void Remove(FolderMonitorConfigElement item)
        {
            BaseRemove(GetElementKey(item));
        }
 
        public void RemoveAt(int index)
        {
            BaseRemoveAt(index);
        }
 
        public void Clear()
        {
            BaseClear();
        }
 
        #endregion
    }
 
    public class FolderMonitorConfigElement : ConfigurationElement
    {
        #region Constructors
 
        public FolderMonitorConfigElement()
        {
        }
 
        #endregion
 
        #region Configuration Properties
 
        [ConfigurationProperty("targetFolder", IsRequired = true)]
        public string TargetFolder
        {
            get { return (string)this["targetFolder"]; }
            set { this["targetFolder"] = value; }
        }
 
        [ConfigurationProperty("fileMask", IsRequired = true, DefaultValue = "*.*")]
        public string FileMask
        {
            get { return (string)this["fileMask"]; }
            set { this["fileMask"] = value; }
        }
 
        [ConfigurationProperty("timerInterval", IsRequired = true, DefaultValue = "3")]
        [IntegerValidator(ExcludeRange = false, MaxValue = 1440, MinValue = 1)]
        public int TimerInterval
        {
            get { return (int)this["timerInterval"]; }
            set { this["timerInterval"] = value; }
        }
 
        [ConfigurationProperty("processingInterval", IsRequired = true, DefaultValue = "5")]
        [IntegerValidator(ExcludeRange = false, MaxValue = 1440, MinValue = 1)]
        public int ProcessingInterval
        {
            get { return (int)this["processingInterval"]; }
            set { this["processingInterval"] = value; }
        }
 
        [ConfigurationProperty("alert")]
        public AlertConfigElement AlertConfig
        {
            get { return (AlertConfigElement)this["alert"]; }
            set { this["alert"] = value; }
        }
 
        public class AlertConfigElement : ConfigurationElement
        {
            public AlertConfigElement()
            {
            }
 
            [ConfigurationProperty("alertInterval", IsRequired = true, DefaultValue = "20")]
            [IntegerValidator(ExcludeRange = false, MaxValue = 1440, MinValue = 1)]
            public int AlertInterval
            {
                get { return (int)this["alertInterval"]; }
                set { this["alertInterval"] = value; }
            }
 
            [ConfigurationProperty("sender")]
            public string Sender
            {
                get { return (string)this["sender"]; }
                set { this["sender"] = value; }
            }
 
            [ConfigurationProperty("destination")]
            public string Destination
            {
                get { return (string)this["destination"]; }
                set { this["destination"] = value; }
            }
 
            [ConfigurationProperty("messageType")]
            public string MessageType
            {
                get { return (string)this["messageType"]; }
                set { this["messageType"] = value; }
            }
 
            [ConfigurationProperty("errorType")]
            public string ErrorType
            {
                get { return (string)this["errorType"]; }
                set { this["errorType"] = value; }
            }
 
            [ConfigurationProperty("errorCode")]
            public string ErrorCode
            {
                get { return (string)this["errorCode"]; }
                set { this["errorCode"] = value; }
            }
        }
 
        #endregion
 
    }
 
}

And my app.config file looks like this

<?xml version="1.0" encoding="utf-8" ?>
<
configuration>

  <
configSections>
    <!--
Custom config section declaration for Folder Monitor-->
    <
section
    
name="folderMonitorConfig"
      type="Breeze.BizTalk.WorkflowLibrary.FolderMonitorSection, Breeze.BizTalk.WorkflowLibrary, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
    />
  </
configSections>

  <!--
Custom config section for Folder Monitor-->
  <
folderMonitorConfig>
    <
folderMonitors>
      <
folderMonitor targetFolder="E:\Data\BizTalk\Monitor1" fileMask="*.xml" timerInterval="1" processingInterval="2">
        <
alertalertInterval="5" sender="BIZTALK" destination="LOB1" messageType="Order" errorType="Folder Monitor" errorCode="OFFLINE"/>
      </
folderMonitor>
      <
folderMonitor targetFolder="E:\Data\BizTalk\Monitor2" fileMask="*.xml" timerInterval="1" processingInterval="2">
        <
alertalertInterval="5" sender="BIZTALK" destination="LOB2" messageType="Invoice" errorType="Folder Monitor" errorCode="OFFLINE"/>
      </
folderMonitor>
    </
folderMonitors>
  </
folderMonitorConfig>
 
 
</
configuration>

Tip: If you do end up GAC’ing the assembly your configuration classes are under, don’t forget to update the type in the <configSections> to use the new PublicKeyToken value. smile_wink

Once all that was setup (Birthdays and Christmas sorted out along the way) I was happy with the end result. In my WF I simply used the static accessor to get my configuration collection and bound that to the Repeater Activity in my workflow. Because of the amount of work involved, I think it would of been much better to stick with my usual approach. That is, using a second custom configuration file and deserialise it into my custom collection class. The real test will be what I choose next time I am asked to implement this type of thing…custom config file or custom config section?

Tuesday, June 29, 2010 10:13:26 PM (AUS Eastern Standard Time, UTC+10:00)  #    Comments [0]   .NET Framework | Windows Workflow  |  Trackback
# Monday, June 28, 2010

Being in the integration business we are often are asked to interface with older, legacy systems via the file system. In BizTalk Server this is stock standard stuff. One feature that clients really love is the real-time monitoring and notification capabilities that BAM provides (often a compelling reason to adopt a product like BizTalk Server over the traditional roll-your-own interface approach). During a recent project the client loved the monitoring feature so much they asked if we could use this to monitor other legacy file based interfaces outside of BizTalk. That is, although BizTalk would not be involved in the processing or delivery of these files, could they use BAM to monitor and notify them when the legacy interface application goes down and stops processing them. Essentially they wanted a service that monitors folders and alerts them when files have not been processed (in-activity).

Outside of BizTalk, one tool I love using is Windows Workflow. WF is not new, but I don’t see much adoption out there in the real-world. Why is that? When I speak with other developers they often reply:

  • When should I use WF?
  • What benefits does WF give me over what I’m doing now?
  • What does WF project look like?

To explore these questions I decided to implement the folder monitor service as a workflow hosted by a Windows service. Along the way I found some interesting features and techniques that I thought I might share here (some WF related some not). If you are like me, something like 80% of the code I am writing is the same. Reading from configuration, exception handling, calling services and database operations. Most of the time the business logic is simple and procedural in nature. Do this > If that > Do this > For each of these > perform this …. These applications seem to be well suited to the WF Sequential Workflow approach. A nice feature is of course the visual aspect. Its just too easy to validate your logic when its mapped out for you (or perhaps your client). I can also imagine revisiting this code 6 months down the track and not have to spend time recalling what the developer was trying to do (regardless of how well the code is commented).

To show you what I mean, here is the logic for the folder monitor.

wf folder monitor - workflow overview

Its easy to visualise the what I’m trying to do here. An interesting feature is the Monitor_Each_Folder activity. In WF you implement for-each loops using the Repeater Activity. Essentially you bind the activity to a collection class (like List) and it will iterate over each item in the list and execute the sequential activity(s) grouped under it using the current item as its data context. The collection I am using is a List of custom configuration elements with details of the target folder to monitor (more on that in another post). Expanding the sequential activity (the one that gets executed by the Repeater activity) we can see clearly the logic implemented by the folder monitor. I simply added code to the three activities I defined and left all the repetitive flow operations (If Then Else) to the designer.

wf folder monitor - monitor folder process

Although not shown here, exception handling was also provided by the WF designer and all that was left for me to do was the specific exception messages and flow control (terminate or continue). So compared with the traditional approach I have actually reduced the amount of code I needed to write, just concentrating on the specific implementation bits. This also pays dividends when it comes to testing and debugging your application as instrumentation (tracking) comes out of the box. When debugging, the designer is re-hosted and stepping through the code highlights the activities executed and workflow path taken (very nice). We also have a nice integration story with BizTalk using the BAM WF interceptor as well.

With my workflow done and tested I moved on to hosting. I see this as another very nice feature of WF. My application is agnostic to the environment it is running under. I can choose to host this in a console app (for testing this is what I did) or in a Win Forms app, ASP.NET web app, Windows Service, WCF service, even BizTalk Server (in fact any .NET 3.5 application). To get this running under a Windows Service host took all of about 5 mins using the VS Windows Service project template. So when developing WF applications we manage the workflows separately from the host. Workflows in one project and the host in another referencing it. We may even decide to share out the WF across many hosts.

So while not terribly exciting in functionality, I found that choosing to implement the folder monitor service in WF actually took less code to write, made the code more readable, and provided all of the instrumentation I needed to quickly test and debug my code. I also now have more flexibility to reuse my code in other environments and applications. And we have only scratched the surface of Windows Workflow…

Monday, June 28, 2010 10:07:51 PM (AUS Eastern Standard Time, UTC+10:00)  #    Comments [0]   Windows Workflow  |  Trackback
# Thursday, October 22, 2009

Breeze are seeking an experienced, self motivated and high achiever, BizTalk/.Net Developer or Consultant that has a passion for Microsoft technology and keen to learn from their peers. You will be challenged yet guided by elite BizTalk Architects & MVPs with strong reputations in the Microsoft community. The successful candidate must have at least 1-2 years development experience with .Net, BizTalk Server and SharePoint Server.

This is a unique opportunity to elevate your profile within the Microsoft community, IT industry and work with the best in their field. The role will be challenging with priorities often changing quickly and high expectations of delivery standards with new technologies.

For more info contact info@breeze.net quoting ‘BizTalk Developer’ role.

Thursday, October 22, 2009 9:13:01 AM (AUS Eastern Standard Time, UTC+10:00)  #    Comments [0]   Breeze  |  Trackback
# Monday, May 11, 2009

This week the Breeze RFID Team will be show casing an innovative RFID Document Management solution at CeBit. We have teamed up with Magellan Technologies to develop a hardcopy (paper) document workflow solution built on the Microsoft SharePoint platform. The solution allows printed documents to participate in document management workflow in the same way as traditional electronic documents.

CeBit Demo

Our partners, Magellan, have some very cool RFID gear on show including their RFID Document Tray Reader and their RFID Bookshelf Reader that we use in the demo.

Magellan RFID Document Tray

So if you're at the show, pop in and say hi to the team.

Monday, May 11, 2009 9:09:11 AM (AUS Eastern Standard Time, UTC+10:00)  #    Comments [1]   BizTalk RFID  |  Trackback
Copyright © 2010 Breeze Training. All rights reserved.