Efekt niezagospodarowanych 30 minut w pracy. Log systemowy, oprócz normalnego wyświetlania wiadomości na dwunastej konsoli i zapisywania do pliku, pokazuje je także na stojącym obok monitora głównego wyświetlaczu od kasy sklepowej.
Tuesday, 25 July 2006
Saturday, 17 June 2006
Permanent redirect
Na pewno zdarzyło Ci się kiedyś trafić na stronę z informacją w rodzaju "serwis dostępny teraz pod nowym adresem". Nierzadko data w stopce wskazuje, że przeprowadzka odbyła się już parę lat temu - a użytkownicy (znaczy - Ty) nadal trafiają pod stary adres. Traci też właściciel strony - nie dość, że musi nadal utrzymywać starą domenę strony (aby przekazać użytkownikom informację), to jeszcze wyszukiwarki wyżej oceniają starszy adres, pod niego kierując użytkowników. Nie byłoby wygodniej, gdyby użytkownicy automatycznie trafiali na nową stronę, a wyszukiwarki nie pokazywały już odnośników do starej?
Z pomocą przychodzi kod HTTP Permanent Redirect (301). Najprostsze rozwiązanie, jeśli cały serwis zmienił adres, to wpis w pliku .htaccess:
Options +FollowSymlinks
RewriteBase /
RewriteEngine On
RewriteRule ^.* http://nowy.adres.com%1 [R=permanent,L]
Powyższa regułka włącza mod_rewrite (RewriteEngine On), łapie wszystkie odwołania do strony (^.* - wyrażenie regularne), a następnie przekierowuje je ([R=permanent])na nową domenę, zachowując treść dopasowaną do wyrażenia regularnego (%1). Opcja L oznacza, że serwer ma na tej regule poprzestać zakończyć przetwarzanie pliku .htaccess. mod_rewrite pozwala na złożone przekształcenia adresu, co można wykorzystać, gdy zmieniła się struktura strony. Niestety, nie na wszystkich serwerach jest dostępny. W samym pliku .htaccess przekierowanie można też uzyskać poprzez
Redirect permanent /tutaj/byl/jakis.plik http://nowy.adres.com/jest.tutaj
natomiast w pliku php działanie przekierowania 301 można generować następującym kodem:
<?php
$var_array = explode("/",$_SERVER['REQUEST_URI']);
//print_r($var_array);
$target=$var_array[1];
$url="http://nowy.adres.com/".$target;
header("HTTP/1.1 301 Moved Permanently");
header ( "Location: $url" );
?>
<p>Strona została przeniesiona. Nowy adres:</p>
<a href="<?php echo $url; ?>"><?php echo $url; ?></a>
przy czym samo parsowanie $target trzeba dopasować do swoich potrzeb. Trzeba też pamiętać, że aby header zostało przyjęte przez przeglądarkę, serwer nie może wcześniej wysłać żadnej treści - jeśli np. przed początkiem skryptu w pliku będzie pusta linia, albo chociaż spacja, to przekierowanie nie zadziała.
Przepisywanie adresów za pomocą skryptu php sprawdza się najlepiej, gdy zmieniła się struktura strony - najłatwiej za ich pomocą poprawić adresy wymagające uaktualnienia. A w jaki sposób przekonać Apacha do obsługiwania żądań przy użyciu skryptu? Jeśli interesuje nas przepisywanie adresów z jakiegoś podfolderu, to wystarczy wykorzystać fakt, że Apache, gdy nie znajdzie żądanego adresu, zaczyna przesuwać się w górę wirtualnego drzewa plików, aż trafi na plik potrafiący obsłużyć żądanie. Np. dla http://domena.info/koza/little/beast najpierw zostanie sprawdzone koza/little/beast, potem koza/little, a na końcu koza. Wystarczy poinformować serwer, że koza jest skryptem php poprzez dodanie w .htaccess:
<Files koza>
ForceType application/x-httpd-php
</Files
Globalną obsługę żądań przez skrypt można ustawić za pomocą mod_rewrite:
RewriteRule ^.* rewriter.php [L]
dopasowując według potrzeb wyrażenie regularne.
Jak wyszukiwarki reagują na Permanent Redirect? Z mojego doświadczenia wynika, że po 2 tygodniach nowy adres traktowany jest jako równorzędny staremu, a po 4 tygodniach przejmuje już w całości jego Page Rank (stary adres nie pojawia się więcej w wynikach). Jeśli nie chcesz frustrować użytkowników, pamiętaj też, by uaktualnić linki przychodzące z innych stron - dopóki przekierowanie będzie działać, użytkownicy trafią na stronę, ale jeśli nie planujesz płacić za starą domenę w nieskończoność, wypadało by zupełnie usunąć z sieci odwołania do niej. Linki wchodzące można sprawdzić w logach serwera / statystykach (referer) lub poprzez wyszukiwarki. Tutaj niestety google kłamie bezczelnie, podając tylko małą część znanych mu linków, na szczęście Yahoo (linkdomain:domainname.com albo linksite:domainname.com) oraz MSN (link:domainname.com) zwracają pełną listę (z dokładniejszymi wynikami w Yahoo). Zostaje już tylko skontaktować się z adminem każdej z linkujących stron...
Dlatego lubię plusa
emerge -e world && echo "emerge -e world finished" | mail 48663280xxx@text.plusgsm.pl || echo "emerge failed" | mail 48663280xxx@text.plusgsm.pl
Friday, 2 June 2006
Palcówka
Porządne palcowanie wcale nie jest proste. Każdy, nawet najbardziej początkujący, jakoś sobie z nim radzi, ale jednak co profesjonalista to profesjonalista. Wielokrotnie obiecywałem sobie sumienną naukę - czasem nawet zaczynałem jakieś ćwiczenia, ale wiadomo, jak to jest - zaczyna się zgodnie z rozkładem, a potem nas ponosi i zaczyna się działać jak popadnie, byleby szybko był efekt. Faceci generalnie radzą sobie lepiej, ale to po prostu kwestia częstszych prób. Podobno nauka poprawnego palcowania jest nawet w programie liceum - niestety, mało który nauczyciel przeprowadza ten punkt programu. Większość puszcza uczniów na żywioł i tyle - dadzą sobie radę sami. A takie palcowanie z przypadku, szczególnie, gdy skupia się uwagę na wskazówkach wizualnych, jest nie do porównania z profesjonalnym - jest dużo wolniejsze, a przy tym dużo bardziej męczące.
Biorąc to wszystko pod uwagę, wziąłem się w końcu do roboty i sporządziłem sobie widoczny poniżej przyrząd do nauki palcowania. Wystarczyło trochę farbek modelarskich do zaznaczenia prawidłowego ułożenia palców. Malowanie dało jeszcze dodatkowy bonus - zakryte zostały różnorakie elementy rozpraszające normalnie uwagę.
Saturday, 20 May 2006
Chwila refleksji
Nie, nie zmieniłem pomysłu na prowadzenie bloga, znów będzie o C#. Przestrzenie nazw System.Reflection i System.Reflection.Emit udostępniają zestaw bardzo interesujących klas, służących do dynamicznego analizowania i generowania kodu. Pozwala to np. stworzyć uniwersalne narzędzia, służące do serializowania dowolnych, nieznanych wcześniej typów, czy też pisanie własnych kompilatorów i analizatorów kodu.
Mechanizm refleksji, chociaż potężny, jest niestety dość powolny, w związku z tym wskazane jest uzupełnianie go dynamicznym generowaniem kodu. W ten sposób wystarczy raz tylko wykonać kosztowną analizę obiektu, a potem można korzystać z zebranych wcześniej informacji, wywołując wygenerowane za pierwszym razem metody i klasy. Oczywiście, wyprodukowany w ten sposób kod można zapisać na dysku w formie zwyczajnych assembly, do późniejszego wykonania.
Korzystanie z przestrzeni nazw System.Reflection.Emit wymaga niestety zapoznania się, chociaż pobieżnego, z CIL. Na szczęście SDK od Microsoftu zawiera dwa bardzo przydatne narzędzia. peverify.exe analizuje pliki assembly, zgłaszając wszelkie nieprawidłowości w ich strukturze, jak np. niewłaściwe nazwy obiektów, niezgodności typów, brak inicjalizacji instancji obiektów itp. Jest to istotne, ponieważ CLR bywa nadmiernie wyrozumiały dla licznych błędów, które można popełnić generując kod z pominięciem kompilatora. ildasm.exe oraz jego odpowiednik w mono, czyli monodis, pozwalają podejrzeć kod znajdujący się w plikach assembly, co z kolei umożliwia generowanie własnego kodu per analogiam. Wystarczy napisać kod w C#, skompilować, i sprawdzić, jak efekt wygląda w CIL.
Warto też pamiętać o rozszerzeniach mechanizmu refleksji, które powstała w trakcie rozwoju mono. Są to Cecil, pozwalający na inspekcję kodu bez ładowania go do środowiska CLR oraz CodeGeneration, który generuje CIL używając składni opartej na C#.
Na koniec parę ciekawostek odnośnie CLR. Na początek: interfejsy nie istnieją. CIL modeluje je za pomocą klas abstrakcyjnych. To pociąga za sobą (sic!) wielokrotne dziedziczenie. Dalej - nie istnieją też klasy. Są reprezentowane jako powiązane ze sobą struktury danych i funkcje. Każda metoda oczywiście potrzebuje wskaźnika this, który jest przekazywany do niej jako parametr numer 0 (kojarzy się z C++ ?). Pozwala to na takie operacje, jak wywołanie na obiekcie metod, których on nie posiada (poprzez wrzucenie go na stos parametrów metody innego obiektu - na szczęście, jeśli odpowiednie rzutowanie nie byłoby możliwe, wykonanie zakończy się wyjątkiem) lub uruchamianie metod instancji obiektu bez uprzedniego stworzenia go (wywołanie metody, kiedy stos nie zawiera odpowiedniego wskaźnika do obiektu - CLR zachowuje się wtedy, jakby obiekt istniał, ale wszelkie pola ustawione są na wartości domyślne dla swojego typu). Użyteczna dla wielu zastosowań może być możliwość dostępu do metod i pól prywatnych.
Friday, 12 May 2006
Zwalnianie zasobów (C#)
Jeśli dopiero co zaczynasz korzystać z platformy .Net, a wcześniej nie pisałeś w językach z automatycznym zarządzaniem pamięcią, to na pewno wcześniej czy później popełnisz ten sam błąd co ja. Wyobraź sobie, że pisana przez Ciebie klasa wykorzystuje jakieś zasoby systemowe niezarządzane przez Garbage Collector,. Jak je zwolnić? Naturalne wydaje się zrobienie tego w destruktorze, prawda? Przecież w C# ma on nawet tą samą znaną składnię, ~DummyClass(), co z tego, że nazywa się finalizer?
Haczyki są trzy. Pierwszy polega na tym, że finalizer zostanie wywołany w momencie sprzątania obiektu przez Garbage Collector, a to może stać się długo po usunięciu ostatniego do niego odwołania. Drugi - finalizer nie zostanie wywołany w ogóle, jeśli cała aplikacja jest właśnie wyłączana. Trzeci - obiekty posiadające finalizer śledzone odrębnie od nie posiadających go i usuwane z pamięci później. Całe to zachowanie wynika z faktu, że w środowisku z zarządzaną pamięcią klasyczna implementacja destruktora jest bardzo niewydajna, jeśli nie w ogóle niemożliwa.
Jak więc zwolnić takie zasoby? Należy zaimplementować interfejs IDisposable i pamiętać, by go używać, korzystając z implementujących go klas. Służy do tego specjalne słowo kluczowe using, gwarantujące, że na "opakowanym" w nie obiekcie zostanie wywołane Dispose(), niezależnie od tego,czy kod w klauzuli zakończy się normalnie czy też wyjątkiem. Używa się tego konstruktu tak:
using(DummyObject something = new DummyObject())
{
//jakiś kod, korzystający z obiektu something
}
Gdy korzystamy z obiektów implementujących IDisposable, a niemożliwe jest ujęcie ich w ramy using, należy je zwalniać w metodzie Dispose() korzystającego z nich obiektu.
I jeszcze przykładowe zastosowanie IDisposable, bo w nim też łatwo narobić błędów:
/// <summary></summary>
public class DisposableObject : IDisposable
{
#region implementacja IDisposable
/// <summary>
/// Finalizer, wywołuje Dispose,
/// <strong>nie nalezy na nim polegać!</strong>.
/// </summary>
~DisposableObject()
{
Dispose(false);
}
/// <summary>
/// Zwalnia używane zasoby. Wyrejestrowuje finalizer,
/// by niepotrzebnie nie opóźniać zwalniania pamięci.
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Zwalnia używane zasoby.
/// </summary>
/// <param name="disposing">czy mamy czas na dokładne sprzątanie?</param>
protected virtual void Dispose(bool disposing)
{
if (!disposed)
{
if (disposing)
{
//tutaj trafia to, co _powinniśmy_ posprzątać
}
//a tutaj to, co _musimy_ posprzątać
}
disposed = true;
}
/// <summary>Aby tylko raz zwalniac zasoby.
/// (Dispose może zostać wywołane wielokrotnie.)</summary>
protected bool disposed = false;
#endregion
}
Jeśli nasza klasa dziedziczy z innej, już implementującej IDisposable, to wystarczy ograniczyć się do pokrycia metody protected virtual void Dispose(bool disposing), pamiętając, by wywołać w niej base:Dispose(disposing).
Na koniec ważna uwaga odnośnie kooperacji C++ i .Net. W zarządzanym C++ za pomocą składni ~DummyObject() definiuje się metodę Dispose()! Analogicznie, delete someObject oznacza wywołanie na nim metody Dispose(). Finalizer można zaimplementować poprzez !DummyObject().
Thursday, 11 May 2006
Globalna obsługa wyjątków w .Net
Mały,ale użyteczny kawałek kodu:
AppDomain.CurrentDomain.UnhandledException +=
delegate(object o, UnhandledExceptionEventArgs args)
{Console.WriteLine(args.ExceptionObject.ToString());};
Do czego to służy? Otóż zdarzenie UnhandledException wywoływane jest przy każdym nieobsłużonym przez aplikację wyjątku. Można to wykorzystać by zapisać błąd w dzienniku systemowym czy automatycznie zgłosić go obsłudze klienta / developerom. Nie można w ten sposób usunąć wyjątku - zdarzenie jest wywoływane już po 'wysypaniu' się wątku, w którym wystąpił wyjątek. Jeśli wolisz gotowe rozwiązania, polecam Unhandled Exception Manager z codeproject.