I am using the Subclipse API and I would like to implement the ISVNNotifyListener so that I can find out about the subclipse events as they happen during runtime. I believe I need to add (subscribe) my instance of the notify listener to the set of listeners that the Client Adapter will notify, but I am at a loss for how to get access to the Client Adapter that is being used by Subclipse at runtime. Is there a way to access it so that I can add my listener to the set?
Sorry, but unfortunately Subclipse has not been coded in such a way to provide access to the internals. Subclipse constructs a new ISVNClientAdapter object for each API call it needs to make into Subversion and it adds its ISVNNotifyListener to this object on the fly as needed. So there is no way for you to interject your own listener.
Perhaps you could write a class that implements IConsoleListener and have it act as a proxy for the Subclipse class. You could then call SVNProviderPlugin.getConsoleListener to get the current console listener and store a reference to it in your class. Then call SVNProviderPlugin.setConsoleListener to replace the class held in Subclipse with your class. As the events are fired in your class, you could just forward them on to the Subclipse class and do whatever you want with the events in your code. Something like this:
import java.io.File;
import org.tigris.subversion.subclipse.core.client.IConsoleListener;
import org.tigris.subversion.svnclientadapter.SVNNodeKind;
public class ProxyListener implements IConsoleListener {
private IConsoleListener subclipseListener;
public ProxyListener(IConsoleListener subclipseListener) {
super();
this.subclipseListener = subclipseListener;
}
public void setCommand(int command) {
subclipseListener.setCommand(command);
// TODO add your code
}
public void logCommandLine(String commandLine) {
subclipseListener.logCommandLine(commandLine);
// TODO add your code
}
public void logMessage(String message) {
subclipseListener.logMessage(message);
// TODO add your code
}
public void logError(String message) {
subclipseListener.logError(message);
// TODO add your code
}
public void logRevision(long revision, String path) {
subclipseListener.logRevision(revision , path);
// TODO add your code
}
public void logCompleted(String message) {
subclipseListener.logCompleted(message);
// TODO add your code
}
public void onNotify(File path, SVNNodeKind kind) {
subclipseListener.onNotify(path, kind);
// TODO add your code
}
}
Related
I will try to show my problem with a sample code easier to understand.
I have used WebApplicationFactory to develop my acceptance tests. Let's say that I have the typical minimal Program.cs with the following line to register one of my modules:
builder.Services.RegisterModule<StartupRegistrationModule>(builder.Configuration, builder.Environment);
And this module is declared like this:
internal sealed class StartupRegistrationModule : IServiceRegistrationModule
{
public static Dictionary<string, string> _dictionary = new();
public void Register(IServiceCollection services, IConfiguration configuration, IHostEnvironment hostEnvironment)
{
// Lot of modules being registered
_dictionary.Add("key", "value");
}
}
One of my tests file is like this:
public sealed class MyTests : AcceptanceTestBase
{
[Fact]
public void Test1()
{
// arrange
// act
// assert
}
[Fact]
public void Test2()
{
// arrange
// act
// assert
}
[Fact]
public void Test3()
{
// arrange
// act
// assert
}
}
And AcceptanceTestBase is:
public abstract class AcceptanceTestBase : IDisposable
{
protected HttpClient _httpClient;
protected WebApplicationFactory<Program> _webApplicationFactory;
public AcceptanceTestBase()
{
_webApplicationFactory = new WebApplicationFactory<Program>()
.WithWebHostBuilder(builder =>
{
// ... Configure test services
});
_httpClient = _webApplicationFactory.CreateClient();
}
public void Dispose()
{
_httpClient.Dispose();
_webApplicationFactory.Dispose();
}
}
If I try to execute all these tests my tests will fail in the second test run because the WebApplicationFactory is trying to build again the Application but it already has the key in the dictionary and it will fail. See the image for more understanding on the problem.
So my question is, how can I build the application in different scopes to do not share this dictionary state?
Thanks :)
Update:
The real static dictionary is saved behind this nuget package that keeps the track of all my circuit breaker policies state. I do not actually need even the HttpClients for my tests but did not find a way to remove them and not load this. I tried removing all the HttpClients to see if it also removes their dependencies, but it does not seem to make the trick.
It is because you are using:
internal sealed class StartupRegistrationModule : IServiceRegistrationModule
{
/// .. static here
public static Dictionary<string, string> _dictionary = new();
public void Register(IServiceCollection services, IConfiguration configuration, IHostEnvironment hostEnvironment)
{
// Lot of modules being registered
_dictionary.Add("key", "value");
}
}
The static Dictionary is shared over all your tests because they run in the same process.
Each test starts a new (Test-)WebHost but the dictionary remains untouched.
My proposal is to not use statics anywhere in DI context to prevent such hidden traps.
I don't know the purpose of your Dictionary here but maybe you can extract this to a singleton registration which you can replace in your (Test.)WebHost on each new test / startup?
In my integration test I'm using BlockHound to capture any blocking call.
For setting up the data I am doing a blocking call because I want the data to be persisted in the DB when running each test.
When running the integration test Blockhound is throwing an error at the set up method: reactor.blockhound.BlockingOperationError: Blocking call! java.io.FileInputStream#readBytes
How to avoid this?
#BeforeAll
public static void blockHoundSetup() {
BlockHound.install();
}
#BeforeEach
public void setUp() {
stagingAreaAdapter.deleteAll()
.thenMany(Flux.fromIterable(data))
.flatMap(stagingAreaAdapter::save)
.blockLast();
}
Check BlockHound customizations for allowing and disallowing blocking calls inside methods:
https://github.com/reactor/BlockHound/blob/master/docs/customization.md#dis-allowing-blocking-calls-inside-methods
1. using builder in a #BeforeAll method (as per #KrisKris1):
#BeforeAll
public static void blockHoundSetup() {
BlockHound.builder().allowBlockingCallsInside(
TestClass.class.getName(), "setUp").install();
}
or
2. via implementing the BlockHoundIntegration interface (still applies globally):
public class BlockHoundCustomConfiguration implements BlockHoundIntegration {
#Override
public void applyTo(BlockHound.Builder builder) {
builder.allowBlockingCallsInside("java.base/java.io.RandomAccessFile", "readBytes");
}
}
and create the following file:
<project dir>/src/test/resources/META-INF/services/reactor.blockhound.integration.BlockHoundIntegration
with your custom class:
com.example.config.BlockHoundCustomConfiguration
You need to allow blocking method calls inside java.util.zip.InflaterInputStream#read down the callstack.
Add in your BlockHound customization config.
public class ReactorBlockHoundIntegration implements BlockHoundIntegration {
#Override
public void applyTo(BlockHound.Builder builder) {
builder.allowBlockingCallsInside(InflaterInputStream.class.getName(), "read");
}
}
https://docs.oracle.com/javase/8/docs/api/index.html?java/util/zip/package-summary.html
#Before(value="#annotation(com.aspect.Loggable)",argNames="taskId")
public void logEmail(JoinPoint joinPoint) {
System.out.println("#Before is running!");
System.out.println("hijacked : " + joinPoint.getSignature().getName());
System.out.println("******");
}
i have a pointCut on method sendEmail() with custom annotation.
This method sendEmail() is called from differnt location in our application .
Like we call sendEmail from paymentApproved () method of paymentManager when payment is approved.
We call sendEmail from taskComplete() method of taskManger when task is completed.
i have to find out the event for which sendEmail is triggered.
I applied custom annotation #EVENT("PAYMENT") on paymentApproved () of paymentManager and #EVENT("TASK") on taskComplete() method of taskManger.
How can i get the value of #EVENT in logEmail(JoinPoint joinPoint) aspect.
Scaffolding:
Sorry, I do not like all-caps class names and I also used my own package names as an example because my template already generates them.
package de.scrum_master.app;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
#Retention(RetentionPolicy.RUNTIME)
public #interface Loggable {}
package de.scrum_master.app;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
#Retention(RetentionPolicy.RUNTIME)
public #interface Event {
String value();
}
Driver application:
This is pure Java because I am not a Spring user. Just imagine it is one or more #Components.
Please also note that in one case sendEmail() is called from a method not annotated by #Event. This should not trigger the aspect, only the two calls from the annotated methods.
package de.scrum_master.app;
public class Application {
public static void main(String[] args) {
Application application = new Application();
application.doSomething();
application.paymentApproved();
application.taskComplete();
}
public void doSomething() {
sendEmail();
}
#Event("paymentApproved")
public void paymentApproved() {
sendEmail();
}
#Event("taskComplete")
public void taskComplete() {
sendEmail();
}
#Loggable
public void sendEmail() {}
}
Aspect:
Your pointcut wants to express: Catch methods annotated with #Loggable within the control flow of methods annotated by #Event. Control flow can be expressed by cflow() or cflowbelow() pointcuts.
package de.scrum_master.aspect;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import de.scrum_master.app.Event;
#Aspect
public class LogAspect {
#Before(
"#annotation(de.scrum_master.app.Loggable) &&" +
"execution(* *(..)) &&" + // only needed for AspectJ, not for Spring AOP
"cflow(#annotation(event))"
)
public void logEmail(JoinPoint thisJoinPoint, Event event) {
System.out.println(thisJoinPoint + " -> " + event);
}
}
Console log:
execution(void de.scrum_master.app.Application.sendEmail()) -> #de.scrum_master.app.Event(value=paymentApproved)
execution(void de.scrum_master.app.Application.sendEmail()) -> #de.scrum_master.app.Event(value=taskComplete)
Update: If you were using full AspectJ (e.g. via load-time weaving) instead of Spring AOP, you just could have used a call() pointcut and from there get the enclosing joinpoint's static information. Then the #Event annotation would not have been necessary. But Spring AOP is just "AOP lite" and does not support call().
You can access to the annotation receiving it as a parameter. Something like this:
#Before(value="#annotation(EVENT)",argNames="taskId")
public void logEmail(JoinPoint joinPoint, Event event) {
// do what you need with event. For example, if the field is called value you can do this:
if ("PAYMENT".equals(event.value())) {
// do sth
}
System.out.println("#Before is running!");
System.out.println("hijacked : " + joinPoint.getSignature().getName());
System.out.println("******");
}
I'm migrating code from NSBv4 to NSBv5 (5.2.12 to be exact) and I have a custom profile implementation:
public class MyProfileHandler : IHandleProfile<PerformanceCounters>
{
public MyProfileHandler()
{
}
public void ProfileActivated(BusConfiguration config)
{
// I need to do something based on endpoint configuration, e.g. endpoint name
// this used to work in NSBv4:
// var endpointName = Configure.EndpointName;
}
}
How can I access endpoint configuration here?
I'm hosting this app using NServiceBus.Host (v6.0.0 if it matters) and this is where the IHandleProfile<T> interface comes from.
BusConfiguration is a configuration builder and it seems it's not possible to read anything useful from it. I tried to inject an instance of Configure to the constructor of my profile handler, but then it crashes - NSB needs the handler to have a parameterless constructor.
Implementing IWantTheEndpointConfig is not an option as well, as it is deprecated in v5 and it causes a compilation error. Its obsolete error message states:
IHandleProfile is now passed an instance of Configure
(which would be perfect for my case), but this is not true as far as I can tell (there is no Configure passed to ProfileActivated() and I can't see how I can inject it).
Is my only option to reimplement the profile handler using a completely different approach, or am I missing something?
NServiceBus.Core has an issue how it sets the endpoint name (and unfortunately also the endpoint version) on the BusConfiguration. The set endpoint name is added to the settings dictionary too late. You can work around that issue by doing the following:
public class EndpointConfig : IConfigureThisEndpoint
{
public void Customize(BusConfiguration configuration)
{
var customConfig = new EndpointConfiguration
{
EndpointName = "YourEndpointName",
};
configuration.EndpointName(customConfig.EndpointName);
configuration.GetSettings().Set<EndpointConfiguration>(customConfig);
}
}
public class EndpointConfiguration
{
public string EndpointName { get; set; }
}
BusConfiguration is essentially a dictionary on steroids. If you want to get access to what has been set in the BusConfiguration in the profile handler you can do the following (i.ex. get the endpoint name):
public class MyProfileHandler : IHandleProfile<PerformanceCounters>
{
public void ProfileActivated(BusConfiguration config)
{
var customConfig = config.GetSettings().Get<EndpointConfiguration>();
var endpointName = customConfig.EndpointName;
}
}
In the normal NServiceBus Host the interface offers only the one parameter, BusConfiguration. On Azure the interface offers two methods, where one actually has the Configure object.
Adding #With(Secure.class) to a controller blocks all unauthenticated access. Is there a way to enabled it only for certain actions, or to except certain actions after it's enabled on a controller?
You can't do that with the secure module. As Niels said the secure module is more an example than a solution. You can build your own security system with the #Before annotation. Here is an example:
public class Admin extends Controller {
#Before(unless={"login", "authenticate", "logout", "otherMethod"})
void checkAccess() {
// check the cookie
}
public void login() {
render();
}
public void authenticate(String email, String password) {
// check the params and set a value in the cookie
}
public void logout() {
// delete cookie
}
I recommend you to read the source code of the secure module.
I've since found my earlier #Public solution somewhat limiting since it can't address inherited actions. I've instead gone to a class-level annotation:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.TYPE)
public #interface AllowGuest {
String[] value();
}
and added this code to the beginning of the Secure.checkAccess() method:
AllowGuest guest = getControllerInheritedAnnotation(AllowGuest.class);
if (guest != null) {
for (String action : guest.value()) {
if (action.equals(request.actionMethod))
return;
}
}
which can be used like this: #AllowGuest({"list","view"})
This makes it easy to allow access to local and inherited actions, and to see which actions in a controller are unsecured.
Remove #With(Secure.class) annotation to the controller and add this piece of code inside the controller.
#Before(unless={"show"})
static void checkAccess() throws Throwable {
Secure.checkAccess();
}
where show is the action you need to make publicly available.
In order to get what I was looking for, I copied the Check annotation and created a Public annotation.
package controllers;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.METHOD})
public #interface Public {
}
then I added these two lines to the beginning of the Secure.checkAccess:
if (getActionAnnotation(Public.class) != null)
return;
Now actions in controllers using With(Secure.class) can be made accessible without logging in by adding a #Public annotation to them.
You can set at the #Before-Tag of the Secure Controller the value unless or only. The Secure-Module is more an example than a solution.