I am trying to do a simple OSGi service using Declarative Services on a local Glassfish server. The plugin which provides is always active.
I got trouble with the injection into my servlet that consumes my service, the reference is null when the servlet gets called because it isn't the same object that the one which got injected with the service reference.
I tested it by putting a breakpoint into my reference setter, and I saw my service getting injected, but when I click on the button that calls my servlet into my application the service reference is null because it isn't the same object (i.e. gets injected in servlet_Instance #1 but invoke the method on servlet_Instance #2. I must be missing a little detail, because I can find and use my service when doing
final BundleContext bundleContext = FrameworkUtil.getBundle(getClass()).getBundleContext();
loggingTestServiceInterface = (LoggingTestServiceInterface) bundleContext.getService(bundleContext
.getServiceReference(LoggingTestServiceInterface.class.getName()));
The plugin used to generate my XMLs files : maven-scr-plugin
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-scr-plugin</artifactId>
<version>1.14.0</version>
<executions>
<execution>
<id>generate-scr-scrdescriptor</id>
<goals>
<goal>scr</goal>
</goals>
</execution>
</executions>
<configuration>
<supportedProjectTypes>
<supportedProjectType>war</supportedProjectType>
<supportedProjectType>jar</supportedProjectType>
<supportedProjectType>bundle</supportedProjectType>
</supportedProjectTypes>
</configuration>
</plugin>
This is my service class
#Component(immediate = false, name = "Shikashi", service = {LoggingTestServiceInterface.class}, enabled = true)
public class LoggingTestService implements LoggingTestServiceInterface
{
private final LoggerUtils loggerUtils = new LoggerUtils();
public LoggingTestService()
{
}
#Activate
public void start(final BundleContext bundleContext)
{
System.out.println("StartTest Service Fune");
}
#Deactivate
public void stop()
{
System.out.println("Stop Test Service Jitensha");
}
#Modified
public void modify()
{
System.out.println("Stop Test Service onnanogo");
}
private Logger createLogger(final Class<?> clazz)
{
return Logger.getLogger(clazz);
}
#Override
public void logDebug(final Class<?> clazz, final String message)
{
logDebug(clazz, message, null);
}
#Override
public void logDebug(final Class<?> clazz, final String message, final Throwable throwable)
{
final Logger logger = createLogger(clazz);
logger.debug(message, throwable);
}
}
the generated XML is
<?xml version="1.0" encoding="UTF-8"?>
<components xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0">
<scr:component enabled="true" immediate="false" name="Shikashi" activate="start" deactivate="stop" modified="modify">
<implementation class="com.sti.logging.service.LoggingTestService"/>
<service servicefactory="false">
<provide interface="com.sti.loggingservices.serviceinterface.LoggingTestServiceInterface"/>
</service>
</scr:component>
My servlet is
#WebServlet(name = "Wakarimashita", urlPatterns = { "/Wakarimashita"})
#Component
public class Wakarimashita extends HttpServlet
{
private LoggingTestServiceInterface loggingTestServiceInterface;
#Override
protected void doGet(final HttpServletRequest httpServletRequest, final HttpServletResponse httpServletResponse) throws ServletException, IOException
{
// Method just to setup the Servlet to understand how it works
final String language = "language";
final String path = "/sigbud/language/";
if (httpServletRequest.getParameter(language) != null)
{
if (httpServletRequest.getParameter(language).equalsIgnoreCase("Nihongo"))
{
httpServletResponse.sendRedirect(path + "nihongo.jsp");
}
else if (httpServletRequest.getParameter(language).equalsIgnoreCase("Eigo"))
{
httpServletResponse.sendRedirect(path + "eigo.jsp");
}
else if (httpServletRequest.getParameter(language).equalsIgnoreCase("Funansugo"))
{
httpServletResponse.sendRedirect(path + "funansugo.jsp");
}
else
{
httpServletResponse.sendRedirect(path + "unknown.jsp");
}
}
else
{
super.doGet(httpServletRequest, httpServletResponse);
}
loggingTestServiceInterface.logError(getClass(), "Wakarimasen");
}
#Reference(service = LoggingTestServiceInterface.class, cardinality = ReferenceCardinality.MANDATORY, policy = ReferencePolicy.DYNAMIC)
public void bindLoggingTestServiceInterface(final LoggingTestServiceInterface loggingTestServiceInterface)
{
this.loggingTestServiceInterface = loggingTestServiceInterface;
}
public void unbindLoggingTestServiceInterface(final LoggingTestServiceInterface loggingTestServiceInterface)
{
if (this.loggingTestServiceInterface.equals(loggingTestServiceInterface))
{
this.loggingTestServiceInterface = null;
}
}
#Activate
public void start(final BundleContext bundleContext)
{
System.out.println("StartTest Service Taisho");
}
#Deactivate
public void stop()
{
System.out.println("Stop Test Service Fukutaisho");
}
#Modified
public void modify()
{
System.out.println("Stop Test Service san jyû kyû");
}
}
the Generated XML
<?xml version="1.0" encoding="UTF-8"?>
<components xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0">
<scr:component name="com.sti.sigbud.servlet.Wakarimashita" activate="start" deactivate="stop" modified="modify">
<implementation class="com.sti.sigbud.servlet.Wakarimashita"/>
<reference name="LoggingTestServiceInterface" interface="com.sti.loggingservices.serviceinterface.LoggingTestServiceInterface" cardinality="1..1" policy="dynamic" bind="bindLoggingTestServiceInterface" unbind="unbindLoggingTestServiceInterface"/>
</scr:component>
Also I tried, but no luck because my servlet doesn't seem to be found (Error 404 - The requested resource () is not available.), to do as Peter Kriens wrote there : How to consume OSGi service from OSGi HTTP Service
So I modified my servlet like so :
#Component(service = Servlet.class, property = {"alias=/Wakarimashita"})
public class Wakarimashita extends HttpServlet
The generated XML is
<?xml version="1.0" encoding="UTF-8"?>
<components xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0">
<scr:component name="com.sti.sigbud.servlet.Wakarimashita" activate="start" deactivate="stop" modified="modify">
<implementation class="com.sti.sigbud.servlet.Wakarimashita"/>
<service servicefactory="false">
<provide interface="javax.servlet.Servlet"/>
</service>
<property name="alias" value="/Wakarimashita"/>
<reference name="LoggingTestServiceInterface" interface="com.sti.loggingservices.serviceinterface.LoggingTestServiceInterface" cardinality="1..1" policy="dynamic" bind="bindLoggingTestServiceInterface" unbind="unbindLoggingTestServiceInterface"/>
</scr:component>
I access the servlet from my JSP
<form action="Wakarimashita" method="GET">
<input type="text" name="language" size="50"/>
<input type="submit" value="Submit" />
</form>
To test the above I have in my deployed bundles org.apache.felix.http.api-2.2.1, org.apache.felix.http.whiteboard-2.2.1 just as in the post. Didn't find if there is a switch to put on.
Also I checked with org.apache.felix.webconsole-4.2.0-all the bundles, and the service is there up and running, it says that my consumer bundle is using it.
You have two parties creating instances of your servlet. Once is DS and the other is the web container. You cannot have 2 masters. The web container basically has to be in charge since it will only send requests to the instance of your servlet that it creates.
If there was an implementation that supported both web container and DS, then you would be set. But I have never heard of such a thing.
I don't know if Glassfish supports the OSGi Web Application Specification (Ch 128). If it does, then you can interact with the OSGi service layer as described in 128.6.
Related
We are developing a web application based on
.NET 4.5.1
MVC 5.2.2
OWIN
WebApi 2.2
SignalR 2.2.0
Castle.Windsor 3.3.0
Wcf Integration Facility 3.3.0
For resolving the controllers we use ControllerFactory class which was described in the page below:
http://docs.castleproject.org/Windsor.Windsor-tutorial-part-two-plugging-Windsor-in.ashx
For resolving the dependencies we use WindsorDependencyResolver class:
public class WindsorDependencyResolver : IDependencyResolver
{
public IWindsorContainer Container { get; private set; }
public WindsorDependencyResolver(IWindsorContainer windsorContainer)
{
Container = windsorContainer;
}
public IDependencyScope BeginScope()
{
return new WindsorDependencyScope(this.Container);
}
public object GetService(Type serviceType)
{
return this.Container.Kernel.HasComponent(serviceType) ? this.Container.Resolve(serviceType) : null;
}
public IEnumerable<object> GetServices(Type serviceType)
{
return this.Container.ResolveAll(serviceType).Cast<object>().ToArray();
}
public void Dispose()
{
}
}
public class WindsorDependencyScope : IDependencyScope
{
public IWindsorContainer Container { get; set; }
public IDisposable Scope { get; set; }
public WindsorDependencyScope(IWindsorContainer container)
{
this.Container = container;
this.Scope = container.BeginScope();
}
public object GetService(Type serviceType)
{
return this.Container.Kernel.HasComponent(serviceType) ? this.Container.Resolve(serviceType) : null;
}
public IEnumerable<object> GetServices(Type serviceType)
{
return this.Container.ResolveAll(serviceType).Cast<object>().ToArray();
}
public void Dispose()
{
this.Scope.Dispose();
}
}
Keep in mind we don't resolve the IHub classes of SignalR with Windsor container, they are instantiated by OWIN system in pipeline. The Startup.cs code is shown below:
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
app.MapSignalR();
}
}
All of the controllers, wcf service clients and interceptors -excepting logging classes- are registered with LifestylePerWebRequest in the project. However the classes we use for logging are singleton.
There is a setting in the Web.config below:
<system.webServer>
<modules runAllManagedModulesForAllRequests="true">
...
<add name="PerRequestLifestyle" type="Castle.MicroKernel.Lifestyle.PerWebRequestLifestyleModule, Castle.Windsor" />
...
</modules>
</system.webServer>
So when we try to resolve a wcf client -which has per web request lifestyle- in the SignalR hub we get the exception below:
System.ObjectDisposedException: Cannot access a disposed object.
Object name: 'Scope cache was already disposed. This is most likely a bug in the calling code.'.
at Castle.MicroKernel.Lifestyle.Scoped.ScopeCache.get_Item(Object id)
at Castle.MicroKernel.Lifestyle.Scoped.DefaultLifetimeScope.GetCachedInstance(ComponentModel model, ScopedInstanceActivationCallback createInstance)
at Castle.MicroKernel.Lifestyle.ScopedLifestyleManager.Resolve(CreationContext context, IReleasePolicy releasePolicy)
at Castle.MicroKernel.Handlers.DefaultHandler.ResolveCore(CreationContext context, Boolean requiresDecommission, Boolean instanceRequired, Burden& burden)
at Castle.MicroKernel.Handlers.DefaultHandler.Resolve(CreationContext context, Boolean instanceRequired)
at Castle.MicroKernel.Handlers.AbstractHandler.Resolve(CreationContext context)
at Castle.MicroKernel.DefaultKernel.ResolveComponent(IHandler handler, Type service, IDictionary additionalArguments, IReleasePolicy policy)
at Castle.MicroKernel.DefaultKernel.Castle.MicroKernel.IKernelInternal.Resolve(Type service, IDictionary arguments, IReleasePolicy policy)
at Castle.MicroKernel.DefaultKernel.Resolve(Type service, IDictionary arguments)
at Castle.Windsor.WindsorContainer.Resolve[T]()
at UIServer.WebUI.Hubs.MailThreadHub.Broadcast(MailMessageListDto mailMessage) in c:\Development\DDD\UIServer.WebUI\Hubs\MailThreadHub.cs:line 92
I can see HttpContext in debugger window before the call Container.Resolve() method. I can resolve the singleton logging classes by the way.
The interesting point is my teammate doesn't get any exception. The main difference is our OS versions. I run the code in windows 8.1 and my teammate runs it in windows 7.
We get this exception for only signalr hubs. We don't get any exception in any other place. How can we solve this problem?
I use ServiceLocator for resolve IDependencyResolver in the Startup class. It is look like:
internal class Startup
{
public void Configuration(IAppBuilder appBuilder)
{
var config = new HttpConfiguration();
config.Routes.MapHttpRoute("ActionApi", "{controller}/{action}/{id}", new {id = RouteParameter.Optional});
config.DependencyResolver = ServiceLocator.Instance.Resolve<IDependencyResolver>();
appBuilder.UseWebApi(config);
}
}
Maybe it is help.
In the classic web.xml type configuration you could configure context parameters like so
web.xml
...
<context-param>
<param-name>p-name</param-name>
<param-value>-value</param-value>
</context-param>
...
How is this achieved in spring-boot. I have a filter that requires parameters.
I'm using #EnableAutoConfiguration and have included <artifactId>spring-boot-starter-jetty</artifactId> in my pom.
You can set parameters using the server.servlet.context-parameters application property. For example:
server.servlet.context-parameters.p-name=p-value
In Spring Boot 1.x, which is no longer supported, this property was named server.context-parameters:
servlet.context-parameters=p-name=p-value
Alternatively, you can configure parameters programmatically by declaring a ServletContextInitializer bean:
#Bean
public ServletContextInitializer initializer() {
return new ServletContextInitializer() {
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
servletContext.setInitParameter("p-name", "-value");
}
};
}
You can actually achieve this using Java config. If you have filter that requires some parameters, just put them in your application.yml (or .properties), inject them using #Value in your config class and register them in FilterRegistrationBean.
For example:
#Value("${myFilterParam}")
private String myFilterParam;
#Bean(name="myFilter")
public FilterRegistrationBean myFilter() {
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(new MyFilter());
filterRegistrationBean.setInitParameters(Collections.singletonMap("p-name", "p-value"));
return filterRegistrationBean;
}
Also JavaDoc for FilterRegistrationBean:
http://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/context/embedded/FilterRegistrationBean.html
Update
You can register parameters for servlet context in SpringBootServletInitializer#onStartup() method. Your Application class can extend the SpringBootServletInitializer and you can override the onStartup method and set the parameters there. Example:
#Configuration
#EnableAutoConfiguration
#ComponentScan
public class Application extends SpringBootServletInitializer {
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(Application.class);
}
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
servletContext.setInitParameter("p-name", "p-value");
super.onStartup(servletContext);
}
public static void main(String[] args) {
SpringApplication.run(Application.class);
}
}
Other alternative is to define ServletContextInitializer bean as suggested by Andy Wilkinson.
Since Spring Boot 2.0.0 they updated the way to add context param:
server.servlet.context-parameters.yourProperty.
You can see more updates on this link
Also you can define InitParameterConfiguringServletContextInitializer in your configuration. Example:
#Bean
public InitParameterConfiguringServletContextInitializer initParamsInitializer() {
Map<String, String> contextParams = new HashMap<>();
contextParams.put("p-name", "-value");
return new InitParameterConfiguringServletContextInitializer(contextParams);
}
What is the recommended way to add Spring Security to a web application that is using Spring's new WebApplicationInitializer interface instead of the web.xml file? I'm looking for the equivalent of:
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
UPDATE
The provided answers are reasonable but they both assume that I've got a servletContext instance. I've looked through the hierarchy of WebApplicationInitializers and I don't see any access to the servlet context unless I choose to override one of Spring's initializer methods. AbstractDispatcherServletInitializer.registerServletFilter seems like the sensible choice but it doesn't default to URL pattern mapping and I'd hate to change filter registration for everything if there is a better way.
This is the way that I have done it:
container.addFilter("springSecurityFilterChain", new DelegatingFilterProxy("springSecurityFilterChain"))
.addMappingForUrlPatterns(null, false, "/*");
container is an instance of ServletContext
The Spring Security Reference answers this question and the solution depends on whether or not you are using Spring Security in conjunction with Spring or Spring MVC.
Using Spring Security without Spring or Spring MVC
If you are not using Spring Security with Spring or Spring MVC (i.e. you do not have an existing WebApplicationInitializer) then you need to provide the following additional class:
import org.springframework.security.web.context.*;
public class SecurityWebApplicationInitializer
extends AbstractSecurityWebApplicationInitializer {
public SecurityWebApplicationInitializer() {
super(SecurityConfig.class);
}
}
Where SecurityConfig is your Spring Security Java configuration class.
Using Spring Security with Spring or Spring MVC
If you are using Spring Security with Spring or Spring MVC (i.e. you have an existing WebApplicationInitializer) then firstly you need to provide the following additional class:
import org.springframework.security.web.context.*;
public class SecurityWebApplicationInitializer
extends AbstractSecurityWebApplicationInitializer {
}
Then you need to ensure that your Spring Security Java configuration class, SecurityConfig in this example, is declared in your existing Spring or Spring MVC WebApplicationInitializer. For example:
import org.springframework.web.servlet.support.*;
public class MvcWebApplicationInitializer
extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected Class<?>[] getRootConfigClasses() {
return new Class[] {SecurityConfig.class};
}
// ... other overrides ...
}
Dynamic securityFilter = servletContext.addFilter(AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME, DelegatingFilterProxy.class);
securityFilter.addMappingForUrlPatterns(EnumSet.allOf(DispatcherType.class), false, "/*");
EnumSet.allOf(DispatcherType.class) to be sure that you add a mapping not only for default DispatcherType.REQUEST, but for DispatcherType.FORWARD, etc...
After a bit of work, I've discovered that it's actually quite simple:
public class Initialiser extends AbstractAnnotationConfigDispatcherServletInitializer implements WebApplicationInitializer {
#Override
protected Class< ? >[] getRootConfigClasses() {
return new Class[] { RootConfig.class };
}
#Override
protected Class< ? >[] getServletConfigClasses() {
return new Class[] { WebAppConfig.class };
}
#Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
#Override
protected Filter[] getServletFilters() {
return new Filter[] { new DelegatingFilterProxy("springSecurityFilterChain") };
}
}
The most important thing, though, is that you must have a root context (e.g. RootConfig in this case), and that must contain a reference to all the spring security information.
Thus, my RootConfig class:
#ImportResource("classpath:spring/securityContext.xml")
#ComponentScan({ "com.example.authentication", "com.example.config" })
#Configuration
public class RootConfig {
#Bean
public DatabaseService databaseService() {
return new DefaultDatabaseService();
}
#Bean
public ExceptionMappingAuthenticationFailureHandler authExceptionMapping() {
final ExceptionMappingAuthenticationFailureHandler emafh = new ExceptionMappingAuthenticationFailureHandler();
emafh.setDefaultFailureUrl("/loginFailed");
final Map<String, String> mappings = new HashMap<>();
mappings.put(CredentialsExpiredException.class.getCanonicalName(), "/change_password");
emafh.setExceptionMappings(mappings);
return emafh;
}
}
And spring/securityContext.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:security="http://www.springframework.org/schema/security"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:noNamespaceSchemaLocation="http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd">
<security:http security="none" pattern="/favicon.ico"/>
<!-- Secured pages -->
<security:http use-expressions="true">
<security:intercept-url pattern="/login" access="permitAll" />
<security:intercept-url pattern="/**" access="isAuthenticated()" />
<security:form-login default-target-url="/index" login-processing-url="/login_form" login-page="/login" authentication-failure-handler-ref="authExceptionMapping" />
</security:http>
<security:authentication-manager>
<security:authentication-provider ref="customAuthProvider" />
</security:authentication-manager>
</beans>
I could not get it to work if I merged the RootConfig and WebAppConfig classes into just WebAppConfig and had the following:
#Override
protected Class< ? >[] getRootConfigClasses() {
return null;
}
#Override
protected Class< ? >[] getServletConfigClasses() {
return new Class[] { WebAppConfig.class };
}
public class SIServerSecurityWebApplicationInitializer extends AbstractSecurityWebApplicationInitializer {
#Override
protected void beforeSpringSecurityFilterChain(ServletContext servletContext) {
Dynamic registration = servletContext.addFilter("TenantServletFilter", TenantServletFilter.class);
EnumSet<DispatcherType> dispatcherTypes = getSecurityDispatcherTypes();
registration.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), false, "/*");
}
}
This scenario is for executing a filter before executing other filters.
If you want to execute a filter after other filers pass true in registration.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), true, "/*");. Also check the DispatcherType ASYNC, FORWARD etc.
Faced with the same problem. Merge RootConfig and WebAppConfig - not best way - because this i did not try this solution. Tried all other solutions - everty time got "org.apache.catalina.core.StandardContext.startInternal Error filterStart". After some work, got something like this:
FilterRegistration.Dynamic enc= servletContext.addFilter("encodingFilter",
new CharacterEncodingFilter());
encodingFilter .setInitParameter("encoding", "UTF-8");
encodingFilter .setInitParameter("forceEncoding", "true");
encodingFilter .addMappingForUrlPatterns(null, true, "/*");
But is not working with DelegatingFilterProxy(). Continue finding for best common solution for all filters.
UPDATE:
I did it.
So, the main issue is: if you want add filters using java config, especially if you want to add security filter, such as DelegatingFilterProxy, then you have to create WebAppSecurityConfig:
#Configuration
#EnableWebSecurity
#ImportResource("classpath:security.xml")
public class WebAppSecurityConfig extends WebSecurityConfigurerAdapter {
}
In this case i create WebAppSecurityConfig and make import resource ("security.xml").
This let me to do that in Initializer class:
servletContext.addFilter("securityFilter", new DelegatingFilterProxy("springSecurityFilterChain"))
.addMappingForUrlPatterns(null, false, "/*");
This is related to those interested in Spring Boot with security: You don't need anything, Spring Boot picks up the #components and solves the other issues
I've defined this method in my Spring MVC Controller :
#RequestMapping(value = "{id}/content", method=RequestMethod.POST)
#PreAuthorize("principal.user.userAccount instanceof T(com.anonym.model.identity.PedagoAccount) AND principal.user.userAccount.userId == #object.pedago.userId AND #form.id == #object.id")
public String modifyContent(#PathVariable("id") Project object, #Valid #ModelAttribute("form") ProjectContentForm form) {
....
}
Then in my JUnit test I'd like to call this method and ensure that the PreAuthorize condition is verified. But when I set the user principal in my JUnit test with a bad account there is no error and the method completes. It seems the annotation is bypassed.
But when I call this method in a normal way (not testing), the PreAuthorize is verified.
If it's possible, how to test this annotation in a junit test and how to catch the exception if it throws one ?
Thanks,
Nicolas
Since you want to test features implemented via Spring AOP, you need to use Spring TestContext framework to run tests against application context.
Then you create a base test with minimal security configuration:
abstract-security-test.xml:
<security:authentication-manager alias="authenticationManager">
<security:authentication-provider user-service-ref = "userService" />
</security:authentication-manager>
<security:global-method-security pre-post-annotations="enabled" />
<bean id = "userService" class = "..." />
AbstractSecurityTest.java:
#ContextConfiguration("abstract-security-test.xml")
abstract public class AbstractSecurityTest {
#Autowired
private AuthenticationManager am;
#After
public void clear() {
SecurityContextHolder.clearContext();
}
protected void login(String name, String password) {
Authentication auth = new UsernamePasswordAuthenticationToken(name, password);
SecurityContextHolder.getContext().setAuthentication(am.authenticate(auth));
}
}
Now you can use it in your tests:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(...)
public class CreatePostControllerSecurityTest extends AbstractSecurityTest {
...
#Test
#ExpectedException(AuthenticationCredentialsNotFoundException.class)
public void testNoAuth() {
controller.modifyContent(...);
}
#Test
#ExpectedException(AccessDeniedException.class)
public void testAccessDenied() {
login("userWithoutAccessRight", "...");
controller.modifyContent(...);
}
#Test
public void testAuthOK() {
login("userWithAccessRight", "...");
controller.modifyContent(...);
}
}
i have a .NET 3.5 solution with an asp.net(web site) project with fluentnhibernate and it's test project(class library project).i've referenced the asp.net project in the test project and with all fluentnhibernate/nhibenate dlls.
What i fail to comprehend is that, on a run of a webform (hit from browser) let's say Test.aspx, building of schema is successfull and i could see the tables in my database.
here is the method i call on Test.aspx.cs
public partial class Test : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
ISession session = SessionManager.Instance.OpenSession();
SessionManager.BuildSchema(session);
}
}
i happen to use the same method in CanGenerateSchema method of my test class and it always fails
[TestFixture]
public class CanGenerateSchemaTestSuite
{
[Test]
public void CanGenarateSchema()
{
ISession session = SessionManager.Instance.OpenSession();
SessionManager.BuildSchema(session);
}
}
here is the SessionManager i use :
public sealed class SessionManager
{
private readonly ISessionFactory _sessionFactory;
public static ISessionFactory SessionFactory
{
get { return Instance._sessionFactory; }
}
private ISessionFactory GetSessionFactory()
{
return _sessionFactory;
}
public static SessionManager Instance
{
get { return NestedSessionManager._sessionManager; }
}
public ISession OpenSession()
{
return Instance.GetSessionFactory().OpenSession();
}
private static Configuration SaveConfigs;
private SessionManager()
{
try
{
if (_sessionFactory == null)
{
//from the debugging the code breaks from here when trying to get connectionstring.
string constring = ConfigurationManager.AppSettings["localdb"].ToString();
FluentConfiguration configuration = Fluently.Configure()
.Database(
MsSqlConfiguration.MsSql2005.ConnectionString(constring))
.Mappings(m =>
{
m.FluentMappings.AddFromAssemblyOf<myproject.model.Request>();
m.FluentMappings.AddFromAssemblyOf<myproject.model.Route>();
})
.ExposeConfiguration((x) =>
{
SaveConfigs = x;
x.SetProperty("current_session_context_class", "thread_static");
});
_sessionFactory = configuration.BuildSessionFactory();
}
}
catch (Exception ex)
{
Console.Write(ex.Message);
}
}
public static void BuildSchema(ISession session)
{
var export = new SchemaExport(SaveConfigs);
export.Execute(false,true,false,session.Connection,null);
}
class NestedSessionManager
{
internal static readonly SessionManager _sessionManager = new SessionManager();
}
}
So from my comment the NullReferenceException happens when accessing the connectionstring. I don't have the explanation on why that happens.I'm sure it's some kind of gotchas, i can't get over it.I would be very grateful if anyone could give me a hand here.thanks for reading.
ConfigurationManager.AppSettings["localdb"] from Test.aspx would be pulling from your web.config file on a web project.
That file wouldn't be accessible to your test project (I'm assuming your tests are in a separate project from your web site). You should be able to get around this by adding an app.config file into your test project with the correct localdb settings or rather than using a configuration string, use FluentNHibernate's fluent builder.
Example app.config file:
<?xml version="1.0"?>
<configuration>
<appSettings>
<add key="localdb" value="yourconnectionstring" />
</appSettings>
</configuration>
If the value returned by ConfigurationManager.AppSettings["localdb"] is null then the .ToString() call will cause the NullReferenceException.
Ensure the "localdb" setting exists.