Jackrabbit ignored Credentials - credentials

I'm trying to deploy this example
import javax.jcr.Repository;
import javax.jcr.Session;
import javax.jcr.SimpleCredentials;
import javax.jcr.Node;
import org.apache.jackrabbit.core.TransientRepository;
/**
* Second hop example. Stores, retrieves, and removes example content.
*/
public class Fish {
static Repository repository;
public static void main(String[] args) {
Session session = null;
try {
repository = getRepositoryHandle();
SimpleCredentials simpleCredentials = new SimpleCredentials(
"username", "password".toCharArray());
session = repository.login(simpleCredentials);
Node root = session.getRootNode();
Node hello = root.addNode("Olá");
Node world = hello.addNode("Mundo");
world.setProperty("Message", "olá mundo");
session.save();
Node node = root.getNode("Olá/Mundo");
System.out.println(node.getPath());
System.out.println(node.getProperty("Message").toString());
root.getNode("Olá").remove();
session.save();
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
if (session != null)
session.logout();
}
}
private static Repository getRepositoryHandle() {
if (repository == null)
repository = new TransientRepository();
// "classpath:repository.xml", "target/repository");
return repository;
}
}
adapted from https://jackrabbit.apache.org/
When I deploy it on eclipse, I get following errors and exceptions:
Exception in thread "main" java.lang.RuntimeException: javax.jcr.LoginException: LoginModule ignored Credentials
at recipe.Fish.main(Fish.java:38)
Caused by: javax.jcr.LoginException: LoginModule ignored Credentials
at org.apache.jackrabbit.core.RepositoryImpl.login(RepositoryImpl.java:1526)
at org.apache.jackrabbit.core.TransientRepository.login(TransientRepository.java:381)
at org.apache.jackrabbit.commons.AbstractRepository.login(AbstractRepository.java:144)
at recipe.Fish.main(Fish.java:22)
Caused by: javax.security.auth.login.FailedLoginException: LoginModule ignored Credentials
at org.apache.jackrabbit.core.security.authentication.LocalAuthContext.login(LocalAuthContext.java:87)
at org.apache.jackrabbit.core.RepositoryImpl.login(RepositoryImpl.java:1498)
... 3 more
Can someone help me with this issue, please ?
Thanks in advance.

Seems like someone already experienced your problem when working through Jackrabbit's examples. See answer on another SO question.
Try replacing "username" and "password" by "admin" and "admin".

Related

Implementing Authentication in IgniteDB

I just configured authentication in IgniteDB ( a specific server, not a localhost )
https://apacheignite.readme.io/docs/advanced-security
However I encountered some issue while trying to connect. Where should I provide the credential?
TcpDiscoverySpi spi = new TcpDiscoverySpi();
TcpDiscoveryVmIpFinder ipFinder = new TcpDiscoveryMulticastIpFinder();
String ipList = appConfig.getIgniteIPAddressList();
List<String> addressList= Arrays.asList(ipList.split(";"));
ipFinder.setAddresses(addressList);
spi.setIpFinder(ipFinder);
IgniteConfiguration cfg = new IgniteConfiguration();
cfg.setIgniteInstanceName("IgnitePod");
cfg.setClientMode(true);
cfg.setDiscoverySpi(spi);
Ignite ignite = Ignition.start(cfg);
Anybody has idea on implementing it?
https://apacheignite.readme.io/docs/advanced-security
Describes how to configure the authentication via username and password for THIN connections only (JDBC, ODBC).
You can create users using SQL commands like next:
https://apacheignite-sql.readme.io/docs/create-user
You can provide credentials to thin client connection string using its properties:
https://apacheignite-sql.readme.io/docs/connection-string-and-dsn#section-supported-arguments
https://apacheignite-sql.readme.io/docs/jdbc-driver#section-additional-connection-string-examples
Please also check that you have Ignite persistence configured.
As Andrei notes, Ignite only authenticates thin clients by default, and even then only when persistence is enabled. If you need to have thick-clients authenticate also, you can do this using a plugin. Third-party, commercial solutions also exist.
Apache Ignite does not provide these kinds of security capabilities with its open-source version. One can either implement it on your own or use commercial Gridgain distribution.
Here are the steps to implement a custom security plugin.
One would need to implement GridSecurityProcessor which would be used to authenticate the joining node.
In GridSecurityProcessor, you would have to implement authenticateNode() api as follows
public SecurityContext authenticateNode(ClusterNode node, SecurityCredentials cred) throws IgniteCheckedException {
SecurityCredentials userSecurityCredentials;
if (securityPluginConfiguration != null) {
if ((userSecurityCredentials = securityPluginConfiguration.getSecurityCredentials()) != null) {
return userSecurityCredentials.equals(cred) ? new SecurityContextImpl() : null;
}
if (cred == null && userSecurityCredentials == null) {
return new SecurityContextImpl();
}
}
if (cred == null)
return new SecurityContextImpl();
return null;
}
Also, you would need to extend TcpDiscoverySpi to pass the user credentials during initLocalNode() as follows
#Override
protected void initLocalNode(int srvPort, boolean addExtAddrAttr) {
try {
super.initLocalNode(srvPort, addExtAddrAttr);
this.setSecurityCredentials();
} catch (Exception e) {
e.printStackTrace();
}
}
private void setSecurityCredentials() {
if (securityCredentials != null) {
Map<String,Object> attributes = new HashMap<>(locNode.getAttributes());
attributes.put(IgniteNodeAttributes.ATTR_SECURITY_CREDENTIALS, securityCredentials);
this.locNode.setAttributes(attributes);
}
}
You can follow the link given below to get detailed steps that can be followed to write a custom security plugin and its usage.
https://www.bugdbug.com/post/how-to-secure-apache-ignite-cluster
Was able to solve my own problem by creating my own CustomTCPDiscoveryAPI.
First, create this class :
import org.apache.ignite.IgniteException;
import org.apache.ignite.cluster.ClusterNode;
import org.apache.ignite.internal.IgniteNodeAttributes;
import org.apache.ignite.internal.processors.security.SecurityContext;
import org.apache.ignite.lang.IgniteProductVersion;
import org.apache.ignite.plugin.security.SecurityCredentials;
import org.apache.ignite.spi.discovery.DiscoverySpiNodeAuthenticator;
import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi;
import java.util.Map;
public class CustomTcpDiscoverySpi extends TcpDiscoverySpi implements DiscoverySpiNodeAuthenticator {
SecurityCredentials securityCredentials;
public CustomTcpDiscoverySpi(final SecurityCredentials securityCredentials) {
this.securityCredentials = securityCredentials;
this.setAuthenticator(this);
}
#Override
public SecurityContext authenticateNode(ClusterNode clusterNode, SecurityCredentials securityCredentials) throws IgniteException {
return null;
}
#Override
public boolean isGlobalNodeAuthentication() {
return true;
}
#Override
public void setNodeAttributes(final Map<String, Object> attrs, final IgniteProductVersion ver) {
attrs.put(IgniteNodeAttributes.ATTR_SECURITY_CREDENTIALS, this.securityCredentials);
super.setNodeAttributes(attrs, ver);
}
}
And then, use it like below :
SecurityCredentials cred = new SecurityCredentials();
cred.setLogin(appConfig.getIgniteUser());
cred.setPassword(appConfig.getIgnitePassword());
CustomTcpDiscoverySpi spi = new CustomTcpDiscoverySpi(cred);
//TcpDiscoverySpi spi = new TcpDiscoverySpi(); - > removed to use the CustomTCPDiscovery
TcpDiscoveryVmIpFinder ipFinder = new TcpDiscoveryMulticastIpFinder();
String ipList = appConfig.getIgniteIPAddressList();
List<String> addressList= Arrays.asList(ipList.split(";"));
ipFinder.setAddresses(addressList);
spi.setIpFinder(ipFinder);
IgniteConfiguration cfg = new IgniteConfiguration();
cfg.setIgniteInstanceName("IgnitePod");
cfg.setClientMode(true);
cfg.setAuthenticationEnabled(true);
// Ignite persistence configuration.
DataStorageConfiguration storageCfg = new DataStorageConfiguration();
// Enabling the persistence.
storageCfg.getDefaultDataRegionConfiguration().setPersistenceEnabled(true);
// Applying settings.
// tests
cfg.setDataStorageConfiguration(storageCfg);
cfg.setDiscoverySpi(spi);
Ignite ignite = Ignition.start(cfg);
Hope this helps other people who stuck with the same problem.
The only option for peer-authenticating server nodes which is available in vanilla Apache Ignite is SSL+certificates.

groovy.lang.MissingPropertyException while Downloading Files from FTP Server

I want to create job in grails which download the files from ftp
server after certain interval of time say 2-3 days and store it on
specified local path. the same code with minor changes is written in
java which was working fine but when write the similar code in Grails
I'm facing the Error and not able to resolve it. Can any body Tell me
where I'm making mistake?
Following is the Error that I'm facing when job start.
JOB STARTED::************************************************************************************
2015-08-24 18:20:35,285 INFO org.quartz.core.JobRunShell:207 Job GRAILS_JOBS.com.hoteligence.connector.job.DownloadIpgGzipFilesJob threw a JobExecutionException:
org.quartz.JobExecutionException: groovy.lang.MissingPropertyException: No such property: ftpClient for class: com.hoteligence.connector.job.DownloadIpgGzipFilesJob [See nested exception: groovy.lang.MissingPropertyException: No such property: ftpClient for class: com.hoteligence.connector.job.DownloadIpgGzipFilesJob]
at grails.plugins.quartz.GrailsJobFactory$GrailsJob.execute(GrailsJobFactory.java:111)
at org.quartz.core.JobRunShell.run(JobRunShell.java:202)
at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:573)
Caused by: groovy.lang.MissingPropertyException: No such property: ftpClient for class: com.hoteligence.connector.job.DownloadIpgGzipFilesJob
at com.hoteligence.connector.job.DownloadIpgGzipFilesJob.execute(DownloadIpgGzipFilesJob.groovy:93)
at grails.plugins.quartz.GrailsJobFactory$GrailsJob.execute(GrailsJobFactory.java:104)
... 2 more
/* I've added all the related dependencies in grails Build Config.
*/
package com.hoteligence.connector.job
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import org.codehaus.groovy.grails.commons.ConfigurationHolder as ConfigHolder;
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPReply;
/**
* #author Gajanan
* this is back-end job which download Files from ftp server and store it on locally
*/
class DownloadIpgGzipFilesJob {
static triggers = {
simple repeatInterval: Long.parseLong(ConfigHolder.config.DEVICE_PING_ALERT_JOB_REPEAT_INTERVAL),
startDelay : 60000
}
def execute() {
try{
println "JOB STARTED::************************************************************************************";
/* following is the details which are required for server connectivity
*/
String server = ConfigHolder.config.IPG_SERVER_NAME;
int port = ConfigHolder.config.IPG_SERVER_PORT_NO;
String user = ConfigHolder.config.IPG_SERVER_USER_NAME;
String pass = ConfigHolder.config.IPG_SERVER_USER_PASSWORD;
String [] fileNames = ConfigHolder.config.IPG_DOWNLOADABLE_GZIP_FILE_LIST.split(",");
String downloadFilePath = ConfigHolder.config.IPG_GZIP_DOWNLOAD_LOCATION;
String fileDate = (todaysDate.getYear()+1900)+""+((todaysDate.getMonth()+1)<=9?("0"+(todaysDate.getMonth()+1)):(todaysDate.getMonth()+1))+""+todaysDate.getDate();
FTPClient ftpClient = new FTPClient();
/* Here we are making connection to the server and the reply
from server is printed on console
*/
ftpClient.connect(server, port);
showServerReply(ftpClient);
int replyCode = ftpClient.getReplyCode();
if (!FTPReply.isPositiveCompletion(replyCode)) {
System.out.println("Connect failed");
return;
}
boolean success = ftpClient.login(user, pass);
showServerReply(ftpClient);
if (!success) {
System.out.println("Could not login to the server");
return;
}
/* Here we are iterate the FileList and download them to specified directory
*/
for(int i =0; i<fileNames.length;i++) {
String fileName = "on_"+ConfigHolder.config.IPG_DATA_COUNTRY_CODE+fileNames[i]+fileDate+".xml.gz";
System.out.println(fileName);
downloadFtpFileByName(ftpClient,fileName,downloadFilePath+fileName);
}
}
catch (IOException ex) {
System.out.println("Oops! Something wrong happened");
ex.printStackTrace();
}
catch(Exception e) {
e.printStackTrace();
}
finally {
// logs out and disconnects from server
/* In finally block we forcefully close the connection and close the file node also
*/
try {
if (ftpClient.isConnected()) {
ftpClient.logout();
ftpClient.disconnect();
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
/* this function is nothing but to print the ftp server reply after connection to ftp server
*/
private static void showServerReply(FTPClient ftpClient) {
String[] replies = ftpClient.getReplyStrings();
if (replies != null && replies.length > 0) {
for (String aReply : replies) {
System.out.println("SERVER: " + aReply);
}
}
}
/* This is the actual logic where we copy the file from ftp
and store on local directory
this method accept three parameter FtpClient object, Name of the file which has to be downloaded from server and the path where downloaded file has to be stored
*/
private static void downloadFtpFileByName(FTPClient ftpClient,String fileName,String downloadfileName){
System.out.println("Strat Time::"+System.currentTimeMillis());
try {
String remoteFile1 = "/"+fileName; // file on server
File downloadFile1 = new File(downloadfileName); // new file which is going to be copied on local directory
OutputStream outputStream1 = new BufferedOutputStream(new FileOutputStream(downloadFile1));
Boolean success = ftpClient.retrieveFile(remoteFile1, outputStream1);
if (success) {
System.out.println("File"+fileName+" has been downloaded successfully.");
}
else
{
System.out.println("File"+fileName+" has been DOWNLOAD FAILURE....");
}
outputStream1.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("END Time::"+System.currentTimeMillis());
}
}
Move this line:
FTPClient ftpClient = new FTPClient();
Outside of the try { ... } catch() block (ie, move it up to before the try)
You are declaring the local variable inside the try, then trying to use it in the finally block

Writing custom Shiro realm

I am constructing my own AuthorizingRealm subclass, and am having a tough time wiring it up to my SecurityManager.
The essence of my realm:
public class MyRealm extends AuthorizingRealm {
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
try {
// My custom logic here
} catch(Throwable t) {
System.out.println(t.getMessage());
}
SimpleAuthenticationInfo authn = new SimpleAuthenticationInfo(new MyUser(), "somePassword");
return authn;
}
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
try {
// My custom logic here
} catch(Throwable t) {
System.out.println(t.getMessage());
}
return new SimpleAuthorizationInfo();
}
}
Then in my 'shiro.ini':
# =======================
# Shiro INI configuration
# =======================
[main]
myRealm = com.me.myapp.security.MyRealm
Then in my Driver class/main method (that I'm using for testing):
public class Driver {
public static void main(String[] args) {
Driver d = new Driver();
d.test();
}
public void test() {
Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
SecurityManager securityManager = factory.getInstance();
SecurityUtils.setSecurityManager(securityManager);
UsernamePasswordToken token = new UsernamePasswordToken("", "");
token.setRememberMe(true);
System.out.println("Shiro props:");
System.out.println(securityManager.getProperties());
Subject currentUser = SecurityUtils.getSubject()
try {
currentUser.login(token)
println "I think this worked!"
} catch (UnknownAccountException uae) {
println "Exception: ${uae}"
} catch (IncorrectCredentialsException ice) {
println "Exception: ${ice}"
} catch (LockedAccountException lae) {
println "Exception: ${lae}"
} catch (ExcessiveAttemptsException eae) {
println "Exception: ${eae}"
} catch (AuthenticationException ae) {
println "Exception: ${ae}"
}
}
}
When I run this I get:
Shiro props:
[class:class org.apache.shiro.mgt.DefaultSecurityManager, cacheManager:null, subjectFactory:org.apache.shiro.mgt.DefaultSubjectFactory#6a2b8b42, authorizer:org.apache.shiro.authz.ModularRealmAuthorizer#50c3d082, realms:[com.me.myapp.security.MyRealm#67ae303a], subjectDAO:org.apache.shiro.mgt.DefaultSubjectDAO#5ce06503, rememberMeManager:null, authenticator:org.apache.shiro.authc.pam.ModularRealmAuthenticator#1007d798, sessionManager:org.apache.shiro.session.mgt.DefaultSessionManager#72db4460]
Exception: org.apache.shiro.authc.AuthenticationException: Authentication failed for token submission [org.apache.shiro.authc.UsernamePasswordToken - , rememberMe=true]. Possible unexpected error? (Typical or expected login exceptions should extend from AuthenticationException).
So it looks like its reading my shiro.ini because its picking up the correct realm, but MyRealm doesn't do anything except stub out dummy users that should authenticated regardless of the username/password supplied. Any ideas as to where I'm going awry?
add this to your shiro.ini: securityManager.realms = $myRealm then in your Driver class
UsernamePasswordToken token = new UsernamePasswordToken("", "somePassword");
instead of an empty passowrd.
I think this worked!
I have not done this myself, but here are a couple of things you can try:
If you don't need authorization logic, consider subclassing AuthenticatingRealm instead of AuthorizingRealm
In method doGetAuthenticationInfo, consider using this code:
SimpleAuthenticationInfo authn = new SimpleAuthenticationInfo(token.getPrincipal(), token.getCredentials(), "myRealm");
You seem to have created a Realm properly but have not told the SecurityManager that it is one the realms to be used. In that way, it is just another object created in the main section of shiro.ini.
To tell Shiro's SecurityManager that it needs to use myRealm as a Realm, you need to add this to your shiro.ini:
securityManager.realms = $myRealm

"error": "invalid_client" from custom OWIN implementation

I am implementing OWIN authentication on a mysql backend, I dont thnk thats a problem as my registration work pretty well. I have basically worked off this post (i.e. nicked most of the code).
I am also using DI via autofac so I have changed a few things around to inject dependencies into the SimpleAuthorizationServerProvider
THE PROBLEM
I post grant_type=password, username and password to http://localhost/myappurl/token and I get back "error":"invalid_client". I get no hits when I try to debug so its probably failing in the library and not getting to my own code. Does anyone know why this would be?
Please pardon the lengthy code, I have no idea where the issue could be so I have posted everything I think is relevant, if anyone needs to see more code, please ask.
SimpleAuthorizationServerProvider
public class SimpleAuthorizationServerProvider : OAuthAuthorizationServerProvider
{
private readonly IUserService _userService;
public SimpleAuthorizationServerProvider(IUserService userService)
{
_userService = userService;
}
public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
context.Validated();
}
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });
var authenticate = await _userService.FindUser(context.UserName, context.Password);
if (!authenticate)
{
context.SetError("invalid_grant", "The user name or password is incorrect.");
return;
}
var identity = new ClaimsIdentity(context.Options.AuthenticationType);
identity.AddClaim(new Claim("sub", context.UserName));
identity.AddClaim(new Claim("role", "user"));
context.Validated(identity);
}
}
Startup
public partial class Startup
{
public void ConfigureAuth(IAppBuilder app)
{
HttpConfiguration config = new HttpConfiguration();
ConfigureOAuth(app, (IOAuthAuthorizationServerProvider)config.DependencyResolver.GetService(typeof(IOAuthAuthorizationServerProvider)));
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
app.UseWebApi(config);
}
public void ConfigureOAuth(IAppBuilder app, IOAuthAuthorizationServerProvider provider)
{
OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions()
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(90),
Provider = provider,
ApplicationCanDisplayErrors=true,
};
app.UseOAuthAuthorizationServer(OAuthServerOptions);
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
}
}
IocConfig
public static class IocConfig
{
public static void Register(HttpConfiguration config)
{
var builder = new ContainerBuilder();
// Configure the container
// Register individual components
builder.Register(c => new MySQLContext()).As<IMySqlContext>().InstancePerRequest();
builder.RegisterType<SimpleAuthorizationServerProvider>().As<IOAuthAuthorizationServerProvider>();
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
var container = builder.Build();
config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
}
}
You have a lot of code there, so it's not easy to isolate the problem. As a first step, consider removing the code for Autofac DI and see if that makes any difference. It's hard to tell what the problem might be otherwise.
If the issue is indeed related to the DI code, then perhaps this should be a raised as a separate question. In that case, try to create a small code example that demonstrates the issue succinctly. People are more likely to help if the problem code is short and to the point.
Make sure that you've set up SSL for your site. I had a similar issue and the problem was that I was not using SSL.

Debugging Package Manager Console Update-Database Seed Method

I wanted to debug the Seed() method in my Entity Framework database configuration class when I run Update-Database from the Package Manager Console but didn't know how to do it. I wanted to share the solution with others in case they have the same issue.
Here is similar question with a solution that works really well.
It does NOT require Thread.Sleep.
Just Launches the debugger using this code.
Clipped from the answer
if (!System.Diagnostics.Debugger.IsAttached)
System.Diagnostics.Debugger.Launch();
The way I solved this was to open a new instance of Visual Studio and then open the same solution in this new instance of Visual Studio. I then attached the debugger in this new instance to the old instance (devenv.exe) while running the update-database command. This allowed me to debug the Seed method.
Just to make sure I didn't miss the breakpoint by not attaching in time I added a Thread.Sleep before the breakpoint.
I hope this helps someone.
If you need to get a specific variable's value, a quick hack is to throw an exception:
throw new Exception(variable);
A cleaner solution (I guess this requires EF 6) would IMHO be to call update-database from code:
var configuration = new DbMigrationsConfiguration<TContext>();
var databaseMigrator = new DbMigrator(configuration);
databaseMigrator.Update();
This allows you to debug the Seed method.
You may take this one step further and construct a unit test (or, more precisely, an integration test) that creates an empty test database, applies all EF migrations, runs the Seed method, and drops the test database again:
var configuration = new DbMigrationsConfiguration<TContext>();
Database.Delete("TestDatabaseNameOrConnectionString");
var databaseMigrator = new DbMigrator(configuration);
databaseMigrator.Update();
Database.Delete("TestDatabaseNameOrConnectionString");
But be careful not to run this against your development database!
I know this is an old question, but if all you want is messages, and you don't care to include references to WinForms in your project, I made some simple debug window where I can send Trace events.
For more serious and step-by-step debugging, I'll open another Visual Studio instance, but it's not necessary for simple stuff.
This is the whole code:
SeedApplicationContext.cs
using System;
using System.Data.Entity;
using System.Diagnostics;
using System.Drawing;
using System.Windows.Forms;
namespace Data.Persistence.Migrations.SeedDebug
{
public class SeedApplicationContext<T> : ApplicationContext
where T : DbContext
{
private class SeedTraceListener : TraceListener
{
private readonly SeedApplicationContext<T> _appContext;
public SeedTraceListener(SeedApplicationContext<T> appContext)
{
_appContext = appContext;
}
public override void Write(string message)
{
_appContext.WriteDebugText(message);
}
public override void WriteLine(string message)
{
_appContext.WriteDebugLine(message);
}
}
private Form _debugForm;
private TextBox _debugTextBox;
private TraceListener _traceListener;
private readonly Action<T> _seedAction;
private readonly T _dbcontext;
public Exception Exception { get; private set; }
public bool WaitBeforeExit { get; private set; }
public SeedApplicationContext(Action<T> seedAction, T dbcontext, bool waitBeforeExit = false)
{
_dbcontext = dbcontext;
_seedAction = seedAction;
WaitBeforeExit = waitBeforeExit;
_traceListener = new SeedTraceListener(this);
CreateDebugForm();
MainForm = _debugForm;
Trace.Listeners.Add(_traceListener);
}
private void CreateDebugForm()
{
var textbox = new TextBox {Multiline = true, Dock = DockStyle.Fill, ScrollBars = ScrollBars.Both, WordWrap = false};
var form = new Form {Font = new Font(#"Lucida Console", 8), Text = "Seed Trace"};
form.Controls.Add(tb);
form.Shown += OnFormShown;
_debugForm = form;
_debugTextBox = textbox;
}
private void OnFormShown(object sender, EventArgs eventArgs)
{
WriteDebugLine("Initializing seed...");
try
{
_seedAction(_dbcontext);
if(!WaitBeforeExit)
_debugForm.Close();
else
WriteDebugLine("Finished seed. Close this window to continue");
}
catch (Exception e)
{
Exception = e;
var einner = e;
while (einner != null)
{
WriteDebugLine(string.Format("[Exception {0}] {1}", einner.GetType(), einner.Message));
WriteDebugLine(einner.StackTrace);
einner = einner.InnerException;
if (einner != null)
WriteDebugLine("------- Inner Exception -------");
}
}
}
protected override void Dispose(bool disposing)
{
if (disposing && _traceListener != null)
{
Trace.Listeners.Remove(_traceListener);
_traceListener.Dispose();
_traceListener = null;
}
base.Dispose(disposing);
}
private void WriteDebugText(string message)
{
_debugTextBox.Text += message;
Application.DoEvents();
}
private void WriteDebugLine(string message)
{
WriteDebugText(message + Environment.NewLine);
}
}
}
And on your standard Configuration.cs
// ...
using System.Windows.Forms;
using Data.Persistence.Migrations.SeedDebug;
// ...
namespace Data.Persistence.Migrations
{
internal sealed class Configuration : DbMigrationsConfiguration<MyContext>
{
public Configuration()
{
// Migrations configuration here
}
protected override void Seed(MyContext context)
{
// Create our application context which will host our debug window and message loop
var appContext = new SeedApplicationContext<MyContext>(SeedInternal, context, false);
Application.Run(appContext);
var e = appContext.Exception;
Application.Exit();
// Rethrow the exception to the package manager console
if (e != null)
throw e;
}
// Our original Seed method, now with Trace support!
private void SeedInternal(MyContext context)
{
// ...
Trace.WriteLine("I'm seeding!")
// ...
}
}
}
Uh Debugging is one thing but don't forget to call:
context.Update()
Also don't wrap in try catch without a good inner exceptions spill to the console.
https://coderwall.com/p/fbcyaw/debug-into-entity-framework-code-first
with catch (DbEntityValidationException ex)
I have 2 workarounds (without Debugger.Launch() since it doesn't work for me):
To print message in Package Manager Console use exception:
throw new Exception("Your message");
Another way is to print message in file by creating a cmd process:
// Logs to file {solution folder}\seed.log data from Seed method (for DEBUG only)
private void Log(string msg)
{
string echoCmd = $"/C echo {DateTime.Now} - {msg} >> seed.log";
System.Diagnostics.Process.Start("cmd.exe", echoCmd);
}

Resources