I attempt to update the task status progress (Status Assignment) using project server 2013 CSOM, but I got "Unknown Error" Exception and the following StackTrace:
at
Microsoft.SharePoint.Client.ClientRequest.ProcessResponseStream(Stream
responseStream) at
Microsoft.SharePoint.Client.ClientRequest.ProcessResponse() at
Limitless.Components.Project2013.ProjectComponent.UpdateTask(ProjectServerConnection
connection, Guid ProjectUID, List`1 data)
ProjectContext context = GetProjectContext(connection);
// Get the user name and their assignments
EnterpriseResource self = EnterpriseResource.GetSelf(context);
context.Load(self, r => r.Name, r => r.Assignments
.IncludeWithDefaultProperties(a => a.Project, a=>a.Comments));
context.ExecuteQuery();
foreach(var item in self.Assignments)
{
Entities.Task task = data.Where(t => t.ID == item.Id).SingleOrDefault();
if(task!= null)
{
item.PercentComplete = (short)task.PercentComplete;
item.Comments = "comment";
}
}
// Update the assignments and submit the status updates.
self.Assignments.Update();
self.Assignments.SubmitAllStatusUpdates("By PS Web App");
context.ExecuteQuery();
I couldn't find any error in the sharepoint logs. its a fresh installation Project Server 2013, and it retrieves Tasks (StatusAssignments) successfully, but after self.Assignments.Update(); and self.Assignments.SubmitAllStatusUpdates("By PS Web App"); the exception is thrown on context.ExecuteQuery();.
any one came cross such scenario ?
I hope it's not that late for anyone who face the same issue, this was solved and working properly but I'm not sure if it is the right way.
instead of updating the TaskAssignment by the current user context, I get it using elevated context (admin context for example), and get resource's assignments, and update them.
//private function to get admin context
ProjectContext elevatedContext = GetProjectElevatedContext(connection);
//private function to get current user context
ProjectContext context = GetProjectContext(connection);
// Get the user name and their assignments
EnterpriseResource self = EnterpriseResource.GetSelf(context);
context.Load(self, r => r.Id, r => r.Assignments.IncludeWithDefaultProperties(a => a.ActualFinish));
context.ExecuteQuery();
var resource = elevatedContext.EnterpriseResources.GetById(self.Id.ToString());
elevatedContext.Load(resource, r => r.Assignments
.IncludeWithDefaultProperties(a => a.Project, a => a.Comments, a => a.PercentComplete));
elevatedContext.ExecuteQuery();
int count = 0;
foreach (var item in resource.Assignments)
{
Entities.Task task = data.Where(t => t.ID == item.Id).SingleOrDefault();
if (task != null)
{
item.PercentComplete = (short)task.PercentComplete;
item.Comments = "comment";
count++;
}
}
resource.Assignments.Update();
resource.Assignments.SubmitAllStatusUpdates($"comment");
elevatedContext.ExecuteQuery();
I couldn't find a permission that is granted for such functionality from CSOM.
If anyone has a better tested solution please contribute :)
Related
My application connects to azure event hub to receive messages and process them. I see that every time I restart my application, all the messages within the retention period gets replayed. I read about offset to avoid this issue and I have a method that sets up connection to an azure event hub as:
MessageConsumer connect() {
// set up JNDI context
BatchEventHubConfig batchEventHubConfig = //MAP CONTAINING CONFIG
String queueName = "EventHub"
String connectionFactoryName = "SBCF"
//Long offset = batchAccountManager.batchStorageManager.batchJobMsgCheckpointService.get(batchEventHubConfig.namespace, batchEventHubConfig.getMessageQueueAddress(partitionInx, true))?.offset
Hashtable<String, String> hashtable = new Hashtable<>()
hashtable.put("connectionfactory.${connectionFactoryName}", batchEventHubConfig.getAMQPConnectionURI())
hashtable.put("queue.${queueName}", batchEventHubConfig.getMessageQueueAddress(partitionInx))
//hashtable.put("apache.org:selector-filter:string", "amqp.annotation.x-opt-offset > '${offset}'")
hashtable.put(Context.INITIAL_CONTEXT_FACTORY, "org.apache.qpid.amqp_1_0.jms.jndi.PropertiesFileInitialContextFactory")
Context context = new InitialContext(hashtable)
ConnectionFactory factory = (ConnectionFactory) context.lookup(connectionFactoryName)
queue = (Destination) context.lookup(queueName)
connection = factory.createConnection(batchEventHubConfig.sasPolicyName, batchEventHubConfig.sasPolicyKey)
connection.setExceptionListener(new BatchExceptionListener(eventHubConnection: this))
connection.start()
session = connection.createSession(false, Session.CLIENT_ACKNOWLEDGE)
messageConsumer = session.createConsumer(queue)
messageConsumer.setMessageListener(messageListener)
messageConsumer
}
The commented out code for offset was what I was trying after reading up here : https://azure.github.io/amqpnetlite/articles/azure_eventhubs.html
What is the correct way to set the offset so the messages don't get re-played when the application restart?
Apche QPID does not support AMQP filters ( the underlying Apache Proton J does ) ..
I patched the AmqpConsumerBuilder.configureSource() metho by adding the following lines in the end :
Symbol filterKey = Symbol.valueOf("apache.org:selector-filter:string");
UnknownDescribedType filterValue = new UnknownDescribedType(filterKey, String.format("%s > '%s'",amqp.annotation.x-opt-offset", lastOffset));
filters.put(filterKey, filterValue);
and it works !
So either you make a fork of Apache QPID and apply this patch ou put the modified class in your class path to override the original ( very bad solution )
It was much easier than I had thought! Found this link : https://timjansen.github.io/jarfiller/guide/jms/selectors.xhtml
So all I had to do was add the filter condition like this:
messageConsumer = session.createConsumer(queue, "amqp.annotation.x-opt-offset >= '${messageOffset}'")
Inspired by the answer of Mourad Zouabi, I created a fork of the qpid-jms repository and changed the AmqpConsumerBuilder class as follows to allow passing the filter to a MessageConsumer:
if (resourceInfo.getSelector() != null && !resourceInfo.getSelector().trim().equals("")) {
if (resourceInfo.getSelector().startsWith("x-opt-offset") || resourceInfo.getSelector().startsWith("x-opt-enqueued-time")) {
// support Azure Event HUB filters
// see: https://azure.github.io/amqpnetlite/articles/azure_eventhubs.html
Symbol filterKey = Symbol.valueOf("apache.org:selector-filter:string");
UnknownDescribedType filterValue = new UnknownDescribedType(filterKey,"amqp.annotation." + resourceInfo.getSelector().trim());
filters.put(filterKey, filterValue);
} else {
filters.put(JMS_SELECTOR_SYMBOL, new AmqpJmsSelectorType(resourceInfo.getSelector()));
}
}
if (!filters.isEmpty()) {
source.setFilter(filters);
}
Now a filter can be passed while creating a MessageConsumer:
MessageConsumer consumer = session.createConsumer(queue, String.format("x-opt-offset > %s", offset));
or
MessageConsumer consumer = session.createConsumer(queue, String.format("x-opt-enqueued-time > %s", timeStamp));
See https://github.com/ekkelenkamp/qpid-jms for a working fork.
I'm having a couple of issues when trying to navigate using MVVMLight.
On iOS, I have created my NavigationService in AppDelegate/FinishedLoading with the following code
var nav = new NavigationService();
nav.Configure(ViewModelLocator.MainPageKey, "MainPage");
nav.Configure(ViewModelLocator.MapPageKey, "MapPage");
// iOS uses the UINavigtionController to move between pages, so will we
nav.Initialize(Window.RootViewController as UINavigationController);
// finally register the service with SimpleIoc
SimpleIoc.Default.Register<INavigationService>(() => nav);
When I use NavigateTo to move between the two pages, I continually get the same error asking if I have called NavigationService.Initialize. Obviously I have. The first ViewController shows without a problem.
On Android, I'm having issues again with the second Activity, but with a different error.
I am passing a List< double> as the object parameter in NavigateTo and then in the Activity, use the following to retrieve the passed object
void GetDataFromViewModel()
{
var NavService = (NavigationService)SimpleIoc.Default.GetInstance<INavigationService>();
var data = NavService.GetAndRemoveParameter<object>(Intent) as List<double>;
if (data != null)
{
ViewModel.Latitude = data[0];
ViewModel.Longitude = data[1];
}
}
Problem is that straight after the base.OnCreate in my OnCreate method, the app crashes out with the error
UNHANDLED EXCEPTION:
[MonoDroid] System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> Microsoft.Practices.ServiceLocation.ActivationException: Type not found in cache: System.Object.
My NavigateTo line looks like this
INavigationService navigationService;
...code
navigationService.NavigateTo(ViewModelLocator.MapPageKey, new List<double> { Latitude, Longitude });
This has got me stumped!
My code (below) using a foreach loop for multiple urls and save screen shots of each url). This code works perfectly in my dev environment ( Visual studio 2010, Windows 7 64 bit).I can take screen shots of url and save it in a folder. But the same code I deployed in a server (Windows server 2003, IIS- 6.0), it is not saving screen shots for some url. I captured the logs and it says System.Threading.ThreadAbortException: Thread was being aborted.System.Threading.Thread.JoinInternal().
Is it works with IIS 6.0 or any other settings has to do with the deployment server? It works perfectly in my local environment. Any help appreciated.
public void Capture(string url, string path)
{
_path = path;
var thread = new Thread(() =>
{
using (var browser = new WebBrowser())
{
browser.ScrollBarsEnabled = false;
browser.AllowNavigation = true;
browser.Navigate(url);
browser.Width = 1600;
browser.Height = 1600;
browser.ScriptErrorsSuppressed = true;
browser.DocumentCompleted += DocumentCompleted;
while (browser.ReadyState != WebBrowserReadyState.Complete)
{
Application.DoEvents();
}
}
});
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
thread.Join();
}
I've created a console application that I run as a continuous web job. It does not use the web job SDK, but it checks for the file specified in the WEBJOBS_SHUTDOWN_FILE environment variable.
I have turned the 'Always On' option on, and I'm running the job in the shared plan as a singleton using the settings.job file.
The web job keeps getting stopped by the WEBJOBS_SHUTDOWN_FILE and is restarted again after that.
I've been looking in the kudu sources, but I can't find why my web job is restarted.
Does anybody have an idea why this happens?
This is the code that initializes the FileSystemWatcher:
private void SetupExitWatcher()
{
var file = Environment.GetEnvironmentVariable("WEBJOBS_SHUTDOWN_FILE");
var dir = Path.GetDirectoryName(file);
var fileSystemWatcher = new FileSystemWatcher(dir);
FileSystemEventHandler changed = (o, e) =>
{
if (e.FullPath.Equals(Path.GetFileName(file), StringComparison.OrdinalIgnoreCase) >= 0)
{
this.Exit();
}
};
fileSystemWatcher.Created += changed;
fileSystemWatcher.NotifyFilter = NotifyFilters.CreationTime | NotifyFilters.FileName | NotifyFilters.LastWrite;
fileSystemWatcher.IncludeSubdirectories = false;
fileSystemWatcher.EnableRaisingEvents = true;
}
I have a legacy event-based object that seems like a perfect fit for RX: after being connected to a network source, it raises events when a message is received, and may terminate with either a single error (connection dies, etc.) or (rarely) an indication that there will be no more messages. This object also has a couple projections -- most users are interested in only a subset of the messages received, so there are alternate events raised only when well-known message subtypes show up.
So, in the process of learning more about reactive programming, I built the following wrapper:
class LegacyReactiveWrapper : IConnectableObservable<TopLevelMessage>
{
private LegacyType _Legacy;
private IConnectableObservable<TopLevelMessage> _Impl;
public LegacyReactiveWrapper(LegacyType t)
{
_Legacy = t;
var observable = Observable.Create<TopLevelMessage>((observer) =>
{
LegacyTopLevelMessageHandler tlmHandler = (sender, tlm) => observer.OnNext(tlm);
LegacyErrorHandler errHandler = (sender, err) => observer.OnError(new ApplicationException(err.Message));
LegacyCompleteHandler doneHandler = (sender) => observer.OnCompleted();
_Legacy.TopLevelMessage += tlmHandler;
_Legacy.Error += errHandler;
_Legacy.Complete += doneHandler;
return Disposable.Create(() =>
{
_Legacy.TopLevelMessage -= tlmHandler;
_Legacy.Error -= errHandler;
_Legacy.Complete -= doneHandler;
});
});
_Impl = observable.Publish();
}
public IDisposable Subscribe(IObserver<TopLevelMessage> observer)
{
return _Impl.RefCount().Subscribe(observer);
}
public IDisposable Connect()
{
_Legacy.ConnectToMessageSource();
return Disposable.Create(() => _Legacy.DisconnectFromMessageSource());
}
public IObservable<SubMessageA> MessageA
{
get
{
// This is the moral equivalent of the projection behavior
// that already exists in the legacy type. We don't hook
// the LegacyType.MessageA event directly.
return _Impl.RefCount()
.Where((tlm) => tlm.MessageType == MessageType.MessageA)
.Select((tlm) => tlm.SubMessageA);
}
}
public IObservable<SubMessageB> MessageB
{
get
{
return _Impl.RefCount()
.Where((tlm) => tlm.MessageType == MessageType.MessageB)
.Select((tlm) => tlm.SubMessageB);
}
}
}
Something about how it's used elsewhere feels... off... somehow, though. Here's sample usage, which works but feels strange. The UI context for my test application is WinForms, but it doesn't really matter.
// in Program.Main...
MainForm frm = new MainForm();
// Updates the UI based on a stream of SubMessageA's
IObserver<SubMessageA> uiManager = new MainFormUiManager(frm);
LegacyType lt = new LegacyType();
// ... setup lt...
var w = new LegacyReactiveWrapper(lt);
var uiUpdateSubscription = (from msgA in w.MessageA
where SomeCondition(msgA)
select msgA).ObserveOn(frm).Subscribe(uiManager);
var nonUiSubscription = (from msgB in w.MessageB
where msgB.SubType == MessageBType.SomeSubType
select msgB).Subscribe(
m => Console.WriteLine("Got MsgB: {0}", m),
ex => Console.WriteLine("MsgB error: {0}", ex.Message),
() => Console.WriteLine("MsgB complete")
);
IDisposable unsubscribeAtExit = null;
frm.Load += (sender, e) =>
{
var connectionSubscription = w.Connect();
unsubscribeAtExit = new CompositeDisposable(
uiUpdateSubscription,
nonUiSubscription,
connectionSubscription);
};
frm.FormClosing += (sender, e) =>
{
if(unsubscribeAtExit != null) { unsubscribeAtExit.Dispose(); }
};
Application.Run(frm);
This WORKS -- The form launches, the UI updates, and when I close it the subscriptions get cleaned up and the process exits (which it won't do if the LegacyType's network connection is still connected). Strictly speaking, it's enough to dispose just connectionSubscription. However, the explicit Connect feels weird to me. Since RefCount is supposed to do that for you, I tried modifying the wrapper such that rather than using _Impl.RefCount in MessageA and MessageB and explicitly connecting later, I used this.RefCount instead and moved the calls to Subscribe to the Load handler. That had a different problem -- the second subscription triggered another call to LegacyReactiveWrapper.Connect. I thought the idea behind Publish/RefCount was "first-in triggers connection, last-out disposes connection."
I guess I have three questions:
Do I fundamentally misunderstand Publish/RefCount?
Is there some preferred way to implement IConnectableObservable<T> that doesn't involve delegation to one obtained via IObservable<T>.Publish? I know you're not supposed to implement IObservable<T> yourself, but I don't understand how to inject connection logic into the IConnectableObservable<T> that Observable.Create().Publish() gives you. Is Connect supposed to be idempotent?
Would someone more familiar with RX/reactive programming look at the sample for how the wrapper is used and say "that's ugly and broken" or is this not as weird as it seems?
I'm not sure that you need to expose Connect directly as you have. I would simplify as follows, using Publish().RefCount() as an encapsulated implementation detail; it would cause the legacy connection to be made only as required. Here the first subscriber in causes connection, and the last one out causes disconnection. Also note this correctly shares a single RefCount across all subscribers, whereas your implementation uses a RefCount per message type, which isn't probably what was intended. Users are not required to Connect explicitly:
public class LegacyReactiveWrapper
{
private IObservable<TopLevelMessage> _legacyRx;
public LegacyReactiveWrapper(LegacyType legacy)
{
_legacyRx = WrapLegacy(legacy).Publish().RefCount();
}
private static IObservable<TopLevelMessage> WrapLegacy(LegacyType legacy)
{
return Observable.Create<TopLevelMessage>(observer =>
{
LegacyTopLevelMessageHandler tlmHandler = (sender, tlm) => observer.OnNext(tlm);
LegacyErrorHandler errHandler = (sender, err) => observer.OnError(new ApplicationException(err.Message));
LegacyCompleteHandler doneHandler = sender => observer.OnCompleted();
legacy.TopLevelMessage += tlmHandler;
legacy.Error += errHandler;
legacy.Complete += doneHandler;
legacy.ConnectToMessageSource();
return Disposable.Create(() =>
{
legacy.DisconnectFromMessageSource();
legacy.TopLevelMessage -= tlmHandler;
legacy.Error -= errHandler;
legacy.Complete -= doneHandler;
});
});
}
public IObservable<TopLevelMessage> TopLevelMessage
{
get
{
return _legacyRx;
}
}
public IObservable<SubMessageA> MessageA
{
get
{
return _legacyRx.Where(tlm => tlm.MessageType == MessageType.MessageA)
.Select(tlm => tlm.SubMessageA);
}
}
public IObservable<SubMessageB> MessageB
{
get
{
return _legacyRx.Where(tlm => tlm.MessageType == MessageType.MessageB)
.Select(tlm => tlm.SubMessageB);
}
}
}
An additional observation is that Publish().RefCount() will drop the underlying subscription when it's subscriber count reaches 0. Typically I only use Connect over this choice when I need to maintain a subscription even when the subscriber count on the published source drops to zero (and may go back up again later). It's rare to need to do this though - only when connecting is more expensive than holding on to the subscription resource when you might not need to.
Your understanding is not entirely wrong, but you do appear to have some points of misunderstanding.
You seem to be under the belief that multiple calls to RefCount on the same source IObservable will result in a shared reference count. They do not; each instance keeps its own count. As such, you are causing multiple subscriptions to _Impl, one per call to subscribe or call to the Message properties.
You also may be expecting that making _Impl an IConnectableObservable somehow causes your Connect method to be called (since you seem surprised you needed to call Connect in your consuming code). All Publish does is cause subscribers to the published object (returned from the .Publish() call) to share a single subscription to the underlying source observable (in this case, the object made from your call to Observable.Create).
Typically, I see Publish and RefCount used immediately together (eg as source.Publish().RefCount()) to get the shared subscription effect described above or to make a cold observable hot without needing to call Connect to start the subscription to the original source. However, this relies on using the same object returned from the .Publish().RefCount() for all subscribers (as noted above).
Your implementation of Connect seems reasonable. I don't know of any recommendations for if Connect should be idempotent, but I would not personally expect it to be. If you wanted it to be, you would just need to track calls to it the disposal of its return value to get the right balance.
I don't think you need to use Publish the way you are, unless there is some reason to avoid multiple event handlers being attached to the legacy object. If you do need to avoid that, I would recommend changing _Impl to a plain IObservable and follow the Publish with a RefCount.
Your MessageA and MessageB properties have potential to be a source of confusion for users, since they return an IObservable, but still require a call to Connect on the base object to start receiving messages. I would either change them to IConnectableObservables that somehow delegate to the original Connect (at which point the idempotency discussion becomes more relevant) or drop the properties and just let the users make the (fairly simple) projections themselves when needed.