ArgumentOutOfRangeException running HtmlAgilityPack - html-agility-pack

I'm at my wits end. I keep getting this exception with HtmlAgilityPack whenever I attempt to select nodes using XPath. The problem remains even if I start a brand new solution with just this example (so it's not a problem with my application code interfering somehow. There's nothing wrong with the webpage, or my internet connection or anything like that. I know I've had this working before. I even suspected a corrupted dll somehow and re-downloaded it, but to no avail. Any ideas??
using System;
using HtmlAgilityPack;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
var web = new HtmlWeb();
var doc = web.Load("http://www.google.com");
var root = doc.DocumentNode;
var links = root.SelectNodes("//a");
// Error! ArgumentOutOfRangeException: Index was out of range.
// Must be non-negative and less than the size of the collection.
}
}
}
/*
System.ArgumentOutOfRangeException: Index was out of range. Must be non-negative and less than the size of the collection.
Parameter name: index
at System.ThrowHelper.ThrowArgumentOutOfRangeException()
at HtmlAgilityPack.HtmlNodeNavigator.MoveToFirstChild()
at MS.Internal.Xml.XPath.XPathDescendantIterator.MoveNext()
at MS.Internal.Xml.XPath.DescendantQuery.Advance()
at MS.Internal.Xml.XPath.XPathSelectionIterator.MoveNext()
at HtmlAgilityPack.HtmlNode.SelectNodes(String xpath)
at ConsoleApplication1.Program.Main(String[] args)
*/
edit:
Well...I'm not sure how it happened or what it did, but I found the .dll installed in a GAC_MSIL folder. Removing that immediately resolved the issue. So, nevermind!

Related

Recompile assemblies to separate appdomains in NET 5

I have a NET 5.0 console application, from which I am trying to compile and execute external code BUT also be able to update the code, unload the previously created appdomain and re-compile everything.
This is my entire static class that handles code compilation and assembly loading
using System;
using System.IO;
using System.Collections.Generic;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using System.Reflection;
using Microsoft.CodeAnalysis.Emit;
using System.Runtime.Loader;
namespace Scripting
{
public static class ScriptCompiler
{
public static Dictionary<string, AppDomain> _appDomainDict = new();
public static object CompileScript(string scriptpath)
{
var tree = SyntaxFactory.ParseSyntaxTree(File.ReadAllText(scriptpath));
//Adding basic references
List<PortableExecutableReference> refs = new List<PortableExecutableReference>();
var assemblyPath = Path.GetDirectoryName(typeof(object).Assembly.Location);
refs.Add(MetadataReference.CreateFromFile(Path.Combine(assemblyPath, "mscorlib.dll")));
refs.Add(MetadataReference.CreateFromFile(Path.Combine(assemblyPath, "System.dll")));
refs.Add(MetadataReference.CreateFromFile(Path.Combine(assemblyPath, "System.Private.CoreLib.dll")));
refs.Add(MetadataReference.CreateFromFile(Path.Combine(assemblyPath, "System.Core.dll")));
refs.Add(MetadataReference.CreateFromFile(Path.Combine(assemblyPath, "System.Runtime.dll")));
// A single, immutable invocation to the compiler
// to produce a library
string hash_name = scriptpath.GetHashCode();
if (_appDomainDict.ContainsKey(hash_name))
{
AppDomain.Unload(_appDomainDict[hash_name]);
_appDomainDict.Remove(hash_name);
}
AppDomain new_domain = AppDomain.CreateDomain(hash_name);
_appDomainDict[hash_name] = new_domain;
var compilation = CSharpCompilation.Create(hash_name)
.WithOptions(
new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary,
optimizationLevel: OptimizationLevel.Release,
allowUnsafe:true))
.AddReferences(refs.ToArray())
.AddSyntaxTrees(tree);
MemoryStream ms = new MemoryStream();
EmitResult compilationResult = compilation.Emit(ms);
ms.Seek(0, SeekOrigin.Begin);
if (compilationResult.Success)
{
// Load the assembly
Assembly asm = new_domain.Load(ms.ToArray());
object main_ob = asm.CreateInstance("SomeClass");
ms.Close();
return main_ob;
}
else
{
foreach (Diagnostic codeIssue in compilationResult.Diagnostics)
{
string issue = $"ID: {codeIssue.Id}, Message: {codeIssue.GetMessage()}," +
$" Location: { codeIssue.Location.GetLineSpan()}," +
$" Severity: { codeIssue.Severity}";
Callbacks.Logger.Log(typeof(NbScriptCompiler), issue, LogVerbosityLevel.WARNING);
}
return null;
}
}
}
}
Its all good when I am trying load the assembly in the current domain and execute from the instantiated object. The problem with this case is that since I wanna do frequent updates to the code, even if I make sure that the assembly names are different. I'll end up loading a ton of unused assemblies to the current domain.
This is why I've been trying to create a new domain and load the assembly there. But for some reason I get a platform not supported exception. Is this not possible to do in NET 5? Are there any workarounds or am I doing something wrong here.
Ok, it turns out that AppDomain support for NET Core + is very limited and in particular there seems to be only one appdomain
On .NET Core, the AppDomain implementation is limited by design and
does not provide isolation, unloading, or security boundaries. For
.NET Core, there is exactly one AppDomain. Isolation and unloading are
provided through AssemblyLoadContext. Security boundaries should be
provided by process boundaries and appropriate remoting techniques.
Source: https://learn.microsoft.com/en-us/dotnet/api/system.appdomain?view=net-6.0
And indeed, when trying to use AssemblyLoadContext and create object instances through these contexts everything worked like a charm!
One last note is that if the created context is not marked as collectible, its not possible to unload it. But this can be very easily set during AssemblyLoadContext construction.

Getting a FileNotFoundException in VSCode, but not in JGrasp

Ok, so this is what's going on. I'm trying to learn how to use vscode (switching over from jgrasp). I'm trying to run this old school assignment that requires the use of outside .txt files. The .txt files, as well as other classes that I have written are in the same folder and everything. When I try to run this program in JGrasp, it works fine. Though, in VSCode, I get an exception. Not sure what is going wrong here. Thanks Here is an example:
import java.io.*;
public class HangmanMain {
public static final String DICTIONARY_FILE = "dictionary.txt";
public static final boolean SHOW_COUNT = true; // show # of choices left
public static void main(String[] args) throws FileNotFoundException {
System.out.println("Welcome to the cse143 hangman game.");
System.out.println();
// open the dictionary file and read dictionary into an ArrayList
Scanner input = new Scanner(new File(DICTIONARY_FILE));
List<String> dictionary = new ArrayList<String>();
while (input.hasNext()) {
dictionary.add(input.next().toLowerCase());
}
// set basic parameters
Scanner console = new Scanner(System.in);
System.out.print("What length word do you want to use? ");
int length = console.nextInt();
System.out.print("How many wrong answers allowed? ");
int max = console.nextInt();
System.out.println();
//The rest of the program is not shown. This was included just so you guys could see a little bit of it.
If you're not using a project, jGRASP makes the working directory for your program the same one that contains the source file. You are creating the file with a relative path, so it is assumed to be in the working directory. You can print new File(DICTIONARY_FILE).getAbsolutePath() to see where VSCode is looking (probably a separate "classes" directory) and move your data file there, or use an absolute path.

redissonClient.poll() only returning the first 8 characters of String type value

Currently using reddison, creating a redissonClient and trying to poll data from redis server. I can see the data in the redis db if I check via redis-cli but when I look at the string value in my java application it is always the first 8 characters of the string and no more. Not sure why it won't give me the whole value.
I've tried using the .peek() method as well and I see the same symptom in that I only get 8 characters of the string returned.
Here is the main part of the code I can provide more details as needed:
#Service
#Slf4j
public class RedisConsumer {
RedisConfig redisConfig;
//RQueue<String> redisQueue;
RBlockingQueue<String> redisQueue;
#Autowired
RedisConsumer(RedisConfig redisConfig) {
this.redisConfig = redisConfig;
}
public void pollAuditQueue() {
//Redisson
redisQueue.add("{JSON string here snipped out for brevity}");
String item = redisQueue.poll();
if (!Objects.isNull(item)) {
log.info("I found this item: " + item);
} else {
log.info("Nothing in queue...");
}
}
#PostConstruct
private void init() throws Exception {
RedissonClient redissonClient = redisConfig.redisson();
redisQueue = redissonClient.getBlockingQueue("test");
while(true) {
pollAuditQueue();
Thread.sleep(5000);
}
}
When I look at the print statement in my console I see:
I found this item: {"AuditEv
When I check the redis-cli I can see the whole value:
1) "\xfc\t{\"AuditEvent\":{\"timestamp\":\"2018-11-27 04:31:47.818000+0000\" snipped the rest out for brevity}"
Lastly if I check that the item was removed from Redis after being polled in the Java app I can confirm that it is.
Any help would be great since it's not throwing any specific error I'm not finding any resources online to help address it.
I've found one thing I didn't notice in my earlier testing. When I manually insert using the redis cli I was replicating what my first tests through Java did which put the \xfc\t at the front which can be seen in my sample above.
Just now when I used redisQueue.add from within my application I noticed in redis it has \xfc\x80\x90\x01 instead and those do return the entire string to me in my application. I assume then this has to do with memory allocation somehow? I'm marking the question as resolved as I am no longer experiencing the issue. If anyone can drop on comment on what those letter/numbers mean though it may be meaningful for anyone that reads this post later. Once I have researched it I will add that comment myself if no one has beat me to it!
Add encoding:
RMap map = redisson.getMap("SessionMap"); -->
RMap map = redisson.getMap("SessionMap", new StringCodec("UTF-8"));

ListChangeListener, JavaFX

I have a question related to filters on a ObservableList. The code that i have works fine, but i think is too slow. This is beacause i load like 40,000 orders at the beginning of the app, after that the app keeps receiving orders, but for now i only have the problem in the initial load. My main problem is that the copy of my original collection of orders is considerably slower, why? because the code that i have in the changeListener i think that could be better.. but i haven't found a solution yet. Well here's an example of my code.
public MainController()
{
filteredData.addAll(Repository.masterObservableList);
Repository.masterObservableList.addListener(new ListChangeListener<OrderVo>()
{
#Override
public void onChanged(ListChangeListener.Change<? extends OrderVo> change)
{
filteredData.clear();
for (OrderVo o : Repository.masterObservableList)
filteredData.add(o);
}
});
}
}
I'll explain the code a little bit. the "Repository" is a singleton, the masterObservableList is a ObservableList and as the name says.. is the "master" or the original. The filteredData is also a ObservableList but is only declared in the controller of my fxml (MainController) and works as the copy of the master collection. Every time that my master collection recieve a change (update or new order) the filteredData should apply that change.. but im doing a for each iteration and this is problem, because it works but, too slow. Why im saying that is slow? Because in the beginning i was using the master collection as the data provider of a TableView that shows the orders and it worked fast and clean. After that i wanted to add filters to the table and thats when i began to do a research and found the filtered data and another methods.. i keeped this method (is a larger code, but the main problem is here).. and it works.. but the time that took to load the orders in the beginning is like 1:30 mins more than before.. So guys, if you have any idea! of how to keep the filteredData updated without making a for each in the changeListener i will be very happy and grateful. Thanks for reading!
Your algorithm is forcefully resetting all elements of filteredData everytime masterObservableList changes.
This is too exhaustive, as the listener can be triggered upon each item added or removed.
Maybe it's better to only add or remove the elements that have changed?
Repository.masterObservableList.addListener(new ListChangeListener<OrderVo>()
{
#Override
public void onChanged(ListChangeListener.Change<? extends OrderVo> change)
{
while(change.next()) {
if(change.wasAdded()) {
List added = change.getAddedSubList();
//add those elements to your data
}
if(change.wasRemoved()) {
List removed = change.getRemoved();
//remove those elements from your data
}
}
}
});
}

Skip single file from Minifying?

I'm trying to use ASP.Nets BundleTable to optomize some javascript files, but have run into a problem where a specific addon (jQuery-Timepicker) fails to work when the code has been minified. See here.
Bundle code is currently similar to:
// Add our commonBundle
var commonBundle= new Bundle("~/CommonJS" + culture.ToString());
// JQuery and related entries.
commonBundle.Include("~/Scripts/jquery-1.7.2.js");
commonBundle.Include("~/Scripts/jquery-ui-1.8.22.js");
commonBundle.Include("~/Scripts/jquery.cookie.js");
commonBundle.Include("~/Scripts/jquery-ui/jquery-ui-timepicker-addon.js"); // This is the one that does not work when bundled
// JS Transformer
commonBundle.Transforms.Add(new JsMinify());
BundleTable.Bundles.Add(commonBundle);
If I remove the jquery-ui-timepicker-addon.js file, then include it separate in my webpage, then it works properly. (Otherwise I get the Uncaught TypeError: undefined is not a function error).
I'm wondering if I can somehow setup my bundling code to skip minifying this one file (but still have it included in the bundle)? I've been looking around but have not come up with any solutions for doing so.
So the issue is that all the files are bundled together, and then the entire bundle is minimized. As a result you aren't going to easily be able to skip minification of just one file. Probably the best way to do this would be to create a new Transform that appended the contents of this file you want unminified. Then you would append this Transform to your registered ScriptBundle:
commonBundle.Transforms.Add(new AppendFileTransform(""~/Scripts/jquery-ui/jquery-ui-timepicker-addon.js""));
AppendFileTransform would simply append the contents of the file to the bundled response. You would no longer include the timepicker in the bundle explicitly, but instead this transform would be including it, and this would effectively give you the behavior you are looking since the JsMinify transform would run first and minify the bundle, and then you would add the file you want at the end unminified.
This can be solved better from the other direction - instead of trying to not minify a single file, add transforms for individual items instead.
First - create a class that implements IItemTransform and uses the same code to minify the given input:
public class JsItemMinify : System.Web.Optimization.IItemTransform
{
public string Process(string includedVirtualPath, string input)
{
var min = new Microsoft.Ajax.Utilities.Minifier();
var result = min.MinifyJavaScript(input);
if (min.ErrorList.Count > 0)
return "/*minification failed*/" + input;
return result;
}
}
Second - add this item transform to the individual files and remove the bundle transform:
var commonBundle= new Bundle("~/CommonJS");
// the first two includes will be minified
commonBundle.Include("~/Scripts/jquery-1.7.2.js", new JsItemMinify());
commonBundle.Include("~/Scripts/jquery-ui-1.8.22.js", new JsItemMinify());
// this one will not
commonBundle.Include("~/Scripts/jquery.cookie.js");
// Remove the default JsMinify bundle transform
commonBundle.Transforms.Clear();
BundleTable.Bundles.Add(commonBundle);
You cannot setup Bundle to skip minifying certain files and to minify rest of the files.
You could implement your own Bundle or Transform by overriding Bundle.ApplyTransform or JsMinify.Process methods, but you would need to take care not to break change-tracking of files, key generation, cache invalidation, etc... (or doing some ugly hack). It's not worth the effort.
I would keep separate js file, as you already mentioned.
This is just complete example based on Hao Kung's answer
var myBundle = new ScriptBundle("~/bundles/myBundle").Include(
"~/Scripts/script1.js",
"~/Scripts/script2.js",
);
myBundle.Transforms.Add(new AppendFileTransform("~/Scripts/excludedFile.min.js"));
bundles.Add(myBundle);
And here is example implementation of the AppendFileTransform:
public class AppendFileTransform : IBundleTransform
{
private readonly string _filePath;
public AppendFileTransform(string filePath)
{
_filePath = filePath;
}
public void Process(BundleContext context, BundleResponse response)
{
response.Content += File.ReadAllText(context.HttpContext.Server.MapPath(_filePath));
}
}

Resources