<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Rinto Priambodo on Medium]]></title>
        <description><![CDATA[Stories by Rinto Priambodo on Medium]]></description>
        <link>https://medium.com/@rintoprie?source=rss-832eb4c8bf58------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*HqECJIhOxN-vWac5KRQ8Aw.png</url>
            <title>Stories by Rinto Priambodo on Medium</title>
            <link>https://medium.com/@rintoprie?source=rss-832eb4c8bf58------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Tue, 19 May 2026 12:49:13 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@rintoprie/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[Python for Cybersecurity Beginners: Creating Your First Port Scanner and Vulnerability Checker]]></title>
            <link>https://medium.com/@rintoprie/python-for-cybersecurity-beginners-creating-your-first-port-scanner-and-vulnerability-checker-cc5837c166a1?source=rss-832eb4c8bf58------2</link>
            <guid isPermaLink="false">https://medium.com/p/cc5837c166a1</guid>
            <category><![CDATA[vulnerability]]></category>
            <category><![CDATA[networking]]></category>
            <category><![CDATA[cybersecurity]]></category>
            <category><![CDATA[python]]></category>
            <dc:creator><![CDATA[Rinto Priambodo]]></dc:creator>
            <pubDate>Tue, 19 May 2026 04:50:35 GMT</pubDate>
            <atom:updated>2026-05-19T04:50:35.213Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*SeJ3xIeWmbP1_2WC" /><figcaption>Photo by <a href="https://unsplash.com/@jefflssantos?utm_source=medium&amp;utm_medium=referral">Jefferson Santos</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p>Cybersecurity is not only about advanced hacking tools or complex malware analysis. Many basic security tasks can be learned by building small tools from scratch. One of the best beginner projects is a <strong>port scanner</strong>.</p><p>A port scanner helps identify which network ports are open on a target machine. Open ports can indicate running services such as SSH, HTTP, HTTPS, FTP, or databases. By extending the scanner slightly, we can also create a simple <strong>vulnerability checker</strong> that gives basic security warnings.</p><p>This tutorial is for learning purposes only. Run the code only on systems you own or have permission to test.</p><h3>What Is a Port?</h3><p>A port is a communication endpoint used by network services. For example:</p><pre>Port  Service<br>----  -------<br>22    SSH<br>80    HTTP<br>443   HTTPS<br>3306  MySQL<br>5432  PostgreSQL</pre><p>When a port is open, it usually means a service is listening for connections.</p><p>For example, if port 22 is open, the server may allow SSH login. If port 80 is open, the server may be running a web server.</p><h3>Project Goal</h3><p>In this beginner project, we will build a Python tool that can:</p><ol><li>Scan a range of ports.</li><li>Detect open ports.</li><li>Identify common services.</li><li>Display simple security warnings.</li><li>Generate beginner-friendly vulnerability notes.</li></ol><h3>Basic Port Scanner in Python</h3><p>Python provides the socket module, which allows us to create network connections.</p><p>Here is a simple port scanner:</p><pre>import socket<br><br>target = &quot;127.0.0.1&quot;<br>for port in range(1, 1025):<br>    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)<br>    sock.settimeout(0.5)<br>    result = sock.connect_ex((target, port))<br>    if result == 0:<br>        print(f&quot;[OPEN] Port {port}&quot;)<br>    sock.close()</pre><p>The function connect_ex() tries to connect to a port. If the result is 0, the connection is successful, meaning the port is open.</p><p>You can test this on your own computer using:</p><pre>python scanner.py</pre><h3>Improving the Scanner</h3><p>The first version works, but it is very basic. Let us improve it by adding:</p><ul><li>User input</li><li>Common service detection</li><li>Security warning messages</li></ul><pre>import socket<br><br>COMMON_SERVICES = {<br>    21: &quot;FTP&quot;,<br>    22: &quot;SSH&quot;,<br>    23: &quot;Telnet&quot;,<br>    25: &quot;SMTP&quot;,<br>    53: &quot;DNS&quot;,<br>    80: &quot;HTTP&quot;,<br>    110: &quot;POP3&quot;,<br>    139: &quot;NetBIOS&quot;,<br>    143: &quot;IMAP&quot;,<br>    443: &quot;HTTPS&quot;,<br>    445: &quot;SMB&quot;,<br>    3306: &quot;MySQL&quot;,<br>    3389: &quot;Remote Desktop&quot;,<br>    5432: &quot;PostgreSQL&quot;<br>}<br><br>RISKY_PORTS = {<br>    21: &quot;FTP sends data in plain text. Consider using SFTP instead.&quot;,<br>    23: &quot;Telnet is insecure because it sends credentials in plain text.&quot;,<br>    445: &quot;SMB should not be exposed to the public internet.&quot;,<br>    3306: &quot;MySQL should usually be restricted to trusted hosts only.&quot;,<br>    3389: &quot;Remote Desktop should be protected with VPN or strong access control.&quot;<br>}<br><br>def scan_port(target, port):<br>    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)<br>    sock.settimeout(0.5)<br>    result = sock.connect_ex((target, port))<br>    sock.close()<br>    return result == 0<br><br>def check_vulnerability(port):<br>    if port in RISKY_PORTS:<br>        return RISKY_PORTS[port]<br>    return &quot;No basic warning available.&quot;<br><br>def main():<br>    target = input(&quot;Enter target IP address: &quot;)<br>    start_port = int(input(&quot;Enter start port: &quot;))<br>    end_port = int(input(&quot;Enter end port: &quot;))<br>    print(f&quot;\nScanning {target} from port {start_port} to {end_port}...\n&quot;)<br>    for port in range(start_port, end_port + 1):<br>        if scan_port(target, port):<br>            service = COMMON_SERVICES.get(port, &quot;Unknown Service&quot;)<br>            warning = check_vulnerability(port)<br>            print(f&quot;[OPEN] Port {port} ({service})&quot;)<br>            print(f&quot;       Security note: {warning}&quot;)<br><br>if __name__ == &quot;__main__&quot;:<br>    main()</pre><h3>Example Output</h3><p>If several ports are open, the output may look like this:</p><pre>Scanning 127.0.0.1 from port 1 to 1000...<br><br>[OPEN] Port 22 (SSH)<br>       Security note: No basic warning available.<br>[OPEN] Port 80 (HTTP)<br>       Security note: No basic warning available.<br>[OPEN] Port 3306 (MySQL)<br>       Security note: MySQL should usually be restricted to trusted hosts only.</pre><p>This output helps beginners understand which services are exposed and which ones may require security review.</p><h3>Adding Banner Grabbing</h3><p>Some services send a text response when a connection is made. This is called a <strong>banner</strong>. A banner may reveal the software name or version.</p><p>For example, an FTP server may show something like:</p><pre>220 FTP Server Ready</pre><p>Here is a simple banner grabbing function:</p><pre>def grab_banner(target, port):<br>    try:<br>        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)<br>        sock.settimeout(1)<br>        sock.connect((target, port))<br><br>        banner = sock.recv(1024).decode(errors=&quot;ignore&quot;).strip()<br>        sock.close()<br>        return banner<br>    except:<br>        return None</pre><p>Then we can modify the open port section:</p><pre>banner = grab_banner(target, port)<br><br>if banner:<br>    print(f&quot;       Banner: {banner}&quot;)<br>else:<br>    print(&quot;       Banner: Not available&quot;)</pre><p>Banner grabbing is useful because exposed software version information can help administrators identify outdated services. However, this beginner tool does not exploit anything. It only reports what is visible.</p><h3>Complete Version</h3><pre>import socket<br><br>COMMON_SERVICES = {<br>    21: &quot;FTP&quot;,<br>    22: &quot;SSH&quot;,<br>    23: &quot;Telnet&quot;,<br>    25: &quot;SMTP&quot;,<br>    53: &quot;DNS&quot;,<br>    80: &quot;HTTP&quot;,<br>    110: &quot;POP3&quot;,<br>    139: &quot;NetBIOS&quot;,<br>    143: &quot;IMAP&quot;,<br>    443: &quot;HTTPS&quot;,<br>    445: &quot;SMB&quot;,<br>    3306: &quot;MySQL&quot;,<br>    3389: &quot;Remote Desktop&quot;,<br>    5432: &quot;PostgreSQL&quot;<br>}<br>RISKY_PORTS = {<br>    21: &quot;FTP sends data in plain text. Consider using SFTP instead.&quot;,<br>    23: &quot;Telnet is insecure because it sends credentials in plain text.&quot;,<br>    445: &quot;SMB should not be exposed to the public internet.&quot;,<br>    3306: &quot;MySQL should usually be restricted to trusted hosts only.&quot;,<br>    3389: &quot;Remote Desktop should be protected with VPN or strong access control.&quot;<br>}<br><br>def scan_port(target, port):<br>    try:<br>        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)<br>        sock.settimeout(0.5)<br>        result = sock.connect_ex((target, port))<br>        sock.close()<br>        return result == 0<br>    except socket.error:<br>        return False<br><br>def grab_banner(target, port):<br>    try:<br>        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)<br>        sock.settimeout(1)<br>        sock.connect((target, port))<br>        banner = sock.recv(1024).decode(errors=&quot;ignore&quot;).strip()<br>        sock.close()<br>        return banner<br>    except:<br>        return None<br><br>def check_vulnerability(port):<br>    if port in RISKY_PORTS:<br>        return RISKY_PORTS[port]<br>    return &quot;No basic warning available.&quot;<br><br>def main():<br>    target = input(&quot;Enter target IP address: &quot;)<br>    start_port = int(input(&quot;Enter start port: &quot;))<br>    end_port = int(input(&quot;Enter end port: &quot;))<br>    print(f&quot;\nScanning {target} from port {start_port} to {end_port}...\n&quot;)<br>    open_ports = []<br>    for port in range(start_port, end_port + 1):<br>        if scan_port(target, port):<br>            open_ports.append(port)<br>            service = COMMON_SERVICES.get(port, &quot;Unknown Service&quot;)<br>            warning = check_vulnerability(port)<br>            banner = grab_banner(target, port)<br>            print(f&quot;[OPEN] Port {port} ({service})&quot;)<br>            print(f&quot;       Security note: {warning}&quot;)<br>            if banner:<br>                print(f&quot;       Banner: {banner}&quot;)<br>            else:<br>                print(&quot;       Banner: Not available&quot;)<br>            print()<br>    print(&quot;Scan completed.&quot;)<br>    print(f&quot;Total open ports found: {len(open_ports)}&quot;)<br><br>if __name__ == &quot;__main__&quot;:<br>    main()</pre><h3>How This Tool Can Be Improved</h3><p>This beginner project can be expanded in several ways:</p><ol><li><strong>Add multithreading</strong><br>This will make scanning faster.</li><li><strong>Save results to a file</strong><br>The scanner can export results to TXT, CSV, or JSON.</li><li><strong>Add HTTP checking</strong><br>The tool can inspect HTTP response headers.</li><li><strong>Add CVE lookup</strong><br>Service versions can be compared with known vulnerability databases.</li><li><strong>Create a simple dashboard</strong><br>Results can be displayed using Flask or Streamlit.</li></ol><h3>Security Reminder</h3><p>Port scanning is a normal activity in cybersecurity learning, but it must be done responsibly. Only scan:</p><ul><li>Your own computer</li><li>Your own virtual machines</li><li>Your own lab network</li><li>Systems where you have written permission</li><li>Never scan public systems without authorization.</li></ul><h3>Conclusion</h3><p>In this tutorial, we created a simple cybersecurity tool using Python. The tool can scan ports, identify common services, grab banners, and display basic security warnings.</p><p>This project is a good starting point for beginners because it introduces several important cybersecurity concepts:</p><ul><li>Network ports</li><li>Open services</li><li>Service exposure</li><li>Basic vulnerability checking</li><li>Defensive security awareness</li></ul><p>By building tools like this from scratch, beginners can better understand how cybersecurity tools work behind the scenes.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=cc5837c166a1" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Building a Simple Web Application with Python and Flask in 2026]]></title>
            <link>https://medium.com/@rintoprie/building-a-simple-web-application-with-python-and-flask-in-2026-c760065fb4b8?source=rss-832eb4c8bf58------2</link>
            <guid isPermaLink="false">https://medium.com/p/c760065fb4b8</guid>
            <category><![CDATA[python]]></category>
            <category><![CDATA[web-development]]></category>
            <category><![CDATA[flask]]></category>
            <category><![CDATA[sqlite]]></category>
            <category><![CDATA[gunicorn]]></category>
            <dc:creator><![CDATA[Rinto Priambodo]]></dc:creator>
            <pubDate>Wed, 07 Jan 2026 08:33:36 GMT</pubDate>
            <atom:updated>2026-01-07T08:33:36.175Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*xA_4u90Nuvoio1-D" /><figcaption>Photo by <a href="https://unsplash.com/@vantaymedia?utm_source=medium&amp;utm_medium=referral">Van Tay Media</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p>Flask is still one of the nicest ways to build a web app in Python when you want something lightweight, readable, and easy to grow. In early 2026, you can comfortably pair <strong>modern Python (3.14.x)</strong> with <strong>Flask 3.1.x</strong> and get a clean developer experience without a lot of ceremony.</p><p>In this tutorial, we’ll build a small but complete “Notes” web app:</p><ul><li>A homepage that lists notes</li><li>Create / edit / delete notes</li><li>Server-rendered pages with Jinja templates</li><li>SQLite database with SQLAlchemy (via Flask-SQLAlchemy)</li><li>A tiny JSON API endpoint</li><li>A production-style entry point (so it’s deployable)</li></ul><p>You can finish this in one sitting, and you’ll end with a structure you can reuse for bigger apps.</p><h3>Prerequisites</h3><ul><li><strong>Python 3.9+</strong> (Flask supports 3.9 and newer), ideally the latest stable Python. (<a href="https://flask.palletsprojects.com/en/stable/installation/?utm_source=chatgpt.com">Flask Documentation</a>)</li><li>Basic comfort with terminal commands and Python files.</li></ul><p>As of <strong>January 2026</strong>, python.org shows <strong>Python 3.14.2</strong> as the latest release on the downloads page.</p><h3>1) Create the project and virtual environment</h3><pre>mkdir flask-notes-2026<br>cd flask-notes-2026<br>python -m venv .venv</pre><p>Activate the environment:</p><p><strong>macOS / Linux</strong></p><pre>source .venv/bin/activate</pre><p><strong>Windows (PowerShell)</strong></p><pre>.venv\Scripts\Activate.ps1</pre><p>Install dependencies:</p><pre>pip install Flask Flask-SQLAlchemy</pre><p>(Flask 3.1.2 is listed on PyPI as a recent stable release.)</p><p>Optionally, freeze requirements:</p><pre>pip freeze &gt; requirements.txt</pre><h3>2) Project structure</h3><p>Create this folder layout:</p><pre>flask-notes-2026/<br>  app/<br>    __init__.py<br>    db.py<br>    models.py<br>    routes.py<br>    templates/<br>      base.html<br>      index.html<br>      form.html<br>  instance/<br>    notes.sqlite3   (auto-created)<br>  run.py</pre><p>Why this structure? Because as your app grows, a single-file Flask app becomes hard to extend cleanly. Flask’s own tutorial recommends moving toward patterns like an application factory to avoid “tricky issues” as projects scale.</p><h3>3) Write the application factory</h3><p>Instead of creating the Flask app at the top level, we’ll use an <strong>application factory</strong> — a function (create_app) that <em>builds and returns</em> the app instance. This pattern is common in Flask projects because it makes the codebase easier to scale: you can load different configurations (dev/test/prod), initialize extensions (like SQLAlchemy) cleanly, and keep your project modular by registering components like Blueprints.</p><p>Create app/__init__.py:</p><pre># app/__init__.py<br>import os<br>from flask import Flask<br>from .db import db<br><br>def create_app():<br>    app = Flask(__name__, instance_relative_config=True)<br>    # Basic config<br>    app.config[&quot;SECRET_KEY&quot;] = os.environ.get(&quot;SECRET_KEY&quot;, &quot;dev-secret-change-me&quot;)<br>    # SQLite database in /instance<br>    app.config[&quot;SQLALCHEMY_DATABASE_URI&quot;] = &quot;sqlite:///&quot; + os.path.join(app.instance_path, &quot;notes.sqlite3&quot;)<br>    app.config[&quot;SQLALCHEMY_TRACK_MODIFICATIONS&quot;] = False<br>    # Ensure instance folder exists<br>    os.makedirs(app.instance_path, exist_ok=True)<br>    # Init extensions<br>    db.init_app(app)<br>    # Register routes (blueprint)<br>    from .routes import bp<br>    app.register_blueprint(bp)<br>    # Create tables (simple approach for this tutorial)<br>    with app.app_context():<br>        from . import models  # noqa: F401<br>        db.create_all()<br>    return app</pre><p>Two notes:</p><ol><li>We’re using an <strong>app factory</strong> (create_app) so it’s easier to test and deploy.</li><li>db.create_all() is fine for a tutorial. For real projects, you’ll likely want migrations—but we’ll keep this minimal.</li></ol><h3>4) Database setup (SQLAlchemy)</h3><p>Most web apps need persistent storage. Instead of keeping notes in memory (which disappears when the server restarts), we’ll store them in a database called<strong> SQLite</strong> for simplicity.</p><p>To talk to the database, we’ll use <strong>SQLAlchemy</strong>, a popular Python ORM (Object-Relational Mapper). Instead of writing raw SQL for every operation, SQLAlchemy lets us:</p><ul><li>Define tables as <strong>Python classes</strong> (models)</li><li>Query data using Python code (Note.query...) instead of manual SQL strings</li><li>Reduce repetitive boilerplate (connections, cursor handling, row mapping</li><li>Keep the app portable — switching from SQLite to PostgreSQL later becomes much easier</li></ul><p>In short: SQLAlchemy keeps database code cleaner, safer, and easier to maintain as your project grows.</p><p>Create app/db.py:</p><pre># app/db.py<br>from flask_sqlalchemy import SQLAlchemy<br><br>db = SQLAlchemy()</pre><p>Create app/models.py:</p><pre># app/models.py<br>from datetime import datetime<br>from .db import db<br><br>class Note(db.Model):<br>    id = db.Column(db.Integer, primary_key=True)<br>    title = db.Column(db.String(120), nullable=False)<br>    body = db.Column(db.Text, nullable=False)<br>    created_at = db.Column(db.DateTime, default=datetime.utcnow, nullable=False)<br>    def to_dict(self):<br>        return {<br>            &quot;id&quot;: self.id,<br>            &quot;title&quot;: self.title,<br>            &quot;body&quot;: self.body,<br>            &quot;created_at&quot;: self.created_at.isoformat() + &quot;Z&quot;,<br>        }</pre><p>That’s our entire data model.</p><h3>5) Routes: list, create, edit, delete (+ JSON API)</h3><p>This is the part where the app becomes interactive. <strong>Routes</strong> (also called <em>endpoints</em>) map a URL (like / or /new) to a Python function. Each function handles an HTTP request (GET/POST), performs any needed logic (read/write the database), and then returns a response which usually an HTML page, a redirect, or JSON.</p><p>We’ll also use a <strong>Blueprint</strong> so our routes are grouped in one module (notes) instead of living directly in the main app file. This keeps the project organized as it grows.</p><p>Create app/routes.py:</p><pre># app/routes.py<br>from flask import Blueprint, jsonify, redirect, render_template, request, url_for, abort<br>from .db import db<br>from .models import Note<br><br>bp = Blueprint(&quot;notes&quot;, __name__)<br><br>@bp.get(&quot;/&quot;)<br>def index():<br>    notes = Note.query.order_by(Note.created_at.desc()).all()<br>    return render_template(&quot;index.html&quot;, notes=notes)<br><br>@bp.route(&quot;/new&quot;, methods=[&quot;GET&quot;, &quot;POST&quot;])<br>def new_note():<br>    if request.method == &quot;POST&quot;:<br>        title = request.form.get(&quot;title&quot;, &quot;&quot;).strip()<br>        body = request.form.get(&quot;body&quot;, &quot;&quot;).strip()<br>        if not title or not body:<br>            return render_template(&quot;form.html&quot;, note=None, error=&quot;Title and body are required.&quot;)<br>        note = Note(title=title, body=body)<br>        db.session.add(note)<br>        db.session.commit()<br>        return redirect(url_for(&quot;notes.index&quot;))<br>    return render_template(&quot;form.html&quot;, note=None, error=None)<br><br>@bp.route(&quot;/&lt;int:note_id&gt;/edit&quot;, methods=[&quot;GET&quot;, &quot;POST&quot;])<br>def edit_note(note_id):<br>    note = Note.query.get(note_id)<br>    if note is None:<br>        abort(404)<br>    if request.method == &quot;POST&quot;:<br>        title = request.form.get(&quot;title&quot;, &quot;&quot;).strip()<br>        body = request.form.get(&quot;body&quot;, &quot;&quot;).strip()<br>        if not title or not body:<br>            return render_template(&quot;form.html&quot;, note=note, error=&quot;Title and body are required.&quot;)<br>        note.title = title<br>        note.body = body<br>        db.session.commit()<br>        return redirect(url_for(&quot;notes.index&quot;))<br>    return render_template(&quot;form.html&quot;, note=note, error=None)<br><br>@bp.post(&quot;/&lt;int:note_id&gt;/delete&quot;)<br>def delete_note(note_id):<br>    note = Note.query.get(note_id)<br>    if note is None:<br>        abort(404)<br>    db.session.delete(note)<br>    db.session.commit()<br>    return redirect(url_for(&quot;notes.index&quot;))<br><br># Tiny JSON API<br>@bp.get(&quot;/api/notes&quot;)<br>def api_notes():<br>    notes = Note.query.order_by(Note.created_at.desc()).all()<br>    return jsonify([n.to_dict() for n in notes])</pre><p>This is classic Flask: routes are just Python functions.</p><h3>6) Templates (Jinja)</h3><p><strong>Jinja</strong> is Flask’s built-in templating engine: it lets you write normal HTML, then “inject” dynamic data from Python using placeholders like {{ ... }} and control structures like {% for ... %} and {% if ... %}.</p><p>We need Jinja because it helps us:</p><ul><li><strong>Render dynamic pages</strong> (e.g., loop through notes and display them).</li><li><strong>Avoid repeating HTML</strong> using <strong>template inheritance</strong> (base.html → index.html, form.html), so shared layout (header, styles, navigation) lives in one place.</li><li><strong>Keep code clean</strong> by separating <strong>presentation (HTML)</strong> from <strong>logic (Python routes)</strong>.</li><li><strong>Stay safer by default</strong> since Jinja <strong>auto-escapes</strong> variables to reduce XSS risks when displaying user input.</li></ul><p>In short: Jinja turns plain HTML into reusable, data-driven pages without building strings manually in Python.</p><p>To use Jinja in this project, first create app/templates/base.html:</p><pre>&lt;!doctype html&gt;<br>&lt;html lang=&quot;en&quot;&gt;<br>  &lt;head&gt;<br>    &lt;meta charset=&quot;utf-8&quot; /&gt;<br>    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width,initial-scale=1&quot; /&gt;<br>    &lt;title&gt;{{ title or &quot;Flask Notes&quot; }}&lt;/title&gt;<br>    &lt;style&gt;<br>      body { font-family: system-ui, sans-serif; margin: 2rem; max-width: 860px; }<br>      header { display: flex; justify-content: space-between; align-items: center; gap: 1rem; }<br>      a { text-decoration: none; }<br>      .note { border: 1px solid #ddd; border-radius: 10px; padding: 1rem; margin: 1rem 0; }<br>      .muted { color: #666; font-size: 0.9rem; }<br>      .error { background: #fff3f3; border: 1px solid #ffd0d0; padding: 0.75rem; border-radius: 10px; }<br>      input, textarea { width: 100%; padding: 0.6rem; margin: 0.3rem 0 1rem; }<br>      button { padding: 0.6rem 0.9rem; border-radius: 10px; border: 1px solid #ccc; cursor: pointer; }<br>      .row { display:flex; gap:0.5rem; flex-wrap: wrap; align-items:center; }<br>      form { margin: 0; }<br>    &lt;/style&gt;<br>  &lt;/head&gt;<br>  &lt;body&gt;<br>    &lt;header&gt;<br>      &lt;h1 style=&quot;margin:0;&quot;&gt;&lt;a href=&quot;{{ url_for(&#39;notes.index&#39;) }}&quot;&gt;Flask Notes&lt;/a&gt;&lt;/h1&gt;<br>      &lt;nav class=&quot;row&quot;&gt;<br>        &lt;a href=&quot;{{ url_for(&#39;notes.new_note&#39;) }}&quot;&gt;New Note&lt;/a&gt;<br>        &lt;a class=&quot;muted&quot; href=&quot;{{ url_for(&#39;notes.api_notes&#39;) }}&quot;&gt;API&lt;/a&gt;<br>      &lt;/nav&gt;<br>    &lt;/header&gt;<br>    &lt;main style=&quot;margin-top: 1.5rem;&quot;&gt;<br>      {% block content %}{% endblock %}<br>    &lt;/main&gt;<br>  &lt;/body&gt;<br>&lt;/html&gt;</pre><p>Create app/templates/index.html:</p><pre>{% extends &quot;base.html&quot; %}<br>{% block content %}<br><br>{% if notes|length == 0 %}<br>  &lt;p class=&quot;muted&quot;&gt;No notes yet. Create your first one!&lt;/p&gt;<br>{% endif %}<br>{% for note in notes %}<br>  &lt;div class=&quot;note&quot;&gt;<br>    &lt;h2 style=&quot;margin:0 0 0.25rem 0;&quot;&gt;{{ note.title }}&lt;/h2&gt;<br>    &lt;div class=&quot;muted&quot;&gt;Created {{ note.created_at.strftime(&quot;%Y-%m-%d %H:%M&quot;) }} UTC&lt;/div&gt;<br>    &lt;p style=&quot;white-space: pre-wrap;&quot;&gt;{{ note.body }}&lt;/p&gt;<br>    &lt;div class=&quot;row&quot;&gt;<br>      &lt;a href=&quot;{{ url_for(&#39;notes.edit_note&#39;, note_id=note.id) }}&quot;&gt;Edit&lt;/a&gt;<br>      &lt;form action=&quot;{{ url_for(&#39;notes.delete_note&#39;, note_id=note.id) }}&quot; method=&quot;post&quot;<br>            onsubmit=&quot;return confirm(&#39;Delete this note?&#39;);&quot;&gt;<br>        &lt;button type=&quot;submit&quot;&gt;Delete&lt;/button&gt;<br>      &lt;/form&gt;<br>    &lt;/div&gt;<br>  &lt;/div&gt;<br>{% endfor %}<br>{% endblock %}</pre><p>Create app/templates/form.html:</p><pre>{% extends &quot;base.html&quot; %}<br>{% block content %}<br><br>{% if error %}<br>  &lt;div class=&quot;error&quot;&gt;{{ error }}&lt;/div&gt;<br>{% endif %}<br>&lt;form method=&quot;post&quot;&gt;<br>  &lt;label&gt;Title&lt;/label&gt;<br>  &lt;input name=&quot;title&quot; value=&quot;{{ note.title if note else &#39;&#39; }}&quot; maxlength=&quot;120&quot; required /&gt;<br>  &lt;label&gt;Body&lt;/label&gt;<br>  &lt;textarea name=&quot;body&quot; rows=&quot;8&quot; required&gt;{{ note.body if note else &#39;&#39; }}&lt;/textarea&gt;<br>  &lt;div class=&quot;row&quot;&gt;<br>    &lt;button type=&quot;submit&quot;&gt;Save&lt;/button&gt;<br>    &lt;a class=&quot;muted&quot; href=&quot;{{ url_for(&#39;notes.index&#39;) }}&quot;&gt;Cancel&lt;/a&gt;<br>  &lt;/div&gt;<br>&lt;/form&gt;<br>{% endblock %}</pre><h3>7) Add a runnable entry point</h3><p>Before we run the app, we need a <strong>single entry point</strong> file that exposes a Flask application object. This makes development simpler (one obvious place to start the app), and it also helps when deploying because most production servers (like Gunicorn/Waitress) expect something they can import, such as run:app.</p><p>Now let’s create run.py:</p><pre># run.py<br>from app import create_app<br><br>app = create_app()<br>if __name__ == &quot;__main__&quot;:<br>    app.run()</pre><h3>8) Run the app locally</h3><p>Flask’s docs show running with the CLI like this:</p><pre>flask --app run run --debug</pre><p>…but since our entry point is run.py, use:</p><pre>flask --app run:app run --debug</pre><p>Then open in browser: <a href="http://127.0.0.1:5000">http://127.0.0.1:5000</a></p><p>Important: Flask’s <strong>development server is only for local development</strong> — the docs explicitly warn not to use it for production.</p><h3>9) Quick “production” run (Gunicorn or Waitress)</h3><p>If you want to run this beyond your laptop, you can use a production WSGI server. When you run flask run, Flask starts a <strong>development server</strong>. It’s great for local work because it auto-reloads and gives helpful debug output, but it’s <strong>not designed for production</strong>: it’s single-process by default, not optimized for handling many concurrent users, and debug mode can expose sensitive information if misconfigured.</p><p>In production, you typically run your Flask app behind a <strong>WSGI server</strong> (Web Server Gateway Interface). Think of WSGI as the “adapter standard” that lets a web server talk to your Python web app. A production WSGI server (like <strong>Gunicorn</strong> or <strong>Waitress</strong>) will:</p><ul><li><strong>Run multiple worker processes/threads</strong> to handle more requests at once</li><li><strong>Manage crashes and restarts</strong> more gracefully than the dev server</li><li>Integrate cleanly with a real front-facing web server (often <strong>Nginx/Caddy</strong>) for things like TLS (HTTPS), caching, request size limits, and static files</li></ul><p>A common real-world stack looks like:</p><p><strong>Browser → Nginx/Caddy (HTTPS, static files) → Gunicorn/Waitress (WSGI) → Flask app</strong></p><p>For this tutorial, we’ll keep it simple and just show how to start the app with Gunicorn or Waitress so you’re using the “right kind” of server when you move beyond your laptop.</p><h3>Option A: Gunicorn (common on Linux; runs on Windows via WSL)</h3><p>Flask’s deployment docs show Gunicorn can load either an app object or call an app factory.</p><p>How to install:</p><pre>pip install gunicorn</pre><p>Run (app object):</p><pre>gunicorn -w 4 &#39;run:app&#39;</pre><p>Or run via factory (if you prefer):</p><pre>gunicorn -w 4 &#39;app:create_app()&#39;</pre><h3>Option B: Waitress (works directly on Windows too)</h3><p>Flask’s deployment docs also show Waitress, including the --call option for app factories.</p><p>How to install:</p><pre>pip install waitress</pre><p>Run:</p><pre>waitress-serve --host 127.0.0.1 run:app</pre><p>Or (factory style):</p><pre>waitress-serve --host 127.0.0.1 --call app:create_app</pre><h3>10) A couple of “2026-friendly” improvements (optional, but worth it)</h3><p>These aren’t required to learn Flask, but they’re the upgrades that quickly separate a “tutorial app” from something you can maintain and deploy confidently.</p><ol><li><strong>Add migrations (Alembic / Flask-Migrate)</strong><br>Right now we use db.create_all(), which creates tables from models <strong>only if they don’t exist</strong>. The problem: when your models change (add a column, rename a field, split a table), create_all() won’t safely update an existing database. Migrations solve this by generating versioned schema changes (like “add column created_at”).</li><li><strong>Add CSRF protection for forms (Flask-WTF or equivalent)</strong><br>A web form that performs actions (create/edit/delete) can be vulnerable to <strong>CSRF</strong> (Cross-Site Request Forgery), where a malicious site tricks a logged-in user’s browser into submitting a request to your app without them realizing. CSRF protection works by attaching a hidden token to forms and verifying it on submit.</li><li><strong>Add tests (Flask test client + pytest)</strong><br>Once your app has real behavior (CRUD + validation), tests protect you from accidental breakage. Flask includes a test client that can simulate requests without running a server, so you can write quick regression tests.</li></ol><h3>What you built (and why it matters)</h3><p>In a small amount of code, you now have:</p><ul><li>A real database-backed web app</li><li>Clean separation of concerns (factory, routes, models, templates)</li><li>A starting point that can scale into authentication, user accounts, file uploads, background jobs, and more</li></ul><p>Flask stays popular because it doesn’t force a framework-shaped life onto your code. You choose the pieces you need, when you need them.</p><p>If you want, tell me what direction you want to take next (authentication, REST API, admin panel, Tailwind styling, or deployment to a specific platform), and I’ll extend this project structure accordingly.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=c760065fb4b8" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Copying Files Across My Local Network Has Never Been This Easy]]></title>
            <link>https://medium.com/@rintoprie/copying-files-across-my-local-network-has-never-been-this-easy-889d224ebe5e?source=rss-832eb4c8bf58------2</link>
            <guid isPermaLink="false">https://medium.com/p/889d224ebe5e</guid>
            <category><![CDATA[file-sharing]]></category>
            <category><![CDATA[python]]></category>
            <dc:creator><![CDATA[Rinto Priambodo]]></dc:creator>
            <pubDate>Mon, 29 Dec 2025 13:19:20 GMT</pubDate>
            <atom:updated>2025-12-29T13:19:20.147Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*H2VwUqWS1N9ZUu6s" /><figcaption>Photo by <a href="https://unsplash.com/@taanhuyn?utm_source=medium&amp;utm_medium=referral">Taan Huyn</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p>We’ve all been there. You want to send a file, maybe a small PDF, maybe a 10GB video, from one computer to another on the same Wi-Fi.</p><p>Sure, you could set up SSH, install FTP, or grab a USB stick. But what if you could do it instantly with a tool you <em>already have</em>?</p><p>Good news: you can. If you have Python installed, you’re just one command away from turning your computer into a temporary file server.</p><h3>The Magic Command</h3><p>Open a terminal, move into the folder that contains your file, and run:</p><pre>python3 -m http.server 8000 --bind 0.0.0.0</pre><p>That’s it.</p><p>You now have a simple HTTP server running on port <strong>8000</strong>. Python will share every file in that folder over your local network.</p><h3>Step 1: Find Your IP Address</h3><p>On the sending computer, grab your local IP from the terminal:</p><ul><li><strong>Linux/macOS</strong>:</li></ul><pre>hostname -I</pre><ul><li><strong>Windows</strong>:</li></ul><pre>ipconfig</pre><p>You will get a series of number like 192.168.1.25.</p><h3>Step 2: Download from Another Computer</h3><p>On the receiving computer, open a web browser and visit:</p><pre>http://192.168.1.25:8000</pre><p>You’ll see a file listing. Click the file, and it downloads.</p><h3>(Optional) Command-Line Download</h3><p>If you prefer terminal tools to download the file:</p><ul><li><strong>Linux/macOS</strong>:</li></ul><pre>wget http://192.168.1.25:8000/filename.ext</pre><ul><li>or</li></ul><pre>curl -O http://192.168.1.25:8000/filename.ext</pre><ul><li><strong>Windows PowerShell</strong>:</li></ul><pre>Invoke-WebRequest &quot;http://192.168.1.25:8000/filename.ext&quot; -OutFile &quot;C:\Users\You\Downloads\filename.ext&quot;</pre><h3>Why This Trick Is Awesome</h3><ul><li><strong>No setup needed</strong> — Python comes preinstalled on most systems.</li><li><strong>Cross-platform</strong> — Works the same on Linux, macOS, and Windows.</li><li><strong>Scales from small to huge</strong> — Send a 1KB text file or a 10GB ISO, no problem.</li><li><strong>Temporary by design</strong> — Shut it down with Ctrl+C when you’re done.</li></ul><h3>When to Use It</h3><ul><li>Sharing files quickly with a teammate on the same Wi-Fi.</li><li>Moving files between your laptop and a desktop at home.</li><li>Sending files to a Raspberry Pi or headless server without setting up SSH.</li></ul><h3>A Few Notes of Caution</h3><ul><li><strong>No password protection</strong>: Anyone on your network who knows the IP and port can see the shared folder.</li><li><strong>Local only</strong>: This works inside your LAN/Wi-Fi network. Don’t expose it to the internet.</li><li><strong>Temporary use</strong>: Use it as a quick-and-dirty transfer tool, not a permanent solution.</li></ul><h3>Final Thoughts</h3><p>Sometimes the simplest tools are the most powerful. With just one line of Python, you can skip the hassle of configuring network shares, USB drives, or SSH.</p><p>Next time you need to move files across your local network, just type:</p><pre>python3 -m http.server</pre><p>…and you’re done.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=889d224ebe5e" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Learning Operating Systems by Building a Web Server in Java]]></title>
            <link>https://medium.com/@rintoprie/learning-operating-systems-by-building-a-web-server-in-java-00bdc3887b0d?source=rss-832eb4c8bf58------2</link>
            <guid isPermaLink="false">https://medium.com/p/00bdc3887b0d</guid>
            <category><![CDATA[java]]></category>
            <category><![CDATA[web-server]]></category>
            <category><![CDATA[operating-systems]]></category>
            <category><![CDATA[object-oriented]]></category>
            <category><![CDATA[objectorientedprogramming]]></category>
            <dc:creator><![CDATA[Rinto Priambodo]]></dc:creator>
            <pubDate>Wed, 17 Sep 2025 04:32:01 GMT</pubDate>
            <atom:updated>2025-09-17T04:32:01.979Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*n9iwOU_IKdJpglIG" /><figcaption>Photo by <a href="https://unsplash.com/@karlp?utm_source=medium&amp;utm_medium=referral">Karl Pawlowicz</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p>You might have learned about operating systems (OS) through thick textbooks, lectures on CPU scheduling, and diagrams about memory management. But until you’ve built something that <em>talks</em> directly to the OS, the concepts may still feel a bit distant.</p><p>That’s why, in this tutorial, we’re going to bring OS concepts to life — by building a simple yet functional <strong>web server in Java</strong>.</p><p>This project isn’t just about serving HTML files. It’s a gateway into:</p><ul><li>Understanding <strong>how sockets and ports work</strong></li><li>Managing <strong>concurrent client connections using threads</strong></li><li>Interacting with the <strong>file system</strong></li><li>Using <strong>OOP design</strong> to structure a clean, extensible application</li></ul><p>Whether you’re a beginner in Java or someone brushing up on systems programming, this tutorial will help bridge the gap between software and systems.</p><h3>What You’ll Build</h3><p>We’re going to build a minimal HTTP server that:</p><ul><li>Listens on a port (default: 8080)</li><li>Accepts incoming HTTP requests</li><li>Parses basic GET requests</li><li>Serves static .html and .txt files from a /www folder</li><li>Handles multiple clients concurrently using Java threads</li></ul><p>It’ll be modular, clean, and a perfect hands-on way to apply both OOP and OS principles.</p><h3>Concepts You’ll Learn</h3><p>Throughout this project, you’ll encounter a variety of fundamental concepts that blend Java programming with core operating system principles. From the <strong>OS side</strong>, you’ll learn about <strong>sockets for communication</strong>, <strong>multithreading for concurrency</strong>, and <strong>how file I/O works</strong> under the hood. On the <strong>programming</strong> side, this tutorial reinforces <strong>Object-Oriented Programming (OOP)</strong> practices such as <strong>modularity</strong>, <strong>encapsulation</strong>, and <strong>separation of concerns</strong>. You’ll also explore <strong>basic networking</strong> concepts like the <strong>HTTP</strong> protocol and <strong>TCP/IP</strong>. Finally, from a <strong>software architecture</strong> perspective, you’ll get hands-on experience in designing clean, maintainable systems by <strong>separating logic into manageable components</strong>.</p><h3>Project Structure</h3><p>Here’s what our codebase will look like:</p><pre>SimpleJavaWebServer/<br>│<br>├── WebServer.java         # Entry point<br>├── ClientHandler.java     # Handles a single client request<br>├── RequestParser.java     # Parses the HTTP request<br>├── FileManager.java       # Loads files from disk<br>├── Logger.java            # (Optional) Logs incoming requests<br>└── www/                   # Folder containing static files<br>    ├── index.html<br>    └── hello.txt</pre><h3>Step 1: The Web Server Entry Point</h3><p>This class is the entry point of the application. It opens a server socket on port 8080 and enters an infinite loop to accept client connections. Each connection is passed to a new ClientHandler thread so the server can continue listening while serving multiple clients concurrently.</p><pre>// WebServer.java<br>import java.io.IOException;<br>import java.net.ServerSocket;<br>import java.net.Socket;<br><br>public class WebServer {<br>    public static final int PORT = 8080;<br><br>    public static void main(String[] args) {<br>        try (ServerSocket serverSocket = new ServerSocket(PORT)) {<br>            System.out.println(&quot;Server running on http://localhost:&quot; + PORT);<br>            while (true) {<br>                Socket clientSocket = serverSocket.accept();<br>                Thread clientThread = new Thread(new ClientHandler(clientSocket));<br>                clientThread.start();<br>            }<br>        } catch (IOException e) {<br>            e.printStackTrace();<br>        }<br>    }<br>}</pre><p>This is where OS meets Java. The ServerSocket opens a port and listens for incoming connections. Every new client spawns a new thread via ClientHandler.</p><h3>Step 2: Handling Clients with Threads</h3><p>This class represents a single client session. It implements Runnable, allowing it to be run in a new thread. When executed, it reads input from the client, parses the request to find the requested file, then delegates the task of file loading and response creation to the FileManager. Finally, it writes the response back to the client’s output stream.</p><pre>// ClientHandler.java<br>import java.io.*;<br>import java.net.Socket;<br><br>public class ClientHandler implements Runnable {<br>    private final Socket socket;<br>    public ClientHandler(Socket socket) {<br>        this.socket = socket;<br>    }<br>    @Override<br>    public void run() {<br>        try (<br>            InputStream input = socket.getInputStream();<br>            OutputStream output = socket.getOutputStream()<br>        ) {<br>            RequestParser parser = new RequestParser(input);<br>            String path = parser.getRequestedPath();<br>            FileManager fileManager = new FileManager(&quot;www&quot;);<br>            byte[] response = fileManager.getFileResponse(path);<br>            output.write(response);<br>        } catch (IOException e) {<br>            e.printStackTrace();<br>        }<br>    }<br>}</pre><h3>Step 3: Parsing the HTTP Request</h3><p>This class is responsible for reading and interpreting the HTTP request. It only supports simple GET requests. It reads the first line of the request, splits it to identify the path, and defaults to /index.html if the path is /. This helps route the request to the correct file.</p><pre>// RequestParser.java<br>import java.io.BufferedReader;<br>import java.io.InputStream;<br>import java.io.InputStreamReader;<br><br>public class RequestParser {<br>    private String path = &quot;/&quot;;<br>    public RequestParser(InputStream input) throws IOException {<br>        BufferedReader reader = new BufferedReader(new InputStreamReader(input));<br>        String line = reader.readLine();<br>        if (line != null &amp;&amp; line.startsWith(&quot;GET&quot;)) {<br>            String[] parts = line.split(&quot; &quot;);<br>            if (parts.length &gt;= 2) {<br>                path = parts[1];<br>            }<br>        }<br>    }<br>    public String getRequestedPath() {<br>        return path.equals(&quot;/&quot;) ? &quot;/index.html&quot; : path;<br>    }<br>}</pre><h3>Step 4: Serving Files from Disk</h3><p>The FileManager class takes care of locating and loading files from disk. It constructs an HTTP response by reading the file contents, identifying the MIME type, and adding HTTP headers. If the file doesn’t exist, it returns a 404 response. This abstraction keeps file handling logic separate from networking and request parsing.</p><pre>// FileManager.java<br>import java.io.File;<br>import java.io.IOException;<br>import java.nio.file.Files;<br><br>public class FileManager {<br>    private final String baseDir;<br>    public FileManager(String baseDir) {<br>        this.baseDir = baseDir;<br>    }<br>    public byte[] getFileResponse(String path) throws IOException {<br>        File file = new File(baseDir + path);<br>        if (!file.exists() || file.isDirectory()) {<br>            String notFound = &quot;HTTP/1.1 404 Not Found\r\n\r\nFile not found.&quot;;<br>            return notFound.getBytes();<br>        }<br>        String mimeType = Files.probeContentType(file.toPath());<br>        byte[] body = Files.readAllBytes(file.toPath());<br>        String header = &quot;HTTP/1.1 200 OK\r\n&quot;<br>                      + &quot;Content-Type: &quot; + mimeType + &quot;\r\n&quot;<br>                      + &quot;Content-Length: &quot; + body.length + &quot;\r\n&quot;<br>                      + &quot;\r\n&quot;;<br>        byte[] headerBytes = header.getBytes();<br>        byte[] response = new byte[headerBytes.length + body.length];<br>        System.arraycopy(headerBytes, 0, response, 0, headerBytes.length);<br>        System.arraycopy(body, 0, response, headerBytes.length, body.length);<br>        return response;<br>    }<br>}</pre><h3>Test It!</h3><ol><li>Create a folder called www in the project root</li><li>Add an index.html file with simple content</li><li>Run WebServer.java</li><li>Open your browser and visit <a href="http://localhost:8080/.">http://localhost:8080/</a></li></ol><p>You’ve just served a web page from your own Java server.</p><h3>OS Concepts in Action</h3><ul><li><strong>Sockets</strong>: Communication via TCP/IP</li><li><strong>Threads</strong>: Each client runs in its own thread</li><li><strong>File I/O</strong>: Reading and writing files using the Java file system</li><li><strong>Memory</strong>: Managing byte arrays for efficient data transfer</li></ul><h3>Bonus: Thread Pool</h3><p>For performance and resource efficiency, especially with many clients, it’s better to reuse a fixed set of threads using a thread pool. This avoids the overhead of creating and destroying threads repeatedly.</p><pre>// In WebServer.java<br>import java.util.concurrent.*;<br>...<br>ExecutorService pool = Executors.newFixedThreadPool(10);<br>while (true) {<br>    Socket clientSocket = serverSocket.accept();<br>    pool.execute(new ClientHandler(clientSocket));<br>}</pre><p>This avoids creating too many threads and is more efficient under load.</p><h3>📊 Optional Logger</h3><p>Logging helps track what’s happening in your server — who’s connecting, what they’re requesting, and when. This simple logger adds timestamps to your console output, which is helpful for debugging and monitoring.</p><pre>// Logger.java<br>import java.text.SimpleDateFormat;<br>import java.util.Date;<br><br>public class Logger {<br>    public static void log(String message) {<br>        String timestamp = new SimpleDateFormat(&quot;HH:mm:ss&quot;).format(new Date());<br>        System.out.println(&quot;[&quot; + timestamp + &quot;] &quot; + message);<br>    }<br>}</pre><h3>📍 Conclusion</h3><p>You’ve built a real web server and learned:</p><ul><li>Core OS features like threads, sockets, and file I/O</li><li>How Java supports systems-level programming</li><li>How to structure a maintainable OOP-based application</li></ul><p>This is a small step, but it opens the door to deeper systems programming.</p><h3>🔗 Resources</h3><ul><li>Java Sockets Tutorial: <a href="https://docs.oracle.com/javase/tutorial/networking/sockets/">https://docs.oracle.com/javase/tutorial/networking/sockets/</a></li><li>Operating Systems: Three Easy Pieces: <a href="https://pages.cs.wisc.edu/~remzi/OSTEP/">https://pages.cs.wisc.edu/~remzi/OSTEP/</a></li></ul><p>Happy coding!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=00bdc3887b0d" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Learn OOP with Java by Simulating a Chat App]]></title>
            <link>https://medium.com/@rintoprie/learn-oop-with-java-by-simulating-a-chat-app-6346d19991c0?source=rss-832eb4c8bf58------2</link>
            <guid isPermaLink="false">https://medium.com/p/6346d19991c0</guid>
            <category><![CDATA[java]]></category>
            <category><![CDATA[object-oriented]]></category>
            <category><![CDATA[chat-application]]></category>
            <dc:creator><![CDATA[Rinto Priambodo]]></dc:creator>
            <pubDate>Mon, 14 Jul 2025 03:45:32 GMT</pubDate>
            <atom:updated>2025-07-14T04:27:58.190Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*IY1yqrMiOAHNNWAl0FWvLw.jpeg" /><figcaption>Image from pexels.com</figcaption></figure><p>If you’re trying to learn object-oriented programming (OOP) in Java and want something more engaging than abstract examples, a console-based chat app is a perfect practice project. It mimics real-world logic, involves multiple objects, and stays entirely in memory — no GUI, no database.</p><p>In this article, we’ll build a simple chat simulator in Java using clean OOP principles like encapsulation, abstraction, inheritance, and interfaces. Each class is designed to teach a specific OOP concept in a practical context. The final version will even allow two users to chat with each other by taking turns in the console.</p><h3>Project Structure</h3><h4>1. User Class (Implements Participant Interface)</h4><pre>public class User implements Participant {<br>    private String name;<br><br>    public User(String name) {<br>        this.name = name;<br>    }<br>    public String getName() {<br>        return name;<br>    }<br>    public void setName(String name) {<br>        this.name = name;<br>    }<br>}</pre><p>The User class models someone participating in a chat. It implements the Participant interface, meaning it agrees to provide a name and methods to get or set it. By making name private and exposing it through getters and setters, we follow encapsulation—hiding internal state while allowing controlled access. The interface allows flexibility: you could later create different types of participants (e.g., bots or admins) without changing the system logic.</p><h4>2. Participant Interface</h4><pre>public interface Participant {<br>    String getName();<br>    void setName(String name);<br>}</pre><p>This interface defines the behavior expected of any chat participant. It’s an example of abstraction — it says what a class must do, but not how. This allows different participant types to be used interchangeably as long as they fulfill the contract, promoting code flexibility and modularity.</p><h4>3. Message Class (Extends AbstractMessage)</h4><pre>public class Message extends AbstractMessage {<br>    public Message(String sender, String content) {<br>        super(sender, content);<br>    }<br><br>    @Override<br>    public String format() {<br>        return getSender() + &quot;: &quot; + getContent();<br>    }<br>}</pre><p>The Message class provides a concrete implementation of an abstract message. It inherits from AbstractMessage, reusing core message data and structure, and customizes how a message should be displayed. This is an example of inheritance, where shared logic is moved to a base class to avoid repetition.</p><h4>4. AbstractMessage Class</h4><pre>public abstract class AbstractMessage {<br>    private String sender;<br>    private String content;<br><br>    public AbstractMessage(String sender, String content) {<br>        this.sender = sender;<br>        this.content = content;<br>    }<br>    public String getSender() {<br>        return sender;<br>    }<br>    public String getContent() {<br>        return content;<br>    }<br>    public abstract String format();<br>}</pre><p>AbstractMessage acts as a reusable base for all types of messages. It stores shared attributes and behavior, while deferring the exact formatting to subclasses. This enforces a structure (every message must define format()) while encouraging reuse. Abstract classes like this are key to achieving both structure and flexibility.</p><h4>5. ChatRoom Class</h4><pre>import java.util.*;<br><br>public class ChatRoom {<br>    private String name;<br>    private List&lt;AbstractMessage&gt; messages = new ArrayList&lt;&gt;();<br>    public ChatRoom(String name) {<br>        this.name = name;<br>    }<br>    public String getName() {<br>        return name;<br>    }<br>    public void setName(String name) {<br>        this.name = name;<br>    }<br>    public void addMessage(AbstractMessage message) {<br>        messages.add(message);<br>        System.out.println(message.format());<br>    }<br>    public void showHistory() {<br>        System.out.println(&quot;--- Chat History in &quot; + name + &quot; ---&quot;);<br>        for (AbstractMessage msg : messages) {<br>            System.out.println(msg.format());<br>        }<br>    }<br>}</pre><p>ChatRoom is responsible for holding messages and printing them when added. It stores messages as AbstractMessage, so it can accept any subclass of message, enabling polymorphism. Again, private fields with public accessors uphold encapsulation, making the code safer and easier to maintain.</p><h4>6. ChatApp Main Class</h4><pre>import java.util.*;<br><br>public class ChatApp {<br>    public static void main(String[] args) {<br>        Scanner scanner = new Scanner(System.in);<br>        Map&lt;String, ChatRoom&gt; rooms = new HashMap&lt;&gt;();<br>        System.out.print(&quot;Enter name for User 1: &quot;);<br>        Participant user1 = new User(scanner.nextLine());<br>        System.out.print(&quot;Enter name for User 2: &quot;);<br>        Participant user2 = new User(scanner.nextLine());<br>        System.out.print(&quot;Enter room name to join: &quot;);<br>        String roomName = scanner.nextLine();<br>        rooms.putIfAbsent(roomName, new ChatRoom(roomName));<br>        ChatRoom room = rooms.get(roomName);<br>        System.out.println(&quot;Both users joined room: &quot; + roomName);<br>        while (true) {<br>            System.out.println(&quot;\n--- &quot; + user1.getName() + &quot;&#39;s Turn ---&quot;);<br>            System.out.print(&quot;Message (or type &#39;exit&#39;): &quot;);<br>            String msg1 = scanner.nextLine();<br>            if (msg1.equalsIgnoreCase(&quot;exit&quot;)) break;<br>            room.addMessage(new Message(user1.getName(), msg1));<br>            System.out.println(&quot;\n--- &quot; + user2.getName() + &quot;&#39;s Turn ---&quot;);<br>            System.out.print(&quot;Message (or type &#39;exit&#39;): &quot;);<br>            String msg2 = scanner.nextLine();<br>            if (msg2.equalsIgnoreCase(&quot;exit&quot;)) break;<br>            room.addMessage(new Message(user2.getName(), msg2));<br>        }<br>        System.out.println(&quot;\nFinal chat history:&quot;);<br>        room.showHistory();<br>        scanner.close();<br>    }<br>}</pre><p>In the class of ChatApp, the console simulates two users taking turns to send messages in a shared room. It demonstrates user interaction without needing real-time networking or concurrency. Both users share the same room instance and messages are appended to and displayed from the room’s message list.</p><h3>Final Thoughts</h3><p>This simple console chat app shows how OOP can be applied in a meaningful, hands-on way. It uses encapsulation to protect data, interfaces and abstract classes to enforce structure and promote flexibility, and inheritance to reduce repetition. Even without a UI or a database, this design shows how real-world software is structured for clarity, maintainability, and growth.</p><p>As extensions, you could add private messaging, role-based access (e.g., admin/moderator), or even persistence using files or databases. But the foundation remains the same: good object-oriented design.</p><p>Try it, experiment with it, and make it your own!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=6346d19991c0" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Building a Simple Web Application with Java Servlet, JSP, and Hibernate in 2025]]></title>
            <link>https://medium.com/@rintoprie/building-a-simple-web-application-with-java-servlet-jsp-and-hibernate-in-2025-e93f17d0ddcc?source=rss-832eb4c8bf58------2</link>
            <guid isPermaLink="false">https://medium.com/p/e93f17d0ddcc</guid>
            <category><![CDATA[java]]></category>
            <category><![CDATA[jsp]]></category>
            <category><![CDATA[web-applications]]></category>
            <category><![CDATA[servlet]]></category>
            <category><![CDATA[hibernate]]></category>
            <dc:creator><![CDATA[Rinto Priambodo]]></dc:creator>
            <pubDate>Thu, 15 May 2025 10:30:04 GMT</pubDate>
            <atom:updated>2025-05-15T10:31:12.766Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*LwS-IPM89bemrU-lcRfg8g.jpeg" /><figcaption>Images from pexels.com</figcaption></figure><p>Java remains a powerful and relevant choice for building web applications, combining simplicity, scalability, and maintainability. Although frameworks like Servlet, JSP (JavaServer Pages), and Hibernate are well-established technologies, they continue to be useful and efficient for building robust web applications in 2025. This article walks you through creating a simple yet effective web application using Maven to manage dependencies and project structure.</p><h3>Step 1: Setting Up the Development Environment</h3><p>Begin by ensuring you have the following installed:</p><ul><li>Java Development Kit (JDK) 17</li><li>Apache Tomcat 10.1</li><li>IDE (Eclipse, IntelliJ IDEA)</li><li>MySQL 8.3</li><li>Maven 3.9</li></ul><h3>Step 2: Maven Project Structure</h3><p>Use Maven for efficient dependency management and clear project structure:</p><pre>SimpleWebApp<br>├── src<br>│   └── main<br>│       ├── java<br>│       │   └── com.example.simplewebapp<br>│       │       ├── UserServlet.java<br>│       │       ├── UserDAO.java<br>│       │       └── User.java<br>│       ├── resources<br>│       │   └── hibernate.cfg.xml<br>│       └── webapp<br>│           ├── WEB-INF<br>│           │   └── web.xml<br>│           ├── index.jsp<br>|           └── userAdded.jsp<br>└── pom.xml</pre><h3>Step 3: Configuring Dependencies with Maven</h3><p>Include dependencies in your pom.xml:</p><pre>&lt;dependencies&gt;<br>    &lt;dependency&gt;<br>        &lt;groupId&gt;org.hibernate.orm&lt;/groupId&gt;<br>        &lt;artifactId&gt;hibernate-core&lt;/artifactId&gt;<br>        &lt;version&gt;6.4.4.Final&lt;/version&gt;<br>    &lt;/dependency&gt;<br>    &lt;dependency&gt;<br>        &lt;groupId&gt;com.mysql&lt;/groupId&gt;<br>        &lt;artifactId&gt;mysql-connector-j&lt;/artifactId&gt;<br>        &lt;version&gt;8.3.0&lt;/version&gt;<br>    &lt;/dependency&gt;<br>    &lt;dependency&gt;<br>        &lt;groupId&gt;jakarta.servlet&lt;/groupId&gt;<br>        &lt;artifactId&gt;jakarta.servlet-api&lt;/artifactId&gt;<br>        &lt;version&gt;6.0.0&lt;/version&gt;<br>        &lt;scope&gt;provided&lt;/scope&gt;<br>    &lt;/dependency&gt;<br>    &lt;dependency&gt;<br>        &lt;groupId&gt;jakarta.servlet.jsp&lt;/groupId&gt;<br>        &lt;artifactId&gt;jakarta.servlet.jsp-api&lt;/artifactId&gt;<br>        &lt;version&gt;3.1.1&lt;/version&gt;<br>        &lt;scope&gt;provided&lt;/scope&gt;<br>    &lt;/dependency&gt;<br>&lt;/dependencies&gt;</pre><h3>Step 4: Database Setup</h3><p>Create the users table in your MySQL database using the following SQL query:</p><pre>CREATE TABLE users (<br>    id INT AUTO_INCREMENT PRIMARY KEY,<br>    username VARCHAR(255) NOT NULL,<br>    email VARCHAR(255) NOT NULL<br>);</pre><h3>Step 5: Hibernate Configuration</h3><p>Configure Hibernate to connect your database. Make sure you define the correct database name, username, password, and the same package name.</p><p><strong>hibernate.cfg.xml:</strong></p><pre>&lt;hibernate-configuration&gt;<br>    &lt;session-factory&gt;<br>        &lt;property name=&quot;hibernate.connection.driver_class&quot;&gt;com.mysql.cj.jdbc.Driver&lt;/property&gt;<br>        &lt;property name=&quot;hibernate.connection.url&quot;&gt;jdbc:mysql://localhost:3306/simplewebappdb&lt;/property&gt;<br>        &lt;property name=&quot;hibernate.connection.username&quot;&gt;root&lt;/property&gt;<br>        &lt;property name=&quot;hibernate.connection.password&quot;&gt;password&lt;/property&gt;<br>        &lt;property name=&quot;hibernate.dialect&quot;&gt;org.hibernate.dialect.MySQLDialect&lt;/property&gt;<br>        &lt;property name=&quot;hibernate.hbm2ddl.auto&quot;&gt;update&lt;/property&gt;<br>        &lt;property name=&quot;hibernate.show_sql&quot;&gt;true&lt;/property&gt;<br>        &lt;mapping class=&quot;com.example.simplewebapp.User&quot;/&gt;<br>    &lt;/session-factory&gt;<br>&lt;/hibernate-configuration&gt;</pre><h3>Step 6: Creating the Model and DAO</h3><p>Define an entity and data access object (DAO).</p><p><strong>User.java:</strong></p><pre>@Entity<br>@Table(name=&quot;users&quot;)<br>public class User {<br>    @Id<br>    @GeneratedValue(strategy = GenerationType.IDENTITY)<br>    private int id;<br>    private String username;<br>    private String email;<br><br>    // Getters and Setters<br>    void setUsername(String username) {<br>        this.username = username;<br>    }<br><br>    void setEmail(String email) {<br>        this.email = email;<br>    }<br>}</pre><p><strong>UserDAO.java:</strong></p><pre>public class UserDAO {<br>    public void saveUser(User user) {<br>        SessionFactory sessionFactory = new Configuration().configure().buildSessionFactory();<br>        Session session = sessionFactory.openSession();<br>        session.beginTransaction();<br>        session.save(user);<br>        session.getTransaction().commit();<br>        session.close();<br>    }<br>}</pre><h3>Step 7: Servlet for Handling Requests</h3><p>Implement a Servlet to manage user interactions.</p><p><strong>UserServlet.java:</strong></p><pre>@WebServlet(&quot;/addUser&quot;)<br>public class UserServlet extends HttpServlet {<br>    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {<br>        String username = request.getParameter(&quot;username&quot;);<br>        String email = request.getParameter(&quot;email&quot;);<br>        User user = new User();<br>        user.setUsername(username);<br>        user.setEmail(email);<br>        new UserDAO().saveUser(user);<br>        response.sendRedirect(&quot;userAdded.jsp&quot;);<br>    }<br>}</pre><h3>Step 8: JSP Views</h3><p>Create JSP pages for user interaction:</p><p><strong>index.jsp:</strong></p><pre>&lt;form action=&quot;addUser&quot; method=&quot;post&quot;&gt;<br>    Username: &lt;input type=&quot;text&quot; name=&quot;username&quot;&gt;&lt;br&gt;<br>    Email: &lt;input type=&quot;email&quot; name=&quot;email&quot;&gt;&lt;br&gt;<br>    &lt;input type=&quot;submit&quot; value=&quot;Submit&quot;&gt;<br>&lt;/form&gt;</pre><p><strong>userAdded.jsp:</strong></p><pre>&lt;p&gt;User added successfully!&lt;/p&gt;</pre><h3>Step 9: Deploy and Test</h3><p>Deploy your application on Tomcat, access http://localhost:8080/SimpleWebApp/, and test your form submissions.</p><h3>Conclusion</h3><p>In 2025, Java, Servlet, JSP, and Hibernate continue to provide a strong framework for developing maintainable, scalable web applications. Using Maven simplifies dependency management and ensures compatibility. Happy coding!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=e93f17d0ddcc" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[OOP for Beginners: A Game-Based Approach to Learning]]></title>
            <link>https://medium.com/@rintoprie/oop-for-beginners-a-game-based-approach-to-learning-01acf2643cd6?source=rss-832eb4c8bf58------2</link>
            <guid isPermaLink="false">https://medium.com/p/01acf2643cd6</guid>
            <category><![CDATA[object-oriented]]></category>
            <category><![CDATA[java]]></category>
            <category><![CDATA[game-development]]></category>
            <dc:creator><![CDATA[Rinto Priambodo]]></dc:creator>
            <pubDate>Tue, 18 Mar 2025 09:44:31 GMT</pubDate>
            <atom:updated>2025-05-05T09:48:46.667Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*I8_dJ_9LAhOmFnUe5nuRuQ.jpeg" /><figcaption>Image from pexels.com</figcaption></figure><p>Object-Oriented Programming (OOP) is a fundamental paradigm in software development, yet many beginners struggle to grasp its concepts. A game-based approach can make learning more engaging and intuitive by demonstrating OOP principles in a practical, interactive manner. In this article, we explore OOP concepts using Java and apply them to a simple game-based learning example.</p><h3>1. Understanding OOP Concepts Through Games</h3><p>OOP consists of four main principles: <strong>Encapsulation, Inheritance, Polymorphism, and Abstraction</strong>. Each of these principles plays a vital role in structuring and organizing code efficiently, making it easier to maintain and scale. Let’s explore how these concepts can be applied in a game-based context.</p><h4>Classes and Objects</h4><p>A <strong>class</strong> is a template for creating objects, defining their attributes and behaviors. An <strong>object</strong> is an instance of a class, meaning it has specific values for the attributes defined in the class. In a game, a Character class can serve as a template for different characters:</p><pre>class Character {<br>    String name;<br>    int health;<br>    <br>    Character(String name, int health) {<br>        this.name = name;<br>        this.health = health;<br>    }<br>    <br>    void display() {<br>        System.out.println(name + &quot; has &quot; + health + &quot; health.&quot;);<br>    }<br>}</pre><p>Each object has its own copy of attributes (name, health), and calling methods on the object allows interaction with its properties. For example:</p><pre>Character hero = new Character(&quot;Knight&quot;, 120);<br>Character villain = new Character(&quot;Goblin&quot;, 80);<br>hero.display();<br>villain.display();</pre><p>This demonstrates how objects represent unique entities within a game.</p><h4>Encapsulation</h4><p>Encapsulation is the concept of <strong>hiding data</strong> to prevent direct modification and ensuring controlled access through getter and setter methods. This protects an object’s integrity and prevents unintended modifications.</p><pre>class Player {<br>    private int score;<br>    <br>    public void setScore(int score) {<br>        if (score &gt;= 0) {<br>            this.score = score;<br>        }<br>    }<br>    <br>    public int getScore() {<br>        return score;<br>    }<br>}</pre><p>Here, score is private, meaning it can only be accessed or modified through methods like setScore() and getScore(), preventing invalid values.</p><h4>Inheritance</h4><p>Inheritance allows a subclass to acquire properties and behaviors from a parent class, promoting <strong>code reuse</strong> and reducing redundancy.</p><pre>class Enemy extends Character {<br>    int damage;<br>    <br>    Enemy(String name, int health, int damage) {<br>        super(name, health);<br>        this.damage = damage;<br>    }<br>    <br>    void attack(Character target) {<br>        System.out.println(name + &quot; attacks &quot; + target.name + &quot; with &quot; + damage + &quot; damage!&quot;);<br>        target.health -= damage;<br>    }<br>}</pre><p>The Enemy class <strong>inherits</strong> attributes (name, health) from Character, but extends functionality by adding an attack() method.</p><h4>Polymorphism</h4><p>Polymorphism allows objects of different classes to be treated as objects of a <strong>common superclass</strong>, providing flexibility in method implementation.</p><pre>class Mage extends Character {<br>    Mage(String name, int health) {<br>        super(name, health);<br>    }<br>    <br>    @Override<br>    void display() {<br>        System.out.println(name + &quot; is a magical character with &quot; + health + &quot; health.&quot;);<br>    }<br>}</pre><p>Even though Mage and Character both have a display() method, Mage <strong>overrides</strong> it to provide a customized message.</p><h4>Abstraction</h4><p>Abstraction focuses on <strong>hiding complex details</strong> and exposing only essential functionality. It is implemented using abstract classes and interfaces.</p><pre>abstract class GameCharacter {<br>    String name;<br>    int health;<br>    <br>    GameCharacter(String name, int health) {<br>        this.name = name;<br>        this.health = health;<br>    }<br>    <br>    abstract void attack(Character target);<br>}</pre><pre>class Warrior extends GameCharacter {<br>    Warrior(String name, int health) {<br>        super(name, health);<br>    }<br>    <br>    @Override<br>    void attack(Character target) {<br>        System.out.println(name + &quot; slashes at &quot; + target.name + &quot;!&quot;);<br>        target.health -= 10;<br>    }<br>}</pre><p>Here, GameCharacter provides a generic structure, requiring subclasses like Warrior to define specific behavior.</p><h3>2. Implementing a Playable Turn-Based Game</h3><p>To make our game interactive, we need a <strong>game loop</strong> where the player and the enemy take turns attacking each other until one is defeated.</p><pre>import java.util.Scanner;</pre><pre>public class BattleGame {<br>    public static void main(String[] args) {<br>        Scanner scanner = new Scanner(System.in);<br>        Character hero = new Character(&quot;Knight&quot;, 120);<br>        Enemy villain = new Enemy(&quot;Goblin&quot;, 80, 15);<br>        <br>        System.out.println(&quot;Battle Start!&quot;);<br>        hero.display();<br>        villain.display();<br>        <br>        while (hero.health &gt; 0 &amp;&amp; villain.health &gt; 0) {<br>            System.out.println(&quot;Choose an action: 1. Attack 2. Defend&quot;);<br>            int choice = scanner.nextInt();<br>            <br>            if (choice == 1) {<br>                System.out.println(hero.name + &quot; attacks!&quot;);<br>                villain.health -= 20;<br>                villain.display();<br>            }<br>            <br>            if (villain.health &gt; 0) {<br>                villain.attack(hero);<br>                hero.display();<br>            }<br>        }<br>        <br>        if (hero.health &gt; 0) {<br>            System.out.println(&quot;You won!&quot;);<br>        } else {<br>            System.out.println(&quot;Game Over. You lost.&quot;);<br>        }<br>        <br>        scanner.close();<br>    }<br>}</pre><p>This example now allows user input, decision-making, and an actual turn-based combat system.</p><h3>Conclusion</h3><p>Using a game-based approach, beginners can understand OOP concepts in a fun and interactive way. Java’s OOP capabilities make it an excellent language for building simple game prototypes to reinforce learning. The turn-based battle game we implemented provides an engaging way to practice OOP principles. Start experimenting with your own game-based OOP projects to deepen your understanding!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=01acf2643cd6" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>