Browse Source

dist

master
art.dambrine 5 years ago
commit
72f422f4c2
  1. BIN
      .DS_Store
  2. 207
      JsonNotificationHTTPServer.java
  3. 14
      MessageListener.java
  4. 33
      README.md
  5. 362
      RobotIndex.java
  6. 180
      UrlParser.java
  7. BIN
      dist/.DS_Store
  8. BIN
      dist/RobotIndex.jar
  9. BIN
      dist/mysql-connector-java.jar
  10. 2
      dist/run.sh

BIN
.DS_Store

Binary file not shown.

207
JsonNotificationHTTPServer.java

@ -0,0 +1,207 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package robotindex;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Date;
import java.util.StringTokenizer;
/**
*
* @author arthurdambrine
*/
public class JsonNotificationHTTPServer implements Runnable {
// keepRunning
static boolean keepRunning = true; // nous permettra de signaliser au serveur que c'est sa dernière itération d'écoute
static String messageToJson = ""; // message qui sera communiqué en JSON à travers HTTP
// mode verbose - pour debug
static final boolean verbose = true;
// Connexion client via la classe de Socket
private Socket connect;
public JsonNotificationHTTPServer(Socket c) {
connect = c;
}
public static void startNotificationServer(int PORT) {
RobotIndex robotIndex = new RobotIndex();
robotIndex.setMessageListener(new MessageListener() {
@Override
public void newMessage(String message) {
messageToJson = message;
System.out.println("De HTTPServer - message reçu du robot : " + message);
if (message.contains("\"message\":\"finish\"")) {
System.out.println("Dernière iteration d'écoute - envoyer une req HTTP pour terminer le thread");
keepRunning = false;
}
}
});
try {
ServerSocket serverConnect = new ServerSocket(PORT);
System.out.println("Server lancé.\nEn ecoute sur le port : " + PORT + " ...\n");
// On écoute jusqu'à ce que la demande de fermetture soit envoyée
while (keepRunning) {
JsonNotificationHTTPServer myServer = new JsonNotificationHTTPServer(serverConnect.accept());
if (verbose) {
System.out.println("Connexion ouverte. (" + new Date() + ")");
}
// Creation d'un thread dedié pour les connexions client
Thread thread = new Thread(myServer);
thread.start();
}
} catch (IOException e) {
System.err.println("Erreur connexion server : " + e.getMessage());
}
}
@Override
public void run() {
// nous gérons notre connexion client
BufferedReader in = null;
PrintWriter out = null;
BufferedOutputStream dataOut = null;
String fileRequested = null;
try {
// nous lisons les caractères du client via le flux d'entrée sur le socket
in = new BufferedReader(new InputStreamReader(connect.getInputStream()));
// nous obtenons le flux de sortie des caractères vers le client (pour les en-têtes)
out = new PrintWriter(connect.getOutputStream());
// obtenir le flux de sortie binaire vers le client (pour les données demandées)
dataOut = new BufferedOutputStream(connect.getOutputStream());
// obtenir la première ligne de la request client
String input = in.readLine();
// on parse la requête HTTP avec un StringTokenizer
StringTokenizer parse = new StringTokenizer(input);
String method = parse.nextToken().toUpperCase(); // on obtient la méthode HTTP du client (GET, POST ..)
// on obtient la requete - fichier demandé
fileRequested = parse.nextToken().toLowerCase();
// on ne prend en charge que les méthodes GET et HEAD, vérification
if (!method.equals("GET") && !method.equals("HEAD")) {
if (verbose) {
System.out.println("501 Not Implemented : " + method + " method.");
}
// fichier non pris en charge - on renvoie une erreur 501
String file = "{\"message\":\"501\"}";
int fileLength = (int) file.length();
String contentMimeType = "application/json";
//lecture des données à retourner au client
byte[] fileData = file.getBytes();
// on envoie le HTTP header avec données au client
out.println("HTTP/1.1 501 Not Implemented");
out.println("Server: Java HTTP Server from robot : 1.0");
out.println("Date: " + new Date());
out.println("Content-type: " + contentMimeType);
out.println("Content-length: " + fileLength);
out.println(); // ligne d'espace entre le header et le contenu, très important
out.flush(); // vidage du tampon de flux de sortie des caractères
// file
dataOut.write(fileData, 0, fileLength);
dataOut.flush();
} else {
// GET or HEAD method
String file = messageToJson;
int fileLength = (int) file.length();
String content = "application/json";
if (method.equals("GET")) { // GET method on envoie le contenu
byte[] fileData = file.getBytes();
// send HTTP Headers
out.println("HTTP/1.1 200 OK");
out.println("Server: Java HTTP Server from robot : 1.0");
out.println("Date: " + new Date());
out.println("Content-type: " + content);
out.println("Content-length: " + fileLength);
out.println(); // ligne d'espace entre le header et le contenu, très important
out.flush(); // vidage du tampon de flux de sortie des caractères
dataOut.write(fileData, 0, fileLength);
dataOut.flush();
}
if (verbose) {
System.out.println("File " + fileRequested + " of type " + content + " returned");
}
}
} catch (FileNotFoundException fnfe) {
try {
fileNotFound(out, dataOut, fileRequested);
} catch (IOException ioe) {
System.err.println("Error with file not found exception : " + ioe.getMessage());
}
} catch (IOException ioe) {
System.err.println("Server error : " + ioe);
} finally {
try {
in.close();
out.close();
dataOut.close();
connect.close(); // Fermeture de la connexion socket avec le client
} catch (Exception e) {
System.err.println("Error closing stream : " + e.getMessage());
}
if (verbose) {
System.out.println("Connection closed.\n");
}
}
}
private void fileNotFound(PrintWriter out, OutputStream dataOut, String fileRequested) throws IOException {
// En renvoie une erreur 404 au client
String file = "{\"message\":\"404\"}";
int fileLength = (int) file.length();
String content = "application/json";
//lecture des données à retourner au client
byte[] fileData = file.getBytes();
out.println("HTTP/1.1 404 File Not Found");
out.println("Server: Java HTTP Server from robot : 1.0");
out.println("Date: " + new Date());
out.println("Content-type: " + content);
out.println("Content-length: " + fileLength);
out.println(); // ligne d'espace entre le header et le contenu, très important
out.flush(); // vidage du tampon de flux de sortie des caractères
dataOut.write(fileData, 0, fileLength);
dataOut.flush();
if (verbose) {
System.out.println("File " + fileRequested + " not found");
}
}
}

14
MessageListener.java

@ -0,0 +1,14 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package robotindex;
/**
*
* @author arthurdambrine
*/
public interface MessageListener {
public void newMessage(String message);
}

33
README.md

@ -0,0 +1,33 @@
# Voici comment installer/utiliser le programme JAVA :
NOTE : Vous trouverez dans le dossier dist une version compilée sans les accès à la base de donnée,
cepedant vous pouvez recompiler le projet chez vous pour y ajouter les accès à votre base.
## 1/ Recuperer le .jar dans le dossier dist et le connecteur mysql .jar dans lib_to_add
## 2/ Les placer sur le serveur dans un dossier (necessite JRE 11 pour être lancés)
## 3/ Depuis le dossier lancer la commande :
NOTE : Vous pouvez tester le programme rapidement en lançant ./run.sh dans le dossier dist (necessite JRE 11)
-----
$ java -cp mysql-connector-java.jar:RobotIndex.jar robotindex.RobotIndex 'param1:URL' 'param2:nbIterations' 'param3:portSrvWebNotification'
-----
3 Arguments au lancement du programme JAVA :
1 - URL du site à tester
2 - Nb d'itérations, nombre de liens parcourus par le programme (default 15)
3 - Numero du port pour le serveur web de notif (default 8080)
Utilisation de la notification d'avancement :
Le developeur du site web pourra ainsi tester avec une requête GET l'etat d'avancement de l'algo :
( Exemple : boîte de dialogue javascript test toutes les 1 sec l'avancement ).
-> Requette GET : http://urlprogrammejava.dev:port (format application/json)
NOTE: Le developpeur utilisant ce programme doit s'assurer avant de le lancer que le PORT utilisé pour le WebNotification est LIBRE. (e.g. avoir une fonction de test côté serveur avant de lancer le programme JAVA)

362
RobotIndex.java

@ -0,0 +1,362 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package robotindex;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
/**
*
* @author arthurdambrine
*/
public class RobotIndex implements Runnable {
private static int PORT = 8080; // Port par défaut pour le HTTP notification server
private static List<String> urlList;
private static Boolean isErrorMode = false;
private static ArrayList<String> urlListInternesParcourir = new ArrayList<String>();
private static ArrayList<String> urlListParcourus = new ArrayList<String>();
private static ArrayList<String> urlListIndexed = new ArrayList<String>();
private static ArrayList<Integer> compteurPointsIndexed = new ArrayList<Integer>();
private static MessageListener messageListener;
public void setMessageListener(MessageListener messageListener) {
RobotIndex.messageListener = messageListener;
}
/**
* @param args the command line arguments
* @throws java.net.MalformedURLException
*/
public static void main(String[] args) throws IOException, InterruptedException {
String urlDepart; // variable qui viendra acceuillr l'URL en entrée de notre programme
int nbIterations; // nombre de pages à parcourir dans le site Web
if (args.length < 2) {
System.out.println("Pas assez d'argument en entrée, demande URL + Nb iterations");
// Par defaut notre programme va tester le site https://art-dambrine.ovh sur une base de 10 pages à tester.
urlDepart = "https://art-dambrine.ovh";
nbIterations = 10;
// Si on donne uniquement une URl en premier paramètre le programme l'accepte et fera le test sur une base de 10 pages à tester.
if (args.length == 1) {
urlDepart = args[0];
}
} else {
/* Si tous les arguments sont donnés en entrée on prepare le programme avec :
- urlDepart
- nbIterations
*/
urlDepart = args[0];
nbIterations = Integer.parseInt(args[1]);
if (args.length == 3) {
// Si le troisième argument est donné en entrée port de notification pour le HTTP notification server
PORT = Integer.parseInt(args[2]);
}
// Verifications légères sur l'URL donnée en paramètre
// NOTE: si le site n'accepte pas le robot ou n'est pas un site valide alors le robot va s'arrêter à la première itération
if (!args[0].startsWith("http")) {
System.out.println("N'est pas une url valide");
return;
}
if (!args[0].contains("//")) {
System.out.println("N'est pas une url valide");
return;
}
if (!args[0].contains(".")) {
System.out.println("N'est pas une url valide");
return;
}
}
// Creation d'un thread dedié pour le server HTTP notificaton
RobotIndex myServer = new RobotIndex();
Thread thread = new Thread(myServer);
thread.start();
// Preparation du parcours de la première page du site Web
URL siteUrl = new URL(nettoyerURL(urlDepart));
urlListParcourus.add(urlDepart);
urlList = fullUrlList(siteUrl.toString());
messageListener.newMessage("{\"message\":\"" + "Analyse premiere page" + "\"}");
// on va ici parcourir la première page du site web
if (!isErrorMode) {
URL formatUrl = null;
System.out.println(" === Sites à indexer dans notre base\n\n");
for (String url : urlList) {
formatUrl = new URL(url);
if (!formatUrl.getHost().equals(siteUrl.getHost())) {
// Indexer dans la liste urlListIndexed
if (!urlListIndexed.contains(formatUrl.getHost())) {
System.out.println("Adding: " + formatUrl.getHost());
urlListIndexed.add(formatUrl.getHost());
compteurPointsIndexed.add(1);
} else {
int getIndex = urlListIndexed.indexOf(formatUrl.getHost());
compteurPointsIndexed.set(getIndex, compteurPointsIndexed.get(getIndex) + 1);
}
}
}
System.out.println("\n\n === Liens à parcourir \n\n");
for (String url : urlList) {
// Préparation pour parcourir les URL internes
if (url.endsWith("/")) {
formatUrl = new URL(url.substring(0, url.length() - 1));
} else {
formatUrl = new URL(url);
}
if (formatUrl.getHost().equals(siteUrl.getHost())) {
if (siteUrl.getHost().equals(formatUrl.getHost())) {
if (isValidInterne(formatUrl.toString())) {
// Liens valides internes à parcourir:
System.out.println(formatUrl);
urlListInternesParcourir.add(formatUrl.toString());
}
}
}
}
} // fin de parcours de la promière page du site web
int cpt = 0;
String lienCourant;
if (!urlListInternesParcourir.isEmpty()) {
// On demarre ici la boucle principale de parcours
while (cpt < nbIterations) {
float progression = ((float) cpt / (float) nbIterations) * 100;
messageListener.newMessage("{\"message\":\"" + "Iteration page numero " + cpt + "\", \"progression\" : \"" + (int) progression + "\"}");
System.out.println("\n\n\n ++++ Lien parcouru pour cette iteration :" + urlListInternesParcourir.get(0) + "\n");
System.out.println("• urlListInternesParcourir :");
for (int i = 0; i < urlListInternesParcourir.size(); i++) {
System.out.println("-> " + urlListInternesParcourir.get(i));
}
lienCourant = urlListInternesParcourir.get(0);
urlListParcourus.add(urlListInternesParcourir.get(0));
siteUrl = new URL(urlListInternesParcourir.get(0));
urlListInternesParcourir.remove(0);
urlList = fullUrlList(siteUrl.toString());
if (!isErrorMode) {
URL formatUrl = null;
System.out.println("\n === Sites à indexer dans notre base\n");
for (String url : urlList) {
formatUrl = new URL(url);
if (!formatUrl.getHost().equals(siteUrl.getHost())) {
// Indexer dans la liste urlListIndexed
if (!urlListIndexed.contains(formatUrl.getHost())) {
System.out.println("Adding: " + formatUrl.getHost());
urlListIndexed.add(formatUrl.getHost());
compteurPointsIndexed.add(1);
} else {
int getIndex = urlListIndexed.indexOf(formatUrl.getHost());
compteurPointsIndexed.set(getIndex, compteurPointsIndexed.get(getIndex) + 1);
}
}
}
for (String url : urlList) {
// Préparation pour parcourir les URL internes
if (url.endsWith("/")) {
formatUrl = new URL(url.substring(0, url.length() - 1));
} else {
formatUrl = new URL(url);
}
if (formatUrl.getHost().equals(siteUrl.getHost())) {
if (siteUrl.getHost().equals(formatUrl.getHost())) {
if (isValidInterne(formatUrl.toString())) {
// Liens valides internes à parcourir:
if (!formatUrl.toString().equals(lienCourant)) { // NE PAS AJOUTER LIEN EN COURS
if (!urlListInternesParcourir.contains(formatUrl.toString())) { // NON CONTENU DEDEANS
if (!urlListParcourus.contains(formatUrl.toString())) {
// Ajout d'un lien dans la liste à parcourir après verifications.
urlListInternesParcourir.add(formatUrl.toString());
}
}
}
}
}
}
}
}
if (urlListInternesParcourir.isEmpty()) {
// Si la liste des url à parcourir est vide on sort de la boucle principale
break;
}
cpt++;
} // fin de la boucle while principale
}
// Affichage de tous les scores
messageListener.newMessage("{\"message\":\"" + "SQL insert" + "\", \"progression\" : \"" + 100 + "\"}");
System.out.println("\n\n == Printing all scores : ==");
for (int i = 0; i < urlListIndexed.size(); i++) {
System.out.println("N°" + i + " " + urlListIndexed.get(i) + " scored : " + compteurPointsIndexed.get(i));
}
// Conexion à MYSQL et envoi des resultats
try {
Class.forName("com.mysql.cj.jdbc.Driver");
// Connexion à la base de donnée
try ( Connection con = DriverManager.getConnection(
"jdbc:mysql://DATABASE_URL:3306/DATABASE_NAME", "USERNAME", "PASSWORD")) {
Statement stmt = con.createStatement();
// INSERTION de la donnée SITE
stmt.execute("INSERT INTO SITE VALUES ('" + urlDepart + "', NOW()) ON DUPLICATE KEY UPDATE creation_date = NOW();");
for (int i = 0; i < urlListIndexed.size(); i++) {
// Insertion des données LINK
stmt.execute("INSERT INTO LINK VALUES('" + urlListIndexed.get(i) + "'," + compteurPointsIndexed.get(i) + ",'" + urlDepart + "')ON DUPLICATE KEY UPDATE score = " + compteurPointsIndexed.get(i) + ";");
}
}
} catch (ClassNotFoundException | SQLException e) {
System.out.println(e);
}
// Ordonne au serveur HTTP de se preparer à se fermer à la prochaine requête
messageListener.newMessage("{\"message\":\"" + "finish" + "\", \"progression\" : \"" + 100 + "\"}");
// Envoie un requête GET qui va fermer le serveur en le faisant sortir de sa boucle d'écoute
siteUrl = new URL(nettoyerURL("http://localhost:" + PORT));
urlList = fullUrlList(siteUrl.toString());
}
public static List<String> fullUrlList(String siteUrl) throws MalformedURLException, IOException {
// Cette methode retourne l'ensemble des Url de la page en appellant le parser
URL url = new URL(siteUrl);
UrlParser parser = new UrlParser(url);
BufferedReader br = null;
try {
br = new BufferedReader(new InputStreamReader(url.openStream()));
} catch (IOException iOException) {
System.out.println("ERROR: " + iOException.getMessage() + "\n\n");
isErrorMode = true;
}
String line;
if (br != null) {
try {
while ((line = br.readLine()) != null) {
parser.parseLine(line);
}
} catch (IOException e) {
// Affiche l'exception
System.out.println(e);
} finally {
if (br != null) {
br.close();
}
}
}
return parser.getFullUrlList();
}
public static String nettoyerURL(String URLentry) {
// Retire le / à la fin de l'URL donnée
if (URLentry.endsWith("/")) {
URLentry = URLentry.substring(0, URLentry.length() - 1);
}
System.out.println("URL de départ: " + URLentry + "\n\n");
return URLentry;
}
public static Boolean isValidInterne(String formatUrl) {
// Permet d'exclure les fichiers contenant les extensions suivantes exemple: https://art-dambrine.ovh/mon-image.png
if (formatUrl.endsWith(".png") == false
&& formatUrl.endsWith(".js") == false
&& formatUrl.endsWith(".svg") == false
&& formatUrl.endsWith(".css") == false
&& formatUrl.endsWith(".ico") == false
&& formatUrl.endsWith(".jpg") == false
&& formatUrl.endsWith("#") == false) {
return true;
} else {
return false;
}
}
@Override
public void run() {
// Lancement du thread server
JsonNotificationHTTPServer.startNotificationServer(PORT);
}
}

180
UrlParser.java

@ -0,0 +1,180 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package robotindex;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
*
* @author arthurdambrine
*/
public class UrlParser {
private static URL Monsite;
private URL aURL;
private static List urlList, fullUrlList;
// regex pour les liens de type https://monsite.ovh
private static final Pattern ptn
= Pattern.compile("(https?|ftp|file)://[-a-zA-Z0-9+&@#/%?=~_|!:,.;]*[-a-zA-Z0-9+&@#/%=~_|]");
// regex pour les liens internes de type href="/chemin"
private static final Pattern ptn2
= Pattern.compile("<a\\s+(?:[^>]*?\\s+)?href=([\"'])(.*?)\\1");
// constructeur
public UrlParser() {
urlList = new ArrayList();
fullUrlList = new ArrayList();
}
public UrlParser(URL MonSite) {
this();
this.Monsite = MonSite;
}
// methodes
public void parseLine(String line) {
urlList = captureValues(line);
for (Object item : urlList) {
if (!fullUrlList.contains(item)) {
fullUrlList.add(item);
}
}
}
public void displayUrl() throws MalformedURLException {
String lURL; // Tampon pour l'URL en cours de traitement
for (int i = 0; i < urlList.size(); i++) {
// Chaque URL est affichée indépendemment
lURL = urlList.get(i).toString();
//System.out.println(lUrl); // affiche l'URL complète
aURL = new URL(lURL);
// Préparation pour parcourir les URL internes
if (Monsite.getHost().equals(aURL.getHost())) {
if (aURL.getPath().endsWith("/")) {
System.out.println(aURL);
}
}
//System.out.println(aURL.getPath());
//System.out.println("Site = " + aURL.getHost()); // Affiche le nom de domaine example.com
}
}
// Cette methode permet d'extraire le pattern et retourne au format liste
public static List<String> captureValues(String largeText) {
Matcher mtch = ptn.matcher(largeText);
Matcher m = ptn2.matcher(largeText);
List<String> ips = new ArrayList<String>();
// Première regex sur url http(s)://...
while (mtch.find()) {
if (IsMatch(mtch.group())) {
ips.add(mtch.group());
}
}
while (m.find()) { // Deuxième regex (prendre en compte les href)
if (m.group(2).endsWith(".html")) {
//System.out.println("TEST: " + m.group(2));
if (!m.group(2).startsWith("htt")) {
// Si on a un lien comme href="docs/blabla.html"
if (!m.group(2).startsWith("/")) {
//System.out.println("TEST: " + m.group()) ;
// Ajouter un / à la fin est une astuce de différenciation, il faudra le retirer ensuite pour parcourir
if (Monsite.toString().charAt(4) == 's') {
ips.add("https://" + Monsite.getHost().toString() + Monsite.getPath().toString() + "/" + m.group(2) + "/");
} else {
ips.add("http://" + Monsite.getHost().toString() + Monsite.getPath().toString() + "/" + m.group(2) + "/");
}
}
}
if (m.group(2).startsWith("htt")) { // Si on a un lien href="http://monsite.fr/blabla.html"
if (!m.group(2).startsWith("/")) {
//System.out.println("MATCH de http href: " + m.group(2)) ;
ips.add(m.group(2));
}
}
} else if (m.group(2).length() > 2) // securisation pour eviter de planter le m.group(2).charAt(1) == '/'
{
if (m.group(2).charAt(1) == '/') // si on a un lien de type : href:"//en.wikipedia.org"
{
if (Monsite.toString().charAt(4) == 's') {
//System.out.println("https:"+m.group(2));
ips.add("https:" + m.group(2));
} else {
//System.out.println("http:"+m.group(2));
ips.add("http:" + m.group(2));
}
} else // si lien interne commenaçant par /
{
if (m.group(2).startsWith("/")) {
//System.out.println("TEST: " + m.group()) ;
// Ajouter un / à la fin est une astuce de différenciation, il faudra le retirer ensuite pour parcourir
if (Monsite.toString().charAt(4) == 's') {
ips.add("https://" + Monsite.getHost().toString() + m.group(2) + "/");
} else {
ips.add("http://" + Monsite.getHost().toString() + m.group(2) + "/");
}
}
}
}
}
return ips;
}
// Cette methode verifie si oui ou non la chaîne correspond à mon pattern
private static boolean IsMatch(String s) {
try {
Matcher matcher = ptn.matcher(s);
return matcher.matches();
} catch (RuntimeException e) {
return false;
}
}
public static void displayFullUrlList() {
for (int i = 0; i < fullUrlList.size(); i++) {
// Chaque URL est affichée indépendemment
System.out.println(fullUrlList.get(i));
}
}
public static List<String> getFullUrlList() {
return fullUrlList;
}
}

BIN
dist/.DS_Store

Binary file not shown.

BIN
dist/RobotIndex.jar

Binary file not shown.

BIN
dist/mysql-connector-java.jar

Binary file not shown.

2
dist/run.sh

@ -0,0 +1,2 @@
#!/bin/sh
java -cp mysql-connector-java.jar:RobotIndex.jar robotindex.RobotIndex
Loading…
Cancel
Save